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,27 @@ 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_or_null(getDecl()); + if (!ND) + return false; + + llvm::SmallString<128> Str; + llvm::raw_svector_ostream Out(Str); + + Out << *ND; + + if (CD.getFunctionName() != Out.str()) + 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 = nullptr; + if (isa(E)) + Call = CEMgr.getCall(E, State, SFC); + else if (const auto *CtorE = dyn_cast(E)) + Call = CEMgr.getCXXConstructorCall(CtorE, /*Target=*/nullptr, State, SFC); const bool *LookupResult = RM.lookup(*Call); // Check that we've found the function in the map @@ -97,29 +103,31 @@ } }; -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(); }")); +} - // 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); }")); +} - // 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); }")); +} - // Test qualified names. +TEST(CallDescriptionMap, QualifiedName) { EXPECT_TRUE(tooling::runToolOnCode( std::unique_ptr(new CallDescriptionAction({ {{{"std", "basic_string", "c_str"}}, true}, @@ -135,15 +143,18 @@ " 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(); }")); +} +TEST(CallDescriptionMap, MaybeBuiltin) { // Test CDF_MaybeBuiltin - a flag that allows matching weird builtins. EXPECT_TRUE(tooling::runToolOnCode( std::unique_ptr(new CallDescriptionAction({ @@ -157,6 +168,63 @@ "}")); } +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, 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;" + "}")); +} + +TEST(CallDescriptionMap, CXXOperatorCallExpr) { + EXPECT_TRUE(tooling::runToolOnCode( + std::unique_ptr(new CallDescriptionAction({ + {{"operator+"}, false}, + {{"operator>>"}, true}, + })), + "namespace std {" + " template" + " basic_istream &operator>>(basic_istream &st, CharT *ch);" + " typedef basic_istream istream;" + " extern istream cin;" + "}" + "void foo() {" + " using namespace std;" + " char buf[13];" + " std::cin >> buf;" + "}")); +} + } // namespace } // namespace ento } // namespace clang