PathDiagnostic.cpp revision 21a25167b8279df3d8f889d041a4fd98733d3c27
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::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 PathDiagnosticConsumer from multiple bug reporters. 89 HandlePathDiagnosticImpl(D); 90} 91 92//===----------------------------------------------------------------------===// 93// PathDiagnosticLocation methods. 94//===----------------------------------------------------------------------===// 95 96static SourceLocation getValidSourceLocation(const Stmt* S, 97 LocationOrAnalysisDeclContext LAC) { 98 SourceLocation L = S->getLocStart(); 99 assert(!LAC.isNull() && "A valid LocationContext or AnalysisDeclContext 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<AnalysisDeclContext*>()->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 LocationOrAnalysisDeclContext 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 198PathDiagnosticLocation 199 PathDiagnosticLocation::createEndOfPath(const ExplodedNode* N, 200 const SourceManager &SM) { 201 assert(N && "Cannot create a location with a null node."); 202 203 const ExplodedNode *NI = N; 204 205 while (NI) { 206 ProgramPoint P = NI->getLocation(); 207 const LocationContext *LC = P.getLocationContext(); 208 if (const StmtPoint *PS = dyn_cast<StmtPoint>(&P)) 209 return PathDiagnosticLocation(PS->getStmt(), SM, LC); 210 else if (const BlockEdge *BE = dyn_cast<BlockEdge>(&P)) { 211 const Stmt *Term = BE->getSrc()->getTerminator(); 212 assert(Term); 213 return PathDiagnosticLocation(Term, SM, LC); 214 } 215 NI = NI->succ_empty() ? 0 : *(NI->succ_begin()); 216 } 217 218 return createDeclEnd(N->getLocationContext(), SM); 219} 220 221PathDiagnosticLocation PathDiagnosticLocation::createSingleLocation( 222 const PathDiagnosticLocation &PDL) { 223 FullSourceLoc L = PDL.asLocation(); 224 return PathDiagnosticLocation(L, L.getManager(), SingleLocK); 225} 226 227FullSourceLoc 228 PathDiagnosticLocation::genLocation(SourceLocation L, 229 LocationOrAnalysisDeclContext LAC) const { 230 assert(isValid()); 231 // Note that we want a 'switch' here so that the compiler can warn us in 232 // case we add more cases. 233 switch (K) { 234 case SingleLocK: 235 case RangeK: 236 break; 237 case StmtK: 238 return FullSourceLoc(getValidSourceLocation(S, LAC), 239 const_cast<SourceManager&>(*SM)); 240 case DeclK: 241 return FullSourceLoc(D->getLocation(), const_cast<SourceManager&>(*SM)); 242 } 243 244 return FullSourceLoc(L, const_cast<SourceManager&>(*SM)); 245} 246 247PathDiagnosticRange 248 PathDiagnosticLocation::genRange(LocationOrAnalysisDeclContext LAC) const { 249 assert(isValid()); 250 // Note that we want a 'switch' here so that the compiler can warn us in 251 // case we add more cases. 252 switch (K) { 253 case SingleLocK: 254 return PathDiagnosticRange(SourceRange(Loc,Loc), true); 255 case RangeK: 256 break; 257 case StmtK: { 258 const Stmt *S = asStmt(); 259 switch (S->getStmtClass()) { 260 default: 261 break; 262 case Stmt::DeclStmtClass: { 263 const DeclStmt *DS = cast<DeclStmt>(S); 264 if (DS->isSingleDecl()) { 265 // Should always be the case, but we'll be defensive. 266 return SourceRange(DS->getLocStart(), 267 DS->getSingleDecl()->getLocation()); 268 } 269 break; 270 } 271 // FIXME: Provide better range information for different 272 // terminators. 273 case Stmt::IfStmtClass: 274 case Stmt::WhileStmtClass: 275 case Stmt::DoStmtClass: 276 case Stmt::ForStmtClass: 277 case Stmt::ChooseExprClass: 278 case Stmt::IndirectGotoStmtClass: 279 case Stmt::SwitchStmtClass: 280 case Stmt::BinaryConditionalOperatorClass: 281 case Stmt::ConditionalOperatorClass: 282 case Stmt::ObjCForCollectionStmtClass: { 283 SourceLocation L = getValidSourceLocation(S, LAC); 284 return SourceRange(L, L); 285 } 286 } 287 SourceRange R = S->getSourceRange(); 288 if (R.isValid()) 289 return R; 290 break; 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(Loc,Loc); 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