Index: clang/include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h =================================================================== --- clang/include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h +++ clang/include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h @@ -80,7 +80,7 @@ class CallEventRef : public IntrusiveRefCntPtr { public: CallEventRef(const T *Call) : IntrusiveRefCntPtr(Call) {} - CallEventRef(const CallEventRef &Orig) : IntrusiveRefCntPtr(Orig) {} + CallEventRef(const CallEventRef &Orig) = default; CallEventRef cloneWithState(ProgramStateRef State) const { return this->get()->template cloneWithState(State); Index: clang/lib/StaticAnalyzer/Core/CallEvent.cpp =================================================================== --- clang/lib/StaticAnalyzer/Core/CallEvent.cpp +++ clang/lib/StaticAnalyzer/Core/CallEvent.cpp @@ -360,17 +360,15 @@ if (getKind() == CE_ObjCMessage) return false; - const IdentifierInfo *II = getCalleeIdentifier(); - if (!II) - return false; - const FunctionDecl *FD = dyn_cast_or_null(getDecl()); - if (!FD) + if (!getDecl()) return false; if (CD.Flags & CDF_MaybeBuiltin) { - return CheckerContext::isCLibraryFunction(FD, CD.getFunctionName()) && - (!CD.RequiredArgs || CD.RequiredArgs <= getNumArgs()) && - (!CD.RequiredParams || CD.RequiredParams <= parameters().size()); + if (const FunctionDecl *FD = dyn_cast(getDecl())) + return CheckerContext::isCLibraryFunction(FD, CD.getFunctionName()) && + (!CD.RequiredArgs || CD.RequiredArgs <= getNumArgs()) && + (!CD.RequiredParams || CD.RequiredParams <= parameters().size()); + return false; } if (!CD.IsLookupDone) { @@ -379,13 +377,22 @@ CD.getFunctionName()); } - if (II != CD.II) - return false; + if (const IdentifierInfo *II = getCalleeIdentifier()) { + if (II != CD.II) + return false; + } else { + const auto *ND = dyn_cast(getDecl()); + if (!ND) + return false; + + if (CD.getFunctionName() != ND->getNameAsString()) + return false; + } // If CallDescription provides prefix names, use them to improve matching // accuracy. - if (CD.QualifiedName.size() > 1 && FD) { - const DeclContext *Ctx = FD->getDeclContext(); + if (CD.QualifiedName.size() > 1) { + const DeclContext *Ctx = getDecl()->getDeclContext(); // See if we'll be able to match them all. size_t NumUnmatched = CD.QualifiedName.size() - 1; for (; Ctx && isa(Ctx); Ctx = Ctx->getParent()) { Index: clang/unittests/StaticAnalyzer/CallDescriptionTest.cpp =================================================================== --- clang/unittests/StaticAnalyzer/CallDescriptionTest.cpp +++ clang/unittests/StaticAnalyzer/CallDescriptionTest.cpp @@ -56,12 +56,18 @@ if (!D->hasBody()) return; - const CallExpr *CE = findNode(D, callExpr()); + const Expr *E = + findNode(D, expr(anyOf(callExpr(), cxxConstructExpr()))); const StackFrameContext *SFC = Eng.getAnalysisDeclContextManager().getStackFrame(D); ProgramStateRef State = Eng.getInitialState(SFC); - CallEventRef<> Call = - Eng.getStateManager().getCallEventManager().getCall(CE, State, SFC); + CallEventManager &CEMgr = Eng.getStateManager().getCallEventManager(); + + CallEventRef<> Call = isa(E) + ? (CallEventRef<>)CEMgr.getCall(E, State, SFC) + : (CallEventRef<>)CEMgr.getCXXConstructorCall( + cast(E), + /*Target=*/nullptr, State, SFC); const bool *LookupResult = RM.lookup(*Call); // Check that we've found the function in the map @@ -72,8 +78,7 @@ } public: - CallDescriptionConsumer(CompilerInstance &C, - ResultMap &RM) + CallDescriptionConsumer(CompilerInstance &C, ResultMap &RM) : ExprEngineConsumer(C), RM(RM) {} bool HandleTopLevelDecl(DeclGroupRef DG) override { @@ -97,29 +102,34 @@ } }; -TEST(CallEvent, CallDescription) { - // Test simple name matching. +TEST(CallDescriptionMap, SimpleName) { EXPECT_TRUE(tooling::runToolOnCode( std::unique_ptr(new CallDescriptionAction({ {{"bar"}, false}, // false: there's no call to 'bar' in this code. {{"foo"}, true}, // true: there's a call to 'foo' in this code. - })), "void foo(); void bar() { foo(); }")); + })), + "void foo(); void bar() { foo(); }")); +} - // Test arguments check. +TEST(CallDescriptionMap, HasArgument) { EXPECT_TRUE(tooling::runToolOnCode( std::unique_ptr(new CallDescriptionAction({ {{"foo", 1}, true}, {{"foo", 2}, false}, - })), "void foo(int); void foo(int, int); void bar() { foo(1); }")); + })), + "void foo(int); void foo(int, int); void bar() { foo(1); }")); +} - // Test lack of arguments check. +TEST(CallDescriptionMap, NoArgument) { EXPECT_TRUE(tooling::runToolOnCode( std::unique_ptr(new CallDescriptionAction({ {{"foo", None}, true}, {{"foo", 2}, false}, - })), "void foo(int); void foo(int, int); void bar() { foo(1); }")); + })), + "void foo(int); void foo(int, int); void bar() { foo(1); }")); +} - // Test qualified names. +TEST(CallDescriptionMap, QualifiedName) { EXPECT_TRUE(tooling::runToolOnCode( std::unique_ptr(new CallDescriptionAction({ {{{"std", "basic_string", "c_str"}}, true}, @@ -135,21 +145,23 @@ " basic_string s;" " s.c_str();" "}")); +} - // A negative test for qualified names. +TEST(CallDescriptionMap, NonQualifiedName) { EXPECT_TRUE(tooling::runToolOnCode( std::unique_ptr(new CallDescriptionAction({ {{{"foo", "bar"}}, false}, {{{"bar", "foo"}}, false}, {{"foo"}, true}, - })), "void foo(); struct bar { void foo(); }; void test() { foo(); }")); + })), + "void foo(); struct bar { void foo(); }; void test() { foo(); }")); +} +TEST(CallDescriptionMap, MaybeBuiltin) { // Test CDF_MaybeBuiltin - a flag that allows matching weird builtins. EXPECT_TRUE(tooling::runToolOnCode( - std::unique_ptr(new CallDescriptionAction({ - {{"memset", 3}, false}, - {{CDF_MaybeBuiltin, "memset", 3}, true} - })), + std::unique_ptr(new CallDescriptionAction( + {{{"memset", 3}, false}, {{CDF_MaybeBuiltin, "memset", 3}, true}})), "void foo() {" " int x;" " __builtin___memset_chk(&x, 0, sizeof(x)," @@ -157,6 +169,62 @@ "}")); } +TEST(CallDescriptionMap, CXXConstructExpr) { + EXPECT_TRUE(tooling::runToolOnCode( + std::unique_ptr(new CallDescriptionAction({ + {{{"std", "basic_string"}, 1}, true}, + {{{"std", "basic_string"}, 0}, false}, + })), + "namespace std { inline namespace __1 {" + " template class basic_string {" + " public:" + " basic_string();" + " basic_string(const T *s);" + " };" + "}}" + "void foo(const char *str) {" + " using namespace std;" + " basic_string s(str);" + "}")); +} + +TEST(CallDescriptionMap, CXXConstructExprFullyQualified) { + EXPECT_TRUE(tooling::runToolOnCode( + std::unique_ptr(new CallDescriptionAction({ + {{{"std", "basic_string", "basic_string"}}, true}, + })), + "namespace std { inline namespace __1 {" + " template class basic_string {" + " public:" + " basic_string();" + " basic_string(const T *s);" + " };" + "}}" + "void foo(const char *str) {" + " using namespace std;" + " basic_string s(str);" + "}")); +} + +TEST(CallDescriptionMap, CXXConstructExprTypedef) { + EXPECT_TRUE(tooling::runToolOnCode( + std::unique_ptr(new CallDescriptionAction({ + {{{"std", "string"}}, false}, + {{{"std", "basic_string"}}, true}, + })), + "namespace std { inline namespace __1 {" + " template class basic_string {" + " public:" + " basic_string();" + " };" + " typedef basic_string string;" + "}}" + "void foo() {" + " using namespace std;" + " std::string s;" + "}")); +} + } // namespace } // namespace ento } // namespace clang