PathDiagnostic.cpp revision 56a938ff85a444eb3d30d2634d92ce5b1f6fae56
1//===--- PathDiagnostic.cpp - Path-Specific Diagnostic Handling -*- C++ -*-===// 2// 3// The LLVM Compiler Infrastructure 4// 5// This file is distributed under the University of Illinois Open Source 6// License. See LICENSE.TXT for details. 7// 8//===----------------------------------------------------------------------===// 9// 10// This file defines the PathDiagnostic-related interfaces. 11// 12//===----------------------------------------------------------------------===// 13 14#include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h" 15#include "clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h" 16#include "clang/Basic/SourceManager.h" 17#include "clang/AST/Expr.h" 18#include "clang/AST/Decl.h" 19#include "clang/AST/DeclObjC.h" 20#include "clang/AST/ParentMap.h" 21#include "clang/AST/StmtCXX.h" 22#include "llvm/ADT/SmallString.h" 23 24using namespace clang; 25using namespace ento; 26 27bool PathDiagnosticMacroPiece::containsEvent() const { 28 for (PathPieces::const_iterator I = subPieces.begin(), E = subPieces.end(); 29 I!=E; ++I) { 30 if (isa<PathDiagnosticEventPiece>(*I)) 31 return true; 32 if (PathDiagnosticMacroPiece *MP = dyn_cast<PathDiagnosticMacroPiece>(*I)) 33 if (MP->containsEvent()) 34 return true; 35 } 36 return false; 37} 38 39static StringRef StripTrailingDots(StringRef s) { 40 for (StringRef::size_type i = s.size(); i != 0; --i) 41 if (s[i - 1] != '.') 42 return s.substr(0, i); 43 return ""; 44} 45 46PathDiagnosticPiece::PathDiagnosticPiece(StringRef s, 47 Kind k, DisplayHint hint) 48 : str(StripTrailingDots(s)), kind(k), Hint(hint) {} 49 50PathDiagnosticPiece::PathDiagnosticPiece(Kind k, DisplayHint hint) 51 : kind(k), Hint(hint) {} 52 53PathDiagnosticPiece::~PathDiagnosticPiece() {} 54PathDiagnosticEventPiece::~PathDiagnosticEventPiece() {} 55PathDiagnosticCallPiece::~PathDiagnosticCallPiece() {} 56PathDiagnosticControlFlowPiece::~PathDiagnosticControlFlowPiece() {} 57PathDiagnosticMacroPiece::~PathDiagnosticMacroPiece() {} 58PathDiagnostic::PathDiagnostic() : path(pathImpl) {} 59PathPieces::~PathPieces() {} 60PathDiagnostic::~PathDiagnostic() {} 61 62PathDiagnostic::PathDiagnostic(StringRef bugtype, StringRef desc, 63 StringRef category) 64 : BugType(StripTrailingDots(bugtype)), 65 Desc(StripTrailingDots(desc)), 66 Category(StripTrailingDots(category)), 67 path(pathImpl) {} 68 69void PathDiagnosticConsumer::anchor() { } 70 71PathDiagnosticConsumer::~PathDiagnosticConsumer() { 72 // Delete the contents of the FoldingSet if it isn't empty already. 73 for (llvm::FoldingSet<PathDiagnostic>::iterator it = 74 Diags.begin(), et = Diags.end() ; it != et ; ++it) { 75 delete &*it; 76 } 77} 78 79void PathDiagnosticConsumer::HandlePathDiagnostic(PathDiagnostic *D) { 80 llvm::OwningPtr<PathDiagnostic> OwningD(D); 81 82 if (!D || D->path.empty()) 83 return; 84 85 // We need to flatten the locations (convert Stmt* to locations) because 86 // the referenced statements may be freed by the time the diagnostics 87 // are emitted. 88 D->flattenLocations(); 89 90 // If the PathDiagnosticConsumer does not support diagnostics that 91 // cross file boundaries, prune out such diagnostics now. 92 if (!supportsCrossFileDiagnostics()) { 93 // Verify that the entire path is from the same FileID. 94 FileID FID; 95 const SourceManager &SMgr = (*D->path.begin())->getLocation().getManager(); 96 llvm::SmallVector<const PathPieces *, 5> WorkList; 97 WorkList.push_back(&D->path); 98 99 while (!WorkList.empty()) { 100 const PathPieces &path = *WorkList.back(); 101 WorkList.pop_back(); 102 103 for (PathPieces::const_iterator I = path.begin(), E = path.end(); 104 I != E; ++I) { 105 const PathDiagnosticPiece *piece = I->getPtr(); 106 FullSourceLoc L = piece->getLocation().asLocation().getExpansionLoc(); 107 108 if (FID.isInvalid()) { 109 FID = SMgr.getFileID(L); 110 } else if (SMgr.getFileID(L) != FID) 111 return; // FIXME: Emit a warning? 112 113 // Check the source ranges. 114 for (PathDiagnosticPiece::range_iterator RI = piece->ranges_begin(), 115 RE = piece->ranges_end(); 116 RI != RE; ++RI) { 117 SourceLocation L = SMgr.getExpansionLoc(RI->getBegin()); 118 if (!L.isFileID() || SMgr.getFileID(L) != FID) 119 return; // FIXME: Emit a warning? 120 L = SMgr.getExpansionLoc(RI->getEnd()); 121 if (!L.isFileID() || SMgr.getFileID(L) != FID) 122 return; // FIXME: Emit a warning? 123 } 124 125 if (const PathDiagnosticCallPiece *call = 126 dyn_cast<PathDiagnosticCallPiece>(piece)) { 127 WorkList.push_back(&call->path); 128 } 129 else if (const PathDiagnosticMacroPiece *macro = 130 dyn_cast<PathDiagnosticMacroPiece>(piece)) { 131 WorkList.push_back(¯o->subPieces); 132 } 133 } 134 } 135 136 if (FID.isInvalid()) 137 return; // FIXME: Emit a warning? 138 } 139 140 // Profile the node to see if we already have something matching it 141 llvm::FoldingSetNodeID profile; 142 D->Profile(profile); 143 void *InsertPos = 0; 144 145 if (PathDiagnostic *orig = Diags.FindNodeOrInsertPos(profile, InsertPos)) { 146 // Keep the PathDiagnostic with the shorter path. 147 const unsigned orig_size = orig->full_size(); 148 const unsigned new_size = D->full_size(); 149 150 if (orig_size <= new_size) { 151 bool shouldKeepOriginal = true; 152 if (orig_size == new_size) { 153 // Here we break ties in a fairly arbitrary, but deterministic, way. 154 llvm::FoldingSetNodeID fullProfile, fullProfileOrig; 155 D->FullProfile(fullProfile); 156 orig->FullProfile(fullProfileOrig); 157 if (fullProfile.ComputeHash() < fullProfileOrig.ComputeHash()) 158 shouldKeepOriginal = false; 159 } 160 161 if (shouldKeepOriginal) 162 return; 163 } 164 Diags.RemoveNode(orig); 165 delete orig; 166 } 167 168 Diags.InsertNode(OwningD.take()); 169} 170 171 172namespace { 173struct CompareDiagnostics { 174 // Compare if 'X' is "<" than 'Y'. 175 bool operator()(const PathDiagnostic *X, const PathDiagnostic *Y) const { 176 // First compare by location 177 const FullSourceLoc &XLoc = X->getLocation().asLocation(); 178 const FullSourceLoc &YLoc = Y->getLocation().asLocation(); 179 if (XLoc < YLoc) 180 return true; 181 if (XLoc != YLoc) 182 return false; 183 184 // Next, compare by bug type. 185 StringRef XBugType = X->getBugType(); 186 StringRef YBugType = Y->getBugType(); 187 if (XBugType < YBugType) 188 return true; 189 if (XBugType != YBugType) 190 return false; 191 192 // Next, compare by bug description. 193 StringRef XDesc = X->getDescription(); 194 StringRef YDesc = Y->getDescription(); 195 if (XDesc < YDesc) 196 return true; 197 if (XDesc != YDesc) 198 return false; 199 200 // FIXME: Further refine by comparing PathDiagnosticPieces? 201 return false; 202 } 203}; 204} 205 206void 207PathDiagnosticConsumer::FlushDiagnostics(SmallVectorImpl<std::string> *Files) { 208 if (flushed) 209 return; 210 211 flushed = true; 212 213 std::vector<const PathDiagnostic *> BatchDiags; 214 for (llvm::FoldingSet<PathDiagnostic>::iterator it = Diags.begin(), 215 et = Diags.end(); it != et; ++it) { 216 BatchDiags.push_back(&*it); 217 } 218 219 // Clear out the FoldingSet. 220 Diags.clear(); 221 222 // Sort the diagnostics so that they are always emitted in a deterministic 223 // order. 224 if (!BatchDiags.empty()) 225 std::sort(BatchDiags.begin(), BatchDiags.end(), CompareDiagnostics()); 226 227 FlushDiagnosticsImpl(BatchDiags, Files); 228 229 // Delete the flushed diagnostics. 230 for (std::vector<const PathDiagnostic *>::iterator it = BatchDiags.begin(), 231 et = BatchDiags.end(); it != et; ++it) { 232 const PathDiagnostic *D = *it; 233 delete D; 234 } 235} 236 237//===----------------------------------------------------------------------===// 238// PathDiagnosticLocation methods. 239//===----------------------------------------------------------------------===// 240 241static SourceLocation getValidSourceLocation(const Stmt* S, 242 LocationOrAnalysisDeclContext LAC) { 243 SourceLocation L = S->getLocStart(); 244 assert(!LAC.isNull() && "A valid LocationContext or AnalysisDeclContext should " 245 "be passed to PathDiagnosticLocation upon creation."); 246 247 // S might be a temporary statement that does not have a location in the 248 // source code, so find an enclosing statement and use it's location. 249 if (!L.isValid()) { 250 251 ParentMap *PM = 0; 252 if (LAC.is<const LocationContext*>()) 253 PM = &LAC.get<const LocationContext*>()->getParentMap(); 254 else 255 PM = &LAC.get<AnalysisDeclContext*>()->getParentMap(); 256 257 while (!L.isValid()) { 258 S = PM->getParent(S); 259 L = S->getLocStart(); 260 } 261 } 262 263 return L; 264} 265 266PathDiagnosticLocation 267 PathDiagnosticLocation::createBegin(const Decl *D, 268 const SourceManager &SM) { 269 return PathDiagnosticLocation(D->getLocStart(), SM, SingleLocK); 270} 271 272PathDiagnosticLocation 273 PathDiagnosticLocation::createBegin(const Stmt *S, 274 const SourceManager &SM, 275 LocationOrAnalysisDeclContext LAC) { 276 return PathDiagnosticLocation(getValidSourceLocation(S, LAC), 277 SM, SingleLocK); 278} 279 280PathDiagnosticLocation 281 PathDiagnosticLocation::createOperatorLoc(const BinaryOperator *BO, 282 const SourceManager &SM) { 283 return PathDiagnosticLocation(BO->getOperatorLoc(), SM, SingleLocK); 284} 285 286PathDiagnosticLocation 287 PathDiagnosticLocation::createMemberLoc(const MemberExpr *ME, 288 const SourceManager &SM) { 289 return PathDiagnosticLocation(ME->getMemberLoc(), SM, SingleLocK); 290} 291 292PathDiagnosticLocation 293 PathDiagnosticLocation::createBeginBrace(const CompoundStmt *CS, 294 const SourceManager &SM) { 295 SourceLocation L = CS->getLBracLoc(); 296 return PathDiagnosticLocation(L, SM, SingleLocK); 297} 298 299PathDiagnosticLocation 300 PathDiagnosticLocation::createEndBrace(const CompoundStmt *CS, 301 const SourceManager &SM) { 302 SourceLocation L = CS->getRBracLoc(); 303 return PathDiagnosticLocation(L, SM, SingleLocK); 304} 305 306PathDiagnosticLocation 307 PathDiagnosticLocation::createDeclBegin(const LocationContext *LC, 308 const SourceManager &SM) { 309 // FIXME: Should handle CXXTryStmt if analyser starts supporting C++. 310 if (const CompoundStmt *CS = 311 dyn_cast_or_null<CompoundStmt>(LC->getDecl()->getBody())) 312 if (!CS->body_empty()) { 313 SourceLocation Loc = (*CS->body_begin())->getLocStart(); 314 return PathDiagnosticLocation(Loc, SM, SingleLocK); 315 } 316 317 return PathDiagnosticLocation(); 318} 319 320PathDiagnosticLocation 321 PathDiagnosticLocation::createDeclEnd(const LocationContext *LC, 322 const SourceManager &SM) { 323 SourceLocation L = LC->getDecl()->getBodyRBrace(); 324 return PathDiagnosticLocation(L, SM, SingleLocK); 325} 326 327PathDiagnosticLocation 328 PathDiagnosticLocation::create(const ProgramPoint& P, 329 const SourceManager &SMng) { 330 331 const Stmt* S = 0; 332 if (const BlockEdge *BE = dyn_cast<BlockEdge>(&P)) { 333 const CFGBlock *BSrc = BE->getSrc(); 334 S = BSrc->getTerminatorCondition(); 335 } 336 else if (const PostStmt *PS = dyn_cast<PostStmt>(&P)) { 337 S = PS->getStmt(); 338 } 339 340 return PathDiagnosticLocation(S, SMng, P.getLocationContext()); 341} 342 343PathDiagnosticLocation 344 PathDiagnosticLocation::createEndOfPath(const ExplodedNode* N, 345 const SourceManager &SM) { 346 assert(N && "Cannot create a location with a null node."); 347 348 const ExplodedNode *NI = N; 349 350 while (NI) { 351 ProgramPoint P = NI->getLocation(); 352 const LocationContext *LC = P.getLocationContext(); 353 if (const StmtPoint *PS = dyn_cast<StmtPoint>(&P)) 354 return PathDiagnosticLocation(PS->getStmt(), SM, LC); 355 else if (const BlockEdge *BE = dyn_cast<BlockEdge>(&P)) { 356 const Stmt *Term = BE->getSrc()->getTerminator(); 357 if (Term) { 358 return PathDiagnosticLocation(Term, SM, LC); 359 } 360 } 361 NI = NI->succ_empty() ? 0 : *(NI->succ_begin()); 362 } 363 364 return createDeclEnd(N->getLocationContext(), SM); 365} 366 367PathDiagnosticLocation PathDiagnosticLocation::createSingleLocation( 368 const PathDiagnosticLocation &PDL) { 369 FullSourceLoc L = PDL.asLocation(); 370 return PathDiagnosticLocation(L, L.getManager(), SingleLocK); 371} 372 373FullSourceLoc 374 PathDiagnosticLocation::genLocation(SourceLocation L, 375 LocationOrAnalysisDeclContext LAC) const { 376 assert(isValid()); 377 // Note that we want a 'switch' here so that the compiler can warn us in 378 // case we add more cases. 379 switch (K) { 380 case SingleLocK: 381 case RangeK: 382 break; 383 case StmtK: 384 // Defensive checking. 385 if (!S) 386 break; 387 return FullSourceLoc(getValidSourceLocation(S, LAC), 388 const_cast<SourceManager&>(*SM)); 389 case DeclK: 390 // Defensive checking. 391 if (!D) 392 break; 393 return FullSourceLoc(D->getLocation(), const_cast<SourceManager&>(*SM)); 394 } 395 396 return FullSourceLoc(L, const_cast<SourceManager&>(*SM)); 397} 398 399PathDiagnosticRange 400 PathDiagnosticLocation::genRange(LocationOrAnalysisDeclContext LAC) const { 401 assert(isValid()); 402 // Note that we want a 'switch' here so that the compiler can warn us in 403 // case we add more cases. 404 switch (K) { 405 case SingleLocK: 406 return PathDiagnosticRange(SourceRange(Loc,Loc), true); 407 case RangeK: 408 break; 409 case StmtK: { 410 const Stmt *S = asStmt(); 411 switch (S->getStmtClass()) { 412 default: 413 break; 414 case Stmt::DeclStmtClass: { 415 const DeclStmt *DS = cast<DeclStmt>(S); 416 if (DS->isSingleDecl()) { 417 // Should always be the case, but we'll be defensive. 418 return SourceRange(DS->getLocStart(), 419 DS->getSingleDecl()->getLocation()); 420 } 421 break; 422 } 423 // FIXME: Provide better range information for different 424 // terminators. 425 case Stmt::IfStmtClass: 426 case Stmt::WhileStmtClass: 427 case Stmt::DoStmtClass: 428 case Stmt::ForStmtClass: 429 case Stmt::ChooseExprClass: 430 case Stmt::IndirectGotoStmtClass: 431 case Stmt::SwitchStmtClass: 432 case Stmt::BinaryConditionalOperatorClass: 433 case Stmt::ConditionalOperatorClass: 434 case Stmt::ObjCForCollectionStmtClass: { 435 SourceLocation L = getValidSourceLocation(S, LAC); 436 return SourceRange(L, L); 437 } 438 } 439 SourceRange R = S->getSourceRange(); 440 if (R.isValid()) 441 return R; 442 break; 443 } 444 case DeclK: 445 if (const ObjCMethodDecl *MD = dyn_cast<ObjCMethodDecl>(D)) 446 return MD->getSourceRange(); 447 if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) { 448 if (Stmt *Body = FD->getBody()) 449 return Body->getSourceRange(); 450 } 451 else { 452 SourceLocation L = D->getLocation(); 453 return PathDiagnosticRange(SourceRange(L, L), true); 454 } 455 } 456 457 return SourceRange(Loc,Loc); 458} 459 460void PathDiagnosticLocation::flatten() { 461 if (K == StmtK) { 462 K = RangeK; 463 S = 0; 464 D = 0; 465 } 466 else if (K == DeclK) { 467 K = SingleLocK; 468 S = 0; 469 D = 0; 470 } 471} 472 473PathDiagnosticLocation PathDiagnostic::getLocation() const { 474 assert(path.size() > 0 && 475 "getLocation() requires a non-empty PathDiagnostic."); 476 477 PathDiagnosticPiece *p = path.rbegin()->getPtr(); 478 479 while (true) { 480 if (PathDiagnosticCallPiece *cp = dyn_cast<PathDiagnosticCallPiece>(p)) { 481 assert(!cp->path.empty()); 482 p = cp->path.rbegin()->getPtr(); 483 continue; 484 } 485 break; 486 } 487 488 return p->getLocation(); 489} 490 491//===----------------------------------------------------------------------===// 492// Manipulation of PathDiagnosticCallPieces. 493//===----------------------------------------------------------------------===// 494 495static PathDiagnosticLocation getLastStmtLoc(const ExplodedNode *N, 496 const SourceManager &SM) { 497 while (N) { 498 ProgramPoint PP = N->getLocation(); 499 if (const StmtPoint *SP = dyn_cast<StmtPoint>(&PP)) 500 return PathDiagnosticLocation(SP->getStmt(), SM, PP.getLocationContext()); 501 if (N->pred_empty()) 502 break; 503 N = *N->pred_begin(); 504 } 505 return PathDiagnosticLocation(); 506} 507 508PathDiagnosticCallPiece * 509PathDiagnosticCallPiece::construct(const ExplodedNode *N, 510 const CallExit &CE, 511 const SourceManager &SM) { 512 const Decl *caller = CE.getLocationContext()->getParent()->getDecl(); 513 PathDiagnosticLocation pos = getLastStmtLoc(N, SM); 514 return new PathDiagnosticCallPiece(caller, pos); 515} 516 517PathDiagnosticCallPiece * 518PathDiagnosticCallPiece::construct(PathPieces &path, 519 const Decl *caller) { 520 PathDiagnosticCallPiece *C = new PathDiagnosticCallPiece(path, caller); 521 path.clear(); 522 path.push_front(C); 523 return C; 524} 525 526void PathDiagnosticCallPiece::setCallee(const CallEnter &CE, 527 const SourceManager &SM) { 528 const Decl *D = CE.getCalleeContext()->getDecl(); 529 Callee = D; 530 callEnter = PathDiagnosticLocation(CE.getCallExpr(), SM, 531 CE.getLocationContext()); 532 callEnterWithin = PathDiagnosticLocation::createBegin(D, SM); 533} 534 535IntrusiveRefCntPtr<PathDiagnosticEventPiece> 536PathDiagnosticCallPiece::getCallEnterEvent() const { 537 if (!Callee) 538 return 0; 539 SmallString<256> buf; 540 llvm::raw_svector_ostream Out(buf); 541 if (isa<BlockDecl>(Callee)) 542 Out << "Calling anonymous block"; 543 else if (const NamedDecl *ND = dyn_cast<NamedDecl>(Callee)) 544 Out << "Calling '" << *ND << "'"; 545 StringRef msg = Out.str(); 546 if (msg.empty()) 547 return 0; 548 return new PathDiagnosticEventPiece(callEnter, msg); 549} 550 551IntrusiveRefCntPtr<PathDiagnosticEventPiece> 552PathDiagnosticCallPiece::getCallEnterWithinCallerEvent() const { 553 SmallString<256> buf; 554 llvm::raw_svector_ostream Out(buf); 555 if (const NamedDecl *ND = dyn_cast_or_null<NamedDecl>(Caller)) 556 Out << "Entered call from '" << *ND << "'"; 557 else 558 Out << "Entered call"; 559 StringRef msg = Out.str(); 560 if (msg.empty()) 561 return 0; 562 return new PathDiagnosticEventPiece(callEnterWithin, msg); 563} 564 565IntrusiveRefCntPtr<PathDiagnosticEventPiece> 566PathDiagnosticCallPiece::getCallExitEvent() const { 567 if (NoExit) 568 return 0; 569 SmallString<256> buf; 570 llvm::raw_svector_ostream Out(buf); 571 if (const NamedDecl *ND = dyn_cast_or_null<NamedDecl>(Callee)) 572 Out << "Returning from '" << *ND << "'"; 573 else 574 Out << "Returning to caller"; 575 if (!CallStackMessage.empty()) 576 Out << CallStackMessage; 577 return new PathDiagnosticEventPiece(callReturn, Out.str()); 578} 579 580static void compute_path_size(const PathPieces &pieces, unsigned &size) { 581 for (PathPieces::const_iterator it = pieces.begin(), 582 et = pieces.end(); it != et; ++it) { 583 const PathDiagnosticPiece *piece = it->getPtr(); 584 if (const PathDiagnosticCallPiece *cp = 585 dyn_cast<PathDiagnosticCallPiece>(piece)) { 586 compute_path_size(cp->path, size); 587 } 588 else 589 ++size; 590 } 591} 592 593unsigned PathDiagnostic::full_size() { 594 unsigned size = 0; 595 compute_path_size(path, size); 596 return size; 597} 598 599//===----------------------------------------------------------------------===// 600// FoldingSet profiling methods. 601//===----------------------------------------------------------------------===// 602 603void PathDiagnosticLocation::Profile(llvm::FoldingSetNodeID &ID) const { 604 ID.AddInteger(Range.getBegin().getRawEncoding()); 605 ID.AddInteger(Range.getEnd().getRawEncoding()); 606 ID.AddInteger(Loc.getRawEncoding()); 607 return; 608} 609 610void PathDiagnosticPiece::Profile(llvm::FoldingSetNodeID &ID) const { 611 ID.AddInteger((unsigned) getKind()); 612 ID.AddString(str); 613 // FIXME: Add profiling support for code hints. 614 ID.AddInteger((unsigned) getDisplayHint()); 615 for (range_iterator I = ranges_begin(), E = ranges_end(); I != E; ++I) { 616 ID.AddInteger(I->getBegin().getRawEncoding()); 617 ID.AddInteger(I->getEnd().getRawEncoding()); 618 } 619} 620 621void PathDiagnosticCallPiece::Profile(llvm::FoldingSetNodeID &ID) const { 622 PathDiagnosticPiece::Profile(ID); 623 for (PathPieces::const_iterator it = path.begin(), 624 et = path.end(); it != et; ++it) { 625 ID.Add(**it); 626 } 627} 628 629void PathDiagnosticSpotPiece::Profile(llvm::FoldingSetNodeID &ID) const { 630 PathDiagnosticPiece::Profile(ID); 631 ID.Add(Pos); 632} 633 634void PathDiagnosticControlFlowPiece::Profile(llvm::FoldingSetNodeID &ID) const { 635 PathDiagnosticPiece::Profile(ID); 636 for (const_iterator I = begin(), E = end(); I != E; ++I) 637 ID.Add(*I); 638} 639 640void PathDiagnosticMacroPiece::Profile(llvm::FoldingSetNodeID &ID) const { 641 PathDiagnosticSpotPiece::Profile(ID); 642 for (PathPieces::const_iterator I = subPieces.begin(), E = subPieces.end(); 643 I != E; ++I) 644 ID.Add(**I); 645} 646 647void PathDiagnostic::Profile(llvm::FoldingSetNodeID &ID) const { 648 if (!path.empty()) 649 getLocation().Profile(ID); 650 ID.AddString(BugType); 651 ID.AddString(Desc); 652 ID.AddString(Category); 653} 654 655void PathDiagnostic::FullProfile(llvm::FoldingSetNodeID &ID) const { 656 Profile(ID); 657 for (PathPieces::const_iterator I = path.begin(), E = path.end(); I != E; ++I) 658 ID.Add(**I); 659 for (meta_iterator I = meta_begin(), E = meta_end(); I != E; ++I) 660 ID.AddString(*I); 661} 662 663StackHintGenerator::~StackHintGenerator() {} 664 665std::string StackHintGeneratorForSymbol::getMessage(const ExplodedNode *N){ 666 ProgramPoint P = N->getLocation(); 667 const CallExit *CExit = dyn_cast<CallExit>(&P); 668 assert(CExit && "Stack Hints should be constructed at CallExit points."); 669 670 const CallExpr *CE = dyn_cast_or_null<CallExpr>(CExit->getStmt()); 671 if (!CE) 672 return ""; 673 674 // Get the successor node to make sure the return statement is evaluated and 675 // CE is set to the result value. 676 N = *N->succ_begin(); 677 if (!N) 678 return getMessageForSymbolNotFound(); 679 680 // Check if one of the parameters are set to the interesting symbol. 681 ProgramStateRef State = N->getState(); 682 const LocationContext *LCtx = N->getLocationContext(); 683 unsigned ArgIndex = 0; 684 for (CallExpr::const_arg_iterator I = CE->arg_begin(), 685 E = CE->arg_end(); I != E; ++I, ++ArgIndex){ 686 SVal SV = State->getSVal(*I, LCtx); 687 688 // Check if the variable corresponding to the symbol is passed by value. 689 SymbolRef AS = SV.getAsLocSymbol(); 690 if (AS == Sym) { 691 return getMessageForArg(*I, ArgIndex); 692 } 693 694 // Check if the parameter is a pointer to the symbol. 695 if (const loc::MemRegionVal *Reg = dyn_cast<loc::MemRegionVal>(&SV)) { 696 SVal PSV = State->getSVal(Reg->getRegion()); 697 SymbolRef AS = PSV.getAsLocSymbol(); 698 if (AS == Sym) { 699 return getMessageForArg(*I, ArgIndex); 700 } 701 } 702 } 703 704 // Check if we are returning the interesting symbol. 705 SVal SV = State->getSVal(CE, LCtx); 706 SymbolRef RetSym = SV.getAsLocSymbol(); 707 if (RetSym == Sym) { 708 return getMessageForReturn(CE); 709 } 710 711 return getMessageForSymbolNotFound(); 712} 713 714/// TODO: This is copied from clang diagnostics. Maybe we could just move it to 715/// some common place. (Same as HandleOrdinalModifier.) 716void StackHintGeneratorForSymbol::printOrdinal(unsigned ValNo, 717 llvm::raw_svector_ostream &Out) { 718 assert(ValNo != 0 && "ValNo must be strictly positive!"); 719 720 // We could use text forms for the first N ordinals, but the numeric 721 // forms are actually nicer in diagnostics because they stand out. 722 Out << ValNo; 723 724 // It is critically important that we do this perfectly for 725 // user-written sequences with over 100 elements. 726 switch (ValNo % 100) { 727 case 11: 728 case 12: 729 case 13: 730 Out << "th"; return; 731 default: 732 switch (ValNo % 10) { 733 case 1: Out << "st"; return; 734 case 2: Out << "nd"; return; 735 case 3: Out << "rd"; return; 736 default: Out << "th"; return; 737 } 738 } 739} 740 741std::string StackHintGeneratorForSymbol::getMessageForArg(const Expr *ArgE, 742 unsigned ArgIndex) { 743 SmallString<200> buf; 744 llvm::raw_svector_ostream os(buf); 745 746 os << Msg << " via "; 747 // Printed parameters start at 1, not 0. 748 printOrdinal(++ArgIndex, os); 749 os << " parameter"; 750 751 return os.str(); 752} 753