Index: clang/include/clang/StaticAnalyzer/Checkers/Checkers.td =================================================================== --- clang/include/clang/StaticAnalyzer/Checkers/Checkers.td +++ clang/include/clang/StaticAnalyzer/Checkers/Checkers.td @@ -100,6 +100,10 @@ let ParentPackage = Core in { +def ArgumentSizeChecker : Checker<"ArgumentSize">, + HelpText<"Check that function pointers are called with the correct number of arguments">, + DescFile<"ArgumentSizeChecker.cpp">; + def DereferenceChecker : Checker<"NullDereference">, HelpText<"Check for dereferences of null pointers">, DescFile<"DereferenceChecker.cpp">; Index: clang/lib/StaticAnalyzer/Checkers/ArgumentSizeChecker.cpp =================================================================== --- /dev/null +++ clang/lib/StaticAnalyzer/Checkers/ArgumentSizeChecker.cpp @@ -0,0 +1,52 @@ +#include "ClangSACheckers.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" +#include "clang/StaticAnalyzer/Core/Checker.h" +#include "clang/StaticAnalyzer/Core/CheckerManager.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h" + +using namespace clang; +using namespace ento; + +class ArgumentSizeChecker : public Checker< check::PreCall > { + mutable std::unique_ptr BT; + +public: + // TODO: do not duplicate compiler warnings. + void checkPreCall(const CallEvent &Call, CheckerContext &Ctx) const { + if (Call.getNumArgs() > Call.parameters().size()) { + + // Skip functions without declarations. + if (!Call.getDecl()) + return; + + // FIXME: investigate is this a good idea. + if (!Call.getRuntimeDefinition().getDecl()) + return; + + // Skip variadic functions. + if (CallEvent::isVariadic(Call.getDecl())) + return; + + // Skip implicit declarations. + if (Call.getDecl()->isImplicit()) + return; + + if (!BT) + BT.reset(new BuiltinBug( + this, "Function called with too many arguments")); + + ExplodedNode *N = Ctx.generateErrorNode(); + + // TODO: more detailed error message. + auto R = llvm::make_unique(*BT, BT->getDescription(), N); + Ctx.emitReport(std::move(R)); + } + } +}; + +void ento::registerArgumentSizeChecker(CheckerManager &Mgr) { + Mgr.registerChecker(); +} Index: clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt =================================================================== --- clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt +++ clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt @@ -6,6 +6,7 @@ AllocationDiagnostics.cpp AnalysisOrderChecker.cpp AnalyzerStatsChecker.cpp + ArgumentSizeChecker.cpp ArrayBoundChecker.cpp ArrayBoundCheckerV2.cpp BasicObjCFoundationChecks.cpp Index: clang/test/Analysis/diagnostics/no-store-func-path-notes.cpp =================================================================== --- clang/test/Analysis/diagnostics/no-store-func-path-notes.cpp +++ clang/test/Analysis/diagnostics/no-store-func-path-notes.cpp @@ -151,12 +151,12 @@ } void rdar40335545() { - int local; // expected-note{{}} + int local; void (*takes_int_ptr_argument)(int *) = (void (*)(int*))has_no_argument_and_returns_null; - takes_int_ptr_argument(&local); // no-crash + takes_int_ptr_argument(&local); // expected-warning{{Function called with too many arguments}} + // expected-note@-1{{Function called with too many arguments}} - int useLocal = local; //expected-warning{{}} - //expected-note@-1{{}} + int useLocal = local; (void)useLocal; } Index: clang/test/Analysis/inline.c =================================================================== --- clang/test/Analysis/inline.c +++ clang/test/Analysis/inline.c @@ -30,6 +30,7 @@ void test2_f3() { test2_f1(test2_f2()); // expected-warning{{too many arguments in call to 'test2_f1'}} + // expected-warning@-1{{Function called with too many arguments}} } // Test that inlining works with recursive functions.