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