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