Index: cfe/trunk/include/clang/StaticAnalyzer/Core/AnalyzerOptions.h =================================================================== --- cfe/trunk/include/clang/StaticAnalyzer/Core/AnalyzerOptions.h +++ cfe/trunk/include/clang/StaticAnalyzer/Core/AnalyzerOptions.h @@ -307,6 +307,9 @@ /// \sa shouldDisplayNotesAsEvents Optional DisplayNotesAsEvents; + /// \sa shouldDisplayMacroExpansions + Optional DisplayMacroExpansions; + /// \sa shouldAggressivelySimplifyBinaryOperation Optional AggressiveBinaryOperationSimplification; @@ -693,6 +696,13 @@ /// to false when unset. bool shouldDisplayNotesAsEvents(); + /// Returns true if macros related to the bugpath should be expanded and + /// included in the plist output. + /// + /// This is controlled by the 'expand-macros' option, which defaults to false + /// when unset. + bool shouldDisplayMacroExpansions(); + /// Returns true if SValBuilder should rearrange comparisons and additive /// operations of symbolic expressions which consist of a sum of a symbol and /// a concrete integer into the format where symbols are on the left-hand Index: cfe/trunk/lib/StaticAnalyzer/Core/AnalyzerOptions.cpp =================================================================== --- cfe/trunk/lib/StaticAnalyzer/Core/AnalyzerOptions.cpp +++ cfe/trunk/lib/StaticAnalyzer/Core/AnalyzerOptions.cpp @@ -464,6 +464,13 @@ return DisplayNotesAsEvents.getValue(); } +bool AnalyzerOptions::shouldDisplayMacroExpansions() { + if (!DisplayMacroExpansions.hasValue()) + DisplayMacroExpansions = + getBooleanOption("expand-macros", /*Default=*/false); + return DisplayMacroExpansions.getValue(); +} + bool AnalyzerOptions::shouldAggressivelySimplifyBinaryOperation() { if (!AggressiveBinaryOperationSimplification.hasValue()) AggressiveBinaryOperationSimplification = Index: cfe/trunk/lib/StaticAnalyzer/Core/BugReporter.cpp =================================================================== --- cfe/trunk/lib/StaticAnalyzer/Core/BugReporter.cpp +++ cfe/trunk/lib/StaticAnalyzer/Core/BugReporter.cpp @@ -546,7 +546,8 @@ } } -static void CompactPathDiagnostic(PathPieces &path, const SourceManager& SM); +static void CompactMacroExpandedPieces(PathPieces &path, + const SourceManager& SM); std::shared_ptr generateDiagForSwitchOP( @@ -1972,8 +1973,6 @@ PathDiagnosticLocation::createBegin(D, SM)); } - if (!AddPathEdges && GenerateDiagnostics) - CompactPathDiagnostic(PD->getMutablePieces(), SM); // Finally, prune the diagnostic path of uninteresting stuff. if (!PD->path.empty()) { @@ -2007,6 +2006,10 @@ removeRedundantMsgs(PD->getMutablePieces()); removeEdgesToDefaultInitializers(PD->getMutablePieces()); } + + if (GenerateDiagnostics && Opts.shouldDisplayMacroExpansions()) + CompactMacroExpandedPieces(PD->getMutablePieces(), SM); + return PD; } @@ -2436,9 +2439,10 @@ return true; } -/// CompactPathDiagnostic - This function postprocesses a PathDiagnostic object -/// and collapses PathDiagosticPieces that are expanded by macros. -static void CompactPathDiagnostic(PathPieces &path, const SourceManager& SM) { +/// CompactMacroExpandedPieces - This function postprocesses a PathDiagnostic +/// object and collapses PathDiagosticPieces that are expanded by macros. +static void CompactMacroExpandedPieces(PathPieces &path, + const SourceManager& SM) { using MacroStackTy = std::vector< std::pair, SourceLocation>>; @@ -2454,7 +2458,7 @@ // Recursively compact calls. if (auto *call = dyn_cast(&*piece)) { - CompactPathDiagnostic(call->path, SM); + CompactMacroExpandedPieces(call->path, SM); } // Get the location of the PathDiagnosticPiece. Index: cfe/trunk/lib/StaticAnalyzer/Core/PlistDiagnostics.cpp =================================================================== --- cfe/trunk/lib/StaticAnalyzer/Core/PlistDiagnostics.cpp +++ cfe/trunk/lib/StaticAnalyzer/Core/PlistDiagnostics.cpp @@ -72,6 +72,7 @@ const FIDMap& FM; AnalyzerOptions &AnOpts; const Preprocessor &PP; + llvm::SmallVector MacroPieces; public: PlistPrinter(const FIDMap& FM, AnalyzerOptions &AnOpts, @@ -86,6 +87,14 @@ (void)AnOpts; } + /// Print the expansions of the collected macro pieces. + /// + /// Each time ReportDiag is called on a PathDiagnosticMacroPiece (or, if one + /// is found through a call piece, etc), it's subpieces are reported, and the + /// piece itself is collected. Call this function after the entire bugpath + /// was reported. + void ReportMacroExpansions(raw_ostream &o, unsigned indent); + private: void ReportPiece(raw_ostream &o, const PathDiagnosticPiece &P, unsigned indent, unsigned depth, bool includeControlFlow, @@ -104,7 +113,8 @@ isKeyEvent); break; case PathDiagnosticPiece::Macro: - ReportMacro(o, cast(P), indent, depth); + ReportMacroSubPieces(o, cast(P), indent, + depth); break; case PathDiagnosticPiece::Note: ReportNote(o, cast(P), indent); @@ -123,14 +133,25 @@ unsigned indent, unsigned depth, bool isKeyEvent = false); void ReportCall(raw_ostream &o, const PathDiagnosticCallPiece &P, unsigned indent, unsigned depth); - void ReportMacro(raw_ostream &o, const PathDiagnosticMacroPiece& P, - unsigned indent, unsigned depth); + void ReportMacroSubPieces(raw_ostream &o, const PathDiagnosticMacroPiece& P, + unsigned indent, unsigned depth); void ReportNote(raw_ostream &o, const PathDiagnosticNotePiece& P, unsigned indent); }; } // end of anonymous namespace +namespace { + +struct ExpansionInfo { + std::string MacroName; + std::string Expansion; + ExpansionInfo(std::string N, std::string E) + : MacroName(std::move(N)), Expansion(std::move(E)) {} +}; + +} // end of anonymous namespace + static void printBugPath(llvm::raw_ostream &o, const FIDMap& FM, AnalyzerOptions &AnOpts, const Preprocessor &PP, @@ -143,6 +164,10 @@ SmallVectorImpl &Fids, FIDMap &FM, llvm::raw_fd_ostream &o); + +static ExpansionInfo getExpandedMacro(SourceLocation MacroLoc, + const Preprocessor &PP); + //===----------------------------------------------------------------------===// // Methods of PlistPrinter. //===----------------------------------------------------------------------===// @@ -299,16 +324,52 @@ ReportPiece(o, *callExit, indent, depth, /*includeControlFlow*/ true); } -void PlistPrinter::ReportMacro(raw_ostream &o, - const PathDiagnosticMacroPiece& P, - unsigned indent, unsigned depth) { - - for (PathPieces::const_iterator I = P.subPieces.begin(), E=P.subPieces.end(); - I!=E; ++I) { +void PlistPrinter::ReportMacroSubPieces(raw_ostream &o, + const PathDiagnosticMacroPiece& P, + unsigned indent, unsigned depth) { + MacroPieces.push_back(&P); + + for (PathPieces::const_iterator I = P.subPieces.begin(), + E = P.subPieces.end(); + I != E; ++I) { ReportPiece(o, **I, indent, depth, /*includeControlFlow*/ false); } } +void PlistPrinter::ReportMacroExpansions(raw_ostream &o, unsigned indent) { + + for (const PathDiagnosticMacroPiece *P : MacroPieces) { + const SourceManager &SM = PP.getSourceManager(); + ExpansionInfo EI = getExpandedMacro(P->getLocation().asLocation(), PP); + + Indent(o, indent) << "\n"; + ++indent; + + // Output the location. + FullSourceLoc L = P->getLocation().asLocation(); + + Indent(o, indent) << "location\n"; + EmitLocation(o, SM, L, FM, indent); + + // Output the ranges (if any). + ArrayRef Ranges = P->getRanges(); + EmitRanges(o, Ranges, indent); + + // Output the macro name. + Indent(o, indent) << "name"; + EmitString(o, EI.MacroName) << '\n'; + + // Output what it expands into. + Indent(o, indent) << "expansion"; + EmitString(o, EI.Expansion) << '\n'; + + // Finish up. + --indent; + Indent(o, indent); + o << "\n"; + } +} + void PlistPrinter::ReportNote(raw_ostream &o, const PathDiagnosticNotePiece& P, unsigned indent) { @@ -339,6 +400,12 @@ // Static function definitions. //===----------------------------------------------------------------------===// +static ExpansionInfo getExpandedMacro(SourceLocation MacroLoc, + const Preprocessor &PP) { + // TODO: Implement macro expansion. + return { "", "" }; +} + /// Print coverage information to output stream {@code o}. /// May modify the used list of files {@code Fids} by inserting new ones. static void printCoverage(const PathDiagnostic *D, @@ -408,6 +475,14 @@ Printer.ReportDiag(o, **I); o << " \n"; + + if (!AnOpts.shouldDisplayMacroExpansions()) + return; + + o << " macro_expansions\n" + " \n"; + Printer.ReportMacroExpansions(o, /* indent */ 4); + o << " \n"; } //===----------------------------------------------------------------------===// Index: cfe/trunk/test/Analysis/Inputs/expected-plists/plist-macros-with-expansion.cpp.plist =================================================================== --- cfe/trunk/test/Analysis/Inputs/expected-plists/plist-macros-with-expansion.cpp.plist +++ cfe/trunk/test/Analysis/Inputs/expected-plists/plist-macros-with-expansion.cpp.plist @@ -0,0 +1,351 @@ + + + + + diagnostics + + + path + + + kindcontrol + edges + + + start + + + line25 + col3 + file0 + + + line25 + col5 + file0 + + + end + + + line26 + col3 + file0 + + + line26 + col21 + file0 + + + + + + + kindevent + location + + line26 + col3 + file0 + + ranges + + + + line26 + col3 + file0 + + + line26 + col21 + file0 + + + + depth0 + extended_message + Null pointer value stored to 'ptr' + message + Null pointer value stored to 'ptr' + + + kindcontrol + edges + + + start + + + line27 + col3 + file0 + + + line27 + col3 + file0 + + + end + + + line27 + col8 + file0 + + + line27 + col8 + file0 + + + + + + + kindevent + location + + line27 + col8 + file0 + + ranges + + + + line27 + col4 + file0 + + + line27 + col6 + file0 + + + + depth0 + extended_message + Dereference of null pointer (loaded from variable 'ptr') + message + Dereference of null pointer (loaded from variable 'ptr') + + + macro_expansions + + + location + + line26 + col3 + file0 + + name + expansion + + + descriptionDereference of null pointer (loaded from variable 'ptr') + categoryLogic error + typeDereference of null pointer + check_namecore.NullDereference + + issue_hash_content_of_line_in_contextf8fbc46cc5afbb056d92bd3d3d702781 + issue_context_kindfunction + issue_contextnonFunctionLikeMacroTest + issue_hash_function_offset3 + location + + line27 + col8 + file0 + + ExecutedLines + + 0 + + 24 + 25 + 26 + 27 + + + + + path + + + kindcontrol + edges + + + start + + + line38 + col3 + file0 + + + line38 + col5 + file0 + + + end + + + line39 + col3 + file0 + + + line39 + col39 + file0 + + + + + + + kindevent + location + + line39 + col3 + file0 + + ranges + + + + line39 + col3 + file0 + + + line39 + col39 + file0 + + + + depth0 + extended_message + Null pointer value stored to 'ptr' + message + Null pointer value stored to 'ptr' + + + kindcontrol + edges + + + start + + + line40 + col3 + file0 + + + line40 + col3 + file0 + + + end + + + line40 + col8 + file0 + + + line40 + col8 + file0 + + + + + + + kindevent + location + + line40 + col8 + file0 + + ranges + + + + line40 + col4 + file0 + + + line40 + col6 + file0 + + + + depth0 + extended_message + Dereference of null pointer (loaded from variable 'ptr') + message + Dereference of null pointer (loaded from variable 'ptr') + + + macro_expansions + + + location + + line39 + col3 + file0 + + name + expansion + + + descriptionDereference of null pointer (loaded from variable 'ptr') + categoryLogic error + typeDereference of null pointer + check_namecore.NullDereference + + issue_hash_content_of_line_in_contextd5eba61193b41c27fc7b2705cbd607ba + issue_context_kindfunction + issue_contextnonFunctionLikeNestedMacroTest + issue_hash_function_offset3 + location + + line40 + col8 + file0 + + ExecutedLines + + 0 + + 37 + 38 + 39 + 40 + + + + + files + + /home/eumakri/Documents/2codechecker_dev_env/llvm/tools/clang/test/Analysis/plist-macros-with-expansion.cpp + + + Index: cfe/trunk/test/Analysis/plist-macros-with-expansion.cpp =================================================================== --- cfe/trunk/test/Analysis/plist-macros-with-expansion.cpp +++ cfe/trunk/test/Analysis/plist-macros-with-expansion.cpp @@ -0,0 +1,44 @@ +// RUN: %clang_analyze_cc1 -analyzer-checker=core -verify %s +// +// RUN: %clang_analyze_cc1 -analyzer-checker=core %s \ +// RUN: -analyzer-output=plist -o %t.plist \ +// RUN: -analyzer-config expand-macros=true +// +// Check the actual plist output. +// RUN: cat %t.plist | %diff_plist \ +// RUN: %S/Inputs/expected-plists/plist-macros-with-expansion.cpp.plist +// +// Check the macro expansions from the plist output here, to make the test more +// understandable. +// RUN: FileCheck --input-file=%t.plist %s + +void print(const void*); + +//===----------------------------------------------------------------------===// +// Tests for non-function-like macro expansions. +//===----------------------------------------------------------------------===// + +#define SET_PTR_VAR_TO_NULL \ + ptr = 0 + +void nonFunctionLikeMacroTest() { + int *ptr; + SET_PTR_VAR_TO_NULL; + *ptr = 5; // expected-warning{{Dereference of null pointer}} +} + +// CHECK: name +// CHECK-NEXT: expansion + +#define NULL 0 +#define SET_PTR_VAR_TO_NULL_WITH_NESTED_MACRO \ + ptr = NULL + +void nonFunctionLikeNestedMacroTest() { + int *ptr; + SET_PTR_VAR_TO_NULL_WITH_NESTED_MACRO; + *ptr = 5; // expected-warning{{Dereference of null pointer}} +} + +// CHECK: name +// CHECK-NEXT: expansion