1248 lines · cpp
1//===- PathDiagnostic.cpp - Path-Specific Diagnostic Handling -------------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8//9// This file defines the PathDiagnostic-related interfaces.10//11//===----------------------------------------------------------------------===//12 13#include "clang/Analysis/PathDiagnostic.h"14#include "clang/AST/Decl.h"15#include "clang/AST/DeclBase.h"16#include "clang/AST/DeclCXX.h"17#include "clang/AST/DeclObjC.h"18#include "clang/AST/DeclTemplate.h"19#include "clang/AST/Expr.h"20#include "clang/AST/ExprCXX.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/IssueHash.h"28#include "clang/Analysis/ProgramPoint.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/StringExtras.h"36#include "llvm/ADT/StringRef.h"37#include "llvm/Support/ErrorHandling.h"38#include "llvm/Support/raw_ostream.h"39#include <cassert>40#include <cstring>41#include <memory>42#include <optional>43#include <utility>44#include <vector>45 46using namespace clang;47using namespace ento;48 49static StringRef StripTrailingDots(StringRef s) { return s.rtrim('.'); }50 51PathDiagnosticPiece::PathDiagnosticPiece(StringRef s,52 Kind k, DisplayHint hint)53 : str(StripTrailingDots(s)), kind(k), Hint(hint) {}54 55PathDiagnosticPiece::PathDiagnosticPiece(Kind k, DisplayHint hint)56 : kind(k), Hint(hint) {}57 58PathDiagnosticPiece::~PathDiagnosticPiece() = default;59 60PathDiagnosticEventPiece::~PathDiagnosticEventPiece() = default;61 62PathDiagnosticCallPiece::~PathDiagnosticCallPiece() = default;63 64PathDiagnosticControlFlowPiece::~PathDiagnosticControlFlowPiece() = default;65 66PathDiagnosticMacroPiece::~PathDiagnosticMacroPiece() = default;67 68PathDiagnosticNotePiece::~PathDiagnosticNotePiece() = default;69 70PathDiagnosticPopUpPiece::~PathDiagnosticPopUpPiece() = default;71 72void PathPieces::flattenTo(PathPieces &Primary, PathPieces &Current,73 bool ShouldFlattenMacros) const {74 for (auto &Piece : *this) {75 switch (Piece->getKind()) {76 case PathDiagnosticPiece::Call: {77 auto &Call = cast<PathDiagnosticCallPiece>(*Piece);78 if (auto CallEnter = Call.getCallEnterEvent())79 Current.push_back(std::move(CallEnter));80 Call.path.flattenTo(Primary, Primary, ShouldFlattenMacros);81 if (auto callExit = Call.getCallExitEvent())82 Current.push_back(std::move(callExit));83 break;84 }85 case PathDiagnosticPiece::Macro: {86 auto &Macro = cast<PathDiagnosticMacroPiece>(*Piece);87 if (ShouldFlattenMacros) {88 Macro.subPieces.flattenTo(Primary, Primary, ShouldFlattenMacros);89 } else {90 Current.push_back(Piece);91 PathPieces NewPath;92 Macro.subPieces.flattenTo(Primary, NewPath, ShouldFlattenMacros);93 // FIXME: This probably shouldn't mutate the original path piece.94 Macro.subPieces = NewPath;95 }96 break;97 }98 case PathDiagnosticPiece::Event:99 case PathDiagnosticPiece::ControlFlow:100 case PathDiagnosticPiece::Note:101 case PathDiagnosticPiece::PopUp:102 Current.push_back(Piece);103 break;104 }105 }106}107 108PathDiagnostic::~PathDiagnostic() = default;109 110PathDiagnostic::PathDiagnostic(111 StringRef CheckerName, const Decl *declWithIssue, StringRef bugtype,112 StringRef verboseDesc, StringRef shortDesc, StringRef category,113 PathDiagnosticLocation LocationToUnique, const Decl *DeclToUnique,114 const Decl *AnalysisEntryPoint,115 std::unique_ptr<FilesToLineNumsMap> ExecutedLines)116 : CheckerName(CheckerName), DeclWithIssue(declWithIssue),117 BugType(StripTrailingDots(bugtype)),118 VerboseDesc(StripTrailingDots(verboseDesc)),119 ShortDesc(StripTrailingDots(shortDesc)),120 Category(StripTrailingDots(category)), UniqueingLoc(LocationToUnique),121 UniqueingDecl(DeclToUnique), AnalysisEntryPoint(AnalysisEntryPoint),122 ExecutedLines(std::move(ExecutedLines)), path(pathImpl) {123 assert(AnalysisEntryPoint);124}125 126void PathDiagnosticConsumer::anchor() {}127 128PathDiagnosticConsumer::~PathDiagnosticConsumer() {129 // Delete the contents of the FoldingSet if it isn't empty already.130 for (auto &Diag : Diags)131 delete &Diag;132}133 134void PathDiagnosticConsumer::HandlePathDiagnostic(135 std::unique_ptr<PathDiagnostic> D) {136 if (!D || D->path.empty())137 return;138 139 // We need to flatten the locations (convert Stmt* to locations) because140 // the referenced statements may be freed by the time the diagnostics141 // are emitted.142 D->flattenLocations();143 144 // If the PathDiagnosticConsumer does not support diagnostics that145 // cross file boundaries, prune out such diagnostics now.146 if (!supportsCrossFileDiagnostics()) {147 // Verify that the entire path is from the same FileID.148 FileID FID;149 const SourceManager &SMgr = D->path.front()->getLocation().getManager();150 SmallVector<const PathPieces *, 5> WorkList;151 WorkList.push_back(&D->path);152 SmallString<128> buf;153 llvm::raw_svector_ostream warning(buf);154 warning << "warning: Path diagnostic report is not generated. Current "155 << "output format does not support diagnostics that cross file "156 << "boundaries. Refer to --analyzer-output for valid output "157 << "formats\n";158 159 while (!WorkList.empty()) {160 const PathPieces &path = *WorkList.pop_back_val();161 162 for (const auto &I : path) {163 const PathDiagnosticPiece *piece = I.get();164 FullSourceLoc L = piece->getLocation().asLocation().getExpansionLoc();165 166 if (FID.isInvalid()) {167 FID = SMgr.getFileID(L);168 } else if (SMgr.getFileID(L) != FID) {169 llvm::errs() << warning.str();170 return;171 }172 173 // Check the source ranges.174 ArrayRef<SourceRange> Ranges = piece->getRanges();175 for (const auto &I : Ranges) {176 SourceLocation L = SMgr.getExpansionLoc(I.getBegin());177 if (!L.isFileID() || SMgr.getFileID(L) != FID) {178 llvm::errs() << warning.str();179 return;180 }181 L = SMgr.getExpansionLoc(I.getEnd());182 if (!L.isFileID() || SMgr.getFileID(L) != FID) {183 llvm::errs() << warning.str();184 return;185 }186 }187 188 if (const auto *call = dyn_cast<PathDiagnosticCallPiece>(piece))189 WorkList.push_back(&call->path);190 else if (const auto *macro = dyn_cast<PathDiagnosticMacroPiece>(piece))191 WorkList.push_back(¯o->subPieces);192 }193 }194 195 if (FID.isInvalid())196 return; // FIXME: Emit a warning?197 }198 199 // Profile the node to see if we already have something matching it200 llvm::FoldingSetNodeID profile;201 D->Profile(profile);202 void *InsertPos = nullptr;203 204 if (PathDiagnostic *orig = Diags.FindNodeOrInsertPos(profile, InsertPos)) {205 // Keep the PathDiagnostic with the shorter path.206 // Note, the enclosing routine is called in deterministic order, so the207 // results will be consistent between runs (no reason to break ties if the208 // size is the same).209 const unsigned orig_size = orig->full_size();210 const unsigned new_size = D->full_size();211 if (orig_size <= new_size)212 return;213 214 assert(orig != D.get());215 Diags.RemoveNode(orig);216 delete orig;217 }218 219 Diags.InsertNode(D.release());220}221 222static std::optional<bool> comparePath(const PathPieces &X,223 const PathPieces &Y);224 225static std::optional<bool>226compareControlFlow(const PathDiagnosticControlFlowPiece &X,227 const PathDiagnosticControlFlowPiece &Y) {228 FullSourceLoc XSL = X.getStartLocation().asLocation();229 FullSourceLoc YSL = Y.getStartLocation().asLocation();230 if (XSL != YSL)231 return XSL.isBeforeInTranslationUnitThan(YSL);232 FullSourceLoc XEL = X.getEndLocation().asLocation();233 FullSourceLoc YEL = Y.getEndLocation().asLocation();234 if (XEL != YEL)235 return XEL.isBeforeInTranslationUnitThan(YEL);236 return std::nullopt;237}238 239static std::optional<bool> compareMacro(const PathDiagnosticMacroPiece &X,240 const PathDiagnosticMacroPiece &Y) {241 return comparePath(X.subPieces, Y.subPieces);242}243 244static std::optional<bool> compareCall(const PathDiagnosticCallPiece &X,245 const PathDiagnosticCallPiece &Y) {246 FullSourceLoc X_CEL = X.callEnter.asLocation();247 FullSourceLoc Y_CEL = Y.callEnter.asLocation();248 if (X_CEL != Y_CEL)249 return X_CEL.isBeforeInTranslationUnitThan(Y_CEL);250 FullSourceLoc X_CEWL = X.callEnterWithin.asLocation();251 FullSourceLoc Y_CEWL = Y.callEnterWithin.asLocation();252 if (X_CEWL != Y_CEWL)253 return X_CEWL.isBeforeInTranslationUnitThan(Y_CEWL);254 FullSourceLoc X_CRL = X.callReturn.asLocation();255 FullSourceLoc Y_CRL = Y.callReturn.asLocation();256 if (X_CRL != Y_CRL)257 return X_CRL.isBeforeInTranslationUnitThan(Y_CRL);258 return comparePath(X.path, Y.path);259}260 261static std::optional<bool> comparePiece(const PathDiagnosticPiece &X,262 const PathDiagnosticPiece &Y) {263 if (X.getKind() != Y.getKind())264 return X.getKind() < Y.getKind();265 266 FullSourceLoc XL = X.getLocation().asLocation();267 FullSourceLoc YL = Y.getLocation().asLocation();268 if (XL != YL)269 return XL.isBeforeInTranslationUnitThan(YL);270 271 if (X.getString() != Y.getString())272 return X.getString() < Y.getString();273 274 if (X.getRanges().size() != Y.getRanges().size())275 return X.getRanges().size() < Y.getRanges().size();276 277 const SourceManager &SM = XL.getManager();278 279 for (unsigned i = 0, n = X.getRanges().size(); i < n; ++i) {280 SourceRange XR = X.getRanges()[i];281 SourceRange YR = Y.getRanges()[i];282 if (XR != YR) {283 if (XR.getBegin() != YR.getBegin())284 return SM.isBeforeInTranslationUnit(XR.getBegin(), YR.getBegin());285 return SM.isBeforeInTranslationUnit(XR.getEnd(), YR.getEnd());286 }287 }288 289 switch (X.getKind()) {290 case PathDiagnosticPiece::ControlFlow:291 return compareControlFlow(cast<PathDiagnosticControlFlowPiece>(X),292 cast<PathDiagnosticControlFlowPiece>(Y));293 case PathDiagnosticPiece::Macro:294 return compareMacro(cast<PathDiagnosticMacroPiece>(X),295 cast<PathDiagnosticMacroPiece>(Y));296 case PathDiagnosticPiece::Call:297 return compareCall(cast<PathDiagnosticCallPiece>(X),298 cast<PathDiagnosticCallPiece>(Y));299 case PathDiagnosticPiece::Event:300 case PathDiagnosticPiece::Note:301 case PathDiagnosticPiece::PopUp:302 return std::nullopt;303 }304 llvm_unreachable("all cases handled");305}306 307static std::optional<bool> comparePath(const PathPieces &X,308 const PathPieces &Y) {309 if (X.size() != Y.size())310 return X.size() < Y.size();311 312 PathPieces::const_iterator X_I = X.begin(), X_end = X.end();313 PathPieces::const_iterator Y_I = Y.begin(), Y_end = Y.end();314 315 for (; X_I != X_end && Y_I != Y_end; ++X_I, ++Y_I)316 if (std::optional<bool> b = comparePiece(**X_I, **Y_I))317 return *b;318 319 return std::nullopt;320}321 322static bool compareCrossTUSourceLocs(FullSourceLoc XL, FullSourceLoc YL) {323 if (XL.isInvalid() && YL.isValid())324 return true;325 if (XL.isValid() && YL.isInvalid())326 return false;327 FileIDAndOffset XOffs = XL.getDecomposedLoc();328 FileIDAndOffset YOffs = YL.getDecomposedLoc();329 const SourceManager &SM = XL.getManager();330 std::pair<bool, bool> InSameTU = SM.isInTheSameTranslationUnit(XOffs, YOffs);331 if (InSameTU.first)332 return XL.isBeforeInTranslationUnitThan(YL);333 OptionalFileEntryRef XFE =334 SM.getFileEntryRefForID(XL.getSpellingLoc().getFileID());335 OptionalFileEntryRef YFE =336 SM.getFileEntryRefForID(YL.getSpellingLoc().getFileID());337 if (!XFE || !YFE)338 return XFE && !YFE;339 int NameCmp = XFE->getName().compare(YFE->getName());340 if (NameCmp != 0)341 return NameCmp < 0;342 // Last resort: Compare raw file IDs that are possibly expansions.343 return XL.getFileID() < YL.getFileID();344}345 346static bool compare(const PathDiagnostic &X, const PathDiagnostic &Y) {347 FullSourceLoc XL = X.getLocation().asLocation();348 FullSourceLoc YL = Y.getLocation().asLocation();349 if (XL != YL)350 return compareCrossTUSourceLocs(XL, YL);351 FullSourceLoc XUL = X.getUniqueingLoc().asLocation();352 FullSourceLoc YUL = Y.getUniqueingLoc().asLocation();353 if (XUL != YUL)354 return compareCrossTUSourceLocs(XUL, YUL);355 if (X.getBugType() != Y.getBugType())356 return X.getBugType() < Y.getBugType();357 if (X.getCategory() != Y.getCategory())358 return X.getCategory() < Y.getCategory();359 if (X.getVerboseDescription() != Y.getVerboseDescription())360 return X.getVerboseDescription() < Y.getVerboseDescription();361 if (X.getShortDescription() != Y.getShortDescription())362 return X.getShortDescription() < Y.getShortDescription();363 auto CompareDecls = [&XL](const Decl *D1,364 const Decl *D2) -> std::optional<bool> {365 if (D1 == D2)366 return std::nullopt;367 if (!D1)368 return true;369 if (!D2)370 return false;371 SourceLocation D1L = D1->getLocation();372 SourceLocation D2L = D2->getLocation();373 if (D1L != D2L) {374 const SourceManager &SM = XL.getManager();375 return compareCrossTUSourceLocs(FullSourceLoc(D1L, SM),376 FullSourceLoc(D2L, SM));377 }378 return std::nullopt;379 };380 if (auto Result = CompareDecls(X.getDeclWithIssue(), Y.getDeclWithIssue()))381 return *Result;382 if (XUL.isValid()) {383 if (auto Result = CompareDecls(X.getUniqueingDecl(), Y.getUniqueingDecl()))384 return *Result;385 }386 PathDiagnostic::meta_iterator XI = X.meta_begin(), XE = X.meta_end();387 PathDiagnostic::meta_iterator YI = Y.meta_begin(), YE = Y.meta_end();388 if (XE - XI != YE - YI)389 return (XE - XI) < (YE - YI);390 for ( ; XI != XE ; ++XI, ++YI) {391 if (*XI != *YI)392 return (*XI) < (*YI);393 }394 return *comparePath(X.path, Y.path);395}396 397void PathDiagnosticConsumer::FlushDiagnostics(398 PathDiagnosticConsumer::FilesMade *Files) {399 if (flushed)400 return;401 402 flushed = true;403 404 std::vector<const PathDiagnostic *> BatchDiags;405 for (const auto &D : Diags)406 BatchDiags.push_back(&D);407 408 // Sort the diagnostics so that they are always emitted in a deterministic409 // order.410 int (*Comp)(const PathDiagnostic *const *, const PathDiagnostic *const *) =411 [](const PathDiagnostic *const *X, const PathDiagnostic *const *Y) {412 assert(*X != *Y && "PathDiagnostics not uniqued!");413 if (compare(**X, **Y))414 return -1;415 assert(compare(**Y, **X) && "Not a total order!");416 return 1;417 };418 array_pod_sort(BatchDiags.begin(), BatchDiags.end(), Comp);419 420 FlushDiagnosticsImpl(BatchDiags, Files);421 422 // Delete the flushed diagnostics.423 for (const auto D : BatchDiags)424 delete D;425 426 // Clear out the FoldingSet.427 Diags.clear();428}429 430PathDiagnosticConsumer::FilesMade::~FilesMade() {431 for (auto It = Set.begin(); It != Set.end();)432 (It++)->~PDFileEntry();433}434 435void PathDiagnosticConsumer::FilesMade::addDiagnostic(const PathDiagnostic &PD,436 StringRef ConsumerName,437 StringRef FileName) {438 llvm::FoldingSetNodeID NodeID;439 NodeID.Add(PD);440 void *InsertPos;441 PDFileEntry *Entry = Set.FindNodeOrInsertPos(NodeID, InsertPos);442 if (!Entry) {443 Entry = Alloc.Allocate<PDFileEntry>();444 Entry = new (Entry) PDFileEntry(NodeID);445 Set.InsertNode(Entry, InsertPos);446 }447 448 // Allocate persistent storage for the file name.449 char *FileName_cstr = (char*) Alloc.Allocate(FileName.size(), 1);450 memcpy(FileName_cstr, FileName.data(), FileName.size());451 452 Entry->files.push_back(std::make_pair(ConsumerName,453 StringRef(FileName_cstr,454 FileName.size())));455}456 457PathDiagnosticConsumer::PDFileEntry::ConsumerFiles *458PathDiagnosticConsumer::FilesMade::getFiles(const PathDiagnostic &PD) {459 llvm::FoldingSetNodeID NodeID;460 NodeID.Add(PD);461 void *InsertPos;462 PDFileEntry *Entry = Set.FindNodeOrInsertPos(NodeID, InsertPos);463 if (!Entry)464 return nullptr;465 return &Entry->files;466}467 468//===----------------------------------------------------------------------===//469// PathDiagnosticLocation methods.470//===----------------------------------------------------------------------===//471 472SourceLocation PathDiagnosticLocation::getValidSourceLocation(473 const Stmt *S, LocationOrAnalysisDeclContext LAC, bool UseEndOfStatement) {474 SourceLocation L = UseEndOfStatement ? S->getEndLoc() : S->getBeginLoc();475 assert(!LAC.isNull() &&476 "A valid LocationContext or AnalysisDeclContext should be passed to "477 "PathDiagnosticLocation upon creation.");478 479 // S might be a temporary statement that does not have a location in the480 // source code, so find an enclosing statement and use its location.481 if (!L.isValid()) {482 AnalysisDeclContext *ADC;483 if (auto *LC = dyn_cast<const LocationContext *>(LAC))484 ADC = LC->getAnalysisDeclContext();485 else486 ADC = cast<AnalysisDeclContext *>(LAC);487 488 ParentMap &PM = ADC->getParentMap();489 490 const Stmt *Parent = S;491 do {492 Parent = PM.getParent(Parent);493 494 // In rare cases, we have implicit top-level expressions,495 // such as arguments for implicit member initializers.496 // In this case, fall back to the start of the body (even if we were497 // asked for the statement end location).498 if (!Parent) {499 const Stmt *Body = ADC->getBody();500 if (Body)501 L = Body->getBeginLoc();502 else503 L = ADC->getDecl()->getEndLoc();504 break;505 }506 507 L = UseEndOfStatement ? Parent->getEndLoc() : Parent->getBeginLoc();508 } while (!L.isValid());509 }510 511 // FIXME: Ironically, this assert actually fails in some cases.512 //assert(L.isValid());513 return L;514}515 516static PathDiagnosticLocation517getLocationForCaller(const StackFrameContext *SFC,518 const LocationContext *CallerCtx,519 const SourceManager &SM) {520 const CFGBlock &Block = *SFC->getCallSiteBlock();521 CFGElement Source = Block[SFC->getIndex()];522 523 switch (Source.getKind()) {524 case CFGElement::Statement:525 case CFGElement::Constructor:526 case CFGElement::CXXRecordTypedCall:527 return PathDiagnosticLocation(Source.castAs<CFGStmt>().getStmt(),528 SM, CallerCtx);529 case CFGElement::Initializer: {530 const CFGInitializer &Init = Source.castAs<CFGInitializer>();531 return PathDiagnosticLocation(Init.getInitializer()->getInit(),532 SM, CallerCtx);533 }534 case CFGElement::AutomaticObjectDtor: {535 const CFGAutomaticObjDtor &Dtor = Source.castAs<CFGAutomaticObjDtor>();536 return PathDiagnosticLocation::createEnd(Dtor.getTriggerStmt(),537 SM, CallerCtx);538 }539 case CFGElement::DeleteDtor: {540 const CFGDeleteDtor &Dtor = Source.castAs<CFGDeleteDtor>();541 return PathDiagnosticLocation(Dtor.getDeleteExpr(), SM, CallerCtx);542 }543 case CFGElement::BaseDtor:544 case CFGElement::MemberDtor: {545 const AnalysisDeclContext *CallerInfo = CallerCtx->getAnalysisDeclContext();546 if (const Stmt *CallerBody = CallerInfo->getBody())547 return PathDiagnosticLocation::createEnd(CallerBody, SM, CallerCtx);548 return PathDiagnosticLocation::create(CallerInfo->getDecl(), SM);549 }550 case CFGElement::NewAllocator: {551 const CFGNewAllocator &Alloc = Source.castAs<CFGNewAllocator>();552 return PathDiagnosticLocation(Alloc.getAllocatorExpr(), SM, CallerCtx);553 }554 case CFGElement::TemporaryDtor: {555 // Temporary destructors are for temporaries. They die immediately at around556 // the location of CXXBindTemporaryExpr. If they are lifetime-extended,557 // they'd be dealt with via an AutomaticObjectDtor instead.558 const auto &Dtor = Source.castAs<CFGTemporaryDtor>();559 return PathDiagnosticLocation::createEnd(Dtor.getBindTemporaryExpr(), SM,560 CallerCtx);561 }562 case CFGElement::ScopeBegin:563 case CFGElement::ScopeEnd:564 case CFGElement::CleanupFunction:565 llvm_unreachable("not yet implemented!");566 case CFGElement::LifetimeEnds:567 case CFGElement::LoopExit:568 llvm_unreachable("CFGElement kind should not be on callsite!");569 }570 571 llvm_unreachable("Unknown CFGElement kind");572}573 574PathDiagnosticLocation575PathDiagnosticLocation::createBegin(const Decl *D,576 const SourceManager &SM) {577 return PathDiagnosticLocation(D->getBeginLoc(), SM, SingleLocK);578}579 580PathDiagnosticLocation581PathDiagnosticLocation::createBegin(const Stmt *S,582 const SourceManager &SM,583 LocationOrAnalysisDeclContext LAC) {584 assert(S && "Statement cannot be null");585 return PathDiagnosticLocation(getValidSourceLocation(S, LAC),586 SM, SingleLocK);587}588 589PathDiagnosticLocation590PathDiagnosticLocation::createEnd(const Stmt *S,591 const SourceManager &SM,592 LocationOrAnalysisDeclContext LAC) {593 if (const auto *CS = dyn_cast<CompoundStmt>(S))594 return createEndBrace(CS, SM);595 return PathDiagnosticLocation(getValidSourceLocation(S, LAC, /*End=*/true),596 SM, SingleLocK);597}598 599PathDiagnosticLocation600PathDiagnosticLocation::createOperatorLoc(const BinaryOperator *BO,601 const SourceManager &SM) {602 return PathDiagnosticLocation(BO->getOperatorLoc(), SM, SingleLocK);603}604 605PathDiagnosticLocation606PathDiagnosticLocation::createConditionalColonLoc(607 const ConditionalOperator *CO,608 const SourceManager &SM) {609 return PathDiagnosticLocation(CO->getColonLoc(), SM, SingleLocK);610}611 612PathDiagnosticLocation613PathDiagnosticLocation::createMemberLoc(const MemberExpr *ME,614 const SourceManager &SM) {615 616 assert(ME->getMemberLoc().isValid() || ME->getBeginLoc().isValid());617 618 // In some cases, getMemberLoc isn't valid -- in this case we'll return with619 // some other related valid SourceLocation.620 if (ME->getMemberLoc().isValid())621 return PathDiagnosticLocation(ME->getMemberLoc(), SM, SingleLocK);622 623 return PathDiagnosticLocation(ME->getBeginLoc(), SM, SingleLocK);624}625 626PathDiagnosticLocation627PathDiagnosticLocation::createBeginBrace(const CompoundStmt *CS,628 const SourceManager &SM) {629 SourceLocation L = CS->getLBracLoc();630 return PathDiagnosticLocation(L, SM, SingleLocK);631}632 633PathDiagnosticLocation634PathDiagnosticLocation::createEndBrace(const CompoundStmt *CS,635 const SourceManager &SM) {636 SourceLocation L = CS->getRBracLoc();637 return PathDiagnosticLocation(L, SM, SingleLocK);638}639 640PathDiagnosticLocation641PathDiagnosticLocation::createDeclBegin(const LocationContext *LC,642 const SourceManager &SM) {643 // FIXME: Should handle CXXTryStmt if analyser starts supporting C++.644 if (const auto *CS = dyn_cast_or_null<CompoundStmt>(LC->getDecl()->getBody()))645 if (!CS->body_empty()) {646 SourceLocation Loc = (*CS->body_begin())->getBeginLoc();647 return PathDiagnosticLocation(Loc, SM, SingleLocK);648 }649 650 return PathDiagnosticLocation();651}652 653PathDiagnosticLocation654PathDiagnosticLocation::createDeclEnd(const LocationContext *LC,655 const SourceManager &SM) {656 SourceLocation L = LC->getDecl()->getBodyRBrace();657 return PathDiagnosticLocation(L, SM, SingleLocK);658}659 660PathDiagnosticLocation661PathDiagnosticLocation::create(const ProgramPoint& P,662 const SourceManager &SMng) {663 const Stmt* S = nullptr;664 if (std::optional<BlockEdge> BE = P.getAs<BlockEdge>()) {665 const CFGBlock *BSrc = BE->getSrc();666 if (BSrc->getTerminator().isVirtualBaseBranch()) {667 // TODO: VirtualBaseBranches should also appear for destructors.668 // In this case we should put the diagnostic at the end of decl.669 return PathDiagnosticLocation::createBegin(670 P.getLocationContext()->getDecl(), SMng);671 672 } else {673 S = BSrc->getTerminatorCondition();674 if (!S) {675 // If the BlockEdge has no terminator condition statement but its676 // source is the entry of the CFG (e.g. a checker crated the branch at677 // the beginning of a function), use the function's declaration instead.678 assert(BSrc == &BSrc->getParent()->getEntry() && "CFGBlock has no "679 "TerminatorCondition and is not the enrty block of the CFG");680 return PathDiagnosticLocation::createBegin(681 P.getLocationContext()->getDecl(), SMng);682 }683 }684 } else if (std::optional<StmtPoint> SP = P.getAs<StmtPoint>()) {685 S = SP->getStmt();686 if (P.getAs<PostStmtPurgeDeadSymbols>())687 return PathDiagnosticLocation::createEnd(S, SMng, P.getLocationContext());688 } else if (std::optional<PostInitializer> PIP = P.getAs<PostInitializer>()) {689 return PathDiagnosticLocation(PIP->getInitializer()->getSourceLocation(),690 SMng);691 } else if (std::optional<PreImplicitCall> PIC = P.getAs<PreImplicitCall>()) {692 return PathDiagnosticLocation(PIC->getLocation(), SMng);693 } else if (std::optional<PostImplicitCall> PIE =694 P.getAs<PostImplicitCall>()) {695 return PathDiagnosticLocation(PIE->getLocation(), SMng);696 } else if (std::optional<CallEnter> CE = P.getAs<CallEnter>()) {697 return getLocationForCaller(CE->getCalleeContext(),698 CE->getLocationContext(),699 SMng);700 } else if (std::optional<CallExitEnd> CEE = P.getAs<CallExitEnd>()) {701 return getLocationForCaller(CEE->getCalleeContext(),702 CEE->getLocationContext(),703 SMng);704 } else if (auto CEB = P.getAs<CallExitBegin>()) {705 if (const ReturnStmt *RS = CEB->getReturnStmt())706 return PathDiagnosticLocation::createBegin(RS, SMng,707 CEB->getLocationContext());708 return PathDiagnosticLocation(709 CEB->getLocationContext()->getDecl()->getSourceRange().getEnd(), SMng);710 } else if (std::optional<BlockEntrance> BE = P.getAs<BlockEntrance>()) {711 if (std::optional<CFGElement> BlockFront = BE->getFirstElement()) {712 if (auto StmtElt = BlockFront->getAs<CFGStmt>()) {713 return PathDiagnosticLocation(StmtElt->getStmt()->getBeginLoc(), SMng);714 } else if (auto NewAllocElt = BlockFront->getAs<CFGNewAllocator>()) {715 return PathDiagnosticLocation(716 NewAllocElt->getAllocatorExpr()->getBeginLoc(), SMng);717 }718 llvm_unreachable("Unexpected CFG element at front of block");719 }720 721 return PathDiagnosticLocation(722 BE->getBlock()->getTerminatorStmt()->getBeginLoc(), SMng);723 } else if (std::optional<FunctionExitPoint> FE =724 P.getAs<FunctionExitPoint>()) {725 return PathDiagnosticLocation(FE->getStmt(), SMng,726 FE->getLocationContext());727 } else {728 llvm_unreachable("Unexpected ProgramPoint");729 }730 731 return PathDiagnosticLocation(S, SMng, P.getLocationContext());732}733 734PathDiagnosticLocation PathDiagnosticLocation::createSingleLocation(735 const PathDiagnosticLocation &PDL) {736 FullSourceLoc L = PDL.asLocation();737 return PathDiagnosticLocation(L, L.getManager(), SingleLocK);738}739 740FullSourceLoc741 PathDiagnosticLocation::genLocation(SourceLocation L,742 LocationOrAnalysisDeclContext LAC) const {743 assert(isValid());744 // Note that we want a 'switch' here so that the compiler can warn us in745 // case we add more cases.746 switch (K) {747 case SingleLocK:748 case RangeK:749 break;750 case StmtK:751 // Defensive checking.752 if (!S)753 break;754 return FullSourceLoc(getValidSourceLocation(S, LAC),755 const_cast<SourceManager&>(*SM));756 case DeclK:757 // Defensive checking.758 if (!D)759 break;760 return FullSourceLoc(D->getLocation(), const_cast<SourceManager&>(*SM));761 }762 763 return FullSourceLoc(L, const_cast<SourceManager&>(*SM));764}765 766PathDiagnosticRange767 PathDiagnosticLocation::genRange(LocationOrAnalysisDeclContext LAC) const {768 assert(isValid());769 // Note that we want a 'switch' here so that the compiler can warn us in770 // case we add more cases.771 switch (K) {772 case SingleLocK:773 return PathDiagnosticRange(SourceRange(Loc,Loc), true);774 case RangeK:775 break;776 case StmtK: {777 const Stmt *S = asStmt();778 switch (S->getStmtClass()) {779 default:780 break;781 case Stmt::DeclStmtClass: {782 const auto *DS = cast<DeclStmt>(S);783 if (DS->isSingleDecl()) {784 // Should always be the case, but we'll be defensive.785 return SourceRange(DS->getBeginLoc(),786 DS->getSingleDecl()->getLocation());787 }788 break;789 }790 // FIXME: Provide better range information for different791 // terminators.792 case Stmt::IfStmtClass:793 case Stmt::WhileStmtClass:794 case Stmt::DoStmtClass:795 case Stmt::ForStmtClass:796 case Stmt::ChooseExprClass:797 case Stmt::IndirectGotoStmtClass:798 case Stmt::SwitchStmtClass:799 case Stmt::BinaryConditionalOperatorClass:800 case Stmt::ConditionalOperatorClass:801 case Stmt::ObjCForCollectionStmtClass: {802 SourceLocation L = getValidSourceLocation(S, LAC);803 return SourceRange(L, L);804 }805 }806 SourceRange R = S->getSourceRange();807 if (R.isValid())808 return R;809 break;810 }811 case DeclK:812 if (const auto *MD = dyn_cast<ObjCMethodDecl>(D))813 return MD->getSourceRange();814 if (const auto *FD = dyn_cast<FunctionDecl>(D)) {815 if (Stmt *Body = FD->getBody())816 return Body->getSourceRange();817 }818 else {819 SourceLocation L = D->getLocation();820 return PathDiagnosticRange(SourceRange(L, L), true);821 }822 }823 824 return SourceRange(Loc, Loc);825}826 827void PathDiagnosticLocation::flatten() {828 if (K == StmtK) {829 K = RangeK;830 S = nullptr;831 D = nullptr;832 }833 else if (K == DeclK) {834 K = SingleLocK;835 S = nullptr;836 D = nullptr;837 }838}839 840//===----------------------------------------------------------------------===//841// Manipulation of PathDiagnosticCallPieces.842//===----------------------------------------------------------------------===//843 844std::shared_ptr<PathDiagnosticCallPiece>845PathDiagnosticCallPiece::construct(const CallExitEnd &CE,846 const SourceManager &SM) {847 const Decl *caller = CE.getLocationContext()->getDecl();848 PathDiagnosticLocation pos = getLocationForCaller(CE.getCalleeContext(),849 CE.getLocationContext(),850 SM);851 return std::shared_ptr<PathDiagnosticCallPiece>(852 new PathDiagnosticCallPiece(caller, pos));853}854 855PathDiagnosticCallPiece *856PathDiagnosticCallPiece::construct(PathPieces &path,857 const Decl *caller) {858 std::shared_ptr<PathDiagnosticCallPiece> C(859 new PathDiagnosticCallPiece(path, caller));860 path.clear();861 auto *R = C.get();862 path.push_front(std::move(C));863 return R;864}865 866void PathDiagnosticCallPiece::setCallee(const CallEnter &CE,867 const SourceManager &SM) {868 const StackFrameContext *CalleeCtx = CE.getCalleeContext();869 Callee = CalleeCtx->getDecl();870 871 callEnterWithin = PathDiagnosticLocation::createBegin(Callee, SM);872 callEnter = getLocationForCaller(CalleeCtx, CE.getLocationContext(), SM);873 874 // Autosynthesized property accessors are special because we'd never875 // pop back up to non-autosynthesized code until we leave them.876 // This is not generally true for autosynthesized callees, which may call877 // non-autosynthesized callbacks.878 // Unless set here, the IsCalleeAnAutosynthesizedPropertyAccessor flag879 // defaults to false.880 if (const auto *MD = dyn_cast<ObjCMethodDecl>(Callee))881 IsCalleeAnAutosynthesizedPropertyAccessor = (882 MD->isPropertyAccessor() &&883 CalleeCtx->getAnalysisDeclContext()->isBodyAutosynthesized());884}885 886static void describeTemplateParameters(raw_ostream &Out,887 const ArrayRef<TemplateArgument> TAList,888 const LangOptions &LO,889 StringRef Prefix = StringRef(),890 StringRef Postfix = StringRef());891 892static void describeTemplateParameter(raw_ostream &Out,893 const TemplateArgument &TArg,894 const LangOptions &LO) {895 896 if (TArg.getKind() == TemplateArgument::ArgKind::Pack) {897 describeTemplateParameters(Out, TArg.getPackAsArray(), LO);898 } else {899 TArg.print(PrintingPolicy(LO), Out, /*IncludeType*/ true);900 }901}902 903static void describeTemplateParameters(raw_ostream &Out,904 const ArrayRef<TemplateArgument> TAList,905 const LangOptions &LO,906 StringRef Prefix, StringRef Postfix) {907 if (TAList.empty())908 return;909 910 Out << Prefix;911 for (int I = 0, Last = TAList.size() - 1; I != Last; ++I) {912 describeTemplateParameter(Out, TAList[I], LO);913 Out << ", ";914 }915 describeTemplateParameter(Out, TAList[TAList.size() - 1], LO);916 Out << Postfix;917}918 919static void describeClass(raw_ostream &Out, const CXXRecordDecl *D,920 StringRef Prefix = StringRef()) {921 if (!D->getIdentifier())922 return;923 Out << Prefix << '\'' << *D;924 if (const auto T = dyn_cast<ClassTemplateSpecializationDecl>(D))925 describeTemplateParameters(Out, T->getTemplateArgs().asArray(),926 D->getLangOpts(), "<", ">");927 928 Out << '\'';929}930 931static bool describeCodeDecl(raw_ostream &Out, const Decl *D,932 bool ExtendedDescription,933 StringRef Prefix = StringRef()) {934 if (!D)935 return false;936 937 if (isa<BlockDecl>(D)) {938 if (ExtendedDescription)939 Out << Prefix << "anonymous block";940 return ExtendedDescription;941 }942 943 if (const auto *MD = dyn_cast<CXXMethodDecl>(D)) {944 Out << Prefix;945 if (ExtendedDescription && !MD->isUserProvided()) {946 if (MD->isExplicitlyDefaulted())947 Out << "defaulted ";948 else949 Out << "implicit ";950 }951 952 if (const auto *CD = dyn_cast<CXXConstructorDecl>(MD)) {953 if (CD->isDefaultConstructor())954 Out << "default ";955 else if (CD->isCopyConstructor())956 Out << "copy ";957 else if (CD->isMoveConstructor())958 Out << "move ";959 960 Out << "constructor";961 describeClass(Out, MD->getParent(), " for ");962 } else if (isa<CXXDestructorDecl>(MD)) {963 if (!MD->isUserProvided()) {964 Out << "destructor";965 describeClass(Out, MD->getParent(), " for ");966 } else {967 // Use ~Foo for explicitly-written destructors.968 Out << "'" << *MD << "'";969 }970 } else if (MD->isCopyAssignmentOperator()) {971 Out << "copy assignment operator";972 describeClass(Out, MD->getParent(), " for ");973 } else if (MD->isMoveAssignmentOperator()) {974 Out << "move assignment operator";975 describeClass(Out, MD->getParent(), " for ");976 } else {977 if (MD->getParent()->getIdentifier())978 Out << "'" << *MD->getParent() << "::" << *MD << "'";979 else980 Out << "'" << *MD << "'";981 }982 983 return true;984 }985 986 Out << Prefix << '\'' << cast<NamedDecl>(*D);987 988 // Adding template parameters.989 if (const auto FD = dyn_cast<FunctionDecl>(D))990 if (const TemplateArgumentList *TAList =991 FD->getTemplateSpecializationArgs())992 describeTemplateParameters(Out, TAList->asArray(), FD->getLangOpts(), "<",993 ">");994 995 Out << '\'';996 return true;997}998 999std::shared_ptr<PathDiagnosticEventPiece>1000PathDiagnosticCallPiece::getCallEnterEvent() const {1001 // We do not produce call enters and call exits for autosynthesized property1002 // accessors. We do generally produce them for other functions coming from1003 // the body farm because they may call callbacks that bring us back into1004 // visible code.1005 if (!Callee || IsCalleeAnAutosynthesizedPropertyAccessor)1006 return nullptr;1007 1008 SmallString<256> buf;1009 llvm::raw_svector_ostream Out(buf);1010 1011 Out << "Calling ";1012 describeCodeDecl(Out, Callee, /*ExtendedDescription=*/true);1013 1014 assert(callEnter.asLocation().isValid());1015 return std::make_shared<PathDiagnosticEventPiece>(callEnter, Out.str());1016}1017 1018std::shared_ptr<PathDiagnosticEventPiece>1019PathDiagnosticCallPiece::getCallEnterWithinCallerEvent() const {1020 if (!callEnterWithin.asLocation().isValid())1021 return nullptr;1022 if (Callee->isImplicit() || !Callee->hasBody())1023 return nullptr;1024 if (const auto *MD = dyn_cast<CXXMethodDecl>(Callee))1025 if (MD->isDefaulted())1026 return nullptr;1027 1028 SmallString<256> buf;1029 llvm::raw_svector_ostream Out(buf);1030 1031 Out << "Entered call";1032 describeCodeDecl(Out, Caller, /*ExtendedDescription=*/false, " from ");1033 1034 return std::make_shared<PathDiagnosticEventPiece>(callEnterWithin, Out.str());1035}1036 1037std::shared_ptr<PathDiagnosticEventPiece>1038PathDiagnosticCallPiece::getCallExitEvent() const {1039 // We do not produce call enters and call exits for autosynthesized property1040 // accessors. We do generally produce them for other functions coming from1041 // the body farm because they may call callbacks that bring us back into1042 // visible code.1043 if (NoExit || IsCalleeAnAutosynthesizedPropertyAccessor)1044 return nullptr;1045 1046 SmallString<256> buf;1047 llvm::raw_svector_ostream Out(buf);1048 1049 if (!CallStackMessage.empty()) {1050 Out << CallStackMessage;1051 } else {1052 bool DidDescribe = describeCodeDecl(Out, Callee,1053 /*ExtendedDescription=*/false,1054 "Returning from ");1055 if (!DidDescribe)1056 Out << "Returning to caller";1057 }1058 1059 assert(callReturn.asLocation().isValid());1060 return std::make_shared<PathDiagnosticEventPiece>(callReturn, Out.str());1061}1062 1063static void compute_path_size(const PathPieces &pieces, unsigned &size) {1064 for (const auto &I : pieces) {1065 const PathDiagnosticPiece *piece = I.get();1066 if (const auto *cp = dyn_cast<PathDiagnosticCallPiece>(piece))1067 compute_path_size(cp->path, size);1068 else1069 ++size;1070 }1071}1072 1073unsigned PathDiagnostic::full_size() {1074 unsigned size = 0;1075 compute_path_size(path, size);1076 return size;1077}1078 1079SmallString<32>1080PathDiagnostic::getIssueHash(const SourceManager &SrcMgr,1081 const LangOptions &LangOpts) const {1082 PathDiagnosticLocation UPDLoc = getUniqueingLoc();1083 FullSourceLoc FullLoc(1084 SrcMgr.getExpansionLoc(UPDLoc.isValid() ? UPDLoc.asLocation()1085 : getLocation().asLocation()),1086 SrcMgr);1087 1088 return clang::getIssueHash(FullLoc, getCheckerName(), getBugType(),1089 getDeclWithIssue(), LangOpts);1090}1091 1092//===----------------------------------------------------------------------===//1093// FoldingSet profiling methods.1094//===----------------------------------------------------------------------===//1095 1096void PathDiagnosticLocation::Profile(llvm::FoldingSetNodeID &ID) const {1097 ID.Add(Range.getBegin());1098 ID.Add(Range.getEnd());1099 ID.Add(static_cast<const SourceLocation &>(Loc));1100}1101 1102void PathDiagnosticPiece::Profile(llvm::FoldingSetNodeID &ID) const {1103 ID.AddInteger((unsigned) getKind());1104 ID.AddString(str);1105 // FIXME: Add profiling support for code hints.1106 ID.AddInteger((unsigned) getDisplayHint());1107 ArrayRef<SourceRange> Ranges = getRanges();1108 for (const auto &I : Ranges) {1109 ID.Add(I.getBegin());1110 ID.Add(I.getEnd());1111 }1112}1113 1114void PathDiagnosticCallPiece::Profile(llvm::FoldingSetNodeID &ID) const {1115 PathDiagnosticPiece::Profile(ID);1116 for (const auto &I : path)1117 ID.Add(*I);1118}1119 1120void PathDiagnosticSpotPiece::Profile(llvm::FoldingSetNodeID &ID) const {1121 PathDiagnosticPiece::Profile(ID);1122 ID.Add(Pos);1123}1124 1125void PathDiagnosticControlFlowPiece::Profile(llvm::FoldingSetNodeID &ID) const {1126 PathDiagnosticPiece::Profile(ID);1127 for (const auto &I : *this)1128 ID.Add(I);1129}1130 1131void PathDiagnosticMacroPiece::Profile(llvm::FoldingSetNodeID &ID) const {1132 PathDiagnosticSpotPiece::Profile(ID);1133 for (const auto &I : subPieces)1134 ID.Add(*I);1135}1136 1137void PathDiagnosticNotePiece::Profile(llvm::FoldingSetNodeID &ID) const {1138 PathDiagnosticSpotPiece::Profile(ID);1139}1140 1141void PathDiagnosticPopUpPiece::Profile(llvm::FoldingSetNodeID &ID) const {1142 PathDiagnosticSpotPiece::Profile(ID);1143}1144 1145void PathDiagnostic::Profile(llvm::FoldingSetNodeID &ID) const {1146 ID.Add(getLocation());1147 ID.Add(getUniqueingLoc());1148 ID.AddString(BugType);1149 ID.AddString(VerboseDesc);1150 ID.AddString(Category);1151}1152 1153void PathDiagnostic::FullProfile(llvm::FoldingSetNodeID &ID) const {1154 Profile(ID);1155 for (const auto &I : path)1156 ID.Add(*I);1157 for (meta_iterator I = meta_begin(), E = meta_end(); I != E; ++I)1158 ID.AddString(*I);1159}1160 1161LLVM_DUMP_METHOD void PathPieces::dump() const {1162 unsigned index = 0;1163 for (const PathDiagnosticPieceRef &Piece : *this) {1164 llvm::errs() << "[" << index++ << "] ";1165 Piece->dump();1166 llvm::errs() << "\n";1167 }1168}1169 1170LLVM_DUMP_METHOD void PathDiagnosticCallPiece::dump() const {1171 llvm::errs() << "CALL\n--------------\n";1172 1173 if (const Stmt *SLoc = getLocation().getStmtOrNull())1174 SLoc->dump();1175 else if (const auto *ND = dyn_cast_or_null<NamedDecl>(getCallee()))1176 llvm::errs() << *ND << "\n";1177 else1178 getLocation().dump();1179}1180 1181LLVM_DUMP_METHOD void PathDiagnosticEventPiece::dump() const {1182 llvm::errs() << "EVENT\n--------------\n";1183 llvm::errs() << getString() << "\n";1184 llvm::errs() << " ---- at ----\n";1185 getLocation().dump();1186}1187 1188LLVM_DUMP_METHOD void PathDiagnosticControlFlowPiece::dump() const {1189 llvm::errs() << "CONTROL\n--------------\n";1190 getStartLocation().dump();1191 llvm::errs() << " ---- to ----\n";1192 getEndLocation().dump();1193}1194 1195LLVM_DUMP_METHOD void PathDiagnosticMacroPiece::dump() const {1196 llvm::errs() << "MACRO\n--------------\n";1197 // FIXME: Print which macro is being invoked.1198}1199 1200LLVM_DUMP_METHOD void PathDiagnosticNotePiece::dump() const {1201 llvm::errs() << "NOTE\n--------------\n";1202 llvm::errs() << getString() << "\n";1203 llvm::errs() << " ---- at ----\n";1204 getLocation().dump();1205}1206 1207LLVM_DUMP_METHOD void PathDiagnosticPopUpPiece::dump() const {1208 llvm::errs() << "POP-UP\n--------------\n";1209 llvm::errs() << getString() << "\n";1210 llvm::errs() << " ---- at ----\n";1211 getLocation().dump();1212}1213 1214LLVM_DUMP_METHOD void PathDiagnosticLocation::dump() const {1215 if (!isValid()) {1216 llvm::errs() << "<INVALID>\n";1217 return;1218 }1219 1220 switch (K) {1221 case RangeK:1222 // FIXME: actually print the range.1223 llvm::errs() << "<range>\n";1224 break;1225 case SingleLocK:1226 asLocation().dump();1227 llvm::errs() << "\n";1228 break;1229 case StmtK:1230 if (S)1231 S->dump();1232 else1233 llvm::errs() << "<NULL STMT>\n";1234 break;1235 case DeclK:1236 if (const auto *ND = dyn_cast_or_null<NamedDecl>(D))1237 llvm::errs() << *ND << "\n";1238 else if (isa<BlockDecl>(D))1239 // FIXME: Make this nicer.1240 llvm::errs() << "<block>\n";1241 else if (D)1242 llvm::errs() << "<unknown decl>\n";1243 else1244 llvm::errs() << "<NULL DECL>\n";1245 break;1246 }1247}1248