PathDiagnostic.cpp revision ef70724e66d8ede0edbe260fbcdd9781688bb1fd
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 PathDiagnosticClient::HandlePathDiagnostic(const PathDiagnostic *D) { 86 // For now this simply forwards to HandlePathDiagnosticImpl. In the future 87 // we can use this indirection to control for multi-threaded access to 88 // the PathDiagnosticClient from multiple bug reporters. 89 HandlePathDiagnosticImpl(D); 90} 91 92//===----------------------------------------------------------------------===// 93// PathDiagnosticLocation methods. 94//===----------------------------------------------------------------------===// 95 96static SourceLocation getValidSourceLocation(const Stmt* S, 97 LocationOrAnalysisContext LAC) { 98 SourceLocation L = S->getLocStart(); 99 assert(!LAC.isNull() && "A valid LocationContext or AnalysisContext should " 100 "be passed to PathDiagnosticLocation upon creation."); 101 102 // S might be a temporary statement that does not have a location in the 103 // source code, so find an enclosing statement and use it's location. 104 if (!L.isValid()) { 105 106 ParentMap *PM = 0; 107 if (LAC.is<const LocationContext*>()) 108 PM = &LAC.get<const LocationContext*>()->getParentMap(); 109 else 110 PM = &LAC.get<AnalysisContext*>()->getParentMap(); 111 112 while (!L.isValid()) { 113 S = PM->getParent(S); 114 L = S->getLocStart(); 115 } 116 } 117 118 return L; 119} 120 121PathDiagnosticLocation 122 PathDiagnosticLocation::createBegin(const Decl *D, 123 const SourceManager &SM) { 124 return PathDiagnosticLocation(D->getLocStart(), SM, SingleLocK); 125} 126 127PathDiagnosticLocation 128 PathDiagnosticLocation::createBegin(const Stmt *S, 129 const SourceManager &SM, 130 LocationOrAnalysisContext LAC) { 131 return PathDiagnosticLocation(getValidSourceLocation(S, LAC), 132 SM, SingleLocK); 133} 134 135PathDiagnosticLocation 136 PathDiagnosticLocation::createOperatorLoc(const BinaryOperator *BO, 137 const SourceManager &SM) { 138 return PathDiagnosticLocation(BO->getOperatorLoc(), SM, SingleLocK); 139} 140 141PathDiagnosticLocation 142 PathDiagnosticLocation::createMemberLoc(const MemberExpr *ME, 143 const SourceManager &SM) { 144 return PathDiagnosticLocation(ME->getMemberLoc(), SM, SingleLocK); 145} 146 147PathDiagnosticLocation 148 PathDiagnosticLocation::createBeginBrace(const CompoundStmt *CS, 149 const SourceManager &SM) { 150 SourceLocation L = CS->getLBracLoc(); 151 return PathDiagnosticLocation(L, SM, SingleLocK); 152} 153 154PathDiagnosticLocation 155 PathDiagnosticLocation::createEndBrace(const CompoundStmt *CS, 156 const SourceManager &SM) { 157 SourceLocation L = CS->getRBracLoc(); 158 return PathDiagnosticLocation(L, SM, SingleLocK); 159} 160 161PathDiagnosticLocation 162 PathDiagnosticLocation::createDeclBegin(const LocationContext *LC, 163 const SourceManager &SM) { 164 // FIXME: Should handle CXXTryStmt if analyser starts supporting C++. 165 if (const CompoundStmt *CS = 166 dyn_cast_or_null<CompoundStmt>(LC->getDecl()->getBody())) 167 if (!CS->body_empty()) { 168 SourceLocation Loc = (*CS->body_begin())->getLocStart(); 169 return PathDiagnosticLocation(Loc, SM, SingleLocK); 170 } 171 172 return PathDiagnosticLocation(); 173} 174 175PathDiagnosticLocation 176 PathDiagnosticLocation::createDeclEnd(const LocationContext *LC, 177 const SourceManager &SM) { 178 SourceLocation L = LC->getDecl()->getBodyRBrace(); 179 return PathDiagnosticLocation(L, SM, SingleLocK); 180} 181 182PathDiagnosticLocation 183 PathDiagnosticLocation::create(const ProgramPoint& P, 184 const SourceManager &SMng) { 185 186 const Stmt* S = 0; 187 if (const BlockEdge *BE = dyn_cast<BlockEdge>(&P)) { 188 const CFGBlock *BSrc = BE->getSrc(); 189 S = BSrc->getTerminatorCondition(); 190 } 191 else if (const PostStmt *PS = dyn_cast<PostStmt>(&P)) { 192 S = PS->getStmt(); 193 } 194 195 return PathDiagnosticLocation(S, SMng, P.getLocationContext()); 196 197 if (!S) 198 return PathDiagnosticLocation(); 199} 200 201PathDiagnosticLocation 202 PathDiagnosticLocation::createEndOfPath(const ExplodedNode* N, 203 const SourceManager &SM) { 204 assert(N && "Cannot create a location with a null node."); 205 206 const ExplodedNode *NI = N; 207 208 while (NI) { 209 ProgramPoint P = NI->getLocation(); 210 const LocationContext *LC = P.getLocationContext(); 211 if (const StmtPoint *PS = dyn_cast<StmtPoint>(&P)) 212 return PathDiagnosticLocation(PS->getStmt(), SM, LC); 213 else if (const BlockEdge *BE = dyn_cast<BlockEdge>(&P)) { 214 const Stmt *Term = BE->getSrc()->getTerminator(); 215 assert(Term); 216 return PathDiagnosticLocation(Term, SM, LC); 217 } 218 NI = NI->succ_empty() ? 0 : *(NI->succ_begin()); 219 } 220 221 return createDeclEnd(N->getLocationContext(), SM); 222} 223 224PathDiagnosticLocation PathDiagnosticLocation::createSingleLocation( 225 const PathDiagnosticLocation &PDL) { 226 FullSourceLoc L = PDL.asLocation(); 227 return PathDiagnosticLocation(L, L.getManager(), SingleLocK); 228} 229 230FullSourceLoc 231 PathDiagnosticLocation::genLocation(SourceLocation L, LocationOrAnalysisContext LAC) const { 232 assert(isValid()); 233 // Note that we want a 'switch' here so that the compiler can warn us in 234 // case we add more cases. 235 switch (K) { 236 case SingleLocK: 237 case RangeK: 238 break; 239 case StmtK: 240 return FullSourceLoc(getValidSourceLocation(S, LAC), 241 const_cast<SourceManager&>(*SM)); 242 case DeclK: 243 return FullSourceLoc(D->getLocation(), const_cast<SourceManager&>(*SM)); 244 } 245 246 return FullSourceLoc(L, const_cast<SourceManager&>(*SM)); 247} 248 249PathDiagnosticRange 250 PathDiagnosticLocation::genRange(SourceLocation L, LocationOrAnalysisContext LAC) const { 251 assert(isValid()); 252 // Note that we want a 'switch' here so that the compiler can warn us in 253 // case we add more cases. 254 switch (K) { 255 case SingleLocK: 256 return PathDiagnosticRange(SourceRange(L,L), true); 257 case RangeK: 258 break; 259 case StmtK: { 260 const Stmt *S = asStmt(); 261 switch (S->getStmtClass()) { 262 default: 263 break; 264 case Stmt::DeclStmtClass: { 265 const DeclStmt *DS = cast<DeclStmt>(S); 266 if (DS->isSingleDecl()) { 267 // Should always be the case, but we'll be defensive. 268 return SourceRange(DS->getLocStart(), 269 DS->getSingleDecl()->getLocation()); 270 } 271 break; 272 } 273 // FIXME: Provide better range information for different 274 // terminators. 275 case Stmt::IfStmtClass: 276 case Stmt::WhileStmtClass: 277 case Stmt::DoStmtClass: 278 case Stmt::ForStmtClass: 279 case Stmt::ChooseExprClass: 280 case Stmt::IndirectGotoStmtClass: 281 case Stmt::SwitchStmtClass: 282 case Stmt::BinaryConditionalOperatorClass: 283 case Stmt::ConditionalOperatorClass: 284 case Stmt::ObjCForCollectionStmtClass: { 285 SourceLocation L = getValidSourceLocation(S, LAC); 286 return SourceRange(L, L); 287 } 288 } 289 290 return S->getSourceRange(); 291 } 292 case DeclK: 293 if (const ObjCMethodDecl *MD = dyn_cast<ObjCMethodDecl>(D)) 294 return MD->getSourceRange(); 295 if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) { 296 if (Stmt *Body = FD->getBody()) 297 return Body->getSourceRange(); 298 } 299 else { 300 SourceLocation L = D->getLocation(); 301 return PathDiagnosticRange(SourceRange(L, L), true); 302 } 303 } 304 305 return SourceRange(L,L); 306} 307 308void PathDiagnosticLocation::flatten() { 309 if (K == StmtK) { 310 K = RangeK; 311 S = 0; 312 D = 0; 313 } 314 else if (K == DeclK) { 315 K = SingleLocK; 316 S = 0; 317 D = 0; 318 } 319} 320 321//===----------------------------------------------------------------------===// 322// FoldingSet profiling methods. 323//===----------------------------------------------------------------------===// 324 325void PathDiagnosticLocation::Profile(llvm::FoldingSetNodeID &ID) const { 326 ID.AddInteger(Range.getBegin().getRawEncoding()); 327 ID.AddInteger(Range.getEnd().getRawEncoding()); 328 ID.AddInteger(Loc.getRawEncoding()); 329 return; 330} 331 332void PathDiagnosticPiece::Profile(llvm::FoldingSetNodeID &ID) const { 333 ID.AddInteger((unsigned) getKind()); 334 ID.AddString(str); 335 // FIXME: Add profiling support for code hints. 336 ID.AddInteger((unsigned) getDisplayHint()); 337 for (range_iterator I = ranges_begin(), E = ranges_end(); I != E; ++I) { 338 ID.AddInteger(I->getBegin().getRawEncoding()); 339 ID.AddInteger(I->getEnd().getRawEncoding()); 340 } 341} 342 343void PathDiagnosticSpotPiece::Profile(llvm::FoldingSetNodeID &ID) const { 344 PathDiagnosticPiece::Profile(ID); 345 ID.Add(Pos); 346} 347 348void PathDiagnosticControlFlowPiece::Profile(llvm::FoldingSetNodeID &ID) const { 349 PathDiagnosticPiece::Profile(ID); 350 for (const_iterator I = begin(), E = end(); I != E; ++I) 351 ID.Add(*I); 352} 353 354void PathDiagnosticMacroPiece::Profile(llvm::FoldingSetNodeID &ID) const { 355 PathDiagnosticSpotPiece::Profile(ID); 356 for (const_iterator I = begin(), E = end(); I != E; ++I) 357 ID.Add(**I); 358} 359 360void PathDiagnostic::Profile(llvm::FoldingSetNodeID &ID) const { 361 ID.AddInteger(Size); 362 ID.AddString(BugType); 363 ID.AddString(Desc); 364 ID.AddString(Category); 365 for (const_iterator I = begin(), E = end(); I != E; ++I) 366 ID.Add(*I); 367 368 for (meta_iterator I = meta_begin(), E = meta_end(); I != E; ++I) 369 ID.AddString(*I); 370} 371