PathDiagnostic.cpp revision 43f48b0b1bc763dc56db6e01de4fcc44ad389bef
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/StmtCXX.h" 20#include "llvm/ADT/SmallString.h" 21 22using namespace clang; 23using namespace ento; 24 25bool PathDiagnosticMacroPiece::containsEvent() const { 26 for (const_iterator I = begin(), E = end(); I!=E; ++I) { 27 if (isa<PathDiagnosticEventPiece>(*I)) 28 return true; 29 30 if (PathDiagnosticMacroPiece *MP = dyn_cast<PathDiagnosticMacroPiece>(*I)) 31 if (MP->containsEvent()) 32 return true; 33 } 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() {} 54PathDiagnosticControlFlowPiece::~PathDiagnosticControlFlowPiece() {} 55 56PathDiagnosticMacroPiece::~PathDiagnosticMacroPiece() { 57 for (iterator I = begin(), E = end(); I != E; ++I) delete *I; 58} 59 60PathDiagnostic::PathDiagnostic() : Size(0) {} 61 62PathDiagnostic::~PathDiagnostic() { 63 for (iterator I = begin(), E = end(); I != E; ++I) delete &*I; 64} 65 66void PathDiagnostic::resetPath(bool deletePieces) { 67 Size = 0; 68 69 if (deletePieces) 70 for (iterator I=begin(), E=end(); I!=E; ++I) 71 delete &*I; 72 73 path.clear(); 74} 75 76 77PathDiagnostic::PathDiagnostic(StringRef bugtype, StringRef desc, 78 StringRef category) 79 : Size(0), 80 BugType(StripTrailingDots(bugtype)), 81 Desc(StripTrailingDots(desc)), 82 Category(StripTrailingDots(category)) {} 83 84void PathDiagnosticClient::HandleDiagnostic(Diagnostic::Level DiagLevel, 85 const DiagnosticInfo &Info) { 86 // Default implementation (Warnings/errors count). 87 DiagnosticClient::HandleDiagnostic(DiagLevel, Info); 88 89 // Create a PathDiagnostic with a single piece. 90 91 PathDiagnostic* D = new PathDiagnostic(); 92 93 const char *LevelStr; 94 switch (DiagLevel) { 95 default: 96 case Diagnostic::Ignored: assert(0 && "Invalid diagnostic type"); 97 case Diagnostic::Note: LevelStr = "note: "; break; 98 case Diagnostic::Warning: LevelStr = "warning: "; break; 99 case Diagnostic::Error: LevelStr = "error: "; break; 100 case Diagnostic::Fatal: LevelStr = "fatal error: "; break; 101 } 102 103 llvm::SmallString<100> StrC; 104 StrC += LevelStr; 105 Info.FormatDiagnostic(StrC); 106 107 PathDiagnosticPiece *P = 108 new PathDiagnosticEventPiece(FullSourceLoc(Info.getLocation(), 109 Info.getSourceManager()), 110 StrC.str()); 111 112 for (unsigned i = 0, e = Info.getNumRanges(); i != e; ++i) 113 P->addRange(Info.getRange(i).getAsRange()); 114 for (unsigned i = 0, e = Info.getNumFixItHints(); i != e; ++i) 115 P->addFixItHint(Info.getFixItHint(i)); 116 D->push_front(P); 117 118 HandlePathDiagnostic(D); 119} 120 121void PathDiagnosticClient::HandlePathDiagnostic(const PathDiagnostic *D) { 122 // For now this simply forwards to HandlePathDiagnosticImpl. In the future 123 // we can use this indirection to control for multi-threaded access to 124 // the PathDiagnosticClient from multiple bug reporters. 125 HandlePathDiagnosticImpl(D); 126} 127 128//===----------------------------------------------------------------------===// 129// PathDiagnosticLocation methods. 130//===----------------------------------------------------------------------===// 131 132PathDiagnosticLocation PathDiagnosticLocation::create(const ExplodedNode* N, 133 const SourceManager &SM) { 134 assert(N && "Cannot create a location with a null node."); 135 136 const ExplodedNode *NI = N; 137 138 while (NI) { 139 ProgramPoint P = NI->getLocation(); 140 141 if (const StmtPoint *PS = dyn_cast<StmtPoint>(&P)) { 142 return PathDiagnosticLocation(PS->getStmt(), SM); 143 } 144 else if (const BlockEdge *BE = dyn_cast<BlockEdge>(&P)) { 145 const Stmt *Term = BE->getSrc()->getTerminator(); 146 assert(Term); 147 return PathDiagnosticLocation(Term, SM); 148 } 149 NI = NI->succ_empty() ? 0 : *(NI->succ_begin()); 150 } 151 152 const Decl &D = N->getCodeDecl(); 153 return PathDiagnosticLocation(D.getBodyRBrace(), SM); 154} 155 156FullSourceLoc PathDiagnosticLocation::asLocation() const { 157 assert(isValid()); 158 // Note that we want a 'switch' here so that the compiler can warn us in 159 // case we add more cases. 160 switch (K) { 161 case SingleLocK: 162 case RangeK: 163 break; 164 case StmtK: 165 return FullSourceLoc(S->getLocStart(), const_cast<SourceManager&>(*SM)); 166 case DeclK: 167 return FullSourceLoc(D->getLocation(), const_cast<SourceManager&>(*SM)); 168 } 169 170 return FullSourceLoc(R.getBegin(), const_cast<SourceManager&>(*SM)); 171} 172 173PathDiagnosticRange PathDiagnosticLocation::asRange() const { 174 assert(isValid()); 175 // Note that we want a 'switch' here so that the compiler can warn us in 176 // case we add more cases. 177 switch (K) { 178 case SingleLocK: 179 return PathDiagnosticRange(R, true); 180 case RangeK: 181 break; 182 case StmtK: { 183 const Stmt *S = asStmt(); 184 switch (S->getStmtClass()) { 185 default: 186 break; 187 case Stmt::DeclStmtClass: { 188 const DeclStmt *DS = cast<DeclStmt>(S); 189 if (DS->isSingleDecl()) { 190 // Should always be the case, but we'll be defensive. 191 return SourceRange(DS->getLocStart(), 192 DS->getSingleDecl()->getLocation()); 193 } 194 break; 195 } 196 // FIXME: Provide better range information for different 197 // terminators. 198 case Stmt::IfStmtClass: 199 case Stmt::WhileStmtClass: 200 case Stmt::DoStmtClass: 201 case Stmt::ForStmtClass: 202 case Stmt::ChooseExprClass: 203 case Stmt::IndirectGotoStmtClass: 204 case Stmt::SwitchStmtClass: 205 case Stmt::BinaryConditionalOperatorClass: 206 case Stmt::ConditionalOperatorClass: 207 case Stmt::ObjCForCollectionStmtClass: { 208 SourceLocation L = S->getLocStart(); 209 return SourceRange(L, L); 210 } 211 } 212 213 return S->getSourceRange(); 214 } 215 case DeclK: 216 if (const ObjCMethodDecl *MD = dyn_cast<ObjCMethodDecl>(D)) 217 return MD->getSourceRange(); 218 if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) { 219 if (Stmt *Body = FD->getBody()) 220 return Body->getSourceRange(); 221 } 222 else { 223 SourceLocation L = D->getLocation(); 224 return PathDiagnosticRange(SourceRange(L, L), true); 225 } 226 } 227 228 return R; 229} 230 231void PathDiagnosticLocation::flatten() { 232 if (K == StmtK) { 233 R = asRange(); 234 K = RangeK; 235 S = 0; 236 D = 0; 237 } 238 else if (K == DeclK) { 239 SourceLocation L = D->getLocation(); 240 R = SourceRange(L, L); 241 K = SingleLocK; 242 S = 0; 243 D = 0; 244 } 245} 246 247//===----------------------------------------------------------------------===// 248// FoldingSet profiling methods. 249//===----------------------------------------------------------------------===// 250 251void PathDiagnosticLocation::Profile(llvm::FoldingSetNodeID &ID) const { 252 ID.AddInteger((unsigned) K); 253 switch (K) { 254 case RangeK: 255 ID.AddInteger(R.getBegin().getRawEncoding()); 256 ID.AddInteger(R.getEnd().getRawEncoding()); 257 break; 258 case SingleLocK: 259 ID.AddInteger(R.getBegin().getRawEncoding()); 260 break; 261 case StmtK: 262 ID.Add(S); 263 break; 264 case DeclK: 265 ID.Add(D); 266 break; 267 } 268 return; 269} 270 271void PathDiagnosticPiece::Profile(llvm::FoldingSetNodeID &ID) const { 272 ID.AddInteger((unsigned) getKind()); 273 ID.AddString(str); 274 // FIXME: Add profiling support for code hints. 275 ID.AddInteger((unsigned) getDisplayHint()); 276 for (range_iterator I = ranges_begin(), E = ranges_end(); I != E; ++I) { 277 ID.AddInteger(I->getBegin().getRawEncoding()); 278 ID.AddInteger(I->getEnd().getRawEncoding()); 279 } 280} 281 282void PathDiagnosticSpotPiece::Profile(llvm::FoldingSetNodeID &ID) const { 283 PathDiagnosticPiece::Profile(ID); 284 ID.Add(Pos); 285} 286 287void PathDiagnosticControlFlowPiece::Profile(llvm::FoldingSetNodeID &ID) const { 288 PathDiagnosticPiece::Profile(ID); 289 for (const_iterator I = begin(), E = end(); I != E; ++I) 290 ID.Add(*I); 291} 292 293void PathDiagnosticMacroPiece::Profile(llvm::FoldingSetNodeID &ID) const { 294 PathDiagnosticSpotPiece::Profile(ID); 295 for (const_iterator I = begin(), E = end(); I != E; ++I) 296 ID.Add(**I); 297} 298 299void PathDiagnostic::Profile(llvm::FoldingSetNodeID &ID) const { 300 ID.AddInteger(Size); 301 ID.AddString(BugType); 302 ID.AddString(Desc); 303 ID.AddString(Category); 304 for (const_iterator I = begin(), E = end(); I != E; ++I) 305 ID.Add(*I); 306 307 for (meta_iterator I = meta_begin(), E = meta_end(); I != E; ++I) 308 ID.AddString(*I); 309} 310