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/Analysis/PathDiagnostic.h" 15 #include "clang/AST/Expr.h" 16 #include "clang/AST/Decl.h" 17 #include "clang/AST/DeclObjC.h" 18 #include "clang/AST/StmtCXX.h" 19 #include "llvm/ADT/SmallString.h" 20 #include "llvm/Support/Casting.h" 21 22 using namespace clang; 23 using llvm::dyn_cast; 24 using llvm::isa; 25 26 bool 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 39 static size_t GetNumCharsToLastNonPeriod(const char *s) { 40 const char *start = s; 41 const char *lastNonPeriod = 0; 42 43 for ( ; *s != '\0' ; ++s) 44 if (*s != '.') lastNonPeriod = s; 45 46 if (!lastNonPeriod) 47 return 0; 48 49 return (lastNonPeriod - start) + 1; 50 } 51 52 static inline size_t GetNumCharsToLastNonPeriod(const std::string &s) { 53 return s.empty () ? 0 : GetNumCharsToLastNonPeriod(&s[0]); 54 } 55 56 PathDiagnosticPiece::PathDiagnosticPiece(const std::string& s, 57 Kind k, DisplayHint hint) 58 : str(s, 0, GetNumCharsToLastNonPeriod(s)), kind(k), Hint(hint) {} 59 60 PathDiagnosticPiece::PathDiagnosticPiece(const char* s, Kind k, 61 DisplayHint hint) 62 : str(s, GetNumCharsToLastNonPeriod(s)), kind(k), Hint(hint) {} 63 64 PathDiagnosticPiece::PathDiagnosticPiece(Kind k, DisplayHint hint) 65 : kind(k), Hint(hint) {} 66 67 PathDiagnosticPiece::~PathDiagnosticPiece() {} 68 PathDiagnosticEventPiece::~PathDiagnosticEventPiece() {} 69 PathDiagnosticControlFlowPiece::~PathDiagnosticControlFlowPiece() {} 70 71 PathDiagnosticMacroPiece::~PathDiagnosticMacroPiece() { 72 for (iterator I = begin(), E = end(); I != E; ++I) delete *I; 73 } 74 75 PathDiagnostic::PathDiagnostic() : Size(0) {} 76 77 PathDiagnostic::~PathDiagnostic() { 78 for (iterator I = begin(), E = end(); I != E; ++I) delete &*I; 79 } 80 81 void PathDiagnostic::resetPath(bool deletePieces) { 82 Size = 0; 83 84 if (deletePieces) 85 for (iterator I=begin(), E=end(); I!=E; ++I) 86 delete &*I; 87 88 path.clear(); 89 } 90 91 92 PathDiagnostic::PathDiagnostic(const char* bugtype, const char* desc, 93 const char* category) 94 : Size(0), 95 BugType(bugtype, GetNumCharsToLastNonPeriod(bugtype)), 96 Desc(desc, GetNumCharsToLastNonPeriod(desc)), 97 Category(category, GetNumCharsToLastNonPeriod(category)) {} 98 99 PathDiagnostic::PathDiagnostic(const std::string& bugtype, 100 const std::string& desc, 101 const std::string& category) 102 : Size(0), 103 BugType(bugtype, 0, GetNumCharsToLastNonPeriod(bugtype)), 104 Desc(desc, 0, GetNumCharsToLastNonPeriod(desc)), 105 Category(category, 0, GetNumCharsToLastNonPeriod(category)) {} 106 107 void PathDiagnosticClient::HandleDiagnostic(Diagnostic::Level DiagLevel, 108 const DiagnosticInfo &Info) { 109 110 // Create a PathDiagnostic with a single piece. 111 112 PathDiagnostic* D = new PathDiagnostic(); 113 114 const char *LevelStr; 115 switch (DiagLevel) { 116 default: 117 case Diagnostic::Ignored: assert(0 && "Invalid diagnostic type"); 118 case Diagnostic::Note: LevelStr = "note: "; break; 119 case Diagnostic::Warning: LevelStr = "warning: "; break; 120 case Diagnostic::Error: LevelStr = "error: "; break; 121 case Diagnostic::Fatal: LevelStr = "fatal error: "; break; 122 } 123 124 llvm::SmallString<100> StrC; 125 StrC += LevelStr; 126 Info.FormatDiagnostic(StrC); 127 128 PathDiagnosticPiece *P = 129 new PathDiagnosticEventPiece(Info.getLocation(), 130 std::string(StrC.begin(), StrC.end())); 131 132 for (unsigned i = 0, e = Info.getNumRanges(); i != e; ++i) 133 P->addRange(Info.getRange(i)); 134 for (unsigned i = 0, e = Info.getNumCodeModificationHints(); i != e; ++i) 135 P->addCodeModificationHint(Info.getCodeModificationHint(i)); 136 D->push_front(P); 137 138 HandlePathDiagnostic(D); 139 } 140 141 //===----------------------------------------------------------------------===// 142 // PathDiagnosticLocation methods. 143 //===----------------------------------------------------------------------===// 144 145 FullSourceLoc PathDiagnosticLocation::asLocation() const { 146 assert(isValid()); 147 // Note that we want a 'switch' here so that the compiler can warn us in 148 // case we add more cases. 149 switch (K) { 150 case SingleLocK: 151 case RangeK: 152 break; 153 case StmtK: 154 return FullSourceLoc(S->getLocStart(), const_cast<SourceManager&>(*SM)); 155 case DeclK: 156 return FullSourceLoc(D->getLocation(), const_cast<SourceManager&>(*SM)); 157 } 158 159 return FullSourceLoc(R.getBegin(), const_cast<SourceManager&>(*SM)); 160 } 161 162 PathDiagnosticRange PathDiagnosticLocation::asRange() const { 163 assert(isValid()); 164 // Note that we want a 'switch' here so that the compiler can warn us in 165 // case we add more cases. 166 switch (K) { 167 case SingleLocK: 168 return PathDiagnosticRange(R, true); 169 case RangeK: 170 break; 171 case StmtK: { 172 const Stmt *S = asStmt(); 173 switch (S->getStmtClass()) { 174 default: 175 break; 176 case Stmt::DeclStmtClass: { 177 const DeclStmt *DS = cast<DeclStmt>(S); 178 if (DS->isSingleDecl()) { 179 // Should always be the case, but we'll be defensive. 180 return SourceRange(DS->getLocStart(), 181 DS->getSingleDecl()->getLocation()); 182 } 183 break; 184 } 185 // FIXME: Provide better range information for different 186 // terminators. 187 case Stmt::IfStmtClass: 188 case Stmt::WhileStmtClass: 189 case Stmt::DoStmtClass: 190 case Stmt::ForStmtClass: 191 case Stmt::ChooseExprClass: 192 case Stmt::IndirectGotoStmtClass: 193 case Stmt::SwitchStmtClass: 194 case Stmt::ConditionalOperatorClass: 195 case Stmt::ObjCForCollectionStmtClass: { 196 SourceLocation L = S->getLocStart(); 197 return SourceRange(L, L); 198 } 199 } 200 201 return S->getSourceRange(); 202 } 203 case DeclK: 204 if (const ObjCMethodDecl *MD = dyn_cast<ObjCMethodDecl>(D)) 205 return MD->getSourceRange(); 206 if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) { 207 // FIXME: We would like to always get the function body, even 208 // when it needs to be de-serialized, but getting the 209 // ASTContext here requires significant changes. 210 if (Stmt *Body = FD->getBodyIfAvailable()) { 211 if (CompoundStmt *CS = dyn_cast<CompoundStmt>(Body)) 212 return CS->getSourceRange(); 213 else 214 return cast<CXXTryStmt>(Body)->getSourceRange(); 215 } 216 } 217 else { 218 SourceLocation L = D->getLocation(); 219 return PathDiagnosticRange(SourceRange(L, L), true); 220 } 221 } 222 223 return R; 224 } 225 226 void PathDiagnosticLocation::flatten() { 227 if (K == StmtK) { 228 R = asRange(); 229 K = RangeK; 230 S = 0; 231 D = 0; 232 } 233 else if (K == DeclK) { 234 SourceLocation L = D->getLocation(); 235 R = SourceRange(L, L); 236 K = SingleLocK; 237 S = 0; 238 D = 0; 239 } 240 } 241 242 243