diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -5525,6 +5525,9 @@ "this %select{function declaration is not|block declaration is not|" "old-style function definition is not preceded by}0 a prototype">, InGroup>, DefaultIgnore; +def warn_call_function_without_prototype : Warning< + "calling function %0 with arguments when function has no prototype">, InGroup< + DiagGroup<"strict-calls-without-prototype">>, DefaultIgnore; def warn_missing_variable_declarations : Warning< "no previous extern declaration for non-static variable %0">, InGroup>, DefaultIgnore; diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -6388,6 +6388,29 @@ Call = ActOnOpenMPCall(Call, Scope, LParenLoc, ArgExprs, RParenLoc, ExecConfig); + // Diagnose calls that pass arguments to functions without a prototype + if (!LangOpts.CPlusPlus) { + if (auto *DRE = dyn_cast(Fn)) { + if (auto *FD = dyn_cast(DRE->getDecl())) { + if (auto *CE = dyn_cast(Call.get())) { + // Skip emitting warning for calls to implicit functions for the + // following reasons: + // * Calls to generated implicit functions are handled by + // `-Wimplicit-function-declaration`. + // * We want to skip implicit builtin functions. Some do not have + // a meaningful type signature (treated as not having a prototype). + if (CE->getNumArgs() > 0 && !FD->hasPrototype() && + !FD->isImplicit()) { + Diag(CE->getExprLoc(), diag::warn_call_function_without_prototype) + << FD << CE->getSourceRange(); + Diag(FD->getLocation(), diag::note_previous_decl) + << FD << FD->getSourceRange(); + } + } + } + } + } + return Call; } diff --git a/clang/test/Sema/warn-calls-without-prototype.c b/clang/test/Sema/warn-calls-without-prototype.c new file mode 100644 --- /dev/null +++ b/clang/test/Sema/warn-calls-without-prototype.c @@ -0,0 +1,151 @@ +// RUN: %clang_cc1 -triple i386-pc-unknown -fsyntax-only -Wstrict-calls-without-prototype -std=c99 -verify %s + +//------------------------------------------------------------------------------ +// Expected -Wstrict-calls-without-prototype warnings +//------------------------------------------------------------------------------ + +unsigned not_a_prototype(); + +unsigned call_to_function_without_prototype(void) { + // expected-warning@+2{{calling function 'not_a_prototype' with arguments when function has no prototype}} + // expected-note@-4{{'not_a_prototype' declared here}} + return not_a_prototype(1); +} + +// K&R style function declaration has no proto-type +unsigned not_a_prototype2(a, b) +int a; +int b; +{ + return a + b; +} + +unsigned call_to_function_without_prototype2(void) { + // expected-warning@+2{{calling function 'not_a_prototype2' with arguments when function has no prototype}} + // expected-note@-9{{'not_a_prototype2' declared here}} + return not_a_prototype2(1, 2); +} + +// K&R style function seems to "hide" the prototype because the merged +// FunctionDecl seems to not have a prototype. +// FIXME(dliew): Is this a bug in `Sema::MergeFunctionDecl`? +unsigned not_a_prototype3(int a, int b, int c); // Start with a prototype +unsigned not_a_prototype3(a, b, c ) + int a; + int b; + int c; +{ + return a + b +c; +} + +unsigned call_to_function_without_prototype3(void) { + // FIXME(dliew): Should we actually warn about this? There is a prototype it + // just gets hidden when the FunctionDecls get merged. + // expected-warning@+2{{calling function 'not_a_prototype3' with arguments when function has no prototype}} + // expected-note@-12{{'not_a_prototype3' declared here}} + return not_a_prototype3(1, 2, 3); +} + +// Merging two FunctionDecls without prototypes should result in a FunctionDecl without a prototype. +unsigned not_a_prototype4(); // Start with something that has no prototype. +unsigned not_a_prototype4(a, b, c) + int a; + int b; + int c; +{ + return a + b +c; +} + +unsigned call_to_function_without_prototype4(void) { + // expected-warning@+2{{calling function 'not_a_prototype4' with arguments when function has no prototype}} + // expected-note@-10{{'not_a_prototype4' declared here}} + return not_a_prototype4(1, 2, 3); +} + +// Definition does not have a prototype +unsigned not_a_prototype5() { + return 0; +} + +unsigned call_to_function_without_prototype5(void) { + // expected-warning@+3{{too many arguments in call to 'not_a_prototype5'}} + // expected-warning@+2{{calling function 'not_a_prototype5' with arguments when function has no prototype}} + // expected-note@-7{{'not_a_prototype5' declared here}} + return not_a_prototype5(1, 2, 3); +} + +//------------------------------------------------------------------------------ +// No expected warnings from -Wstrict-calls-without-prototype +//------------------------------------------------------------------------------ + +unsigned call_to_function_without_declartion(void) { + // `-Wstrict-calls-without-prototype` doesn't emit a warning here because + // `-Wimplicit-function-declaration` cover this. + // expected-warning@+1{{implicit declaration of function 'function_without_declaration' is invalid in C99}} + return function_without_declaration(5); +} + +unsigned call_builtins(void) { + // Builtin with a signature + int *alloc = (int *)__builtin_alloca(sizeof(int)); // no warning + *alloc = 5; + + // Builtin without a meaningful signature + int gt = __builtin_isgreater(0.0, 0.1); // no warning + + return *alloc + gt; +} + +unsigned provides_prototype(int *a); +unsigned provides_prototype_from_definition(int *a) { + return a[1]; +} +unsigned provides_prototype2(void) { + return 0; +} +unsigned call_prototypes(void) { + int a[] = {0, 1, 2}; + int result = provides_prototype_from_definition(a); // no warning + int result2 = provides_prototype(a); // no warning + // Deliberately pass arguments. Even in this case + //`-Wstrict-calls-without-prototype` should not fire. + // expected-error@+2{{too many arguments to function call, expected 0, have 1}} + // expected-note@-10{{'provides_prototype2' declared here}} + int result3 = provides_prototype2(5); + return result + result2 + result3; +} + +unsigned will_be_refined(); +unsigned will_be_refined(void *); // refine the decl to be a prototype +unsigned call_to_refined_function(void) { + int *a; + return will_be_refined(a); // no warning +} + +unsigned will_be_refined2(void *); +unsigned will_be_refined2(); +unsigned call_to_refined_function2(void) { + int *a; + return will_be_refined2(a); // no warning +} + +unsigned provides_prototype_variadic(int, ...); + +unsigned call_to_prototype_variadic(void) { + return provides_prototype_variadic(5, "test"); // no warning +} + +// Declaring a function prototype after a K&R style function causes the merged +// decl to be marked as having a prototype. +unsigned not_a_prototype6(a, b, c ) + int a; + int b; + int c; +{ + return a + b +c; +} +unsigned not_a_prototype6(int a, int b, int c); // Declare prototype after decl + +unsigned call_to_function_without_prototype6(void) { + return not_a_prototype6(1, 2, 3); // no warning +} \ No newline at end of file