| //===- PathDiagnostic.cpp - Path-Specific Diagnostic Handling -------------===// |
| // |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| // See https://llvm.org/LICENSE.txt for license information. |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| // |
| //===----------------------------------------------------------------------===// |
| // |
| // This file defines the PathDiagnostic-related interfaces. |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "clang/Analysis/PathDiagnostic.h" |
| #include "clang/AST/Decl.h" |
| #include "clang/AST/DeclBase.h" |
| #include "clang/AST/DeclCXX.h" |
| #include "clang/AST/DeclObjC.h" |
| #include "clang/AST/DeclTemplate.h" |
| #include "clang/AST/Expr.h" |
| #include "clang/AST/ExprCXX.h" |
| #include "clang/AST/OperationKinds.h" |
| #include "clang/AST/ParentMap.h" |
| #include "clang/AST/Stmt.h" |
| #include "clang/AST/Type.h" |
| #include "clang/Analysis/AnalysisDeclContext.h" |
| #include "clang/Analysis/CFG.h" |
| #include "clang/Analysis/ProgramPoint.h" |
| #include "clang/Basic/FileManager.h" |
| #include "clang/Basic/LLVM.h" |
| #include "clang/Basic/SourceLocation.h" |
| #include "clang/Basic/SourceManager.h" |
| #include "llvm/ADT/ArrayRef.h" |
| #include "llvm/ADT/FoldingSet.h" |
| #include "llvm/ADT/None.h" |
| #include "llvm/ADT/Optional.h" |
| #include "llvm/ADT/STLExtras.h" |
| #include "llvm/ADT/SmallString.h" |
| #include "llvm/ADT/SmallVector.h" |
| #include "llvm/ADT/StringExtras.h" |
| #include "llvm/ADT/StringRef.h" |
| #include "llvm/Support/Casting.h" |
| #include "llvm/Support/ErrorHandling.h" |
| #include "llvm/Support/raw_ostream.h" |
| #include <cassert> |
| #include <cstring> |
| #include <memory> |
| #include <utility> |
| #include <vector> |
| |
| using namespace clang; |
| using namespace ento; |
| |
| static StringRef StripTrailingDots(StringRef s) { |
| for (StringRef::size_type i = s.size(); i != 0; --i) |
| if (s[i - 1] != '.') |
| return s.substr(0, i); |
| return {}; |
| } |
| |
| PathDiagnosticPiece::PathDiagnosticPiece(StringRef s, |
| Kind k, DisplayHint hint) |
| : str(StripTrailingDots(s)), kind(k), Hint(hint) {} |
| |
| PathDiagnosticPiece::PathDiagnosticPiece(Kind k, DisplayHint hint) |
| : kind(k), Hint(hint) {} |
| |
| PathDiagnosticPiece::~PathDiagnosticPiece() = default; |
| |
| PathDiagnosticEventPiece::~PathDiagnosticEventPiece() = default; |
| |
| PathDiagnosticCallPiece::~PathDiagnosticCallPiece() = default; |
| |
| PathDiagnosticControlFlowPiece::~PathDiagnosticControlFlowPiece() = default; |
| |
| PathDiagnosticMacroPiece::~PathDiagnosticMacroPiece() = default; |
| |
| PathDiagnosticNotePiece::~PathDiagnosticNotePiece() = default; |
| |
| PathDiagnosticPopUpPiece::~PathDiagnosticPopUpPiece() = default; |
| |
| void PathPieces::flattenTo(PathPieces &Primary, PathPieces &Current, |
| bool ShouldFlattenMacros) const { |
| for (auto &Piece : *this) { |
| switch (Piece->getKind()) { |
| case PathDiagnosticPiece::Call: { |
| auto &Call = cast<PathDiagnosticCallPiece>(*Piece); |
| if (auto CallEnter = Call.getCallEnterEvent()) |
| Current.push_back(std::move(CallEnter)); |
| Call.path.flattenTo(Primary, Primary, ShouldFlattenMacros); |
| if (auto callExit = Call.getCallExitEvent()) |
| Current.push_back(std::move(callExit)); |
| break; |
| } |
| case PathDiagnosticPiece::Macro: { |
| auto &Macro = cast<PathDiagnosticMacroPiece>(*Piece); |
| if (ShouldFlattenMacros) { |
| Macro.subPieces.flattenTo(Primary, Primary, ShouldFlattenMacros); |
| } else { |
| Current.push_back(Piece); |
| PathPieces NewPath; |
| Macro.subPieces.flattenTo(Primary, NewPath, ShouldFlattenMacros); |
| // FIXME: This probably shouldn't mutate the original path piece. |
| Macro.subPieces = NewPath; |
| } |
| break; |
| } |
| case PathDiagnosticPiece::Event: |
| case PathDiagnosticPiece::ControlFlow: |
| case PathDiagnosticPiece::Note: |
| case PathDiagnosticPiece::PopUp: |
| Current.push_back(Piece); |
| break; |
| } |
| } |
| } |
| |
| PathDiagnostic::~PathDiagnostic() = default; |
| |
| PathDiagnostic::PathDiagnostic( |
| StringRef CheckerName, const Decl *declWithIssue, StringRef bugtype, |
| StringRef verboseDesc, StringRef shortDesc, StringRef category, |
| PathDiagnosticLocation LocationToUnique, const Decl *DeclToUnique, |
| std::unique_ptr<FilesToLineNumsMap> ExecutedLines) |
| : CheckerName(CheckerName), DeclWithIssue(declWithIssue), |
| BugType(StripTrailingDots(bugtype)), |
| VerboseDesc(StripTrailingDots(verboseDesc)), |
| ShortDesc(StripTrailingDots(shortDesc)), |
| Category(StripTrailingDots(category)), UniqueingLoc(LocationToUnique), |
| UniqueingDecl(DeclToUnique), ExecutedLines(std::move(ExecutedLines)), |
| path(pathImpl) {} |
| |
| void PathDiagnosticConsumer::anchor() {} |
| |
| PathDiagnosticConsumer::~PathDiagnosticConsumer() { |
| // Delete the contents of the FoldingSet if it isn't empty already. |
| for (auto &Diag : Diags) |
| delete &Diag; |
| } |
| |
| void PathDiagnosticConsumer::HandlePathDiagnostic( |
| std::unique_ptr<PathDiagnostic> D) { |
| if (!D || D->path.empty()) |
| return; |
| |
| // We need to flatten the locations (convert Stmt* to locations) because |
| // the referenced statements may be freed by the time the diagnostics |
| // are emitted. |
| D->flattenLocations(); |
| |
| // If the PathDiagnosticConsumer does not support diagnostics that |
| // cross file boundaries, prune out such diagnostics now. |
| if (!supportsCrossFileDiagnostics()) { |
| // Verify that the entire path is from the same FileID. |
| FileID FID; |
| const SourceManager &SMgr = D->path.front()->getLocation().getManager(); |
| SmallVector<const PathPieces *, 5> WorkList; |
| WorkList.push_back(&D->path); |
| SmallString<128> buf; |
| llvm::raw_svector_ostream warning(buf); |
| warning << "warning: Path diagnostic report is not generated. Current " |
| << "output format does not support diagnostics that cross file " |
| << "boundaries. Refer to --analyzer-output for valid output " |
| << "formats\n"; |
| |
| while (!WorkList.empty()) { |
| const PathPieces &path = *WorkList.pop_back_val(); |
| |
| for (const auto &I : path) { |
| const PathDiagnosticPiece *piece = I.get(); |
| FullSourceLoc L = piece->getLocation().asLocation().getExpansionLoc(); |
| |
| if (FID.isInvalid()) { |
| FID = SMgr.getFileID(L); |
| } else if (SMgr.getFileID(L) != FID) { |
| llvm::errs() << warning.str(); |
| return; |
| } |
| |
| // Check the source ranges. |
| ArrayRef<SourceRange> Ranges = piece->getRanges(); |
| for (const auto &I : Ranges) { |
| SourceLocation L = SMgr.getExpansionLoc(I.getBegin()); |
| if (!L.isFileID() || SMgr.getFileID(L) != FID) { |
| llvm::errs() << warning.str(); |
| return; |
| } |
| L = SMgr.getExpansionLoc(I.getEnd()); |
| if (!L.isFileID() || SMgr.getFileID(L) != FID) { |
| llvm::errs() << warning.str(); |
| return; |
| } |
| } |
| |
| if (const auto *call = dyn_cast<PathDiagnosticCallPiece>(piece)) |
| WorkList.push_back(&call->path); |
| else if (const auto *macro = dyn_cast<PathDiagnosticMacroPiece>(piece)) |
| WorkList.push_back(¯o->subPieces); |
| } |
| } |
| |
| if (FID.isInvalid()) |
| return; // FIXME: Emit a warning? |
| } |
| |
| // Profile the node to see if we already have something matching it |
| llvm::FoldingSetNodeID profile; |
| D->Profile(profile); |
| void *InsertPos = nullptr; |
| |
| if (PathDiagnostic *orig = Diags.FindNodeOrInsertPos(profile, InsertPos)) { |
| // Keep the PathDiagnostic with the shorter path. |
| // Note, the enclosing routine is called in deterministic order, so the |
| // results will be consistent between runs (no reason to break ties if the |
| // size is the same). |
| const unsigned orig_size = orig->full_size(); |
| const unsigned new_size = D->full_size(); |
| if (orig_size <= new_size) |
| return; |
| |
| assert(orig != D.get()); |
| Diags.RemoveNode(orig); |
| delete orig; |
| } |
| |
| Diags.InsertNode(D.release()); |
| } |
| |
| static Optional<bool> comparePath(const PathPieces &X, const PathPieces &Y); |
| |
| static Optional<bool> |
| compareControlFlow(const PathDiagnosticControlFlowPiece &X, |
| const PathDiagnosticControlFlowPiece &Y) { |
| FullSourceLoc XSL = X.getStartLocation().asLocation(); |
| FullSourceLoc YSL = Y.getStartLocation().asLocation(); |
| if (XSL != YSL) |
| return XSL.isBeforeInTranslationUnitThan(YSL); |
| FullSourceLoc XEL = X.getEndLocation().asLocation(); |
| FullSourceLoc YEL = Y.getEndLocation().asLocation(); |
| if (XEL != YEL) |
| return XEL.isBeforeInTranslationUnitThan(YEL); |
| return None; |
| } |
| |
| static Optional<bool> compareMacro(const PathDiagnosticMacroPiece &X, |
| const PathDiagnosticMacroPiece &Y) { |
| return comparePath(X.subPieces, Y.subPieces); |
| } |
| |
| static Optional<bool> compareCall(const PathDiagnosticCallPiece &X, |
| const PathDiagnosticCallPiece &Y) { |
| FullSourceLoc X_CEL = X.callEnter.asLocation(); |
| FullSourceLoc Y_CEL = Y.callEnter.asLocation(); |
| if (X_CEL != Y_CEL) |
| return X_CEL.isBeforeInTranslationUnitThan(Y_CEL); |
| FullSourceLoc X_CEWL = X.callEnterWithin.asLocation(); |
| FullSourceLoc Y_CEWL = Y.callEnterWithin.asLocation(); |
| if (X_CEWL != Y_CEWL) |
| return X_CEWL.isBeforeInTranslationUnitThan(Y_CEWL); |
| FullSourceLoc X_CRL = X.callReturn.asLocation(); |
| FullSourceLoc Y_CRL = Y.callReturn.asLocation(); |
| if (X_CRL != Y_CRL) |
| return X_CRL.isBeforeInTranslationUnitThan(Y_CRL); |
| return comparePath(X.path, Y.path); |
| } |
| |
| static Optional<bool> comparePiece(const PathDiagnosticPiece &X, |
| const PathDiagnosticPiece &Y) { |
| if (X.getKind() != Y.getKind()) |
| return X.getKind() < Y.getKind(); |
| |
| FullSourceLoc XL = X.getLocation().asLocation(); |
| FullSourceLoc YL = Y.getLocation().asLocation(); |
| if (XL != YL) |
| return XL.isBeforeInTranslationUnitThan(YL); |
| |
| if (X.getString() != Y.getString()) |
| return X.getString() < Y.getString(); |
| |
| if (X.getRanges().size() != Y.getRanges().size()) |
| return X.getRanges().size() < Y.getRanges().size(); |
| |
| const SourceManager &SM = XL.getManager(); |
| |
| for (unsigned i = 0, n = X.getRanges().size(); i < n; ++i) { |
| SourceRange XR = X.getRanges()[i]; |
| SourceRange YR = Y.getRanges()[i]; |
| if (XR != YR) { |
| if (XR.getBegin() != YR.getBegin()) |
| return SM.isBeforeInTranslationUnit(XR.getBegin(), YR.getBegin()); |
| return SM.isBeforeInTranslationUnit(XR.getEnd(), YR.getEnd()); |
| } |
| } |
| |
| switch (X.getKind()) { |
| case PathDiagnosticPiece::ControlFlow: |
| return compareControlFlow(cast<PathDiagnosticControlFlowPiece>(X), |
| cast<PathDiagnosticControlFlowPiece>(Y)); |
| case PathDiagnosticPiece::Macro: |
| return compareMacro(cast<PathDiagnosticMacroPiece>(X), |
| cast<PathDiagnosticMacroPiece>(Y)); |
| case PathDiagnosticPiece::Call: |
| return compareCall(cast<PathDiagnosticCallPiece>(X), |
| cast<PathDiagnosticCallPiece>(Y)); |
| case PathDiagnosticPiece::Event: |
| case PathDiagnosticPiece::Note: |
| case PathDiagnosticPiece::PopUp: |
| return None; |
| } |
| llvm_unreachable("all cases handled"); |
| } |
| |
| static Optional<bool> comparePath(const PathPieces &X, const PathPieces &Y) { |
| if (X.size() != Y.size()) |
| return X.size() < Y.size(); |
| |
| PathPieces::const_iterator X_I = X.begin(), X_end = X.end(); |
| PathPieces::const_iterator Y_I = Y.begin(), Y_end = Y.end(); |
| |
| for ( ; X_I != X_end && Y_I != Y_end; ++X_I, ++Y_I) { |
| Optional<bool> b = comparePiece(**X_I, **Y_I); |
| if (b.hasValue()) |
| return b.getValue(); |
| } |
| |
| return None; |
| } |
| |
| static bool compareCrossTUSourceLocs(FullSourceLoc XL, FullSourceLoc YL) { |
| std::pair<FileID, unsigned> XOffs = XL.getDecomposedLoc(); |
| std::pair<FileID, unsigned> YOffs = YL.getDecomposedLoc(); |
| const SourceManager &SM = XL.getManager(); |
| std::pair<bool, bool> InSameTU = SM.isInTheSameTranslationUnit(XOffs, YOffs); |
| if (InSameTU.first) |
| return XL.isBeforeInTranslationUnitThan(YL); |
| const FileEntry *XFE = SM.getFileEntryForID(XL.getSpellingLoc().getFileID()); |
| const FileEntry *YFE = SM.getFileEntryForID(YL.getSpellingLoc().getFileID()); |
| if (!XFE || !YFE) |
| return XFE && !YFE; |
| int NameCmp = XFE->getName().compare(YFE->getName()); |
| if (NameCmp != 0) |
| return NameCmp == -1; |
| // Last resort: Compare raw file IDs that are possibly expansions. |
| return XL.getFileID() < YL.getFileID(); |
| } |
| |
| static bool compare(const PathDiagnostic &X, const PathDiagnostic &Y) { |
| FullSourceLoc XL = X.getLocation().asLocation(); |
| FullSourceLoc YL = Y.getLocation().asLocation(); |
| if (XL != YL) |
| return compareCrossTUSourceLocs(XL, YL); |
| if (X.getBugType() != Y.getBugType()) |
| return X.getBugType() < Y.getBugType(); |
| if (X.getCategory() != Y.getCategory()) |
| return X.getCategory() < Y.getCategory(); |
| if (X.getVerboseDescription() != Y.getVerboseDescription()) |
| return X.getVerboseDescription() < Y.getVerboseDescription(); |
| if (X.getShortDescription() != Y.getShortDescription()) |
| return X.getShortDescription() < Y.getShortDescription(); |
| if (X.getDeclWithIssue() != Y.getDeclWithIssue()) { |
| const Decl *XD = X.getDeclWithIssue(); |
| if (!XD) |
| return true; |
| const Decl *YD = Y.getDeclWithIssue(); |
| if (!YD) |
| return false; |
| SourceLocation XDL = XD->getLocation(); |
| SourceLocation YDL = YD->getLocation(); |
| if (XDL != YDL) { |
| const SourceManager &SM = XL.getManager(); |
| return compareCrossTUSourceLocs(FullSourceLoc(XDL, SM), |
| FullSourceLoc(YDL, SM)); |
| } |
| } |
| PathDiagnostic::meta_iterator XI = X.meta_begin(), XE = X.meta_end(); |
| PathDiagnostic::meta_iterator YI = Y.meta_begin(), YE = Y.meta_end(); |
| if (XE - XI != YE - YI) |
| return (XE - XI) < (YE - YI); |
| for ( ; XI != XE ; ++XI, ++YI) { |
| if (*XI != *YI) |
| return (*XI) < (*YI); |
| } |
| Optional<bool> b = comparePath(X.path, Y.path); |
| assert(b.hasValue()); |
| return b.getValue(); |
| } |
| |
| void PathDiagnosticConsumer::FlushDiagnostics( |
| PathDiagnosticConsumer::FilesMade *Files) { |
| if (flushed) |
| return; |
| |
| flushed = true; |
| |
| std::vector<const PathDiagnostic *> BatchDiags; |
| for (const auto &D : Diags) |
| BatchDiags.push_back(&D); |
| |
| // Sort the diagnostics so that they are always emitted in a deterministic |
| // order. |
| int (*Comp)(const PathDiagnostic *const *, const PathDiagnostic *const *) = |
| [](const PathDiagnostic *const *X, const PathDiagnostic *const *Y) { |
| assert(*X != *Y && "PathDiagnostics not uniqued!"); |
| if (compare(**X, **Y)) |
| return -1; |
| assert(compare(**Y, **X) && "Not a total order!"); |
| return 1; |
| }; |
| array_pod_sort(BatchDiags.begin(), BatchDiags.end(), Comp); |
| |
| FlushDiagnosticsImpl(BatchDiags, Files); |
| |
| // Delete the flushed diagnostics. |
| for (const auto D : BatchDiags) |
| delete D; |
| |
| // Clear out the FoldingSet. |
| Diags.clear(); |
| } |
| |
| PathDiagnosticConsumer::FilesMade::~FilesMade() { |
| for (PDFileEntry &Entry : Set) |
| Entry.~PDFileEntry(); |
| } |
| |
| void PathDiagnosticConsumer::FilesMade::addDiagnostic(const PathDiagnostic &PD, |
| StringRef ConsumerName, |
| StringRef FileName) { |
| llvm::FoldingSetNodeID NodeID; |
| NodeID.Add(PD); |
| void *InsertPos; |
| PDFileEntry *Entry = Set.FindNodeOrInsertPos(NodeID, InsertPos); |
| if (!Entry) { |
| Entry = Alloc.Allocate<PDFileEntry>(); |
| Entry = new (Entry) PDFileEntry(NodeID); |
| Set.InsertNode(Entry, InsertPos); |
| } |
| |
| // Allocate persistent storage for the file name. |
| char *FileName_cstr = (char*) Alloc.Allocate(FileName.size(), 1); |
| memcpy(FileName_cstr, FileName.data(), FileName.size()); |
| |
| Entry->files.push_back(std::make_pair(ConsumerName, |
| StringRef(FileName_cstr, |
| FileName.size()))); |
| } |
| |
| PathDiagnosticConsumer::PDFileEntry::ConsumerFiles * |
| PathDiagnosticConsumer::FilesMade::getFiles(const PathDiagnostic &PD) { |
| llvm::FoldingSetNodeID NodeID; |
| NodeID.Add(PD); |
| void *InsertPos; |
| PDFileEntry *Entry = Set.FindNodeOrInsertPos(NodeID, InsertPos); |
| if (!Entry) |
| return nullptr; |
| return &Entry->files; |
| } |
| |
| //===----------------------------------------------------------------------===// |
| // PathDiagnosticLocation methods. |
| //===----------------------------------------------------------------------===// |
| |
| SourceLocation PathDiagnosticLocation::getValidSourceLocation( |
| const Stmt *S, LocationOrAnalysisDeclContext LAC, bool UseEndOfStatement) { |
| SourceLocation L = UseEndOfStatement ? S->getEndLoc() : S->getBeginLoc(); |
| assert(!LAC.isNull() && |
| "A valid LocationContext or AnalysisDeclContext should be passed to " |
| "PathDiagnosticLocation upon creation."); |
| |
| // S might be a temporary statement that does not have a location in the |
| // source code, so find an enclosing statement and use its location. |
| if (!L.isValid()) { |
| AnalysisDeclContext *ADC; |
| if (LAC.is<const LocationContext*>()) |
| ADC = LAC.get<const LocationContext*>()->getAnalysisDeclContext(); |
| else |
| ADC = LAC.get<AnalysisDeclContext*>(); |
| |
| ParentMap &PM = ADC->getParentMap(); |
| |
| const Stmt *Parent = S; |
| do { |
| Parent = PM.getParent(Parent); |
| |
| // In rare cases, we have implicit top-level expressions, |
| // such as arguments for implicit member initializers. |
| // In this case, fall back to the start of the body (even if we were |
| // asked for the statement end location). |
| if (!Parent) { |
| const Stmt *Body = ADC->getBody(); |
| if (Body) |
| L = Body->getBeginLoc(); |
| else |
| L = ADC->getDecl()->getEndLoc(); |
| break; |
| } |
| |
| L = UseEndOfStatement ? Parent->getEndLoc() : Parent->getBeginLoc(); |
| } while (!L.isValid()); |
| } |
| |
| // FIXME: Ironically, this assert actually fails in some cases. |
| //assert(L.isValid()); |
| return L; |
| } |
| |
| static PathDiagnosticLocation |
| getLocationForCaller(const StackFrameContext *SFC, |
| const LocationContext *CallerCtx, |
| const SourceManager &SM) { |
| const CFGBlock &Block = *SFC->getCallSiteBlock(); |
| CFGElement Source = Block[SFC->getIndex()]; |
| |
| switch (Source.getKind()) { |
| case CFGElement::Statement: |
| case CFGElement::Constructor: |
| case CFGElement::CXXRecordTypedCall: |
| return PathDiagnosticLocation(Source.castAs<CFGStmt>().getStmt(), |
| SM, CallerCtx); |
| case CFGElement::Initializer: { |
| const CFGInitializer &Init = Source.castAs<CFGInitializer>(); |
| return PathDiagnosticLocation(Init.getInitializer()->getInit(), |
| SM, CallerCtx); |
| } |
| case CFGElement::AutomaticObjectDtor: { |
| const CFGAutomaticObjDtor &Dtor = Source.castAs<CFGAutomaticObjDtor>(); |
| return PathDiagnosticLocation::createEnd(Dtor.getTriggerStmt(), |
| SM, CallerCtx); |
| } |
| case CFGElement::DeleteDtor: { |
| const CFGDeleteDtor &Dtor = Source.castAs<CFGDeleteDtor>(); |
| return PathDiagnosticLocation(Dtor.getDeleteExpr(), SM, CallerCtx); |
| } |
| case CFGElement::BaseDtor: |
| case CFGElement::MemberDtor: { |
| const AnalysisDeclContext *CallerInfo = CallerCtx->getAnalysisDeclContext(); |
| if (const Stmt *CallerBody = CallerInfo->getBody()) |
| return PathDiagnosticLocation::createEnd(CallerBody, SM, CallerCtx); |
| return PathDiagnosticLocation::create(CallerInfo->getDecl(), SM); |
| } |
| case CFGElement::NewAllocator: { |
| const CFGNewAllocator &Alloc = Source.castAs<CFGNewAllocator>(); |
| return PathDiagnosticLocation(Alloc.getAllocatorExpr(), SM, CallerCtx); |
| } |
| case CFGElement::TemporaryDtor: { |
| // Temporary destructors are for temporaries. They die immediately at around |
| // the location of CXXBindTemporaryExpr. If they are lifetime-extended, |
| // they'd be dealt with via an AutomaticObjectDtor instead. |
| const auto &Dtor = Source.castAs<CFGTemporaryDtor>(); |
| return PathDiagnosticLocation::createEnd(Dtor.getBindTemporaryExpr(), SM, |
| CallerCtx); |
| } |
| case CFGElement::ScopeBegin: |
| case CFGElement::ScopeEnd: |
| llvm_unreachable("not yet implemented!"); |
| case CFGElement::LifetimeEnds: |
| case CFGElement::LoopExit: |
| llvm_unreachable("CFGElement kind should not be on callsite!"); |
| } |
| |
| llvm_unreachable("Unknown CFGElement kind"); |
| } |
| |
| PathDiagnosticLocation |
| PathDiagnosticLocation::createBegin(const Decl *D, |
| const SourceManager &SM) { |
| return PathDiagnosticLocation(D->getBeginLoc(), SM, SingleLocK); |
| } |
| |
| PathDiagnosticLocation |
| PathDiagnosticLocation::createBegin(const Stmt *S, |
| const SourceManager &SM, |
| LocationOrAnalysisDeclContext LAC) { |
| return PathDiagnosticLocation(getValidSourceLocation(S, LAC), |
| SM, SingleLocK); |
| } |
| |
| PathDiagnosticLocation |
| PathDiagnosticLocation::createEnd(const Stmt *S, |
| const SourceManager &SM, |
| LocationOrAnalysisDeclContext LAC) { |
| if (const auto *CS = dyn_cast<CompoundStmt>(S)) |
| return createEndBrace(CS, SM); |
| return PathDiagnosticLocation(getValidSourceLocation(S, LAC, /*End=*/true), |
| SM, SingleLocK); |
| } |
| |
| PathDiagnosticLocation |
| PathDiagnosticLocation::createOperatorLoc(const BinaryOperator *BO, |
| const SourceManager &SM) { |
| return PathDiagnosticLocation(BO->getOperatorLoc(), SM, SingleLocK); |
| } |
| |
| PathDiagnosticLocation |
| PathDiagnosticLocation::createConditionalColonLoc( |
| const ConditionalOperator *CO, |
| const SourceManager &SM) { |
| return PathDiagnosticLocation(CO->getColonLoc(), SM, SingleLocK); |
| } |
| |
| PathDiagnosticLocation |
| PathDiagnosticLocation::createMemberLoc(const MemberExpr *ME, |
| const SourceManager &SM) { |
| |
| assert(ME->getMemberLoc().isValid() || ME->getBeginLoc().isValid()); |
| |
| // In some cases, getMemberLoc isn't valid -- in this case we'll return with |
| // some other related valid SourceLocation. |
| if (ME->getMemberLoc().isValid()) |
| return PathDiagnosticLocation(ME->getMemberLoc(), SM, SingleLocK); |
| |
| return PathDiagnosticLocation(ME->getBeginLoc(), SM, SingleLocK); |
| } |
| |
| PathDiagnosticLocation |
| PathDiagnosticLocation::createBeginBrace(const CompoundStmt *CS, |
| const SourceManager &SM) { |
| SourceLocation L = CS->getLBracLoc(); |
| return PathDiagnosticLocation(L, SM, SingleLocK); |
| } |
| |
| PathDiagnosticLocation |
| PathDiagnosticLocation::createEndBrace(const CompoundStmt *CS, |
| const SourceManager &SM) { |
| SourceLocation L = CS->getRBracLoc(); |
| return PathDiagnosticLocation(L, SM, SingleLocK); |
| } |
| |
| PathDiagnosticLocation |
| PathDiagnosticLocation::createDeclBegin(const LocationContext *LC, |
| const SourceManager &SM) { |
| // FIXME: Should handle CXXTryStmt if analyser starts supporting C++. |
| if (const auto *CS = dyn_cast_or_null<CompoundStmt>(LC->getDecl()->getBody())) |
| if (!CS->body_empty()) { |
| SourceLocation Loc = (*CS->body_begin())->getBeginLoc(); |
| return PathDiagnosticLocation(Loc, SM, SingleLocK); |
| } |
| |
| return PathDiagnosticLocation(); |
| } |
| |
| PathDiagnosticLocation |
| PathDiagnosticLocation::createDeclEnd(const LocationContext *LC, |
| const SourceManager &SM) { |
| SourceLocation L = LC->getDecl()->getBodyRBrace(); |
| return PathDiagnosticLocation(L, SM, SingleLocK); |
| } |
| |
| PathDiagnosticLocation |
| PathDiagnosticLocation::create(const ProgramPoint& P, |
| const SourceManager &SMng) { |
| const Stmt* S = nullptr; |
| if (Optional<BlockEdge> BE = P.getAs<BlockEdge>()) { |
| const CFGBlock *BSrc = BE->getSrc(); |
| if (BSrc->getTerminator().isVirtualBaseBranch()) { |
| // TODO: VirtualBaseBranches should also appear for destructors. |
| // In this case we should put the diagnostic at the end of decl. |
| return PathDiagnosticLocation::createBegin( |
| P.getLocationContext()->getDecl(), SMng); |
| |
| } else { |
| S = BSrc->getTerminatorCondition(); |
| if (!S) { |
| // If the BlockEdge has no terminator condition statement but its |
| // source is the entry of the CFG (e.g. a checker crated the branch at |
| // the beginning of a function), use the function's declaration instead. |
| assert(BSrc == &BSrc->getParent()->getEntry() && "CFGBlock has no " |
| "TerminatorCondition and is not the enrty block of the CFG"); |
| return PathDiagnosticLocation::createBegin( |
| P.getLocationContext()->getDecl(), SMng); |
| } |
| } |
| } else if (Optional<StmtPoint> SP = P.getAs<StmtPoint>()) { |
| S = SP->getStmt(); |
| if (P.getAs<PostStmtPurgeDeadSymbols>()) |
| return PathDiagnosticLocation::createEnd(S, SMng, P.getLocationContext()); |
| } else if (Optional<PostInitializer> PIP = P.getAs<PostInitializer>()) { |
| return PathDiagnosticLocation(PIP->getInitializer()->getSourceLocation(), |
| SMng); |
| } else if (Optional<PreImplicitCall> PIC = P.getAs<PreImplicitCall>()) { |
| return PathDiagnosticLocation(PIC->getLocation(), SMng); |
| } else if (Optional<PostImplicitCall> PIE = P.getAs<PostImplicitCall>()) { |
| return PathDiagnosticLocation(PIE->getLocation(), SMng); |
| } else if (Optional<CallEnter> CE = P.getAs<CallEnter>()) { |
| return getLocationForCaller(CE->getCalleeContext(), |
| CE->getLocationContext(), |
| SMng); |
| } else if (Optional<CallExitEnd> CEE = P.getAs<CallExitEnd>()) { |
| return getLocationForCaller(CEE->getCalleeContext(), |
| CEE->getLocationContext(), |
| SMng); |
| } else if (auto CEB = P.getAs<CallExitBegin>()) { |
| if (const ReturnStmt *RS = CEB->getReturnStmt()) |
| return PathDiagnosticLocation::createBegin(RS, SMng, |
| CEB->getLocationContext()); |
| return PathDiagnosticLocation( |
| CEB->getLocationContext()->getDecl()->getSourceRange().getEnd(), SMng); |
| } else if (Optional<BlockEntrance> BE = P.getAs<BlockEntrance>()) { |
| if (Optional<CFGElement> BlockFront = BE->getFirstElement()) { |
| if (auto StmtElt = BlockFront->getAs<CFGStmt>()) { |
| return PathDiagnosticLocation(StmtElt->getStmt()->getBeginLoc(), SMng); |
| } else if (auto NewAllocElt = BlockFront->getAs<CFGNewAllocator>()) { |
| return PathDiagnosticLocation( |
| NewAllocElt->getAllocatorExpr()->getBeginLoc(), SMng); |
| } |
| llvm_unreachable("Unexpected CFG element at front of block"); |
| } |
| |
| return PathDiagnosticLocation( |
| BE->getBlock()->getTerminatorStmt()->getBeginLoc(), SMng); |
| } else if (Optional<FunctionExitPoint> FE = P.getAs<FunctionExitPoint>()) { |
| return PathDiagnosticLocation(FE->getStmt(), SMng, |
| FE->getLocationContext()); |
| } else { |
| llvm_unreachable("Unexpected ProgramPoint"); |
| } |
| |
| return PathDiagnosticLocation(S, SMng, P.getLocationContext()); |
| } |
| |
| PathDiagnosticLocation PathDiagnosticLocation::createSingleLocation( |
| const PathDiagnosticLocation &PDL) { |
| FullSourceLoc L = PDL.asLocation(); |
| return PathDiagnosticLocation(L, L.getManager(), SingleLocK); |
| } |
| |
| FullSourceLoc |
| PathDiagnosticLocation::genLocation(SourceLocation L, |
| LocationOrAnalysisDeclContext LAC) const { |
| assert(isValid()); |
| // Note that we want a 'switch' here so that the compiler can warn us in |
| // case we add more cases. |
| switch (K) { |
| case SingleLocK: |
| case RangeK: |
| break; |
| case StmtK: |
| // Defensive checking. |
| if (!S) |
| break; |
| return FullSourceLoc(getValidSourceLocation(S, LAC), |
| const_cast<SourceManager&>(*SM)); |
| case DeclK: |
| // Defensive checking. |
| if (!D) |
| break; |
| return FullSourceLoc(D->getLocation(), const_cast<SourceManager&>(*SM)); |
| } |
| |
| return FullSourceLoc(L, const_cast<SourceManager&>(*SM)); |
| } |
| |
| PathDiagnosticRange |
| PathDiagnosticLocation::genRange(LocationOrAnalysisDeclContext LAC) const { |
| assert(isValid()); |
| // Note that we want a 'switch' here so that the compiler can warn us in |
| // case we add more cases. |
| switch (K) { |
| case SingleLocK: |
| return PathDiagnosticRange(SourceRange(Loc,Loc), true); |
| case RangeK: |
| break; |
| case StmtK: { |
| const Stmt *S = asStmt(); |
| switch (S->getStmtClass()) { |
| default: |
| break; |
| case Stmt::DeclStmtClass: { |
| const auto *DS = cast<DeclStmt>(S); |
| if (DS->isSingleDecl()) { |
| // Should always be the case, but we'll be defensive. |
| return SourceRange(DS->getBeginLoc(), |
| DS->getSingleDecl()->getLocation()); |
| } |
| break; |
| } |
| // FIXME: Provide better range information for different |
| // terminators. |
| case Stmt::IfStmtClass: |
| case Stmt::WhileStmtClass: |
| case Stmt::DoStmtClass: |
| case Stmt::ForStmtClass: |
| case Stmt::ChooseExprClass: |
| case Stmt::IndirectGotoStmtClass: |
| case Stmt::SwitchStmtClass: |
| case Stmt::BinaryConditionalOperatorClass: |
| case Stmt::ConditionalOperatorClass: |
| case Stmt::ObjCForCollectionStmtClass: { |
| SourceLocation L = getValidSourceLocation(S, LAC); |
| return SourceRange(L, L); |
| } |
| } |
| SourceRange R = S->getSourceRange(); |
| if (R.isValid()) |
| return R; |
| break; |
| } |
| case DeclK: |
| if (const auto *MD = dyn_cast<ObjCMethodDecl>(D)) |
| return MD->getSourceRange(); |
| if (const auto *FD = dyn_cast<FunctionDecl>(D)) { |
| if (Stmt *Body = FD->getBody()) |
| return Body->getSourceRange(); |
| } |
| else { |
| SourceLocation L = D->getLocation(); |
| return PathDiagnosticRange(SourceRange(L, L), true); |
| } |
| } |
| |
| return SourceRange(Loc, Loc); |
| } |
| |
| void PathDiagnosticLocation::flatten() { |
| if (K == StmtK) { |
| K = RangeK; |
| S = nullptr; |
| D = nullptr; |
| } |
| else if (K == DeclK) { |
| K = SingleLocK; |
| S = nullptr; |
| D = nullptr; |
| } |
| } |
| |
| //===----------------------------------------------------------------------===// |
| // Manipulation of PathDiagnosticCallPieces. |
| //===----------------------------------------------------------------------===// |
| |
| std::shared_ptr<PathDiagnosticCallPiece> |
| PathDiagnosticCallPiece::construct(const CallExitEnd &CE, |
| const SourceManager &SM) { |
| const Decl *caller = CE.getLocationContext()->getDecl(); |
| PathDiagnosticLocation pos = getLocationForCaller(CE.getCalleeContext(), |
| CE.getLocationContext(), |
| SM); |
| return std::shared_ptr<PathDiagnosticCallPiece>( |
| new PathDiagnosticCallPiece(caller, pos)); |
| } |
| |
| PathDiagnosticCallPiece * |
| PathDiagnosticCallPiece::construct(PathPieces &path, |
| const Decl *caller) { |
| std::shared_ptr<PathDiagnosticCallPiece> C( |
| new PathDiagnosticCallPiece(path, caller)); |
| path.clear(); |
| auto *R = C.get(); |
| path.push_front(std::move(C)); |
| return R; |
| } |
| |
| void PathDiagnosticCallPiece::setCallee(const CallEnter &CE, |
| const SourceManager &SM) { |
| const StackFrameContext *CalleeCtx = CE.getCalleeContext(); |
| Callee = CalleeCtx->getDecl(); |
| |
| callEnterWithin = PathDiagnosticLocation::createBegin(Callee, SM); |
| callEnter = getLocationForCaller(CalleeCtx, CE.getLocationContext(), SM); |
| |
| // Autosynthesized property accessors are special because we'd never |
| // pop back up to non-autosynthesized code until we leave them. |
| // This is not generally true for autosynthesized callees, which may call |
| // non-autosynthesized callbacks. |
| // Unless set here, the IsCalleeAnAutosynthesizedPropertyAccessor flag |
| // defaults to false. |
| if (const auto *MD = dyn_cast<ObjCMethodDecl>(Callee)) |
| IsCalleeAnAutosynthesizedPropertyAccessor = ( |
| MD->isPropertyAccessor() && |
| CalleeCtx->getAnalysisDeclContext()->isBodyAutosynthesized()); |
| } |
| |
| static void describeTemplateParameters(raw_ostream &Out, |
| const ArrayRef<TemplateArgument> TAList, |
| const LangOptions &LO, |
| StringRef Prefix = StringRef(), |
| StringRef Postfix = StringRef()); |
| |
| static void describeTemplateParameter(raw_ostream &Out, |
| const TemplateArgument &TArg, |
| const LangOptions &LO) { |
| |
| if (TArg.getKind() == TemplateArgument::ArgKind::Pack) { |
| describeTemplateParameters(Out, TArg.getPackAsArray(), LO); |
| } else { |
| TArg.print(PrintingPolicy(LO), Out); |
| } |
| } |
| |
| static void describeTemplateParameters(raw_ostream &Out, |
| const ArrayRef<TemplateArgument> TAList, |
| const LangOptions &LO, |
| StringRef Prefix, StringRef Postfix) { |
| if (TAList.empty()) |
| return; |
| |
| Out << Prefix; |
| for (int I = 0, Last = TAList.size() - 1; I != Last; ++I) { |
| describeTemplateParameter(Out, TAList[I], LO); |
| Out << ", "; |
| } |
| describeTemplateParameter(Out, TAList[TAList.size() - 1], LO); |
| Out << Postfix; |
| } |
| |
| static void describeClass(raw_ostream &Out, const CXXRecordDecl *D, |
| StringRef Prefix = StringRef()) { |
| if (!D->getIdentifier()) |
| return; |
| Out << Prefix << '\'' << *D; |
| if (const auto T = dyn_cast<ClassTemplateSpecializationDecl>(D)) |
| describeTemplateParameters(Out, T->getTemplateArgs().asArray(), |
| D->getASTContext().getLangOpts(), "<", ">"); |
| |
| Out << '\''; |
| } |
| |
| static bool describeCodeDecl(raw_ostream &Out, const Decl *D, |
| bool ExtendedDescription, |
| StringRef Prefix = StringRef()) { |
| if (!D) |
| return false; |
| |
| if (isa<BlockDecl>(D)) { |
| if (ExtendedDescription) |
| Out << Prefix << "anonymous block"; |
| return ExtendedDescription; |
| } |
| |
| if (const auto *MD = dyn_cast<CXXMethodDecl>(D)) { |
| Out << Prefix; |
| if (ExtendedDescription && !MD->isUserProvided()) { |
| if (MD->isExplicitlyDefaulted()) |
| Out << "defaulted "; |
| else |
| Out << "implicit "; |
| } |
| |
| if (const auto *CD = dyn_cast<CXXConstructorDecl>(MD)) { |
| if (CD->isDefaultConstructor()) |
| Out << "default "; |
| else if (CD->isCopyConstructor()) |
| Out << "copy "; |
| else if (CD->isMoveConstructor()) |
| Out << "move "; |
| |
| Out << "constructor"; |
| describeClass(Out, MD->getParent(), " for "); |
| } else if (isa<CXXDestructorDecl>(MD)) { |
| if (!MD->isUserProvided()) { |
| Out << "destructor"; |
| describeClass(Out, MD->getParent(), " for "); |
| } else { |
| // Use ~Foo for explicitly-written destructors. |
| Out << "'" << *MD << "'"; |
| } |
| } else if (MD->isCopyAssignmentOperator()) { |
| Out << "copy assignment operator"; |
| describeClass(Out, MD->getParent(), " for "); |
| } else if (MD->isMoveAssignmentOperator()) { |
| Out << "move assignment operator"; |
| describeClass(Out, MD->getParent(), " for "); |
| } else { |
| if (MD->getParent()->getIdentifier()) |
| Out << "'" << *MD->getParent() << "::" << *MD << "'"; |
| else |
| Out << "'" << *MD << "'"; |
| } |
| |
| return true; |
| } |
| |
| Out << Prefix << '\'' << cast<NamedDecl>(*D); |
| |
| // Adding template parameters. |
| if (const auto FD = dyn_cast<FunctionDecl>(D)) |
| if (const TemplateArgumentList *TAList = |
| FD->getTemplateSpecializationArgs()) |
| describeTemplateParameters(Out, TAList->asArray(), |
| FD->getASTContext().getLangOpts(), "<", ">"); |
| |
| Out << '\''; |
| return true; |
| } |
| |
| std::shared_ptr<PathDiagnosticEventPiece> |
| PathDiagnosticCallPiece::getCallEnterEvent() const { |
| // We do not produce call enters and call exits for autosynthesized property |
| // accessors. We do generally produce them for other functions coming from |
| // the body farm because they may call callbacks that bring us back into |
| // visible code. |
| if (!Callee || IsCalleeAnAutosynthesizedPropertyAccessor) |
| return nullptr; |
| |
| SmallString<256> buf; |
| llvm::raw_svector_ostream Out(buf); |
| |
| Out << "Calling "; |
| describeCodeDecl(Out, Callee, /*ExtendedDescription=*/true); |
| |
| assert(callEnter.asLocation().isValid()); |
| return std::make_shared<PathDiagnosticEventPiece>(callEnter, Out.str()); |
| } |
| |
| std::shared_ptr<PathDiagnosticEventPiece> |
| PathDiagnosticCallPiece::getCallEnterWithinCallerEvent() const { |
| if (!callEnterWithin.asLocation().isValid()) |
| return nullptr; |
| if (Callee->isImplicit() || !Callee->hasBody()) |
| return nullptr; |
| if (const auto *MD = dyn_cast<CXXMethodDecl>(Callee)) |
| if (MD->isDefaulted()) |
| return nullptr; |
| |
| SmallString<256> buf; |
| llvm::raw_svector_ostream Out(buf); |
| |
| Out << "Entered call"; |
| describeCodeDecl(Out, Caller, /*ExtendedDescription=*/false, " from "); |
| |
| return std::make_shared<PathDiagnosticEventPiece>(callEnterWithin, Out.str()); |
| } |
| |
| std::shared_ptr<PathDiagnosticEventPiece> |
| PathDiagnosticCallPiece::getCallExitEvent() const { |
| // We do not produce call enters and call exits for autosynthesized property |
| // accessors. We do generally produce them for other functions coming from |
| // the body farm because they may call callbacks that bring us back into |
| // visible code. |
| if (NoExit || IsCalleeAnAutosynthesizedPropertyAccessor) |
| return nullptr; |
| |
| SmallString<256> buf; |
| llvm::raw_svector_ostream Out(buf); |
| |
| if (!CallStackMessage.empty()) { |
| Out << CallStackMessage; |
| } else { |
| bool DidDescribe = describeCodeDecl(Out, Callee, |
| /*ExtendedDescription=*/false, |
| "Returning from "); |
| if (!DidDescribe) |
| Out << "Returning to caller"; |
| } |
| |
| assert(callReturn.asLocation().isValid()); |
| return std::make_shared<PathDiagnosticEventPiece>(callReturn, Out.str()); |
| } |
| |
| static void compute_path_size(const PathPieces &pieces, unsigned &size) { |
| for (const auto &I : pieces) { |
| const PathDiagnosticPiece *piece = I.get(); |
| if (const auto *cp = dyn_cast<PathDiagnosticCallPiece>(piece)) |
| compute_path_size(cp->path, size); |
| else |
| ++size; |
| } |
| } |
| |
| unsigned PathDiagnostic::full_size() { |
| unsigned size = 0; |
| compute_path_size(path, size); |
| return size; |
| } |
| |
| //===----------------------------------------------------------------------===// |
| // FoldingSet profiling methods. |
| //===----------------------------------------------------------------------===// |
| |
| void PathDiagnosticLocation::Profile(llvm::FoldingSetNodeID &ID) const { |
| ID.AddInteger(Range.getBegin().getRawEncoding()); |
| ID.AddInteger(Range.getEnd().getRawEncoding()); |
| ID.AddInteger(Loc.getRawEncoding()); |
| } |
| |
| void PathDiagnosticPiece::Profile(llvm::FoldingSetNodeID &ID) const { |
| ID.AddInteger((unsigned) getKind()); |
| ID.AddString(str); |
| // FIXME: Add profiling support for code hints. |
| ID.AddInteger((unsigned) getDisplayHint()); |
| ArrayRef<SourceRange> Ranges = getRanges(); |
| for (const auto &I : Ranges) { |
| ID.AddInteger(I.getBegin().getRawEncoding()); |
| ID.AddInteger(I.getEnd().getRawEncoding()); |
| } |
| } |
| |
| void PathDiagnosticCallPiece::Profile(llvm::FoldingSetNodeID &ID) const { |
| PathDiagnosticPiece::Profile(ID); |
| for (const auto &I : path) |
| ID.Add(*I); |
| } |
| |
| void PathDiagnosticSpotPiece::Profile(llvm::FoldingSetNodeID &ID) const { |
| PathDiagnosticPiece::Profile(ID); |
| ID.Add(Pos); |
| } |
| |
| void PathDiagnosticControlFlowPiece::Profile(llvm::FoldingSetNodeID &ID) const { |
| PathDiagnosticPiece::Profile(ID); |
| for (const auto &I : *this) |
| ID.Add(I); |
| } |
| |
| void PathDiagnosticMacroPiece::Profile(llvm::FoldingSetNodeID &ID) const { |
| PathDiagnosticSpotPiece::Profile(ID); |
| for (const auto &I : subPieces) |
| ID.Add(*I); |
| } |
| |
| void PathDiagnosticNotePiece::Profile(llvm::FoldingSetNodeID &ID) const { |
| 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); |
| ID.AddString(VerboseDesc); |
| ID.AddString(Category); |
| } |
| |
| void PathDiagnostic::FullProfile(llvm::FoldingSetNodeID &ID) const { |
| Profile(ID); |
| for (const auto &I : path) |
| ID.Add(*I); |
| for (meta_iterator I = meta_begin(), E = meta_end(); I != E; ++I) |
| ID.AddString(*I); |
| } |
| |
| LLVM_DUMP_METHOD void PathPieces::dump() const { |
| unsigned index = 0; |
| for (PathPieces::const_iterator I = begin(), E = end(); I != E; ++I) { |
| llvm::errs() << "[" << index++ << "] "; |
| (*I)->dump(); |
| llvm::errs() << "\n"; |
| } |
| } |
| |
| LLVM_DUMP_METHOD void PathDiagnosticCallPiece::dump() const { |
| llvm::errs() << "CALL\n--------------\n"; |
| |
| if (const Stmt *SLoc = getLocation().getStmtOrNull()) |
| SLoc->dump(); |
| else if (const auto *ND = dyn_cast_or_null<NamedDecl>(getCallee())) |
| llvm::errs() << *ND << "\n"; |
| else |
| getLocation().dump(); |
| } |
| |
| LLVM_DUMP_METHOD void PathDiagnosticEventPiece::dump() const { |
| llvm::errs() << "EVENT\n--------------\n"; |
| llvm::errs() << getString() << "\n"; |
| llvm::errs() << " ---- at ----\n"; |
| getLocation().dump(); |
| } |
| |
| LLVM_DUMP_METHOD void PathDiagnosticControlFlowPiece::dump() const { |
| llvm::errs() << "CONTROL\n--------------\n"; |
| getStartLocation().dump(); |
| llvm::errs() << " ---- to ----\n"; |
| getEndLocation().dump(); |
| } |
| |
| LLVM_DUMP_METHOD void PathDiagnosticMacroPiece::dump() const { |
| llvm::errs() << "MACRO\n--------------\n"; |
| // FIXME: Print which macro is being invoked. |
| } |
| |
| LLVM_DUMP_METHOD void PathDiagnosticNotePiece::dump() const { |
| llvm::errs() << "NOTE\n--------------\n"; |
| llvm::errs() << getString() << "\n"; |
| llvm::errs() << " ---- at ----\n"; |
| 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() << "<INVALID>\n"; |
| return; |
| } |
| |
| switch (K) { |
| case RangeK: |
| // FIXME: actually print the range. |
| llvm::errs() << "<range>\n"; |
| break; |
| case SingleLocK: |
| asLocation().dump(); |
| llvm::errs() << "\n"; |
| break; |
| case StmtK: |
| if (S) |
| S->dump(); |
| else |
| llvm::errs() << "<NULL STMT>\n"; |
| break; |
| case DeclK: |
| if (const auto *ND = dyn_cast_or_null<NamedDecl>(D)) |
| llvm::errs() << *ND << "\n"; |
| else if (isa<BlockDecl>(D)) |
| // FIXME: Make this nicer. |
| llvm::errs() << "<block>\n"; |
| else if (D) |
| llvm::errs() << "<unknown decl>\n"; |
| else |
| llvm::errs() << "<NULL DECL>\n"; |
| break; |
| } |
| } |