diff --git a/clang/include/clang/StaticAnalyzer/Core/AnalyzerOptions.def b/clang/include/clang/StaticAnalyzer/Core/AnalyzerOptions.def --- a/clang/include/clang/StaticAnalyzer/Core/AnalyzerOptions.def +++ b/clang/include/clang/StaticAnalyzer/Core/AnalyzerOptions.def @@ -296,6 +296,10 @@ "an already tracked variable.", false) +ANALYZER_OPTION(bool, ShouldTrackConditionsDebug, "track-conditions-debug", + "Whether to place an event at each tracked condition.", + false) + //===----------------------------------------------------------------------===// // Unsinged analyzer options. //===----------------------------------------------------------------------===// diff --git a/clang/lib/Frontend/CompilerInvocation.cpp b/clang/lib/Frontend/CompilerInvocation.cpp --- a/clang/lib/Frontend/CompilerInvocation.cpp +++ b/clang/lib/Frontend/CompilerInvocation.cpp @@ -467,6 +467,10 @@ if (!Diags) return; + if (AnOpts.ShouldTrackConditionsDebug && !AnOpts.ShouldTrackConditions) + Diags->Report(diag::err_analyzer_config_invalid_input) + << "track-conditions-debug" << "'track-conditions' to also be enabled"; + if (!AnOpts.CTUDir.empty() && !llvm::sys::fs::is_directory(AnOpts.CTUDir)) Diags->Report(diag::err_analyzer_config_invalid_input) << "ctu-dir" << "a filename"; diff --git a/clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp b/clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp --- a/clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp +++ b/clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp @@ -1667,6 +1667,26 @@ return nullptr; } +static std::shared_ptr +constructDebugPieceForTrackedCondition(const Expr *Cond, + const ExplodedNode *N, + BugReporterContext &BRC) { + + if (BRC.getAnalyzerOptions().AnalysisDiagOpt == PD_NONE || + !BRC.getAnalyzerOptions().ShouldTrackConditionsDebug) + return nullptr; + + std::string ConditionText = Lexer::getSourceText( + CharSourceRange::getTokenRange(Cond->getSourceRange()), + BRC.getSourceManager(), + BRC.getASTContext().getLangOpts()); + + return std::make_shared( + PathDiagnosticLocation::createBegin( + Cond, BRC.getSourceManager(), N->getLocationContext()), + (Twine() + "Tracking condition '" + ConditionText + "'").str()); +} + std::shared_ptr TrackControlDependencyCondBRVisitor::VisitNode(const ExplodedNode *N, BugReporterContext &BRC, @@ -1695,6 +1715,7 @@ if (BR.addTrackedCondition(N)) { bugreporter::trackExpressionValue( N, Condition, BR, /*EnableNullFPSuppression=*/false); + return constructDebugPieceForTrackedCondition(Condition, N, BRC); } } } 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 @@ -85,8 +85,9 @@ // CHECK-NEXT: suppress-inlined-defensive-checks = true // CHECK-NEXT: suppress-null-return-paths = true // CHECK-NEXT: track-conditions = false +// CHECK-NEXT: track-conditions-debug = false // CHECK-NEXT: unix.DynamicMemoryModeling:Optimistic = false // CHECK-NEXT: unroll-loops = false // CHECK-NEXT: widen-loops = false // CHECK-NEXT: [stats] -// CHECK-NEXT: num-entries = 86 +// CHECK-NEXT: num-entries = 87 diff --git a/clang/test/Analysis/track-control-dependency-conditions.cpp b/clang/test/Analysis/track-control-dependency-conditions.cpp --- a/clang/test/Analysis/track-control-dependency-conditions.cpp +++ b/clang/test/Analysis/track-control-dependency-conditions.cpp @@ -3,7 +3,23 @@ // RUN: -analyzer-config track-conditions=true \ // RUN: -analyzer-output=text \ // RUN: -analyzer-checker=core + +// RUN: not %clang_analyze_cc1 -verify %s \ +// RUN: -analyzer-checker=core \ +// RUN: -analyzer-config track-conditions-debug=true \ +// RUN: 2>&1 | FileCheck %s -check-prefix=CHECK-INVALID-DEBUG + +// CHECK-INVALID-DEBUG: (frontend): invalid input for analyzer-config option +// CHECK-INVALID-DEBUG-SAME: 'track-conditions-debug', that expects +// CHECK-INVALID-DEBUG-SAME: 'track-conditions' to also be enabled // +// RUN: %clang_analyze_cc1 %s \ +// RUN: -verify=expected,tracking,debug \ +// RUN: -analyzer-config track-conditions=true \ +// RUN: -analyzer-config track-conditions-debug=true \ +// RUN: -analyzer-output=text \ +// RUN: -analyzer-checker=core + // RUN: %clang_analyze_cc1 %s -verify \ // RUN: -analyzer-output=text \ // RUN: -analyzer-checker=core @@ -30,6 +46,8 @@ if (flag) // expected-note {{Assuming 'flag' is not equal to 0}} // expected-note@-1{{Taking true branch}} + // debug-note@-2{{Tracking condition 'flag'}} + *x = 5; // expected-warning{{Dereference of null pointer}} // expected-note@-1{{Dereference of null pointer}} } @@ -59,6 +77,8 @@ if (flag) // expected-note {{Assuming 'flag' is not equal to 0}} // expected-note@-1{{Taking true branch}} + // debug-note@-2{{Tracking condition 'flag'}} + *x = 5; // expected-warning{{Dereference of null pointer}} // expected-note@-1{{Dereference of null pointer}} } @@ -85,8 +105,11 @@ if (bar) // expected-note {{Assuming 'bar' is not equal to 0}} // expected-note@-1{{Taking true branch}} + // debug-note@-2{{Tracking condition 'bar'}} if (flag) // expected-note {{Assuming 'flag' is not equal to 0}} // expected-note@-1{{Taking true branch}} + // debug-note@-2{{Tracking condition 'flag'}} + *x = 5; // expected-warning{{Dereference of null pointer}} // expected-note@-1{{Dereference of null pointer}} } @@ -107,7 +130,7 @@ if (int flag = foo()) // tracking-note{{Calling 'foo'}} // tracking-note@-1{{Returning from 'foo'}} // tracking-note@-2{{'flag' initialized here}} - + // debug-note@-3{{Tracking condition 'flag'}} // expected-note@-4{{Assuming 'flag' is not equal to 0}} // expected-note@-5{{Taking true branch}} @@ -129,7 +152,7 @@ if (ConvertsToBool()) // tracking-note@-1 {{Calling 'ConvertsToBool::operator bool'}} // tracking-note@-2{{Returning from 'ConvertsToBool::operator bool'}} - + // debug-note@-3{{Tracking condition 'ConvertsToBool()'}} // expected-note@-4{{Assuming the condition is true}} // expected-note@-5{{Taking true branch}} *x = 5; // expected-warning{{Dereference of null pointer}} @@ -150,8 +173,9 @@ if (!flipCoin()) // tracking-note@-1{{Calling 'flipCoin'}} // tracking-note@-2{{Returning from 'flipCoin'}} - // expected-note@-3{{Assuming the condition is true}} - // expected-note@-4{{Taking true branch}} + // debug-note@-3{{Tracking condition '!flipCoin()'}} + // expected-note@-4{{Assuming the condition is true}} + // expected-note@-5{{Taking true branch}} *ptr = 5; // expected-warning{{Dereference of null pointer}} // expected-note@-1{{Dereference of null pointer}} } @@ -163,6 +187,7 @@ bool flipCoin() { if (coin()) // tracking-note{{Assuming the condition is false}} // tracking-note@-1{{Taking false branch}} + // debug-note@-2{{Tracking condition 'coin()'}} return true; return coin(); // tracking-note{{Returning value}} } @@ -174,8 +199,9 @@ if (!flipCoin()) // tracking-note@-1{{Calling 'flipCoin'}} // tracking-note@-2{{Returning from 'flipCoin'}} - // expected-note@-3{{Assuming the condition is true}} - // expected-note@-4{{Taking true branch}} + // debug-note@-3{{Tracking condition '!flipCoin()'}} + // expected-note@-4{{Assuming the condition is true}} + // expected-note@-5{{Taking true branch}} *ptr = 5; // expected-warning{{Dereference of null pointer}} // expected-note@-1{{Dereference of null pointer}} } @@ -189,6 +215,7 @@ int *x = 0; // expected-note{{'x' initialized to a null pointer value}} if (flag) // expected-note{{Assuming 'flag' is not equal to 0}} // expected-note@-1{{Taking true branch}} + // debug-note@-2{{Tracking condition 'flag'}} *x = 5; // expected-warning{{Dereference of null pointer}} // expected-note@-1{{Dereference of null pointer}} } @@ -205,6 +232,7 @@ int *x = 0; // expected-note{{'x' initialized to a null pointer value}} if (flag) // expected-note{{'flag' is 1}} // expected-note@-1{{Taking true branch}} + // debug-note@-2{{Tracking condition 'flag'}} *x = 5; // expected-warning{{Dereference of null pointer}} // expected-note@-1{{Dereference of null pointer}} } @@ -229,6 +257,7 @@ if (flag) // expected-note{{'flag' is 1}} // expected-note@-1{{Taking true branch}} + // debug-note@-2{{Tracking condition 'flag'}} *x = 5; // expected-warning{{Dereference of null pointer}} // expected-note@-1{{Dereference of null pointer}} } @@ -252,6 +281,7 @@ if (flag) // expected-note{{'flag' is not equal to 0}} // expected-note@-1{{Taking true branch}} + // debug-note@-2{{Tracking condition 'flag'}} *x = 5; // expected-warning{{Dereference of null pointer}} // expected-note@-1{{Dereference of null pointer}} } @@ -278,6 +308,7 @@ if (flag) // expected-note{{'flag' is not equal to 0}} // expected-note@-1{{Taking true branch}} + // debug-note@-2{{Tracking condition 'flag'}} *x = 5; // expected-warning{{Dereference of null pointer}} // expected-note@-1{{Dereference of null pointer}} }