Skip to content

Commit

Permalink
[C89/C2x] Diagnose calls to a function without a prototype but passes…
Browse files Browse the repository at this point in the history
… 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).
  • Loading branch information
AaronBallman committed Apr 15, 2022
1 parent 4db65e2 commit 33d3fc4
Show file tree
Hide file tree
Showing 6 changed files with 74 additions and 5 deletions.
8 changes: 6 additions & 2 deletions clang/docs/ReleaseNotes.rst
Expand Up @@ -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
Expand Down
4 changes: 4 additions & 0 deletions clang/include/clang/Basic/DiagnosticSemaKinds.td
Expand Up @@ -5554,6 +5554,10 @@ def warn_missing_sentinel : Warning <
InGroup<Sentinel>;
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<DeprecatedNonPrototype>;
def warn_missing_prototype : Warning<
"no previous prototype for function %0">,
InGroup<DiagGroup<"missing-prototypes">>, DefaultIgnore;
Expand Down
17 changes: 17 additions & 0 deletions clang/lib/Sema/SemaExpr.cpp
Expand Up @@ -7076,6 +7076,23 @@ ExprResult Sema::BuildResolvedCallExpr(Expr *Fn, NamedDecl *NDecl,
Proto = FDecl->getType()->getAs<FunctionProtoType>();
}

// 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];
Expand Down
2 changes: 1 addition & 1 deletion 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() {
Expand Down
2 changes: 1 addition & 1 deletion 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);

Expand Down
46 changes: 45 additions & 1 deletion clang/test/Sema/warn-deprecated-non-prototype.c
Expand Up @@ -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}} \
Expand Down Expand Up @@ -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'}}
}

0 comments on commit 33d3fc4

Please sign in to comment.