Index: clang/include/clang/StaticAnalyzer/Core/AnalyzerOptions.def =================================================================== --- clang/include/clang/StaticAnalyzer/Core/AnalyzerOptions.def +++ 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. //===----------------------------------------------------------------------===// Index: clang/lib/Frontend/CompilerInvocation.cpp =================================================================== --- clang/lib/Frontend/CompilerInvocation.cpp +++ 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"; Index: clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp =================================================================== --- clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp +++ clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp @@ -1603,6 +1603,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, @@ -1623,11 +1643,15 @@ if (!OriginB || !NB) return nullptr; - if (ControlDeps.isControlDependent(OriginB, NB)) - if (const Expr *Condition = NB->getTerminatorConditionExpr()) - if (BR.addTrackedCondition(N)) + if (ControlDeps.isControlDependent(OriginB, NB)) { + if (const Expr *Condition = NB->getTerminatorConditionExpr()) { + if (BR.addTrackedCondition(N)) { bugreporter::trackExpressionValue( N, Condition, BR, /*EnableNullFPSuppression=*/false); + return constructDebugPieceForTrackedCondition(Condition, N, BRC); + } + } + } return nullptr; } Index: clang/test/Analysis/analyzer-config.c =================================================================== --- clang/test/Analysis/analyzer-config.c +++ 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 Index: clang/test/Analysis/track-control-dependency-conditions.cpp =================================================================== --- clang/test/Analysis/track-control-dependency-conditions.cpp +++ clang/test/Analysis/track-control-dependency-conditions.cpp @@ -2,7 +2,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 -verify \ +// RUN: -DTRACKING_CONDITIONS -DTRACKING_CONDITIONS_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 @@ -35,6 +51,9 @@ if (flag) // expected-note {{Assuming 'flag' is not equal to 0}} // expected-note@-1{{Taking true branch}} +#ifdef TRACKING_CONDITIONS_DEBUG + // expected-note@-3{{Tracking condition 'flag'}} +#endif // TRACKING_CONDITIONS_DEBUG *x = 5; // expected-warning{{Dereference of null pointer}} // expected-note@-1{{Dereference of null pointer}} } @@ -70,6 +89,9 @@ if (flag) // expected-note {{Assuming 'flag' is not equal to 0}} // expected-note@-1{{Taking true branch}} +#ifdef TRACKING_CONDITIONS_DEBUG + // expected-note@-3{{Tracking condition 'flag'}} +#endif // TRACKING_CONDITIONS_DEBUG *x = 5; // expected-warning{{Dereference of null pointer}} // expected-note@-1{{Dereference of null pointer}} } @@ -102,8 +124,14 @@ if (bar) // expected-note {{Assuming 'bar' is not equal to 0}} // expected-note@-1{{Taking true branch}} +#ifdef TRACKING_CONDITIONS_DEBUG + // expected-note@-3{{Tracking condition 'bar'}} +#endif // TRACKING_CONDITIONS_DEBUG if (flag) // expected-note {{Assuming 'flag' is not equal to 0}} // expected-note@-1{{Taking true branch}} +#ifdef TRACKING_CONDITIONS_DEBUG + // expected-note@-3{{Tracking condition 'flag'}} +#endif // TRACKING_CONDITIONS_DEBUG *x = 5; // expected-warning{{Dereference of null pointer}} // expected-note@-1{{Dereference of null pointer}} } @@ -129,9 +157,13 @@ // expected-note@-2{{Calling 'foo'}} // expected-note@-3{{Returning from 'foo'}} // expected-note@-4{{'flag' initialized here}} +#ifdef TRACKING_CONDITIONS_DEBUG + // expected-note@-6{{Tracking condition 'flag'}} +#endif // TRACKING_CONDITIONS_DEBUG #endif // TRACKING_CONDITIONS - // expected-note@-6{{Assuming 'flag' is not equal to 0}} - // expected-note@-7{{Taking true branch}} + // expected-note@-9{{Assuming 'flag' is not equal to 0}} + // expected-note@-10{{Taking true branch}} + *x = 5; // expected-warning{{Dereference of null pointer}} // expected-note@-1{{Dereference of null pointer}} } @@ -154,9 +186,12 @@ #ifdef TRACKING_CONDITIONS // expected-note@-2 {{Calling 'ConvertsToBool::operator bool'}} // expected-note@-3{{Returning from 'ConvertsToBool::operator bool'}} +#ifdef TRACKING_CONDITIONS_DEBUG + // expected-note@-5{{Tracking condition 'ConvertsToBool()'}} +#endif // TRACKING_CONDITIONS_DEBUG #endif // TRACKING_CONDITIONS - // expected-note@-5{{Assuming the condition is true}} - // expected-note@-6{{Taking true branch}} + // expected-note@-8{{Assuming the condition is true}} + // expected-note@-9{{Taking true branch}} *x = 5; // expected-warning{{Dereference of null pointer}} // expected-note@-1{{Dereference of null pointer}} } @@ -174,6 +209,9 @@ 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}} +#ifdef TRACKING_CONDITIONS_DEBUG + // expected-note@-3{{Tracking condition 'flag'}} +#endif // TRACKING_CONDITIONS_DEBUG *x = 5; // expected-warning{{Dereference of null pointer}} // expected-note@-1{{Dereference of null pointer}} } @@ -193,6 +231,9 @@ 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}} +#ifdef TRACKING_CONDITIONS_DEBUG + // expected-note@-3{{Tracking condition 'flag'}} +#endif // TRACKING_CONDITIONS_DEBUG *x = 5; // expected-warning{{Dereference of null pointer}} // expected-note@-1{{Dereference of null pointer}} } @@ -222,6 +263,9 @@ #endif // TRACKING_CONDITIONS if (flag) // expected-note{{'flag' is 1}} // expected-note@-1{{Taking true branch}} +#ifdef TRACKING_CONDITIONS_DEBUG + // expected-note@-3{{Tracking condition 'flag'}} +#endif // TRACKING_CONDITIONS_DEBUG *x = 5; // expected-warning{{Dereference of null pointer}} // expected-note@-1{{Dereference of null pointer}} } @@ -249,6 +293,9 @@ #endif // TRACKING_CONDITIONS if (flag) // expected-note{{'flag' is not equal to 0}} // expected-note@-1{{Taking true branch}} +#ifdef TRACKING_CONDITIONS_DEBUG + // expected-note@-3{{Tracking condition 'flag'}} +#endif // TRACKING_CONDITIONS_DEBUG *x = 5; // expected-warning{{Dereference of null pointer}} // expected-note@-1{{Dereference of null pointer}} } @@ -280,6 +327,9 @@ #endif // TRACKING_CONDITIONS if (flag) // expected-note{{'flag' is not equal to 0}} // expected-note@-1{{Taking true branch}} +#ifdef TRACKING_CONDITIONS_DEBUG + // expected-note@-3{{Tracking condition 'flag'}} +#endif // TRACKING_CONDITIONS_DEBUG *x = 5; // expected-warning{{Dereference of null pointer}} // expected-note@-1{{Dereference of null pointer}} }