Index: clang/include/clang/Rewrite/Core/HTMLRewrite.h =================================================================== --- clang/include/clang/Rewrite/Core/HTMLRewrite.h +++ clang/include/clang/Rewrite/Core/HTMLRewrite.h @@ -15,6 +15,7 @@ #define LLVM_CLANG_REWRITE_CORE_HTMLREWRITE_H #include "clang/Basic/SourceLocation.h" +#include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h" #include namespace clang { @@ -23,6 +24,8 @@ class RewriteBuffer; class Preprocessor; +namespace ento { + namespace html { /// HighlightRange - Highlight a range in the source code with the specified @@ -65,6 +68,12 @@ void AddHeaderFooterInternalBuiltinCSS(Rewriter &R, FileID FID, StringRef title); + /// AddPopUpNotes - Relex the specified FileID and annotate the HTML with + /// pop-up notes (hidden by default, show on mouse-hover event). + /// It adds information to given variables similar to 'HighlightMacros()'. + void AddPopUpNotes(Rewriter &R, FileID FID, const Preprocessor &PP, + const PathPieces &Path); + /// SyntaxHighlight - Relex the specified FileID and annotate the HTML with /// information about keywords, comments, etc. void SyntaxHighlight(Rewriter &R, FileID FID, const Preprocessor &PP); @@ -76,6 +85,7 @@ void HighlightMacros(Rewriter &R, FileID FID, const Preprocessor &PP); } // end html namespace +} // end ento namespace } // end clang namespace #endif Index: clang/include/clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h =================================================================== --- clang/include/clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h +++ clang/include/clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h @@ -365,7 +365,7 @@ class PathDiagnosticPiece: public llvm::FoldingSetNode { public: - enum Kind { ControlFlow, Event, Macro, Call, Note }; + enum Kind { ControlFlow, Event, Macro, Call, Note, PopUp }; enum DisplayHint { Above, Below }; private: @@ -480,7 +480,7 @@ static bool classof(const PathDiagnosticPiece *P) { return P->getKind() == Event || P->getKind() == Macro || - P->getKind() == Note; + P->getKind() == Note || P->getKind() == PopUp; } }; @@ -744,7 +744,7 @@ class PathDiagnosticNotePiece: public PathDiagnosticSpotPiece { public: PathDiagnosticNotePiece(const PathDiagnosticLocation &Pos, StringRef S, - bool AddPosRange = true) + bool AddPosRange = true) : PathDiagnosticSpotPiece(Pos, S, Note, AddPosRange) {} ~PathDiagnosticNotePiece() override; @@ -757,6 +757,22 @@ void Profile(llvm::FoldingSetNodeID &ID) const override; }; +class PathDiagnosticPopUpPiece: public PathDiagnosticSpotPiece { +public: + PathDiagnosticPopUpPiece(const PathDiagnosticLocation &Pos, StringRef S, + bool AddPosRange = true) + : PathDiagnosticSpotPiece(Pos, S, PopUp, AddPosRange) {} + ~PathDiagnosticPopUpPiece() override; + + static bool classof(const PathDiagnosticPiece *P) { + return P->getKind() == PopUp; + } + + void dump() const override; + + void Profile(llvm::FoldingSetNodeID &ID) const override; +}; + /// File IDs mapped to sets of line numbers. using FilesToLineNumsMap = std::map>; Index: clang/lib/Frontend/Rewrite/HTMLPrint.cpp =================================================================== --- clang/lib/Frontend/Rewrite/HTMLPrint.cpp +++ clang/lib/Frontend/Rewrite/HTMLPrint.cpp @@ -21,7 +21,10 @@ #include "clang/Rewrite/Core/Rewriter.h" #include "clang/Rewrite/Frontend/ASTConsumers.h" #include "llvm/Support/raw_ostream.h" + using namespace clang; +using namespace ento; + //===----------------------------------------------------------------------===// // Functional HTML pretty-printing. Index: clang/lib/Rewrite/HTMLRewrite.cpp =================================================================== --- clang/lib/Rewrite/HTMLRewrite.cpp +++ clang/lib/Rewrite/HTMLRewrite.cpp @@ -21,7 +21,9 @@ #include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/raw_ostream.h" #include + using namespace clang; +using namespace ento; /// HighlightRange - Highlight a range in the source code with the specified @@ -306,11 +308,15 @@ .keyword { color: blue } .string_literal { color: red } .directive { color: darkmagenta } -/* Macro expansions. */ -.expansion { display: none; } -.macro:hover .expansion { + +/* Macros and variables could have pop-up notes hidden by default. + - Macro pop-up: expansion of the macro (by 'HighlightMacros()'). + - Variable pop-up: value of the variable (by 'AddPopUpNotes()') */ +.macro_popup, .variable_popup { display: none; } + +/* Pop-up appears on mouse-hover event */ +.macro:hover .macro_popup, .variable:hover .variable_popup { display: block; - border: 2px solid #FF0000; padding: 2px; background-color:#FFF0F0; font-weight: normal; @@ -323,6 +329,15 @@ left:10em; z-index: 1 } +.macro_popup { border: 2px solid red; } +.variable_popup { border: 2px solid blue; } + +/* Pop-up notes needs a relative position as a base where they pops up. */ +.macro, .variable { + background-color: PaleGoldenRod; + position: relative; +} +.macro { color: DarkMagenta; } #tooltiphint { position: fixed; @@ -336,12 +351,6 @@ background-color: #c0c0c0; z-index: 2; } -.macro { - color: darkmagenta; - background-color:LemonChiffon; - /* Macros are position: relative to provide base for expansions. */ - position: relative; -} .num { width:2.5em; padding-right:2ex; background-color:#eeeeee } .num { text-align:right; font-size:8pt } @@ -419,6 +428,62 @@ R.InsertTextAfter(EndLoc, "\n"); } +/// AddPopUpNotes - Relex the specified FileID and annotate the HTML with +/// pop-up notes (hidden by default, show on mouse-hover event). +/// It adds information to given variables similar to 'HighlightMacros()'. +void html::AddPopUpNotes(Rewriter &R, FileID FID, const Preprocessor &PP, + const PathPieces &Path) { + if (Path.empty()) + return; + + RewriteBuffer &RB = R.getEditBuffer(FID); + const SourceManager &SM = PP.getSourceManager(); + const llvm::MemoryBuffer *FromFile = SM.getBuffer(FID); + Lexer L(FID, FromFile, SM, PP.getLangOpts()); + const char *BufferStart = L.getBuffer().data(); + + // Lex all the tokens in raw mode, to avoid entering #includes or expanding + // macros. + Token Tok; + L.LexFromRawLexer(Tok); + + while (Tok.isNot(tok::eof)) { + // Since we are lexing unexpanded tokens, all tokens are from the main + // FileID. + unsigned TokOffs = SM.getFileOffset(Tok.getLocation()); + unsigned TokLen = Tok.getLength(); + switch (Tok.getKind()) { + default: break; + case tok::identifier: + llvm_unreachable("tok::identifier in raw lexing mode!"); + case tok::raw_identifier: { + // Fill in Result.IdentifierInfo and update the token kind, + // looking up the identifier in the identifier table. + PP.LookUpIdentifierInfo(Tok); + + // If it is not an identifier do not try to add a pop-up note to it. + if (Tok.isNot(tok::identifier)) + break; + + for (const auto &Piece : Path) { + if (!isa(*Piece) || + Piece->getLocation().asLocation() != Tok.getLocation()) + continue; + + std::string PopUpNote = "" + + Piece->getString().str() + ""; + + HighlightRange(RB, TokOffs, TokOffs + TokLen, BufferStart, + "", PopUpNote.c_str()); + } + break; + } + } + + L.LexFromRawLexer(Tok); + } +} + /// SyntaxHighlight - Relex the specified FileID and annotate the HTML with /// information about keywords, macro expansions etc. This uses the macro /// table state from the end of the file, so it won't be perfectly perfect, @@ -636,10 +701,9 @@ TmpPP.Lex(Tok); } - - // Insert the expansion as the end tag, so that multi-line macros all get - // highlighted. - Expansion = "" + Expansion + ""; + // Insert the 'macro_popup' as the end tag, so that multi-line macros all + // get highlighted. + Expansion = "" + Expansion + ""; HighlightRange(R, LLoc.getBegin(), LLoc.getEnd(), "", Expansion.c_str(), LLoc.isTokenRange()); Index: clang/lib/StaticAnalyzer/Core/BugReporter.cpp =================================================================== --- clang/lib/StaticAnalyzer/Core/BugReporter.cpp +++ clang/lib/StaticAnalyzer/Core/BugReporter.cpp @@ -154,8 +154,6 @@ case PathDiagnosticPiece::Macro: removeRedundantMsgs(cast(*piece).subPieces); break; - case PathDiagnosticPiece::ControlFlow: - break; case PathDiagnosticPiece::Event: { if (i == N-1) break; @@ -175,7 +173,9 @@ } break; } + case PathDiagnosticPiece::ControlFlow: case PathDiagnosticPiece::Note: + case PathDiagnosticPiece::PopUp: break; } path.push_back(std::move(piece)); @@ -230,9 +230,8 @@ break; } case PathDiagnosticPiece::ControlFlow: - break; - case PathDiagnosticPiece::Note: + case PathDiagnosticPiece::PopUp: break; } Index: clang/lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp =================================================================== --- clang/lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp +++ clang/lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp @@ -615,35 +615,43 @@ [](const std::shared_ptr &p) { return isa(*p); }); + unsigned PopUpPieceCount = + std::count_if(path.begin(), path.end(), + [](const std::shared_ptr &p) { + return isa(*p); + }); - unsigned TotalRegularPieces = TotalPieces - TotalNotePieces; + unsigned TotalRegularPieces = TotalPieces - TotalNotePieces - PopUpPieceCount; unsigned NumRegularPieces = TotalRegularPieces; unsigned NumNotePieces = TotalNotePieces; for (auto I = path.rbegin(), E = path.rend(); I != E; ++I) { - if (isa(I->get())) { + auto &Piece = *I->get(); + if (isa(Piece)) { // This adds diagnostic bubbles, but not navigation. // Navigation through note pieces would be added later, // as a separate pass through the piece list. - HandlePiece(R, FID, **I, NumNotePieces, TotalNotePieces); + HandlePiece(R, FID, Piece, NumNotePieces, TotalNotePieces); --NumNotePieces; - } else { - HandlePiece(R, FID, **I, NumRegularPieces, TotalRegularPieces); + + // Pop-up pieces handled in 'AddPopUpNotes()'. + } else if (!isa(Piece)) { + HandlePiece(R, FID, Piece, NumRegularPieces, TotalRegularPieces); --NumRegularPieces; } } // Add line numbers, header, footer, etc. - html::EscapeText(R, FID); html::AddLineNumbers(R, FID); // If we have a preprocessor, relex the file and syntax highlight. - // We might not have a preprocessor if we come from a deserialized AST file, - // for example. - + // Also add pop-up notes on variables with 'AddPopUpNotes()'. + // (We might not have a preprocessor if we come from a deserialized AST file, + // for example.) html::SyntaxHighlight(R, FID, PP); html::HighlightMacros(R, FID, PP); + html::AddPopUpNotes(R, FID, PP, path); } void HTMLDiagnostics::HandlePiece(Rewriter& R, FileID BugFileID, @@ -689,9 +697,7 @@ bool IsNote = false; bool SuppressIndex = (max == 1); switch (P.getKind()) { - case PathDiagnosticPiece::Call: - llvm_unreachable("Calls and extra notes should already be handled"); - case PathDiagnosticPiece::Event: Kind = "Event"; break; + case PathDiagnosticPiece::Event: Kind = "Event"; break; case PathDiagnosticPiece::ControlFlow: Kind = "Control"; break; // Setting Kind to "Control" is intentional. case PathDiagnosticPiece::Macro: Kind = "Control"; break; @@ -700,6 +706,9 @@ IsNote = true; SuppressIndex = true; break; + case PathDiagnosticPiece::Call: + case PathDiagnosticPiece::PopUp: + llvm_unreachable("Calls and extra notes should already be handled"); } std::string sbuf; Index: clang/lib/StaticAnalyzer/Core/PathDiagnostic.cpp =================================================================== --- clang/lib/StaticAnalyzer/Core/PathDiagnostic.cpp +++ clang/lib/StaticAnalyzer/Core/PathDiagnostic.cpp @@ -90,6 +90,8 @@ PathDiagnosticNotePiece::~PathDiagnosticNotePiece() = default; +PathDiagnosticPopUpPiece::~PathDiagnosticPopUpPiece() = default; + void PathPieces::flattenTo(PathPieces &Primary, PathPieces &Current, bool ShouldFlattenMacros) const { for (auto &Piece : *this) { @@ -119,6 +121,7 @@ case PathDiagnosticPiece::Event: case PathDiagnosticPiece::ControlFlow: case PathDiagnosticPiece::Note: + case PathDiagnosticPiece::PopUp: Current.push_back(Piece); break; } @@ -369,15 +372,16 @@ case PathDiagnosticPiece::ControlFlow: return compareControlFlow(cast(X), cast(Y)); - case PathDiagnosticPiece::Event: - case PathDiagnosticPiece::Note: - return None; case PathDiagnosticPiece::Macro: return compareMacro(cast(X), cast(Y)); case PathDiagnosticPiece::Call: return compareCall(cast(X), cast(Y)); + case PathDiagnosticPiece::Event: + case PathDiagnosticPiece::Note: + case PathDiagnosticPiece::PopUp: + return None; } llvm_unreachable("all cases handled"); } @@ -1270,6 +1274,10 @@ PathDiagnosticSpotPiece::Profile(ID); } +void PathDiagnosticPopUpPiece::Profile(llvm::FoldingSetNodeID &ID) const { + PathDiagnosticSpotPiece::Profile(ID); +} + void PathDiagnostic::Profile(llvm::FoldingSetNodeID &ID) const { ID.Add(getLocation()); ID.AddString(BugType); @@ -1395,6 +1403,13 @@ getLocation().dump(); } +LLVM_DUMP_METHOD void PathDiagnosticPopUpPiece::dump() const { + llvm::errs() << "POP-UP\n--------------\n"; + llvm::errs() << getString() << "\n"; + llvm::errs() << " ---- at ----\n"; + getLocation().dump(); +} + LLVM_DUMP_METHOD void PathDiagnosticLocation::dump() const { if (!isValid()) { llvm::errs() << "\n"; Index: clang/lib/StaticAnalyzer/Core/PlistDiagnostics.cpp =================================================================== --- clang/lib/StaticAnalyzer/Core/PlistDiagnostics.cpp +++ clang/lib/StaticAnalyzer/Core/PlistDiagnostics.cpp @@ -21,9 +21,9 @@ #include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h" #include "clang/StaticAnalyzer/Core/IssueHash.h" #include "clang/StaticAnalyzer/Core/PathDiagnosticConsumers.h" -#include "llvm/ADT/Statistic.h" #include "llvm/ADT/SmallPtrSet.h" #include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/Statistic.h" #include "llvm/Support/Casting.h" using namespace clang; @@ -36,52 +36,48 @@ //===----------------------------------------------------------------------===// namespace { - class PlistDiagnostics : public PathDiagnosticConsumer { - const std::string OutputFile; - const Preprocessor &PP; - AnalyzerOptions &AnOpts; - const bool SupportsCrossFileDiagnostics; - public: - PlistDiagnostics(AnalyzerOptions &AnalyzerOpts, - const std::string& prefix, - const Preprocessor &PP, - bool supportsMultipleFiles); - - ~PlistDiagnostics() override {} - - void FlushDiagnosticsImpl(std::vector &Diags, - FilesMade *filesMade) override; - - StringRef getName() const override { - return "PlistDiagnostics"; - } +class PlistDiagnostics : public PathDiagnosticConsumer { + const std::string OutputFile; + const Preprocessor &PP; + AnalyzerOptions &AnOpts; + const bool SupportsCrossFileDiagnostics; - PathGenerationScheme getGenerationScheme() const override { - return Extensive; - } - bool supportsLogicalOpControlFlow() const override { return true; } - bool supportsCrossFileDiagnostics() const override { - return SupportsCrossFileDiagnostics; - } - }; +public: + PlistDiagnostics(AnalyzerOptions &AnalyzerOpts, const std::string &prefix, + const Preprocessor &PP, bool supportsMultipleFiles); + + ~PlistDiagnostics() override {} + + void FlushDiagnosticsImpl(std::vector &Diags, + FilesMade *filesMade) override; + + StringRef getName() const override { return "PlistDiagnostics"; } + + PathGenerationScheme getGenerationScheme() const override { + return Extensive; + } + bool supportsLogicalOpControlFlow() const override { return true; } + bool supportsCrossFileDiagnostics() const override { + return SupportsCrossFileDiagnostics; + } +}; } // end anonymous namespace namespace { /// A helper class for emitting a single report. class PlistPrinter { - const FIDMap& FM; + const FIDMap &FM; AnalyzerOptions &AnOpts; const Preprocessor &PP; llvm::SmallVector MacroPieces; public: - PlistPrinter(const FIDMap& FM, AnalyzerOptions &AnOpts, + PlistPrinter(const FIDMap &FM, AnalyzerOptions &AnOpts, const Preprocessor &PP) - : FM(FM), AnOpts(AnOpts), PP(PP) { - } + : FM(FM), AnOpts(AnOpts), PP(PP) {} - void ReportDiag(raw_ostream &o, const PathDiagnosticPiece& P) { + void ReportDiag(raw_ostream &o, const PathDiagnosticPiece &P) { ReportPiece(o, P, /*indent*/ 4, /*depth*/ 0, /*includeControlFlow*/ true); // Don't emit a warning about an unused private field. @@ -101,25 +97,26 @@ unsigned indent, unsigned depth, bool includeControlFlow, bool isKeyEvent = false) { switch (P.getKind()) { - case PathDiagnosticPiece::ControlFlow: - if (includeControlFlow) - ReportControlFlow(o, cast(P), indent); - break; - case PathDiagnosticPiece::Call: - ReportCall(o, cast(P), indent, - depth); - break; - case PathDiagnosticPiece::Event: - ReportEvent(o, cast(P), indent, depth, - isKeyEvent); - break; - case PathDiagnosticPiece::Macro: - ReportMacroSubPieces(o, cast(P), indent, - depth); - break; - case PathDiagnosticPiece::Note: - ReportNote(o, cast(P), indent); - break; + case PathDiagnosticPiece::ControlFlow: + if (includeControlFlow) + ReportControlFlow(o, cast(P), indent); + break; + case PathDiagnosticPiece::Call: + ReportCall(o, cast(P), indent, depth); + break; + case PathDiagnosticPiece::Event: + ReportEvent(o, cast(P), indent, depth, + isKeyEvent); + break; + case PathDiagnosticPiece::Macro: + ReportMacroSubPieces(o, cast(P), indent, depth); + break; + case PathDiagnosticPiece::Note: + ReportNote(o, cast(P), indent); + break; + case PathDiagnosticPiece::PopUp: + ReportPopUp(o, cast(P), indent); + break; } } @@ -128,16 +125,18 @@ void EmitMessage(raw_ostream &o, StringRef Message, unsigned indent); void ReportControlFlow(raw_ostream &o, - const PathDiagnosticControlFlowPiece& P, + const PathDiagnosticControlFlowPiece &P, unsigned indent); - void ReportEvent(raw_ostream &o, const PathDiagnosticEventPiece& P, + void ReportEvent(raw_ostream &o, const PathDiagnosticEventPiece &P, unsigned indent, unsigned depth, bool isKeyEvent = false); void ReportCall(raw_ostream &o, const PathDiagnosticCallPiece &P, unsigned indent, unsigned depth); - void ReportMacroSubPieces(raw_ostream &o, const PathDiagnosticMacroPiece& P, + void ReportMacroSubPieces(raw_ostream &o, const PathDiagnosticMacroPiece &P, unsigned indent, unsigned depth); - void ReportNote(raw_ostream &o, const PathDiagnosticNotePiece& P, + void ReportNote(raw_ostream &o, const PathDiagnosticNotePiece &P, unsigned indent); + void ReportPopUp(raw_ostream &o, const PathDiagnosticPopUpPiece &P, + unsigned indent); }; } // end of anonymous namespace @@ -148,22 +147,19 @@ std::string MacroName; std::string Expansion; ExpansionInfo(std::string N, std::string E) - : MacroName(std::move(N)), Expansion(std::move(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, +static void printBugPath(llvm::raw_ostream &o, const FIDMap &FM, + AnalyzerOptions &AnOpts, const Preprocessor &PP, const PathPieces &Path); /// 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, - unsigned InputIndentLevel, - SmallVectorImpl &Fids, - FIDMap &FM, +static void printCoverage(const PathDiagnostic *D, unsigned InputIndentLevel, + SmallVectorImpl &Fids, FIDMap &FM, llvm::raw_fd_ostream &o); static ExpansionInfo getExpandedMacro(SourceLocation MacroLoc, @@ -189,8 +185,8 @@ for (auto &R : Ranges) EmitRange(o, SM, - Lexer::getAsCharRange(SM.getExpansionRange(R), SM, LangOpts), - FM, indent + 1); + Lexer::getAsCharRange(SM.getExpansionRange(R), SM, LangOpts), FM, + indent + 1); --indent; Indent(o, indent) << "\n"; } @@ -211,7 +207,7 @@ } void PlistPrinter::ReportControlFlow(raw_ostream &o, - const PathDiagnosticControlFlowPiece& P, + const PathDiagnosticControlFlowPiece &P, unsigned indent) { const SourceManager &SM = PP.getSourceManager(); @@ -227,14 +223,15 @@ ++indent; Indent(o, indent) << "\n"; ++indent; - for (PathDiagnosticControlFlowPiece::const_iterator I=P.begin(), E=P.end(); - I!=E; ++I) { + for (PathDiagnosticControlFlowPiece::const_iterator I = P.begin(), + E = P.end(); + I != E; ++I) { Indent(o, indent) << "\n"; ++indent; - // Make the ranges of the start and end point self-consistent with adjacent edges - // by forcing to use only the beginning of the range. This simplifies the layout - // logic for clients. + // Make the ranges of the start and end point self-consistent with adjacent + // edges by forcing to use only the beginning of the range. This simplifies + // the layout logic for clients. Indent(o, indent) << "start\n"; SourceRange StartEdge( SM.getExpansionLoc(I->getStart().asRange().getBegin())); @@ -264,7 +261,8 @@ Indent(o, indent) << "\n"; } -void PlistPrinter::ReportEvent(raw_ostream &o, const PathDiagnosticEventPiece& P, +void PlistPrinter::ReportEvent(raw_ostream &o, + const PathDiagnosticEventPiece &P, unsigned indent, unsigned depth, bool isKeyEvent) { @@ -298,25 +296,24 @@ // Finish up. --indent; - Indent(o, indent); o << "\n"; + Indent(o, indent) << "\n"; } void PlistPrinter::ReportCall(raw_ostream &o, const PathDiagnosticCallPiece &P, - unsigned indent, - unsigned depth) { + unsigned indent, unsigned depth) { if (auto callEnter = P.getCallEnterEvent()) ReportPiece(o, *callEnter, indent, depth, /*includeControlFlow*/ true, P.isLastInMainSourceFile()); - ++depth; if (auto callEnterWithinCaller = P.getCallEnterWithinCallerEvent()) ReportPiece(o, *callEnterWithinCaller, indent, depth, /*includeControlFlow*/ true); - for (PathPieces::const_iterator I = P.path.begin(), E = P.path.end();I!=E;++I) + for (PathPieces::const_iterator I = P.path.begin(), E = P.path.end(); I != E; + ++I) ReportPiece(o, **I, indent, depth, /*includeControlFlow*/ true); --depth; @@ -326,7 +323,7 @@ } void PlistPrinter::ReportMacroSubPieces(raw_ostream &o, - const PathDiagnosticMacroPiece& P, + const PathDiagnosticMacroPiece &P, unsigned indent, unsigned depth) { MacroPieces.push_back(&P); @@ -366,12 +363,11 @@ // Finish up. --indent; - Indent(o, indent); - o << "\n"; + Indent(o, indent) << "\n"; } } -void PlistPrinter::ReportNote(raw_ostream &o, const PathDiagnosticNotePiece& P, +void PlistPrinter::ReportNote(raw_ostream &o, const PathDiagnosticNotePiece &P, unsigned indent) { const SourceManager &SM = PP.getSourceManager(); @@ -394,7 +390,35 @@ // Finish up. --indent; - Indent(o, indent); o << "\n"; + Indent(o, indent) << "\n"; +} + +void PlistPrinter::ReportPopUp(raw_ostream &o, + const PathDiagnosticPopUpPiece &P, + unsigned indent) { + const SourceManager &SM = PP.getSourceManager(); + + Indent(o, indent) << "\n"; + ++indent; + + Indent(o, indent) << "kindpop-up\n"; + + // 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 text. + EmitMessage(o, P.getString(), indent); + + // Finish up. + --indent; + Indent(o, indent) << "\n"; } //===----------------------------------------------------------------------===// @@ -403,10 +427,8 @@ /// 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, - unsigned InputIndentLevel, - SmallVectorImpl &Fids, - FIDMap &FM, +static void printCoverage(const PathDiagnostic *D, unsigned InputIndentLevel, + SmallVectorImpl &Fids, FIDMap &FM, llvm::raw_fd_ostream &o) { unsigned IndentLevel = InputIndentLevel; @@ -434,21 +456,21 @@ assert(IndentLevel == InputIndentLevel); } -static void printBugPath(llvm::raw_ostream &o, const FIDMap& FM, - AnalyzerOptions &AnOpts, - const Preprocessor &PP, +static void printBugPath(llvm::raw_ostream &o, const FIDMap &FM, + AnalyzerOptions &AnOpts, const Preprocessor &PP, const PathPieces &Path) { PlistPrinter Printer(FM, AnOpts, PP); - assert(std::is_partitioned( - Path.begin(), Path.end(), - [](const std::shared_ptr &E) - { return E->getKind() == PathDiagnosticPiece::Note; }) && + assert(std::is_partitioned(Path.begin(), Path.end(), + [](const std::shared_ptr &E) { + return E->getKind() == PathDiagnosticPiece::Note; + }) && "PathDiagnostic is not partitioned so that notes precede the rest"); - PathPieces::const_iterator FirstNonNote = std::partition_point( - Path.begin(), Path.end(), - [](const std::shared_ptr &E) - { return E->getKind() == PathDiagnosticPiece::Note; }); + PathPieces::const_iterator FirstNonNote = + std::partition_point(Path.begin(), Path.end(), + [](const std::shared_ptr &E) { + return E->getKind() == PathDiagnosticPiece::Note; + }); PathPieces::const_iterator I = Path.begin(); @@ -485,15 +507,15 @@ //===----------------------------------------------------------------------===// PlistDiagnostics::PlistDiagnostics(AnalyzerOptions &AnalyzerOpts, - const std::string& output, + const std::string &output, const Preprocessor &PP, bool supportsMultipleFiles) - : OutputFile(output), PP(PP), AnOpts(AnalyzerOpts), - SupportsCrossFileDiagnostics(supportsMultipleFiles) {} + : OutputFile(output), PP(PP), AnOpts(AnalyzerOpts), + SupportsCrossFileDiagnostics(supportsMultipleFiles) {} void ento::createPlistDiagnosticConsumer(AnalyzerOptions &AnalyzerOpts, PathDiagnosticConsumers &C, - const std::string& s, + const std::string &s, const Preprocessor &PP) { C.push_back(new PlistDiagnostics(AnalyzerOpts, s, PP, /*supportsMultipleFiles*/ false)); @@ -507,13 +529,12 @@ /*supportsMultipleFiles*/ true)); } void PlistDiagnostics::FlushDiagnosticsImpl( - std::vector &Diags, - FilesMade *filesMade) { + std::vector &Diags, FilesMade *filesMade) { // Build up a set of FIDs that we use by scanning the locations and // ranges of the diagnostics. FIDMap FM; SmallVector Fids; - const SourceManager& SM = PP.getSourceManager(); + const SourceManager &SM = PP.getSourceManager(); const LangOptions &LangOpts = PP.getLangOpts(); auto AddPieceFID = [&FM, &Fids, &SM](const PathDiagnosticPiece &Piece) { @@ -568,14 +589,15 @@ // - "clang_version", the string representation of clang version // - "files", an mapping from FIDs to file names // - "diagnostics", an containing the path diagnostics - o << "\n" << - " clang_version\n"; + o << "\n" + << " clang_version\n"; EmitString(o, getClangFullVersion()) << '\n'; o << " diagnostics\n" " \n"; - for (std::vector::iterator DI=Diags.begin(), - DE = Diags.end(); DI!=DE; ++DI) { + for (std::vector::iterator DI = Diags.begin(), + DE = Diags.end(); + DI != DE; ++DI) { o << " \n"; @@ -596,8 +618,8 @@ o << " issue_hash_content_of_line_in_context"; PathDiagnosticLocation UPDLoc = D->getUniqueingLoc(); FullSourceLoc L(SM.getExpansionLoc(UPDLoc.isValid() - ? UPDLoc.asLocation() - : D->getLocation().asLocation()), + ? UPDLoc.asLocation() + : D->getLocation().asLocation()), SM); const Decl *DeclWithIssue = D->getDeclWithIssue(); EmitString(o, GetIssueHash(SM, L, D->getCheckName(), D->getBugType(), @@ -611,20 +633,20 @@ if (const NamedDecl *ND = dyn_cast(DeclWithIssue)) { StringRef declKind; switch (ND->getKind()) { - case Decl::CXXRecord: - declKind = "C++ class"; - break; - case Decl::CXXMethod: - declKind = "C++ method"; - break; - case Decl::ObjCMethod: - declKind = "Objective-C method"; - break; - case Decl::Function: - declKind = "function"; - break; - default: - break; + case Decl::CXXRecord: + declKind = "C++ class"; + break; + case Decl::CXXMethod: + declKind = "C++ method"; + break; + case Decl::ObjCMethod: + declKind = "Objective-C method"; + break; + case Decl::Function: + declKind = "function"; + break; + default: + break; } if (!declKind.empty()) { const std::string &declName = ND->getDeclName().getAsString(); @@ -652,14 +674,13 @@ << L.getExpansionLineNumber() - UFunL.getExpansionLineNumber() << "\n"; - // Otherwise, use the location on which the bug is reported. + // Otherwise, use the location on which the bug is reported. } else { FullSourceLoc FunL(SM.getExpansionLoc(Body->getBeginLoc()), SM); o << " issue_hash_function_offset" << L.getExpansionLineNumber() - FunL.getExpansionLineNumber() << "\n"; } - } } } @@ -674,14 +695,15 @@ PDFileEntry::ConsumerFiles *files = filesMade->getFiles(*D); if (files) { for (PDFileEntry::ConsumerFiles::const_iterator CI = files->begin(), - CE = files->end(); CI != CE; ++CI) { + CE = files->end(); + CI != CE; ++CI) { StringRef newName = CI->first; if (newName != lastName) { if (!lastName.empty()) { o << " \n"; } lastName = newName; - o << " " << lastName << "_files\n"; + o << " " << lastName << "_files\n"; o << " \n"; } o << " " << CI->second << "\n"; @@ -738,7 +760,7 @@ MacroArgMap Args; MacroNameAndArgs(std::string N, const MacroInfo *MI, MacroArgMap M) - : Name(std::move(N)), MI(MI), Args(std::move(M)) {} + : Name(std::move(N)), MI(MI), Args(std::move(M)) {} }; class TokenPrinter { @@ -750,7 +772,7 @@ public: TokenPrinter(llvm::raw_ostream &OS, const Preprocessor &PP) - : OS(OS), PP(PP), ConcatInfo(PP) { + : OS(OS), PP(PP), ConcatInfo(PP) { PrevTok.setKind(tok::unknown); PrevPrevTok.setKind(tok::unknown); } @@ -786,9 +808,7 @@ /// #define f(y) x /// #define x f(x) static std::string getMacroNameAndPrintExpansion( - TokenPrinter &Printer, - SourceLocation MacroLoc, - const Preprocessor &PP, + TokenPrinter &Printer, SourceLocation MacroLoc, const Preprocessor &PP, const MacroArgMap &PrevArgs, llvm::SmallPtrSet &AlreadyProcessedTokens); @@ -818,9 +838,9 @@ const Preprocessor &PP); /// Retrieves the ')' token that matches '(' \p It points to. -static MacroInfo::tokens_iterator getMatchingRParen( - MacroInfo::tokens_iterator It, - MacroInfo::tokens_iterator End); +static MacroInfo::tokens_iterator +getMatchingRParen(MacroInfo::tokens_iterator It, + MacroInfo::tokens_iterator End); /// Retrieves the macro info for \p II refers to at \p Loc. This is important /// because macros can be redefined or undefined. @@ -839,25 +859,22 @@ llvm::SmallString<200> ExpansionBuf; llvm::raw_svector_ostream OS(ExpansionBuf); TokenPrinter Printer(OS, PP); - llvm::SmallPtrSet AlreadyProcessedTokens; + llvm::SmallPtrSet AlreadyProcessedTokens; - std::string MacroName = - getMacroNameAndPrintExpansion(Printer, MacroLoc, PP, MacroArgMap{}, - AlreadyProcessedTokens); - return { MacroName, OS.str() }; + std::string MacroName = getMacroNameAndPrintExpansion( + Printer, MacroLoc, PP, MacroArgMap{}, AlreadyProcessedTokens); + return {MacroName, OS.str()}; } static std::string getMacroNameAndPrintExpansion( - TokenPrinter &Printer, - SourceLocation MacroLoc, - const Preprocessor &PP, + TokenPrinter &Printer, SourceLocation MacroLoc, const Preprocessor &PP, const MacroArgMap &PrevArgs, llvm::SmallPtrSet &AlreadyProcessedTokens) { const SourceManager &SM = PP.getSourceManager(); MacroNameAndArgs Info = getMacroNameAndArgs(SM.getExpansionLoc(MacroLoc), PP); - IdentifierInfo* IDInfo = PP.getIdentifierInfo(Info.Name); + IdentifierInfo *IDInfo = PP.getIdentifierInfo(Info.Name); // TODO: If the macro definition contains another symbol then this function is // called recursively. In case this symbol is the one being defined, it will @@ -887,8 +904,7 @@ } const auto *II = T.getIdentifierInfo(); - assert(II && - "This token is an identifier but has no IdentifierInfo!"); + assert(II && "This token is an identifier but has no IdentifierInfo!"); // If this token is a macro that should be expanded inside the current // macro. @@ -985,7 +1001,7 @@ // directive history (or anything for that matter) from another TU. // TODO: assert when we're not running with CTU. if (!MI) - return { MacroName, MI, {} }; + return {MacroName, MI, {}}; // Acquire the macro's arguments. // @@ -996,7 +1012,7 @@ // which point we start lexing the next argument or finish. ArrayRef MacroArgs = MI->params(); if (MacroArgs.empty()) - return { MacroName, MI, {} }; + return {MacroName, MI, {}}; RawLexer.LexFromRawLexer(TheTok); // When this is a token which expands to another macro function then its @@ -1008,7 +1024,7 @@ // ^ // This is not a tok::l_paren, but foo is a function. if (TheTok.isNot(tok::l_paren)) - return { MacroName, MI, {} }; + return {MacroName, MI, {}}; MacroArgMap Args; @@ -1045,7 +1061,7 @@ RawLexer.LexFromRawLexer(TheTok); while (!(ParenthesesDepth == 1 && - (UnexpArgII == __VA_ARGS__II ? false : TheTok.is(tok::comma)))) { + (UnexpArgII == __VA_ARGS__II ? false : TheTok.is(tok::comma)))) { assert(TheTok.isNot(tok::eof) && "EOF encountered while looking for expanded macro args!"); @@ -1075,12 +1091,12 @@ "Expanded macro argument acquisition failed! After the end of the loop" " this token should be ')'!"); - return { MacroName, MI, Args }; + return {MacroName, MI, Args}; } -static MacroInfo::tokens_iterator getMatchingRParen( - MacroInfo::tokens_iterator It, - MacroInfo::tokens_iterator End) { +static MacroInfo::tokens_iterator +getMatchingRParen(MacroInfo::tokens_iterator It, + MacroInfo::tokens_iterator End) { assert(It->is(tok::l_paren) && "This token should be '('!"); @@ -1139,8 +1155,8 @@ const ExpArgTokens &SuperExpArgTokens = Super.at(II); - It = CurrExpArgTokens.insert( - It, SuperExpArgTokens.begin(), SuperExpArgTokens.end()); + It = CurrExpArgTokens.insert(It, SuperExpArgTokens.begin(), + SuperExpArgTokens.end()); std::advance(It, SuperExpArgTokens.size()); It = CurrExpArgTokens.erase(It); } @@ -1152,8 +1168,8 @@ if (PrevTok.isNot(tok::unknown)) { // If the tokens were already space separated, or if they must be to avoid // them being implicitly pasted, add a space between them. - if(Tok.hasLeadingSpace() || ConcatInfo.AvoidConcat(PrevPrevTok, PrevTok, - Tok)) { + if (Tok.hasLeadingSpace() || + ConcatInfo.AvoidConcat(PrevPrevTok, PrevTok, Tok)) { // AvoidConcat doesn't check for ##, don't print a space around it. if (PrevTok.isNot(tok::hashhash) && Tok.isNot(tok::hashhash)) { OS << ' '; Index: clang/lib/StaticAnalyzer/Core/SarifDiagnostics.cpp =================================================================== --- clang/lib/StaticAnalyzer/Core/SarifDiagnostics.cpp +++ clang/lib/StaticAnalyzer/Core/SarifDiagnostics.cpp @@ -191,15 +191,16 @@ static Importance calculateImportance(const PathDiagnosticPiece &Piece) { switch (Piece.getKind()) { - case PathDiagnosticPiece::Kind::Call: - case PathDiagnosticPiece::Kind::Macro: - case PathDiagnosticPiece::Kind::Note: + case PathDiagnosticPiece::Call: + case PathDiagnosticPiece::Macro: + case PathDiagnosticPiece::Note: + case PathDiagnosticPiece::PopUp: // FIXME: What should be reported here? break; - case PathDiagnosticPiece::Kind::Event: + case PathDiagnosticPiece::Event: return Piece.getTagStr() == "ConditionBRVisitor" ? Importance::Important : Importance::Essential; - case PathDiagnosticPiece::Kind::ControlFlow: + case PathDiagnosticPiece::ControlFlow: return Importance::Unimportant; } return Importance::Unimportant;