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