Index: lib/StaticAnalyzer/Core/BugReporter.cpp =================================================================== --- lib/StaticAnalyzer/Core/BugReporter.cpp +++ lib/StaticAnalyzer/Core/BugReporter.cpp @@ -3547,6 +3547,16 @@ ExecutedLines->operator[](FID.getHashValue()).insert(Line); } +static void populateExecutedLinesWithStmt( + const Stmt *S, SourceManager &SM, + std::unique_ptr &ExecutedLines) { + SourceLocation Loc = S->getSourceRange().getBegin(); + SourceLocation ExpansionLoc = SM.getExpansionLoc(Loc); + FileID FID = SM.getFileID(ExpansionLoc); + unsigned LineNo = SM.getExpansionLineNumber(ExpansionLoc); + ExecutedLines->operator[](FID.getHashValue()).insert(LineNo); +} + /// \return all executed lines including function signatures on the path /// starting from \p N. static std::unique_ptr @@ -3565,15 +3575,24 @@ // Inlined function: show signature. const Decl* D = CE->getCalleeContext()->getDecl(); populateExecutedLinesWithFunctionSignature(D, SM, ExecutedLines); + populateExecutedLinesWithStmt(CE->getCallExpr(), SM, ExecutedLines); } else if (const Stmt *S = PathDiagnosticLocation::getStmt(N)) { + populateExecutedLinesWithStmt(S, SM, ExecutedLines); + + // Show extra context for some parent kinds. + const Stmt *P = N->getParentMap().getParent(S); + + // The path exploration can die before the node with the associated + // return statement is generated, but we do want to show the whole + // return. + if (auto *RS = dyn_cast_or_null(P)) { + populateExecutedLinesWithStmt(RS, SM, ExecutedLines); + P = N->getParentMap().getParent(RS); + } - // Otherwise: show lines associated with the processed statement. - SourceLocation Loc = S->getSourceRange().getBegin(); - SourceLocation ExpansionLoc = SM.getExpansionLoc(Loc); - FileID FID = SM.getFileID(ExpansionLoc); - unsigned LineNo = SM.getExpansionLineNumber(ExpansionLoc); - ExecutedLines->operator[](FID.getHashValue()).insert(LineNo); + if (P && (isa(P) || isa(P))) + populateExecutedLinesWithStmt(P, SM, ExecutedLines); } N = N->getFirstPred(); Index: test/Analysis/html_diagnostics/relevant_lines/goto.c =================================================================== --- /dev/null +++ test/Analysis/html_diagnostics/relevant_lines/goto.c @@ -0,0 +1,13 @@ +int goto_test(int input) { + int *p = 0; + if (input) + goto mylabel; + return 0; +mylabel: + return *p; +} + +// RUN: rm -rf %t.output +// RUN: %clang_analyze_cc1 -analyze -analyzer-checker=core -analyzer-output html -o %t.output %s +// RUN: cat %t.output/* | FileCheck %s --match-full-lines +// CHECK: var relevant_lines = {"1": {"1": 1, "2": 1, "3": 1, "4": 1, "6": 1, "7": 1}}; Index: test/Analysis/html_diagnostics/relevant_lines/switch.c =================================================================== --- /dev/null +++ test/Analysis/html_diagnostics/relevant_lines/switch.c @@ -0,0 +1,20 @@ +enum E { + A, B, C +}; + +int f(enum E input) { + int *x = 0; + switch (input) { + case A: + return 1; + case B: + return 0; + case C: + return *x; + } +} + +// RUN: rm -rf %t.output +// RUN: %clang_analyze_cc1 -analyze -analyzer-checker=core -analyzer-output html -o %t.output %s +// RUN: cat %t.output/* | FileCheck %s --match-full-lines +// CHECK: var relevant_lines = {"1": {"5": 1, "6": 1, "7": 1, "12": 1, "13": 1}}; Index: test/Analysis/html_diagnostics/relevant_lines/switch_default.c =================================================================== --- /dev/null +++ test/Analysis/html_diagnostics/relevant_lines/switch_default.c @@ -0,0 +1,20 @@ +enum E { + A, B, C +}; + +int f(enum E input) { + int *x = 0; + switch (input) { + case A: + return 1; + case B: + return 0; + default: + return *x; + } +} + +// RUN: rm -rf %t.output +// RUN: %clang_analyze_cc1 -analyze -analyzer-checker=core -analyzer-output html -o %t.output %s +// RUN: cat %t.output/* | FileCheck %s --match-full-lines +// CHECK: var relevant_lines = {"1": {"5": 1, "6": 1, "7": 1, "12": 1, "13": 1}};