Path: blob/main/contrib/llvm-project/clang/lib/Analysis/PathDiagnostic.cpp
35234 views
//===- PathDiagnostic.cpp - Path-Specific Diagnostic Handling -------------===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//7//8// This file defines the PathDiagnostic-related interfaces.9//10//===----------------------------------------------------------------------===//1112#include "clang/Analysis/PathDiagnostic.h"13#include "clang/AST/Decl.h"14#include "clang/AST/DeclBase.h"15#include "clang/AST/DeclCXX.h"16#include "clang/AST/DeclObjC.h"17#include "clang/AST/DeclTemplate.h"18#include "clang/AST/Expr.h"19#include "clang/AST/ExprCXX.h"20#include "clang/AST/OperationKinds.h"21#include "clang/AST/ParentMap.h"22#include "clang/AST/PrettyPrinter.h"23#include "clang/AST/Stmt.h"24#include "clang/AST/Type.h"25#include "clang/Analysis/AnalysisDeclContext.h"26#include "clang/Analysis/CFG.h"27#include "clang/Analysis/ProgramPoint.h"28#include "clang/Basic/FileManager.h"29#include "clang/Basic/LLVM.h"30#include "clang/Basic/SourceLocation.h"31#include "clang/Basic/SourceManager.h"32#include "llvm/ADT/ArrayRef.h"33#include "llvm/ADT/FoldingSet.h"34#include "llvm/ADT/STLExtras.h"35#include "llvm/ADT/SmallString.h"36#include "llvm/ADT/SmallVector.h"37#include "llvm/ADT/StringExtras.h"38#include "llvm/ADT/StringRef.h"39#include "llvm/Support/Casting.h"40#include "llvm/Support/ErrorHandling.h"41#include "llvm/Support/raw_ostream.h"42#include <cassert>43#include <cstring>44#include <memory>45#include <optional>46#include <utility>47#include <vector>4849using namespace clang;50using namespace ento;5152static StringRef StripTrailingDots(StringRef s) { return s.rtrim('.'); }5354PathDiagnosticPiece::PathDiagnosticPiece(StringRef s,55Kind k, DisplayHint hint)56: str(StripTrailingDots(s)), kind(k), Hint(hint) {}5758PathDiagnosticPiece::PathDiagnosticPiece(Kind k, DisplayHint hint)59: kind(k), Hint(hint) {}6061PathDiagnosticPiece::~PathDiagnosticPiece() = default;6263PathDiagnosticEventPiece::~PathDiagnosticEventPiece() = default;6465PathDiagnosticCallPiece::~PathDiagnosticCallPiece() = default;6667PathDiagnosticControlFlowPiece::~PathDiagnosticControlFlowPiece() = default;6869PathDiagnosticMacroPiece::~PathDiagnosticMacroPiece() = default;7071PathDiagnosticNotePiece::~PathDiagnosticNotePiece() = default;7273PathDiagnosticPopUpPiece::~PathDiagnosticPopUpPiece() = default;7475void PathPieces::flattenTo(PathPieces &Primary, PathPieces &Current,76bool ShouldFlattenMacros) const {77for (auto &Piece : *this) {78switch (Piece->getKind()) {79case PathDiagnosticPiece::Call: {80auto &Call = cast<PathDiagnosticCallPiece>(*Piece);81if (auto CallEnter = Call.getCallEnterEvent())82Current.push_back(std::move(CallEnter));83Call.path.flattenTo(Primary, Primary, ShouldFlattenMacros);84if (auto callExit = Call.getCallExitEvent())85Current.push_back(std::move(callExit));86break;87}88case PathDiagnosticPiece::Macro: {89auto &Macro = cast<PathDiagnosticMacroPiece>(*Piece);90if (ShouldFlattenMacros) {91Macro.subPieces.flattenTo(Primary, Primary, ShouldFlattenMacros);92} else {93Current.push_back(Piece);94PathPieces NewPath;95Macro.subPieces.flattenTo(Primary, NewPath, ShouldFlattenMacros);96// FIXME: This probably shouldn't mutate the original path piece.97Macro.subPieces = NewPath;98}99break;100}101case PathDiagnosticPiece::Event:102case PathDiagnosticPiece::ControlFlow:103case PathDiagnosticPiece::Note:104case PathDiagnosticPiece::PopUp:105Current.push_back(Piece);106break;107}108}109}110111PathDiagnostic::~PathDiagnostic() = default;112113PathDiagnostic::PathDiagnostic(114StringRef CheckerName, const Decl *declWithIssue, StringRef bugtype,115StringRef verboseDesc, StringRef shortDesc, StringRef category,116PathDiagnosticLocation LocationToUnique, const Decl *DeclToUnique,117const Decl *AnalysisEntryPoint,118std::unique_ptr<FilesToLineNumsMap> ExecutedLines)119: CheckerName(CheckerName), DeclWithIssue(declWithIssue),120BugType(StripTrailingDots(bugtype)),121VerboseDesc(StripTrailingDots(verboseDesc)),122ShortDesc(StripTrailingDots(shortDesc)),123Category(StripTrailingDots(category)), UniqueingLoc(LocationToUnique),124UniqueingDecl(DeclToUnique), AnalysisEntryPoint(AnalysisEntryPoint),125ExecutedLines(std::move(ExecutedLines)), path(pathImpl) {126assert(AnalysisEntryPoint);127}128129void PathDiagnosticConsumer::anchor() {}130131PathDiagnosticConsumer::~PathDiagnosticConsumer() {132// Delete the contents of the FoldingSet if it isn't empty already.133for (auto &Diag : Diags)134delete &Diag;135}136137void PathDiagnosticConsumer::HandlePathDiagnostic(138std::unique_ptr<PathDiagnostic> D) {139if (!D || D->path.empty())140return;141142// We need to flatten the locations (convert Stmt* to locations) because143// the referenced statements may be freed by the time the diagnostics144// are emitted.145D->flattenLocations();146147// If the PathDiagnosticConsumer does not support diagnostics that148// cross file boundaries, prune out such diagnostics now.149if (!supportsCrossFileDiagnostics()) {150// Verify that the entire path is from the same FileID.151FileID FID;152const SourceManager &SMgr = D->path.front()->getLocation().getManager();153SmallVector<const PathPieces *, 5> WorkList;154WorkList.push_back(&D->path);155SmallString<128> buf;156llvm::raw_svector_ostream warning(buf);157warning << "warning: Path diagnostic report is not generated. Current "158<< "output format does not support diagnostics that cross file "159<< "boundaries. Refer to --analyzer-output for valid output "160<< "formats\n";161162while (!WorkList.empty()) {163const PathPieces &path = *WorkList.pop_back_val();164165for (const auto &I : path) {166const PathDiagnosticPiece *piece = I.get();167FullSourceLoc L = piece->getLocation().asLocation().getExpansionLoc();168169if (FID.isInvalid()) {170FID = SMgr.getFileID(L);171} else if (SMgr.getFileID(L) != FID) {172llvm::errs() << warning.str();173return;174}175176// Check the source ranges.177ArrayRef<SourceRange> Ranges = piece->getRanges();178for (const auto &I : Ranges) {179SourceLocation L = SMgr.getExpansionLoc(I.getBegin());180if (!L.isFileID() || SMgr.getFileID(L) != FID) {181llvm::errs() << warning.str();182return;183}184L = SMgr.getExpansionLoc(I.getEnd());185if (!L.isFileID() || SMgr.getFileID(L) != FID) {186llvm::errs() << warning.str();187return;188}189}190191if (const auto *call = dyn_cast<PathDiagnosticCallPiece>(piece))192WorkList.push_back(&call->path);193else if (const auto *macro = dyn_cast<PathDiagnosticMacroPiece>(piece))194WorkList.push_back(¯o->subPieces);195}196}197198if (FID.isInvalid())199return; // FIXME: Emit a warning?200}201202// Profile the node to see if we already have something matching it203llvm::FoldingSetNodeID profile;204D->Profile(profile);205void *InsertPos = nullptr;206207if (PathDiagnostic *orig = Diags.FindNodeOrInsertPos(profile, InsertPos)) {208// Keep the PathDiagnostic with the shorter path.209// Note, the enclosing routine is called in deterministic order, so the210// results will be consistent between runs (no reason to break ties if the211// size is the same).212const unsigned orig_size = orig->full_size();213const unsigned new_size = D->full_size();214if (orig_size <= new_size)215return;216217assert(orig != D.get());218Diags.RemoveNode(orig);219delete orig;220}221222Diags.InsertNode(D.release());223}224225static std::optional<bool> comparePath(const PathPieces &X,226const PathPieces &Y);227228static std::optional<bool>229compareControlFlow(const PathDiagnosticControlFlowPiece &X,230const PathDiagnosticControlFlowPiece &Y) {231FullSourceLoc XSL = X.getStartLocation().asLocation();232FullSourceLoc YSL = Y.getStartLocation().asLocation();233if (XSL != YSL)234return XSL.isBeforeInTranslationUnitThan(YSL);235FullSourceLoc XEL = X.getEndLocation().asLocation();236FullSourceLoc YEL = Y.getEndLocation().asLocation();237if (XEL != YEL)238return XEL.isBeforeInTranslationUnitThan(YEL);239return std::nullopt;240}241242static std::optional<bool> compareMacro(const PathDiagnosticMacroPiece &X,243const PathDiagnosticMacroPiece &Y) {244return comparePath(X.subPieces, Y.subPieces);245}246247static std::optional<bool> compareCall(const PathDiagnosticCallPiece &X,248const PathDiagnosticCallPiece &Y) {249FullSourceLoc X_CEL = X.callEnter.asLocation();250FullSourceLoc Y_CEL = Y.callEnter.asLocation();251if (X_CEL != Y_CEL)252return X_CEL.isBeforeInTranslationUnitThan(Y_CEL);253FullSourceLoc X_CEWL = X.callEnterWithin.asLocation();254FullSourceLoc Y_CEWL = Y.callEnterWithin.asLocation();255if (X_CEWL != Y_CEWL)256return X_CEWL.isBeforeInTranslationUnitThan(Y_CEWL);257FullSourceLoc X_CRL = X.callReturn.asLocation();258FullSourceLoc Y_CRL = Y.callReturn.asLocation();259if (X_CRL != Y_CRL)260return X_CRL.isBeforeInTranslationUnitThan(Y_CRL);261return comparePath(X.path, Y.path);262}263264static std::optional<bool> comparePiece(const PathDiagnosticPiece &X,265const PathDiagnosticPiece &Y) {266if (X.getKind() != Y.getKind())267return X.getKind() < Y.getKind();268269FullSourceLoc XL = X.getLocation().asLocation();270FullSourceLoc YL = Y.getLocation().asLocation();271if (XL != YL)272return XL.isBeforeInTranslationUnitThan(YL);273274if (X.getString() != Y.getString())275return X.getString() < Y.getString();276277if (X.getRanges().size() != Y.getRanges().size())278return X.getRanges().size() < Y.getRanges().size();279280const SourceManager &SM = XL.getManager();281282for (unsigned i = 0, n = X.getRanges().size(); i < n; ++i) {283SourceRange XR = X.getRanges()[i];284SourceRange YR = Y.getRanges()[i];285if (XR != YR) {286if (XR.getBegin() != YR.getBegin())287return SM.isBeforeInTranslationUnit(XR.getBegin(), YR.getBegin());288return SM.isBeforeInTranslationUnit(XR.getEnd(), YR.getEnd());289}290}291292switch (X.getKind()) {293case PathDiagnosticPiece::ControlFlow:294return compareControlFlow(cast<PathDiagnosticControlFlowPiece>(X),295cast<PathDiagnosticControlFlowPiece>(Y));296case PathDiagnosticPiece::Macro:297return compareMacro(cast<PathDiagnosticMacroPiece>(X),298cast<PathDiagnosticMacroPiece>(Y));299case PathDiagnosticPiece::Call:300return compareCall(cast<PathDiagnosticCallPiece>(X),301cast<PathDiagnosticCallPiece>(Y));302case PathDiagnosticPiece::Event:303case PathDiagnosticPiece::Note:304case PathDiagnosticPiece::PopUp:305return std::nullopt;306}307llvm_unreachable("all cases handled");308}309310static std::optional<bool> comparePath(const PathPieces &X,311const PathPieces &Y) {312if (X.size() != Y.size())313return X.size() < Y.size();314315PathPieces::const_iterator X_I = X.begin(), X_end = X.end();316PathPieces::const_iterator Y_I = Y.begin(), Y_end = Y.end();317318for (; X_I != X_end && Y_I != Y_end; ++X_I, ++Y_I)319if (std::optional<bool> b = comparePiece(**X_I, **Y_I))320return *b;321322return std::nullopt;323}324325static bool compareCrossTUSourceLocs(FullSourceLoc XL, FullSourceLoc YL) {326if (XL.isInvalid() && YL.isValid())327return true;328if (XL.isValid() && YL.isInvalid())329return false;330std::pair<FileID, unsigned> XOffs = XL.getDecomposedLoc();331std::pair<FileID, unsigned> YOffs = YL.getDecomposedLoc();332const SourceManager &SM = XL.getManager();333std::pair<bool, bool> InSameTU = SM.isInTheSameTranslationUnit(XOffs, YOffs);334if (InSameTU.first)335return XL.isBeforeInTranslationUnitThan(YL);336OptionalFileEntryRef XFE =337SM.getFileEntryRefForID(XL.getSpellingLoc().getFileID());338OptionalFileEntryRef YFE =339SM.getFileEntryRefForID(YL.getSpellingLoc().getFileID());340if (!XFE || !YFE)341return XFE && !YFE;342int NameCmp = XFE->getName().compare(YFE->getName());343if (NameCmp != 0)344return NameCmp < 0;345// Last resort: Compare raw file IDs that are possibly expansions.346return XL.getFileID() < YL.getFileID();347}348349static bool compare(const PathDiagnostic &X, const PathDiagnostic &Y) {350FullSourceLoc XL = X.getLocation().asLocation();351FullSourceLoc YL = Y.getLocation().asLocation();352if (XL != YL)353return compareCrossTUSourceLocs(XL, YL);354FullSourceLoc XUL = X.getUniqueingLoc().asLocation();355FullSourceLoc YUL = Y.getUniqueingLoc().asLocation();356if (XUL != YUL)357return compareCrossTUSourceLocs(XUL, YUL);358if (X.getBugType() != Y.getBugType())359return X.getBugType() < Y.getBugType();360if (X.getCategory() != Y.getCategory())361return X.getCategory() < Y.getCategory();362if (X.getVerboseDescription() != Y.getVerboseDescription())363return X.getVerboseDescription() < Y.getVerboseDescription();364if (X.getShortDescription() != Y.getShortDescription())365return X.getShortDescription() < Y.getShortDescription();366auto CompareDecls = [&XL](const Decl *D1,367const Decl *D2) -> std::optional<bool> {368if (D1 == D2)369return std::nullopt;370if (!D1)371return true;372if (!D2)373return false;374SourceLocation D1L = D1->getLocation();375SourceLocation D2L = D2->getLocation();376if (D1L != D2L) {377const SourceManager &SM = XL.getManager();378return compareCrossTUSourceLocs(FullSourceLoc(D1L, SM),379FullSourceLoc(D2L, SM));380}381return std::nullopt;382};383if (auto Result = CompareDecls(X.getDeclWithIssue(), Y.getDeclWithIssue()))384return *Result;385if (XUL.isValid()) {386if (auto Result = CompareDecls(X.getUniqueingDecl(), Y.getUniqueingDecl()))387return *Result;388}389PathDiagnostic::meta_iterator XI = X.meta_begin(), XE = X.meta_end();390PathDiagnostic::meta_iterator YI = Y.meta_begin(), YE = Y.meta_end();391if (XE - XI != YE - YI)392return (XE - XI) < (YE - YI);393for ( ; XI != XE ; ++XI, ++YI) {394if (*XI != *YI)395return (*XI) < (*YI);396}397return *comparePath(X.path, Y.path);398}399400void PathDiagnosticConsumer::FlushDiagnostics(401PathDiagnosticConsumer::FilesMade *Files) {402if (flushed)403return;404405flushed = true;406407std::vector<const PathDiagnostic *> BatchDiags;408for (const auto &D : Diags)409BatchDiags.push_back(&D);410411// Sort the diagnostics so that they are always emitted in a deterministic412// order.413int (*Comp)(const PathDiagnostic *const *, const PathDiagnostic *const *) =414[](const PathDiagnostic *const *X, const PathDiagnostic *const *Y) {415assert(*X != *Y && "PathDiagnostics not uniqued!");416if (compare(**X, **Y))417return -1;418assert(compare(**Y, **X) && "Not a total order!");419return 1;420};421array_pod_sort(BatchDiags.begin(), BatchDiags.end(), Comp);422423FlushDiagnosticsImpl(BatchDiags, Files);424425// Delete the flushed diagnostics.426for (const auto D : BatchDiags)427delete D;428429// Clear out the FoldingSet.430Diags.clear();431}432433PathDiagnosticConsumer::FilesMade::~FilesMade() {434for (auto It = Set.begin(); It != Set.end();)435(It++)->~PDFileEntry();436}437438void PathDiagnosticConsumer::FilesMade::addDiagnostic(const PathDiagnostic &PD,439StringRef ConsumerName,440StringRef FileName) {441llvm::FoldingSetNodeID NodeID;442NodeID.Add(PD);443void *InsertPos;444PDFileEntry *Entry = Set.FindNodeOrInsertPos(NodeID, InsertPos);445if (!Entry) {446Entry = Alloc.Allocate<PDFileEntry>();447Entry = new (Entry) PDFileEntry(NodeID);448Set.InsertNode(Entry, InsertPos);449}450451// Allocate persistent storage for the file name.452char *FileName_cstr = (char*) Alloc.Allocate(FileName.size(), 1);453memcpy(FileName_cstr, FileName.data(), FileName.size());454455Entry->files.push_back(std::make_pair(ConsumerName,456StringRef(FileName_cstr,457FileName.size())));458}459460PathDiagnosticConsumer::PDFileEntry::ConsumerFiles *461PathDiagnosticConsumer::FilesMade::getFiles(const PathDiagnostic &PD) {462llvm::FoldingSetNodeID NodeID;463NodeID.Add(PD);464void *InsertPos;465PDFileEntry *Entry = Set.FindNodeOrInsertPos(NodeID, InsertPos);466if (!Entry)467return nullptr;468return &Entry->files;469}470471//===----------------------------------------------------------------------===//472// PathDiagnosticLocation methods.473//===----------------------------------------------------------------------===//474475SourceLocation PathDiagnosticLocation::getValidSourceLocation(476const Stmt *S, LocationOrAnalysisDeclContext LAC, bool UseEndOfStatement) {477SourceLocation L = UseEndOfStatement ? S->getEndLoc() : S->getBeginLoc();478assert(!LAC.isNull() &&479"A valid LocationContext or AnalysisDeclContext should be passed to "480"PathDiagnosticLocation upon creation.");481482// S might be a temporary statement that does not have a location in the483// source code, so find an enclosing statement and use its location.484if (!L.isValid()) {485AnalysisDeclContext *ADC;486if (LAC.is<const LocationContext*>())487ADC = LAC.get<const LocationContext*>()->getAnalysisDeclContext();488else489ADC = LAC.get<AnalysisDeclContext*>();490491ParentMap &PM = ADC->getParentMap();492493const Stmt *Parent = S;494do {495Parent = PM.getParent(Parent);496497// In rare cases, we have implicit top-level expressions,498// such as arguments for implicit member initializers.499// In this case, fall back to the start of the body (even if we were500// asked for the statement end location).501if (!Parent) {502const Stmt *Body = ADC->getBody();503if (Body)504L = Body->getBeginLoc();505else506L = ADC->getDecl()->getEndLoc();507break;508}509510L = UseEndOfStatement ? Parent->getEndLoc() : Parent->getBeginLoc();511} while (!L.isValid());512}513514// FIXME: Ironically, this assert actually fails in some cases.515//assert(L.isValid());516return L;517}518519static PathDiagnosticLocation520getLocationForCaller(const StackFrameContext *SFC,521const LocationContext *CallerCtx,522const SourceManager &SM) {523const CFGBlock &Block = *SFC->getCallSiteBlock();524CFGElement Source = Block[SFC->getIndex()];525526switch (Source.getKind()) {527case CFGElement::Statement:528case CFGElement::Constructor:529case CFGElement::CXXRecordTypedCall:530return PathDiagnosticLocation(Source.castAs<CFGStmt>().getStmt(),531SM, CallerCtx);532case CFGElement::Initializer: {533const CFGInitializer &Init = Source.castAs<CFGInitializer>();534return PathDiagnosticLocation(Init.getInitializer()->getInit(),535SM, CallerCtx);536}537case CFGElement::AutomaticObjectDtor: {538const CFGAutomaticObjDtor &Dtor = Source.castAs<CFGAutomaticObjDtor>();539return PathDiagnosticLocation::createEnd(Dtor.getTriggerStmt(),540SM, CallerCtx);541}542case CFGElement::DeleteDtor: {543const CFGDeleteDtor &Dtor = Source.castAs<CFGDeleteDtor>();544return PathDiagnosticLocation(Dtor.getDeleteExpr(), SM, CallerCtx);545}546case CFGElement::BaseDtor:547case CFGElement::MemberDtor: {548const AnalysisDeclContext *CallerInfo = CallerCtx->getAnalysisDeclContext();549if (const Stmt *CallerBody = CallerInfo->getBody())550return PathDiagnosticLocation::createEnd(CallerBody, SM, CallerCtx);551return PathDiagnosticLocation::create(CallerInfo->getDecl(), SM);552}553case CFGElement::NewAllocator: {554const CFGNewAllocator &Alloc = Source.castAs<CFGNewAllocator>();555return PathDiagnosticLocation(Alloc.getAllocatorExpr(), SM, CallerCtx);556}557case CFGElement::TemporaryDtor: {558// Temporary destructors are for temporaries. They die immediately at around559// the location of CXXBindTemporaryExpr. If they are lifetime-extended,560// they'd be dealt with via an AutomaticObjectDtor instead.561const auto &Dtor = Source.castAs<CFGTemporaryDtor>();562return PathDiagnosticLocation::createEnd(Dtor.getBindTemporaryExpr(), SM,563CallerCtx);564}565case CFGElement::ScopeBegin:566case CFGElement::ScopeEnd:567case CFGElement::CleanupFunction:568llvm_unreachable("not yet implemented!");569case CFGElement::LifetimeEnds:570case CFGElement::LoopExit:571llvm_unreachable("CFGElement kind should not be on callsite!");572}573574llvm_unreachable("Unknown CFGElement kind");575}576577PathDiagnosticLocation578PathDiagnosticLocation::createBegin(const Decl *D,579const SourceManager &SM) {580return PathDiagnosticLocation(D->getBeginLoc(), SM, SingleLocK);581}582583PathDiagnosticLocation584PathDiagnosticLocation::createBegin(const Stmt *S,585const SourceManager &SM,586LocationOrAnalysisDeclContext LAC) {587assert(S && "Statement cannot be null");588return PathDiagnosticLocation(getValidSourceLocation(S, LAC),589SM, SingleLocK);590}591592PathDiagnosticLocation593PathDiagnosticLocation::createEnd(const Stmt *S,594const SourceManager &SM,595LocationOrAnalysisDeclContext LAC) {596if (const auto *CS = dyn_cast<CompoundStmt>(S))597return createEndBrace(CS, SM);598return PathDiagnosticLocation(getValidSourceLocation(S, LAC, /*End=*/true),599SM, SingleLocK);600}601602PathDiagnosticLocation603PathDiagnosticLocation::createOperatorLoc(const BinaryOperator *BO,604const SourceManager &SM) {605return PathDiagnosticLocation(BO->getOperatorLoc(), SM, SingleLocK);606}607608PathDiagnosticLocation609PathDiagnosticLocation::createConditionalColonLoc(610const ConditionalOperator *CO,611const SourceManager &SM) {612return PathDiagnosticLocation(CO->getColonLoc(), SM, SingleLocK);613}614615PathDiagnosticLocation616PathDiagnosticLocation::createMemberLoc(const MemberExpr *ME,617const SourceManager &SM) {618619assert(ME->getMemberLoc().isValid() || ME->getBeginLoc().isValid());620621// In some cases, getMemberLoc isn't valid -- in this case we'll return with622// some other related valid SourceLocation.623if (ME->getMemberLoc().isValid())624return PathDiagnosticLocation(ME->getMemberLoc(), SM, SingleLocK);625626return PathDiagnosticLocation(ME->getBeginLoc(), SM, SingleLocK);627}628629PathDiagnosticLocation630PathDiagnosticLocation::createBeginBrace(const CompoundStmt *CS,631const SourceManager &SM) {632SourceLocation L = CS->getLBracLoc();633return PathDiagnosticLocation(L, SM, SingleLocK);634}635636PathDiagnosticLocation637PathDiagnosticLocation::createEndBrace(const CompoundStmt *CS,638const SourceManager &SM) {639SourceLocation L = CS->getRBracLoc();640return PathDiagnosticLocation(L, SM, SingleLocK);641}642643PathDiagnosticLocation644PathDiagnosticLocation::createDeclBegin(const LocationContext *LC,645const SourceManager &SM) {646// FIXME: Should handle CXXTryStmt if analyser starts supporting C++.647if (const auto *CS = dyn_cast_or_null<CompoundStmt>(LC->getDecl()->getBody()))648if (!CS->body_empty()) {649SourceLocation Loc = (*CS->body_begin())->getBeginLoc();650return PathDiagnosticLocation(Loc, SM, SingleLocK);651}652653return PathDiagnosticLocation();654}655656PathDiagnosticLocation657PathDiagnosticLocation::createDeclEnd(const LocationContext *LC,658const SourceManager &SM) {659SourceLocation L = LC->getDecl()->getBodyRBrace();660return PathDiagnosticLocation(L, SM, SingleLocK);661}662663PathDiagnosticLocation664PathDiagnosticLocation::create(const ProgramPoint& P,665const SourceManager &SMng) {666const Stmt* S = nullptr;667if (std::optional<BlockEdge> BE = P.getAs<BlockEdge>()) {668const CFGBlock *BSrc = BE->getSrc();669if (BSrc->getTerminator().isVirtualBaseBranch()) {670// TODO: VirtualBaseBranches should also appear for destructors.671// In this case we should put the diagnostic at the end of decl.672return PathDiagnosticLocation::createBegin(673P.getLocationContext()->getDecl(), SMng);674675} else {676S = BSrc->getTerminatorCondition();677if (!S) {678// If the BlockEdge has no terminator condition statement but its679// source is the entry of the CFG (e.g. a checker crated the branch at680// the beginning of a function), use the function's declaration instead.681assert(BSrc == &BSrc->getParent()->getEntry() && "CFGBlock has no "682"TerminatorCondition and is not the enrty block of the CFG");683return PathDiagnosticLocation::createBegin(684P.getLocationContext()->getDecl(), SMng);685}686}687} else if (std::optional<StmtPoint> SP = P.getAs<StmtPoint>()) {688S = SP->getStmt();689if (P.getAs<PostStmtPurgeDeadSymbols>())690return PathDiagnosticLocation::createEnd(S, SMng, P.getLocationContext());691} else if (std::optional<PostInitializer> PIP = P.getAs<PostInitializer>()) {692return PathDiagnosticLocation(PIP->getInitializer()->getSourceLocation(),693SMng);694} else if (std::optional<PreImplicitCall> PIC = P.getAs<PreImplicitCall>()) {695return PathDiagnosticLocation(PIC->getLocation(), SMng);696} else if (std::optional<PostImplicitCall> PIE =697P.getAs<PostImplicitCall>()) {698return PathDiagnosticLocation(PIE->getLocation(), SMng);699} else if (std::optional<CallEnter> CE = P.getAs<CallEnter>()) {700return getLocationForCaller(CE->getCalleeContext(),701CE->getLocationContext(),702SMng);703} else if (std::optional<CallExitEnd> CEE = P.getAs<CallExitEnd>()) {704return getLocationForCaller(CEE->getCalleeContext(),705CEE->getLocationContext(),706SMng);707} else if (auto CEB = P.getAs<CallExitBegin>()) {708if (const ReturnStmt *RS = CEB->getReturnStmt())709return PathDiagnosticLocation::createBegin(RS, SMng,710CEB->getLocationContext());711return PathDiagnosticLocation(712CEB->getLocationContext()->getDecl()->getSourceRange().getEnd(), SMng);713} else if (std::optional<BlockEntrance> BE = P.getAs<BlockEntrance>()) {714if (std::optional<CFGElement> BlockFront = BE->getFirstElement()) {715if (auto StmtElt = BlockFront->getAs<CFGStmt>()) {716return PathDiagnosticLocation(StmtElt->getStmt()->getBeginLoc(), SMng);717} else if (auto NewAllocElt = BlockFront->getAs<CFGNewAllocator>()) {718return PathDiagnosticLocation(719NewAllocElt->getAllocatorExpr()->getBeginLoc(), SMng);720}721llvm_unreachable("Unexpected CFG element at front of block");722}723724return PathDiagnosticLocation(725BE->getBlock()->getTerminatorStmt()->getBeginLoc(), SMng);726} else if (std::optional<FunctionExitPoint> FE =727P.getAs<FunctionExitPoint>()) {728return PathDiagnosticLocation(FE->getStmt(), SMng,729FE->getLocationContext());730} else {731llvm_unreachable("Unexpected ProgramPoint");732}733734return PathDiagnosticLocation(S, SMng, P.getLocationContext());735}736737PathDiagnosticLocation PathDiagnosticLocation::createSingleLocation(738const PathDiagnosticLocation &PDL) {739FullSourceLoc L = PDL.asLocation();740return PathDiagnosticLocation(L, L.getManager(), SingleLocK);741}742743FullSourceLoc744PathDiagnosticLocation::genLocation(SourceLocation L,745LocationOrAnalysisDeclContext LAC) const {746assert(isValid());747// Note that we want a 'switch' here so that the compiler can warn us in748// case we add more cases.749switch (K) {750case SingleLocK:751case RangeK:752break;753case StmtK:754// Defensive checking.755if (!S)756break;757return FullSourceLoc(getValidSourceLocation(S, LAC),758const_cast<SourceManager&>(*SM));759case DeclK:760// Defensive checking.761if (!D)762break;763return FullSourceLoc(D->getLocation(), const_cast<SourceManager&>(*SM));764}765766return FullSourceLoc(L, const_cast<SourceManager&>(*SM));767}768769PathDiagnosticRange770PathDiagnosticLocation::genRange(LocationOrAnalysisDeclContext LAC) const {771assert(isValid());772// Note that we want a 'switch' here so that the compiler can warn us in773// case we add more cases.774switch (K) {775case SingleLocK:776return PathDiagnosticRange(SourceRange(Loc,Loc), true);777case RangeK:778break;779case StmtK: {780const Stmt *S = asStmt();781switch (S->getStmtClass()) {782default:783break;784case Stmt::DeclStmtClass: {785const auto *DS = cast<DeclStmt>(S);786if (DS->isSingleDecl()) {787// Should always be the case, but we'll be defensive.788return SourceRange(DS->getBeginLoc(),789DS->getSingleDecl()->getLocation());790}791break;792}793// FIXME: Provide better range information for different794// terminators.795case Stmt::IfStmtClass:796case Stmt::WhileStmtClass:797case Stmt::DoStmtClass:798case Stmt::ForStmtClass:799case Stmt::ChooseExprClass:800case Stmt::IndirectGotoStmtClass:801case Stmt::SwitchStmtClass:802case Stmt::BinaryConditionalOperatorClass:803case Stmt::ConditionalOperatorClass:804case Stmt::ObjCForCollectionStmtClass: {805SourceLocation L = getValidSourceLocation(S, LAC);806return SourceRange(L, L);807}808}809SourceRange R = S->getSourceRange();810if (R.isValid())811return R;812break;813}814case DeclK:815if (const auto *MD = dyn_cast<ObjCMethodDecl>(D))816return MD->getSourceRange();817if (const auto *FD = dyn_cast<FunctionDecl>(D)) {818if (Stmt *Body = FD->getBody())819return Body->getSourceRange();820}821else {822SourceLocation L = D->getLocation();823return PathDiagnosticRange(SourceRange(L, L), true);824}825}826827return SourceRange(Loc, Loc);828}829830void PathDiagnosticLocation::flatten() {831if (K == StmtK) {832K = RangeK;833S = nullptr;834D = nullptr;835}836else if (K == DeclK) {837K = SingleLocK;838S = nullptr;839D = nullptr;840}841}842843//===----------------------------------------------------------------------===//844// Manipulation of PathDiagnosticCallPieces.845//===----------------------------------------------------------------------===//846847std::shared_ptr<PathDiagnosticCallPiece>848PathDiagnosticCallPiece::construct(const CallExitEnd &CE,849const SourceManager &SM) {850const Decl *caller = CE.getLocationContext()->getDecl();851PathDiagnosticLocation pos = getLocationForCaller(CE.getCalleeContext(),852CE.getLocationContext(),853SM);854return std::shared_ptr<PathDiagnosticCallPiece>(855new PathDiagnosticCallPiece(caller, pos));856}857858PathDiagnosticCallPiece *859PathDiagnosticCallPiece::construct(PathPieces &path,860const Decl *caller) {861std::shared_ptr<PathDiagnosticCallPiece> C(862new PathDiagnosticCallPiece(path, caller));863path.clear();864auto *R = C.get();865path.push_front(std::move(C));866return R;867}868869void PathDiagnosticCallPiece::setCallee(const CallEnter &CE,870const SourceManager &SM) {871const StackFrameContext *CalleeCtx = CE.getCalleeContext();872Callee = CalleeCtx->getDecl();873874callEnterWithin = PathDiagnosticLocation::createBegin(Callee, SM);875callEnter = getLocationForCaller(CalleeCtx, CE.getLocationContext(), SM);876877// Autosynthesized property accessors are special because we'd never878// pop back up to non-autosynthesized code until we leave them.879// This is not generally true for autosynthesized callees, which may call880// non-autosynthesized callbacks.881// Unless set here, the IsCalleeAnAutosynthesizedPropertyAccessor flag882// defaults to false.883if (const auto *MD = dyn_cast<ObjCMethodDecl>(Callee))884IsCalleeAnAutosynthesizedPropertyAccessor = (885MD->isPropertyAccessor() &&886CalleeCtx->getAnalysisDeclContext()->isBodyAutosynthesized());887}888889static void describeTemplateParameters(raw_ostream &Out,890const ArrayRef<TemplateArgument> TAList,891const LangOptions &LO,892StringRef Prefix = StringRef(),893StringRef Postfix = StringRef());894895static void describeTemplateParameter(raw_ostream &Out,896const TemplateArgument &TArg,897const LangOptions &LO) {898899if (TArg.getKind() == TemplateArgument::ArgKind::Pack) {900describeTemplateParameters(Out, TArg.getPackAsArray(), LO);901} else {902TArg.print(PrintingPolicy(LO), Out, /*IncludeType*/ true);903}904}905906static void describeTemplateParameters(raw_ostream &Out,907const ArrayRef<TemplateArgument> TAList,908const LangOptions &LO,909StringRef Prefix, StringRef Postfix) {910if (TAList.empty())911return;912913Out << Prefix;914for (int I = 0, Last = TAList.size() - 1; I != Last; ++I) {915describeTemplateParameter(Out, TAList[I], LO);916Out << ", ";917}918describeTemplateParameter(Out, TAList[TAList.size() - 1], LO);919Out << Postfix;920}921922static void describeClass(raw_ostream &Out, const CXXRecordDecl *D,923StringRef Prefix = StringRef()) {924if (!D->getIdentifier())925return;926Out << Prefix << '\'' << *D;927if (const auto T = dyn_cast<ClassTemplateSpecializationDecl>(D))928describeTemplateParameters(Out, T->getTemplateArgs().asArray(),929D->getLangOpts(), "<", ">");930931Out << '\'';932}933934static bool describeCodeDecl(raw_ostream &Out, const Decl *D,935bool ExtendedDescription,936StringRef Prefix = StringRef()) {937if (!D)938return false;939940if (isa<BlockDecl>(D)) {941if (ExtendedDescription)942Out << Prefix << "anonymous block";943return ExtendedDescription;944}945946if (const auto *MD = dyn_cast<CXXMethodDecl>(D)) {947Out << Prefix;948if (ExtendedDescription && !MD->isUserProvided()) {949if (MD->isExplicitlyDefaulted())950Out << "defaulted ";951else952Out << "implicit ";953}954955if (const auto *CD = dyn_cast<CXXConstructorDecl>(MD)) {956if (CD->isDefaultConstructor())957Out << "default ";958else if (CD->isCopyConstructor())959Out << "copy ";960else if (CD->isMoveConstructor())961Out << "move ";962963Out << "constructor";964describeClass(Out, MD->getParent(), " for ");965} else if (isa<CXXDestructorDecl>(MD)) {966if (!MD->isUserProvided()) {967Out << "destructor";968describeClass(Out, MD->getParent(), " for ");969} else {970// Use ~Foo for explicitly-written destructors.971Out << "'" << *MD << "'";972}973} else if (MD->isCopyAssignmentOperator()) {974Out << "copy assignment operator";975describeClass(Out, MD->getParent(), " for ");976} else if (MD->isMoveAssignmentOperator()) {977Out << "move assignment operator";978describeClass(Out, MD->getParent(), " for ");979} else {980if (MD->getParent()->getIdentifier())981Out << "'" << *MD->getParent() << "::" << *MD << "'";982else983Out << "'" << *MD << "'";984}985986return true;987}988989Out << Prefix << '\'' << cast<NamedDecl>(*D);990991// Adding template parameters.992if (const auto FD = dyn_cast<FunctionDecl>(D))993if (const TemplateArgumentList *TAList =994FD->getTemplateSpecializationArgs())995describeTemplateParameters(Out, TAList->asArray(), FD->getLangOpts(), "<",996">");997998Out << '\'';999return true;1000}10011002std::shared_ptr<PathDiagnosticEventPiece>1003PathDiagnosticCallPiece::getCallEnterEvent() const {1004// We do not produce call enters and call exits for autosynthesized property1005// accessors. We do generally produce them for other functions coming from1006// the body farm because they may call callbacks that bring us back into1007// visible code.1008if (!Callee || IsCalleeAnAutosynthesizedPropertyAccessor)1009return nullptr;10101011SmallString<256> buf;1012llvm::raw_svector_ostream Out(buf);10131014Out << "Calling ";1015describeCodeDecl(Out, Callee, /*ExtendedDescription=*/true);10161017assert(callEnter.asLocation().isValid());1018return std::make_shared<PathDiagnosticEventPiece>(callEnter, Out.str());1019}10201021std::shared_ptr<PathDiagnosticEventPiece>1022PathDiagnosticCallPiece::getCallEnterWithinCallerEvent() const {1023if (!callEnterWithin.asLocation().isValid())1024return nullptr;1025if (Callee->isImplicit() || !Callee->hasBody())1026return nullptr;1027if (const auto *MD = dyn_cast<CXXMethodDecl>(Callee))1028if (MD->isDefaulted())1029return nullptr;10301031SmallString<256> buf;1032llvm::raw_svector_ostream Out(buf);10331034Out << "Entered call";1035describeCodeDecl(Out, Caller, /*ExtendedDescription=*/false, " from ");10361037return std::make_shared<PathDiagnosticEventPiece>(callEnterWithin, Out.str());1038}10391040std::shared_ptr<PathDiagnosticEventPiece>1041PathDiagnosticCallPiece::getCallExitEvent() const {1042// We do not produce call enters and call exits for autosynthesized property1043// accessors. We do generally produce them for other functions coming from1044// the body farm because they may call callbacks that bring us back into1045// visible code.1046if (NoExit || IsCalleeAnAutosynthesizedPropertyAccessor)1047return nullptr;10481049SmallString<256> buf;1050llvm::raw_svector_ostream Out(buf);10511052if (!CallStackMessage.empty()) {1053Out << CallStackMessage;1054} else {1055bool DidDescribe = describeCodeDecl(Out, Callee,1056/*ExtendedDescription=*/false,1057"Returning from ");1058if (!DidDescribe)1059Out << "Returning to caller";1060}10611062assert(callReturn.asLocation().isValid());1063return std::make_shared<PathDiagnosticEventPiece>(callReturn, Out.str());1064}10651066static void compute_path_size(const PathPieces &pieces, unsigned &size) {1067for (const auto &I : pieces) {1068const PathDiagnosticPiece *piece = I.get();1069if (const auto *cp = dyn_cast<PathDiagnosticCallPiece>(piece))1070compute_path_size(cp->path, size);1071else1072++size;1073}1074}10751076unsigned PathDiagnostic::full_size() {1077unsigned size = 0;1078compute_path_size(path, size);1079return size;1080}10811082//===----------------------------------------------------------------------===//1083// FoldingSet profiling methods.1084//===----------------------------------------------------------------------===//10851086void PathDiagnosticLocation::Profile(llvm::FoldingSetNodeID &ID) const {1087ID.Add(Range.getBegin());1088ID.Add(Range.getEnd());1089ID.Add(static_cast<const SourceLocation &>(Loc));1090}10911092void PathDiagnosticPiece::Profile(llvm::FoldingSetNodeID &ID) const {1093ID.AddInteger((unsigned) getKind());1094ID.AddString(str);1095// FIXME: Add profiling support for code hints.1096ID.AddInteger((unsigned) getDisplayHint());1097ArrayRef<SourceRange> Ranges = getRanges();1098for (const auto &I : Ranges) {1099ID.Add(I.getBegin());1100ID.Add(I.getEnd());1101}1102}11031104void PathDiagnosticCallPiece::Profile(llvm::FoldingSetNodeID &ID) const {1105PathDiagnosticPiece::Profile(ID);1106for (const auto &I : path)1107ID.Add(*I);1108}11091110void PathDiagnosticSpotPiece::Profile(llvm::FoldingSetNodeID &ID) const {1111PathDiagnosticPiece::Profile(ID);1112ID.Add(Pos);1113}11141115void PathDiagnosticControlFlowPiece::Profile(llvm::FoldingSetNodeID &ID) const {1116PathDiagnosticPiece::Profile(ID);1117for (const auto &I : *this)1118ID.Add(I);1119}11201121void PathDiagnosticMacroPiece::Profile(llvm::FoldingSetNodeID &ID) const {1122PathDiagnosticSpotPiece::Profile(ID);1123for (const auto &I : subPieces)1124ID.Add(*I);1125}11261127void PathDiagnosticNotePiece::Profile(llvm::FoldingSetNodeID &ID) const {1128PathDiagnosticSpotPiece::Profile(ID);1129}11301131void PathDiagnosticPopUpPiece::Profile(llvm::FoldingSetNodeID &ID) const {1132PathDiagnosticSpotPiece::Profile(ID);1133}11341135void PathDiagnostic::Profile(llvm::FoldingSetNodeID &ID) const {1136ID.Add(getLocation());1137ID.Add(getUniqueingLoc());1138ID.AddString(BugType);1139ID.AddString(VerboseDesc);1140ID.AddString(Category);1141}11421143void PathDiagnostic::FullProfile(llvm::FoldingSetNodeID &ID) const {1144Profile(ID);1145for (const auto &I : path)1146ID.Add(*I);1147for (meta_iterator I = meta_begin(), E = meta_end(); I != E; ++I)1148ID.AddString(*I);1149}11501151LLVM_DUMP_METHOD void PathPieces::dump() const {1152unsigned index = 0;1153for (PathPieces::const_iterator I = begin(), E = end(); I != E; ++I) {1154llvm::errs() << "[" << index++ << "] ";1155(*I)->dump();1156llvm::errs() << "\n";1157}1158}11591160LLVM_DUMP_METHOD void PathDiagnosticCallPiece::dump() const {1161llvm::errs() << "CALL\n--------------\n";11621163if (const Stmt *SLoc = getLocation().getStmtOrNull())1164SLoc->dump();1165else if (const auto *ND = dyn_cast_or_null<NamedDecl>(getCallee()))1166llvm::errs() << *ND << "\n";1167else1168getLocation().dump();1169}11701171LLVM_DUMP_METHOD void PathDiagnosticEventPiece::dump() const {1172llvm::errs() << "EVENT\n--------------\n";1173llvm::errs() << getString() << "\n";1174llvm::errs() << " ---- at ----\n";1175getLocation().dump();1176}11771178LLVM_DUMP_METHOD void PathDiagnosticControlFlowPiece::dump() const {1179llvm::errs() << "CONTROL\n--------------\n";1180getStartLocation().dump();1181llvm::errs() << " ---- to ----\n";1182getEndLocation().dump();1183}11841185LLVM_DUMP_METHOD void PathDiagnosticMacroPiece::dump() const {1186llvm::errs() << "MACRO\n--------------\n";1187// FIXME: Print which macro is being invoked.1188}11891190LLVM_DUMP_METHOD void PathDiagnosticNotePiece::dump() const {1191llvm::errs() << "NOTE\n--------------\n";1192llvm::errs() << getString() << "\n";1193llvm::errs() << " ---- at ----\n";1194getLocation().dump();1195}11961197LLVM_DUMP_METHOD void PathDiagnosticPopUpPiece::dump() const {1198llvm::errs() << "POP-UP\n--------------\n";1199llvm::errs() << getString() << "\n";1200llvm::errs() << " ---- at ----\n";1201getLocation().dump();1202}12031204LLVM_DUMP_METHOD void PathDiagnosticLocation::dump() const {1205if (!isValid()) {1206llvm::errs() << "<INVALID>\n";1207return;1208}12091210switch (K) {1211case RangeK:1212// FIXME: actually print the range.1213llvm::errs() << "<range>\n";1214break;1215case SingleLocK:1216asLocation().dump();1217llvm::errs() << "\n";1218break;1219case StmtK:1220if (S)1221S->dump();1222else1223llvm::errs() << "<NULL STMT>\n";1224break;1225case DeclK:1226if (const auto *ND = dyn_cast_or_null<NamedDecl>(D))1227llvm::errs() << *ND << "\n";1228else if (isa<BlockDecl>(D))1229// FIXME: Make this nicer.1230llvm::errs() << "<block>\n";1231else if (D)1232llvm::errs() << "<unknown decl>\n";1233else1234llvm::errs() << "<NULL DECL>\n";1235break;1236}1237}123812391240