PathDiagnostic.cpp revision bac341346f3c8e713a8f165120fd54b500ee3189
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/AST/Expr.h" 17#include "clang/AST/Decl.h" 18#include "clang/AST/DeclObjC.h" 19#include "clang/AST/ParentMap.h" 20#include "clang/AST/StmtCXX.h" 21#include "llvm/ADT/SmallString.h" 22 23using namespace clang; 24using namespace ento; 25 26bool PathDiagnosticMacroPiece::containsEvent() const { 27 for (const_iterator I = begin(), E = end(); I!=E; ++I) { 28 if (isa<PathDiagnosticEventPiece>(*I)) 29 return true; 30 31 if (PathDiagnosticMacroPiece *MP = dyn_cast<PathDiagnosticMacroPiece>(*I)) 32 if (MP->containsEvent()) 33 return true; 34 } 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() {} 55PathDiagnosticControlFlowPiece::~PathDiagnosticControlFlowPiece() {} 56 57PathDiagnosticMacroPiece::~PathDiagnosticMacroPiece() { 58 for (iterator I = begin(), E = end(); I != E; ++I) delete *I; 59} 60 61PathDiagnostic::PathDiagnostic() : Size(0) {} 62 63PathDiagnostic::~PathDiagnostic() { 64 for (iterator I = begin(), E = end(); I != E; ++I) delete &*I; 65} 66 67void PathDiagnostic::resetPath(bool deletePieces) { 68 Size = 0; 69 70 if (deletePieces) 71 for (iterator I=begin(), E=end(); I!=E; ++I) 72 delete &*I; 73 74 path.clear(); 75} 76 77 78PathDiagnostic::PathDiagnostic(StringRef bugtype, StringRef desc, 79 StringRef category) 80 : Size(0), 81 BugType(StripTrailingDots(bugtype)), 82 Desc(StripTrailingDots(desc)), 83 Category(StripTrailingDots(category)) {} 84 85void PathDiagnosticConsumer::anchor() { } 86 87PathDiagnosticConsumer::~PathDiagnosticConsumer() { 88 // Delete the contents of the FoldingSet if it isn't empty already. 89 for (llvm::FoldingSet<PathDiagnostic>::iterator it = 90 Diags.begin(), et = Diags.end() ; it != et ; ++it) { 91 delete &*it; 92 } 93} 94 95void PathDiagnosticConsumer::HandlePathDiagnostic(PathDiagnostic *D) { 96 if (!D) 97 return; 98 99 if (D->empty()) { 100 delete D; 101 return; 102 } 103 104 // We need to flatten the locations (convert Stmt* to locations) because 105 // the referenced statements may be freed by the time the diagnostics 106 // are emitted. 107 D->flattenLocations(); 108 109 // Profile the node to see if we already have something matching it 110 llvm::FoldingSetNodeID profile; 111 D->Profile(profile); 112 void *InsertPos = 0; 113 114 if (PathDiagnostic *orig = Diags.FindNodeOrInsertPos(profile, InsertPos)) { 115 // Keep the PathDiagnostic with the shorter path. 116 if (orig->size() <= D->size()) { 117 bool shouldKeepOriginal = true; 118 if (orig->size() == D->size()) { 119 // Here we break ties in a fairly arbitrary, but deterministic, way. 120 llvm::FoldingSetNodeID fullProfile, fullProfileOrig; 121 D->FullProfile(fullProfile); 122 orig->FullProfile(fullProfileOrig); 123 if (fullProfile.ComputeHash() < fullProfileOrig.ComputeHash()) 124 shouldKeepOriginal = false; 125 } 126 127 if (shouldKeepOriginal) { 128 delete D; 129 return; 130 } 131 } 132 Diags.RemoveNode(orig); 133 delete orig; 134 } 135 136 Diags.InsertNode(D); 137} 138 139 140namespace { 141struct CompareDiagnostics { 142 // Compare if 'X' is "<" than 'Y'. 143 bool operator()(const PathDiagnostic *X, const PathDiagnostic *Y) const { 144 // First compare by location 145 const FullSourceLoc &XLoc = X->getLocation().asLocation(); 146 const FullSourceLoc &YLoc = Y->getLocation().asLocation(); 147 if (XLoc < YLoc) 148 return true; 149 if (XLoc != YLoc) 150 return false; 151 152 // Next, compare by bug type. 153 StringRef XBugType = X->getBugType(); 154 StringRef YBugType = Y->getBugType(); 155 if (XBugType < YBugType) 156 return true; 157 if (XBugType != YBugType) 158 return false; 159 160 // Next, compare by bug description. 161 StringRef XDesc = X->getDescription(); 162 StringRef YDesc = Y->getDescription(); 163 if (XDesc < YDesc) 164 return true; 165 if (XDesc != YDesc) 166 return false; 167 168 // FIXME: Further refine by comparing PathDiagnosticPieces? 169 return false; 170 } 171}; 172} 173 174void 175PathDiagnosticConsumer::FlushDiagnostics(SmallVectorImpl<std::string> *Files) { 176 if (flushed) 177 return; 178 179 flushed = true; 180 181 std::vector<const PathDiagnostic *> BatchDiags; 182 for (llvm::FoldingSet<PathDiagnostic>::iterator it = Diags.begin(), 183 et = Diags.end(); it != et; ++it) { 184 BatchDiags.push_back(&*it); 185 } 186 187 // Clear out the FoldingSet. 188 Diags.clear(); 189 190 // Sort the diagnostics so that they are always emitted in a deterministic 191 // order. 192 if (!BatchDiags.empty()) 193 std::sort(BatchDiags.begin(), BatchDiags.end(), CompareDiagnostics()); 194 195 FlushDiagnosticsImpl(BatchDiags, Files); 196 197 // Delete the flushed diagnostics. 198 for (std::vector<const PathDiagnostic *>::iterator it = BatchDiags.begin(), 199 et = BatchDiags.end(); it != et; ++it) { 200 const PathDiagnostic *D = *it; 201 delete D; 202 } 203} 204 205//===----------------------------------------------------------------------===// 206// PathDiagnosticLocation methods. 207//===----------------------------------------------------------------------===// 208 209static SourceLocation getValidSourceLocation(const Stmt* S, 210 LocationOrAnalysisDeclContext LAC) { 211 SourceLocation L = S->getLocStart(); 212 assert(!LAC.isNull() && "A valid LocationContext or AnalysisDeclContext should " 213 "be passed to PathDiagnosticLocation upon creation."); 214 215 // S might be a temporary statement that does not have a location in the 216 // source code, so find an enclosing statement and use it's location. 217 if (!L.isValid()) { 218 219 ParentMap *PM = 0; 220 if (LAC.is<const LocationContext*>()) 221 PM = &LAC.get<const LocationContext*>()->getParentMap(); 222 else 223 PM = &LAC.get<AnalysisDeclContext*>()->getParentMap(); 224 225 while (!L.isValid()) { 226 S = PM->getParent(S); 227 L = S->getLocStart(); 228 } 229 } 230 231 return L; 232} 233 234PathDiagnosticLocation 235 PathDiagnosticLocation::createBegin(const Decl *D, 236 const SourceManager &SM) { 237 return PathDiagnosticLocation(D->getLocStart(), SM, SingleLocK); 238} 239 240PathDiagnosticLocation 241 PathDiagnosticLocation::createBegin(const Stmt *S, 242 const SourceManager &SM, 243 LocationOrAnalysisDeclContext LAC) { 244 return PathDiagnosticLocation(getValidSourceLocation(S, LAC), 245 SM, SingleLocK); 246} 247 248PathDiagnosticLocation 249 PathDiagnosticLocation::createOperatorLoc(const BinaryOperator *BO, 250 const SourceManager &SM) { 251 return PathDiagnosticLocation(BO->getOperatorLoc(), SM, SingleLocK); 252} 253 254PathDiagnosticLocation 255 PathDiagnosticLocation::createMemberLoc(const MemberExpr *ME, 256 const SourceManager &SM) { 257 return PathDiagnosticLocation(ME->getMemberLoc(), SM, SingleLocK); 258} 259 260PathDiagnosticLocation 261 PathDiagnosticLocation::createBeginBrace(const CompoundStmt *CS, 262 const SourceManager &SM) { 263 SourceLocation L = CS->getLBracLoc(); 264 return PathDiagnosticLocation(L, SM, SingleLocK); 265} 266 267PathDiagnosticLocation 268 PathDiagnosticLocation::createEndBrace(const CompoundStmt *CS, 269 const SourceManager &SM) { 270 SourceLocation L = CS->getRBracLoc(); 271 return PathDiagnosticLocation(L, SM, SingleLocK); 272} 273 274PathDiagnosticLocation 275 PathDiagnosticLocation::createDeclBegin(const LocationContext *LC, 276 const SourceManager &SM) { 277 // FIXME: Should handle CXXTryStmt if analyser starts supporting C++. 278 if (const CompoundStmt *CS = 279 dyn_cast_or_null<CompoundStmt>(LC->getDecl()->getBody())) 280 if (!CS->body_empty()) { 281 SourceLocation Loc = (*CS->body_begin())->getLocStart(); 282 return PathDiagnosticLocation(Loc, SM, SingleLocK); 283 } 284 285 return PathDiagnosticLocation(); 286} 287 288PathDiagnosticLocation 289 PathDiagnosticLocation::createDeclEnd(const LocationContext *LC, 290 const SourceManager &SM) { 291 SourceLocation L = LC->getDecl()->getBodyRBrace(); 292 return PathDiagnosticLocation(L, SM, SingleLocK); 293} 294 295PathDiagnosticLocation 296 PathDiagnosticLocation::create(const ProgramPoint& P, 297 const SourceManager &SMng) { 298 299 const Stmt* S = 0; 300 if (const BlockEdge *BE = dyn_cast<BlockEdge>(&P)) { 301 const CFGBlock *BSrc = BE->getSrc(); 302 S = BSrc->getTerminatorCondition(); 303 } 304 else if (const PostStmt *PS = dyn_cast<PostStmt>(&P)) { 305 S = PS->getStmt(); 306 } 307 308 return PathDiagnosticLocation(S, SMng, P.getLocationContext()); 309} 310 311PathDiagnosticLocation 312 PathDiagnosticLocation::createEndOfPath(const ExplodedNode* N, 313 const SourceManager &SM) { 314 assert(N && "Cannot create a location with a null node."); 315 316 const ExplodedNode *NI = N; 317 318 while (NI) { 319 ProgramPoint P = NI->getLocation(); 320 const LocationContext *LC = P.getLocationContext(); 321 if (const StmtPoint *PS = dyn_cast<StmtPoint>(&P)) 322 return PathDiagnosticLocation(PS->getStmt(), SM, LC); 323 else if (const BlockEdge *BE = dyn_cast<BlockEdge>(&P)) { 324 const Stmt *Term = BE->getSrc()->getTerminator(); 325 assert(Term); 326 return PathDiagnosticLocation(Term, SM, LC); 327 } 328 NI = NI->succ_empty() ? 0 : *(NI->succ_begin()); 329 } 330 331 return createDeclEnd(N->getLocationContext(), SM); 332} 333 334PathDiagnosticLocation PathDiagnosticLocation::createSingleLocation( 335 const PathDiagnosticLocation &PDL) { 336 FullSourceLoc L = PDL.asLocation(); 337 return PathDiagnosticLocation(L, L.getManager(), SingleLocK); 338} 339 340FullSourceLoc 341 PathDiagnosticLocation::genLocation(SourceLocation L, 342 LocationOrAnalysisDeclContext LAC) const { 343 assert(isValid()); 344 // Note that we want a 'switch' here so that the compiler can warn us in 345 // case we add more cases. 346 switch (K) { 347 case SingleLocK: 348 case RangeK: 349 break; 350 case StmtK: 351 // Defensive checking. 352 if (!S) 353 break; 354 return FullSourceLoc(getValidSourceLocation(S, LAC), 355 const_cast<SourceManager&>(*SM)); 356 case DeclK: 357 // Defensive checking. 358 if (!D) 359 break; 360 return FullSourceLoc(D->getLocation(), const_cast<SourceManager&>(*SM)); 361 } 362 363 return FullSourceLoc(L, const_cast<SourceManager&>(*SM)); 364} 365 366PathDiagnosticRange 367 PathDiagnosticLocation::genRange(LocationOrAnalysisDeclContext LAC) const { 368 assert(isValid()); 369 // Note that we want a 'switch' here so that the compiler can warn us in 370 // case we add more cases. 371 switch (K) { 372 case SingleLocK: 373 return PathDiagnosticRange(SourceRange(Loc,Loc), true); 374 case RangeK: 375 break; 376 case StmtK: { 377 const Stmt *S = asStmt(); 378 switch (S->getStmtClass()) { 379 default: 380 break; 381 case Stmt::DeclStmtClass: { 382 const DeclStmt *DS = cast<DeclStmt>(S); 383 if (DS->isSingleDecl()) { 384 // Should always be the case, but we'll be defensive. 385 return SourceRange(DS->getLocStart(), 386 DS->getSingleDecl()->getLocation()); 387 } 388 break; 389 } 390 // FIXME: Provide better range information for different 391 // terminators. 392 case Stmt::IfStmtClass: 393 case Stmt::WhileStmtClass: 394 case Stmt::DoStmtClass: 395 case Stmt::ForStmtClass: 396 case Stmt::ChooseExprClass: 397 case Stmt::IndirectGotoStmtClass: 398 case Stmt::SwitchStmtClass: 399 case Stmt::BinaryConditionalOperatorClass: 400 case Stmt::ConditionalOperatorClass: 401 case Stmt::ObjCForCollectionStmtClass: { 402 SourceLocation L = getValidSourceLocation(S, LAC); 403 return SourceRange(L, L); 404 } 405 } 406 SourceRange R = S->getSourceRange(); 407 if (R.isValid()) 408 return R; 409 break; 410 } 411 case DeclK: 412 if (const ObjCMethodDecl *MD = dyn_cast<ObjCMethodDecl>(D)) 413 return MD->getSourceRange(); 414 if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) { 415 if (Stmt *Body = FD->getBody()) 416 return Body->getSourceRange(); 417 } 418 else { 419 SourceLocation L = D->getLocation(); 420 return PathDiagnosticRange(SourceRange(L, L), true); 421 } 422 } 423 424 return SourceRange(Loc,Loc); 425} 426 427void PathDiagnosticLocation::flatten() { 428 if (K == StmtK) { 429 K = RangeK; 430 S = 0; 431 D = 0; 432 } 433 else if (K == DeclK) { 434 K = SingleLocK; 435 S = 0; 436 D = 0; 437 } 438} 439 440//===----------------------------------------------------------------------===// 441// FoldingSet profiling methods. 442//===----------------------------------------------------------------------===// 443 444void PathDiagnosticLocation::Profile(llvm::FoldingSetNodeID &ID) const { 445 ID.AddInteger(Range.getBegin().getRawEncoding()); 446 ID.AddInteger(Range.getEnd().getRawEncoding()); 447 ID.AddInteger(Loc.getRawEncoding()); 448 return; 449} 450 451void PathDiagnosticPiece::Profile(llvm::FoldingSetNodeID &ID) const { 452 ID.AddInteger((unsigned) getKind()); 453 ID.AddString(str); 454 // FIXME: Add profiling support for code hints. 455 ID.AddInteger((unsigned) getDisplayHint()); 456 for (range_iterator I = ranges_begin(), E = ranges_end(); I != E; ++I) { 457 ID.AddInteger(I->getBegin().getRawEncoding()); 458 ID.AddInteger(I->getEnd().getRawEncoding()); 459 } 460} 461 462void PathDiagnosticSpotPiece::Profile(llvm::FoldingSetNodeID &ID) const { 463 PathDiagnosticPiece::Profile(ID); 464 ID.Add(Pos); 465} 466 467void PathDiagnosticControlFlowPiece::Profile(llvm::FoldingSetNodeID &ID) const { 468 PathDiagnosticPiece::Profile(ID); 469 for (const_iterator I = begin(), E = end(); I != E; ++I) 470 ID.Add(*I); 471} 472 473void PathDiagnosticMacroPiece::Profile(llvm::FoldingSetNodeID &ID) const { 474 PathDiagnosticSpotPiece::Profile(ID); 475 for (const_iterator I = begin(), E = end(); I != E; ++I) 476 ID.Add(**I); 477} 478 479void PathDiagnostic::Profile(llvm::FoldingSetNodeID &ID) const { 480 if (Size) 481 getLocation().Profile(ID); 482 ID.AddString(BugType); 483 ID.AddString(Desc); 484 ID.AddString(Category); 485} 486 487void PathDiagnostic::FullProfile(llvm::FoldingSetNodeID &ID) const { 488 Profile(ID); 489 for (const_iterator I = begin(), E = end(); I != E; ++I) 490 ID.Add(*I); 491 for (meta_iterator I = meta_begin(), E = meta_end(); I != E; ++I) 492 ID.AddString(*I); 493} 494