From 33d3fc4466479285121cbb1a62db249454da0bda Mon Sep 17 00:00:00 2001 From: Aaron Ballman Date: Fri, 15 Apr 2022 09:07:28 -0400 Subject: [PATCH] [C89/C2x] Diagnose calls to a function without a prototype but passes arguments This catches places where a function without a prototype is accidentally used, potentially passing an incorrect number of arguments, and is a follow-up to the work done in https://reviews.llvm.org/D122895 and described in the RFC (https://discourse.llvm.org/t/rfc-enabling-wstrict-prototypes-by-default-in-c). The diagnostic is grouped under the new -Wdeprecated-non-prototypes warning group and is enabled by default. The diagnostic is disabled if the function being called was implicitly declared (the user already gets an on-by-default warning about the creation of the implicit function declaration, so no need to warn them twice on the same line). Additionally, the diagnostic is disabled if the declaration of the function without a prototype was in a location where the user explicitly disabled deprecation warnings for functions without prototypes (this allows the provider of the API a way to disable the diagnostic at call sites because the lack of prototype is intentional). --- clang/docs/ReleaseNotes.rst | 8 +++-- clang/include/clang/Basic/DiagnosticSemaKinds.td | 4 +++ clang/lib/Sema/SemaExpr.cpp | 17 +++++++++ clang/test/Analysis/nullability.c | 2 +- clang/test/Analysis/svalbuilder-float-cast.c | 2 +- clang/test/Sema/warn-deprecated-non-prototype.c | 46 +++++++++++++++++++++++- 6 files changed, 74 insertions(+), 5 deletions(-) diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index 91fc57d..f5d3793 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -142,8 +142,12 @@ Improvements to Clang's diagnostics diagnostic remains off by default but is now enabled via ``-pedantic`` due to it being a deprecation warning. ``-Wdeprecated-non-prototype`` will diagnose cases where the deprecated declarations or definitions of a function without - a prototype will change behavior in C2x. This diagnostic is grouped under the - ``-Wstrict-prototypes`` warning group, but is enabled by default. + a prototype will change behavior in C2x. Additionally, it will diagnose calls + which pass arguments to a function without a prototype. This warning is + enabled only when the ``-Wdeprecated-non-prototype`` option is enabled at the + function declaration site, which allows a developer to disable the diagnostic + for all callers at the point of declaration. This diagnostic is grouped under + the ``-Wstrict-prototypes`` warning group, but is enabled by default. - Clang now appropriately issues an error in C when a definition of a function without a prototype and with no arguments is an invalid redeclaration of a function with a prototype. e.g., ``void f(int); void f() {}`` is now properly diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 7ffd123..6547002b 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -5554,6 +5554,10 @@ def warn_missing_sentinel : Warning < InGroup; def note_sentinel_here : Note< "%select{function|method|block}0 has been explicitly marked sentinel here">; +def warn_strict_uses_without_prototype : Warning< + "passing arguments to %select{a function|%1}0 without a prototype is " + "deprecated in all versions of C and is not supported in C2x">, + InGroup; def warn_missing_prototype : Warning< "no previous prototype for function %0">, InGroup>, DefaultIgnore; diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp index db5ab00..37e54a8 100644 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -7076,6 +7076,23 @@ ExprResult Sema::BuildResolvedCallExpr(Expr *Fn, NamedDecl *NDecl, Proto = FDecl->getType()->getAs(); } + // If we still haven't found a prototype to use but there are arguments to + // the call, diagnose this as calling a function without a prototype. + // However, if we found a function declaration, check to see if + // -Wdeprecated-non-prototype was disabled where the function was declared. + // If so, we will silence the diagnostic here on the assumption that this + // interface is intentional and the user knows what they're doing. We will + // also silence the diagnostic if there is a function declaration but it + // was implicitly defined (the user already gets diagnostics about the + // creation of the implicit function declaration, so the additional warning + // is not helpful). + if (!Proto && !Args.empty() && + (!FDecl || (!FDecl->isImplicit() && + !Diags.isIgnored(diag::warn_strict_uses_without_prototype, + FDecl->getLocation())))) + Diag(LParenLoc, diag::warn_strict_uses_without_prototype) + << (FDecl != nullptr) << FDecl; + // Promote the arguments (C99 6.5.2.2p6). for (unsigned i = 0, e = Args.size(); i != e; i++) { Expr *Arg = Args[i]; diff --git a/clang/test/Analysis/nullability.c b/clang/test/Analysis/nullability.c index e0836c6..fbc03c8 100644 --- a/clang/test/Analysis/nullability.c +++ b/clang/test/Analysis/nullability.c @@ -1,4 +1,4 @@ -// RUN: %clang_analyze_cc1 -fblocks -analyzer-checker=core,nullability -verify %s +// RUN: %clang_analyze_cc1 -fblocks -analyzer-checker=core,nullability -Wno-deprecated-non-prototype -verify %s void it_takes_two(int a, int b); void function_pointer_arity_mismatch() { diff --git a/clang/test/Analysis/svalbuilder-float-cast.c b/clang/test/Analysis/svalbuilder-float-cast.c index 07b28f8..0ca9cec 100644 --- a/clang/test/Analysis/svalbuilder-float-cast.c +++ b/clang/test/Analysis/svalbuilder-float-cast.c @@ -1,4 +1,4 @@ -// RUN: %clang_analyze_cc1 -analyzer-checker debug.ExprInspection -verify %s +// RUN: %clang_analyze_cc1 -analyzer-checker debug.ExprInspection -Wno-deprecated-non-prototype -verify %s void clang_analyzer_denote(int, const char *); void clang_analyzer_express(int); diff --git a/clang/test/Sema/warn-deprecated-non-prototype.c b/clang/test/Sema/warn-deprecated-non-prototype.c index 33fe734..78d4b0d 100644 --- a/clang/test/Sema/warn-deprecated-non-prototype.c +++ b/clang/test/Sema/warn-deprecated-non-prototype.c @@ -36,7 +36,7 @@ void sheesh(a) int a; {} // both-warning {{a function declaration without a prot void another(); // strict-warning {{a function declaration without a prototype is deprecated in all versions of C}} int main(void) { - another(1, 2); // OK for now + another(1, 2); // both-warning {{passing arguments to 'another' without a prototype is deprecated in all versions of C and is not supported in C2x}} } void order1(); // expected-warning {{a function declaration without a prototype is deprecated in all versions of C and is not supported in C2x}} \ @@ -71,3 +71,47 @@ void test(fmt) // both-warning {{a function declaration without a prototy void blapp(int); // both-note {{previous declaration is here}} void blapp() { } // both-error {{conflicting types for 'blapp'}} \ // strict-warning {{a function declaration without a prototype is deprecated in all versions of C}} + +// Disable -Wdeprecated-non-prototype +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wdeprecated-non-prototype" +void depr_non_prot(); // strict-warning {{a function declaration without a prototype is deprecated in all versions of C}} +#pragma GCC diagnostic pop +// Reenable it. + +// Disable -Wstrict-prototypes +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wstrict-prototypes" +void strict_prot(); // OK +#pragma GCC diagnostic pop +// Reenable it. + +void calls(void) { + // Ensure that we diagnose calls to functions without a prototype, but only + // if they pass arguments. + never_defined(); // OK + never_defined(1); // both-warning {{passing arguments to 'never_defined' without a prototype is deprecated in all versions of C and is not supported in C2x}} + + // Ensure that calls to builtins without a traditional prototype are not + // diagnosed. + (void)__builtin_isless(1.0, 1.0); // OK + + // Calling a function whose prototype was provided by a function with an + // identifier list is still fine. + func(1, 2); // OK + + // Ensure that a call through a function pointer is still diagnosed properly. + fp f; + f(); // OK + f(1, 2); // both-warning {{passing arguments to a function without a prototype is deprecated in all versions of C and is not supported in C2x}} + + // Ensure that we don't diagnose when the diagnostic group is disabled. + depr_non_prot(1); // OK + strict_prot(1); // OK + + // Ensure we don't issue diagnostics if the function without a prototype was + // later given a prototype by a definintion. Also ensure we don't duplicate + // diagnostics if such a call is incorrect. + func(1, 2); // OK + func(1, 2, 3); // both-warning {{too many arguments in call to 'func'}} +} -- 2.7.4