diff --git a/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td b/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td --- a/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td +++ b/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td @@ -1372,6 +1372,12 @@ "false", Released, Hide>, + CmdLineOption, CmdLineOption, check::PreCall, check::PostCall, check::EndFunction, check::EndAnalysis, check::NewAllocator, check::Bind, check::PointerEscape, check::RegionChanges, - check::LiveSymbols> { + check::LiveSymbols, eval::Call> { bool isCallbackEnabled(const AnalyzerOptions &Opts, StringRef CallbackName) const { @@ -122,6 +122,19 @@ llvm::errs() << "PostStmt\n"; } + bool evalCall(const CallEvent &Call, CheckerContext &C) const { + if (isCallbackEnabled(C, "EvalCall")) { + llvm::errs() << "EvalCall"; + if (const NamedDecl *ND = dyn_cast_or_null(Call.getDecl())) + llvm::errs() << " (" << ND->getQualifiedNameAsString() << ')'; + llvm::errs() << " {argno: " << Call.getNumArgs() << '}'; + llvm::errs() << " [" << Call.getKindAsString() << ']'; + llvm::errs() << '\n'; + return true; + } + return false; + } + void checkPreCall(const CallEvent &Call, CheckerContext &C) const { if (isCallbackEnabled(C, "PreCall")) { llvm::errs() << "PreCall"; diff --git a/clang/lib/StaticAnalyzer/Core/CallEvent.cpp b/clang/lib/StaticAnalyzer/Core/CallEvent.cpp --- a/clang/lib/StaticAnalyzer/Core/CallEvent.cpp +++ b/clang/lib/StaticAnalyzer/Core/CallEvent.cpp @@ -523,7 +523,7 @@ if (!CC) return None; - ExprEngine::EvalCallOptions CallOpts; + EvalCallOptions CallOpts; ExprEngine &Engine = getState()->getStateManager().getOwningEngine(); SVal RetVal = Engine.computeObjectUnderConstruction(getOriginExpr(), getState(), diff --git a/clang/lib/StaticAnalyzer/Core/CheckerManager.cpp b/clang/lib/StaticAnalyzer/Core/CheckerManager.cpp --- a/clang/lib/StaticAnalyzer/Core/CheckerManager.cpp +++ b/clang/lib/StaticAnalyzer/Core/CheckerManager.cpp @@ -653,7 +653,8 @@ void CheckerManager::runCheckersForEvalCall(ExplodedNodeSet &Dst, const ExplodedNodeSet &Src, const CallEvent &Call, - ExprEngine &Eng) { + ExprEngine &Eng, + const EvalCallOptions &CallOpts) { for (auto *const Pred : Src) { bool anyEvaluated = false; @@ -665,7 +666,7 @@ // TODO: Support the situation when the call doesn't correspond // to any Expr. ProgramPoint L = ProgramPoint::getProgramPoint( - cast(Call.getOriginExpr()), + Call.getOriginExpr(), ProgramPoint::PostStmtKind, Pred->getLocationContext(), EvalCallChecker.Checker); @@ -690,7 +691,7 @@ // If none of the checkers evaluated the call, ask ExprEngine to handle it. if (!anyEvaluated) { NodeBuilder B(Pred, Dst, Eng.getBuilderContext()); - Eng.defaultEvalCall(B, Pred, Call); + Eng.defaultEvalCall(B, Pred, Call, CallOpts); } } } diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp --- a/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp +++ b/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp @@ -615,7 +615,8 @@ } else { for (ExplodedNodeSet::iterator I = DstPreCall.begin(), E = DstPreCall.end(); I != E; ++I) - defaultEvalCall(Bldr, *I, *Call, CallOpts); + getCheckerManager().runCheckersForEvalCall(DstEvaluated, *I, *Call, *this, + CallOpts); } // If the CFG was constructed without elements for temporary destructors diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp --- a/clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp +++ b/clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp @@ -584,7 +584,7 @@ // defaultEvalCall if all of them fail. ExplodedNodeSet dstCallEvaluated; getCheckerManager().runCheckersForEvalCall(dstCallEvaluated, dstPreVisit, - Call, *this); + Call, *this, EvalCallOptions()); // If there were other constructors called for object-type arguments // of this call, clean them up. @@ -717,7 +717,7 @@ ExprEngine::CallInlinePolicy ExprEngine::mayInlineCallKind(const CallEvent &Call, const ExplodedNode *Pred, AnalyzerOptions &Opts, - const ExprEngine::EvalCallOptions &CallOpts) { + const EvalCallOptions &CallOpts) { const LocationContext *CurLC = Pred->getLocationContext(); const StackFrameContext *CallerSFC = CurLC->getStackFrame(); switch (Call.getKind()) { diff --git a/clang/test/Analysis/analyzer-config.c b/clang/test/Analysis/analyzer-config.c --- a/clang/test/Analysis/analyzer-config.c +++ b/clang/test/Analysis/analyzer-config.c @@ -50,6 +50,7 @@ // CHECK-NEXT: debug.AnalysisOrder:Bind = false // CHECK-NEXT: debug.AnalysisOrder:EndAnalysis = false // CHECK-NEXT: debug.AnalysisOrder:EndFunction = false +// CHECK-NEXT: debug.AnalysisOrder:EvalCall = false // CHECK-NEXT: debug.AnalysisOrder:LiveSymbols = false // CHECK-NEXT: debug.AnalysisOrder:NewAllocator = false // CHECK-NEXT: debug.AnalysisOrder:PointerEscape = false diff --git a/clang/test/Analysis/cxxctr-evalcall-analysis-order.cpp b/clang/test/Analysis/cxxctr-evalcall-analysis-order.cpp new file mode 100644 --- /dev/null +++ b/clang/test/Analysis/cxxctr-evalcall-analysis-order.cpp @@ -0,0 +1,33 @@ +// RUN: %clang_analyze_cc1 %s \ +// RUN: -analyzer-checker=debug.AnalysisOrder \ +// RUN: -analyzer-config debug.AnalysisOrder:EvalCall=true \ +// RUN: -analyzer-config debug.AnalysisOrder:PreCall=true \ +// RUN: -analyzer-config debug.AnalysisOrder:PostCall=true \ +// RUN: 2>&1 | FileCheck %s + +// This test ensures that eval::Call event will be triggered for constructors. + +class C { +public: + C(){}; + C(int x){}; + C(int x, int y){}; +}; + +void foo() { + C C0; + C C1(42); + C *C2 = new C{2, 3}; +} + +// CHECK: PreCall (C::C) [CXXConstructorCall] +// CHECK-NEXT: EvalCall (C::C) {argno: 0} [CXXConstructorCall] +// CHECK-NEXT: PostCall (C::C) [CXXConstructorCall] +// CHECK-NEXT: PreCall (C::C) [CXXConstructorCall] +// CHECK-NEXT: EvalCall (C::C) {argno: 1} [CXXConstructorCall] +// CHECK-NEXT: PostCall (C::C) [CXXConstructorCall] +// CHECK-NEXT: PreCall (operator new) [CXXAllocatorCall] +// CHECK-NEXT: PostCall (operator new) [CXXAllocatorCall] +// CHECK-NEXT: PreCall (C::C) [CXXConstructorCall] +// CHECK-NEXT: EvalCall (C::C) {argno: 2} [CXXConstructorCall] +// CHECK-NEXT: PostCall (C::C) [CXXConstructorCall] diff --git a/clang/test/Analysis/new-ctor-conservative.cpp b/clang/test/Analysis/new-ctor-conservative.cpp --- a/clang/test/Analysis/new-ctor-conservative.cpp +++ b/clang/test/Analysis/new-ctor-conservative.cpp @@ -27,6 +27,7 @@ S *s = new S[10]; // FIXME: Should be true once we inline array constructors. clang_analyzer_eval(s[0].x == 1); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(s[1].x == 1); // expected-warning{{UNKNOWN}} } struct NullS {