Diagnostic.cpp revision 2383b7f6aea2cb2bf2b5bfc0ec730f9354fecbbf
1//===--- Diagnostic.cpp - C Language Family Diagnostic Handling -----------===// 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 implements the Diagnostic-related interfaces. 11// 12//===----------------------------------------------------------------------===// 13 14#include "clang/Basic/Diagnostic.h" 15#include "clang/Basic/SourceLocation.h" 16#include <cassert> 17#include <vector> 18#include <map> 19#include <cstring> 20using namespace clang; 21 22//===----------------------------------------------------------------------===// 23// Builtin Diagnostic information 24//===----------------------------------------------------------------------===// 25 26/// Flag values for diagnostics. 27enum { 28 // Diagnostic classes. 29 NOTE = 0x01, 30 WARNING = 0x02, 31 EXTENSION = 0x03, 32 EXTWARN = 0x04, 33 ERROR = 0x05, 34 class_mask = 0x07 35}; 36 37/// DiagnosticFlags - A set of flags, or'd together, that describe the 38/// diagnostic. 39static unsigned char DiagnosticFlags[] = { 40#define DIAG(ENUM,FLAGS,DESC) FLAGS, 41#include "clang/Basic/DiagnosticKinds.def" 42 0 43}; 44 45/// getDiagClass - Return the class field of the diagnostic. 46/// 47static unsigned getBuiltinDiagClass(unsigned DiagID) { 48 assert(DiagID < diag::NUM_BUILTIN_DIAGNOSTICS && 49 "Diagnostic ID out of range!"); 50 return DiagnosticFlags[DiagID] & class_mask; 51} 52 53/// DiagnosticText - An english message to print for the diagnostic. These 54/// should be localized. 55static const char * const DiagnosticText[] = { 56#define DIAG(ENUM,FLAGS,DESC) DESC, 57#include "clang/Basic/DiagnosticKinds.def" 58 0 59}; 60 61//===----------------------------------------------------------------------===// 62// Custom Diagnostic information 63//===----------------------------------------------------------------------===// 64 65namespace clang { 66 namespace diag { 67 class CustomDiagInfo { 68 typedef std::pair<Diagnostic::Level, std::string> DiagDesc; 69 std::vector<DiagDesc> DiagInfo; 70 std::map<DiagDesc, unsigned> DiagIDs; 71 public: 72 73 /// getDescription - Return the description of the specified custom 74 /// diagnostic. 75 const char *getDescription(unsigned DiagID) const { 76 assert(this && DiagID-diag::NUM_BUILTIN_DIAGNOSTICS < DiagInfo.size() && 77 "Invalid diagnosic ID"); 78 return DiagInfo[DiagID-diag::NUM_BUILTIN_DIAGNOSTICS].second.c_str(); 79 } 80 81 /// getLevel - Return the level of the specified custom diagnostic. 82 Diagnostic::Level getLevel(unsigned DiagID) const { 83 assert(this && DiagID-diag::NUM_BUILTIN_DIAGNOSTICS < DiagInfo.size() && 84 "Invalid diagnosic ID"); 85 return DiagInfo[DiagID-diag::NUM_BUILTIN_DIAGNOSTICS].first; 86 } 87 88 unsigned getOrCreateDiagID(Diagnostic::Level L, const char *Message, 89 Diagnostic &Diags) { 90 DiagDesc D(L, Message); 91 // Check to see if it already exists. 92 std::map<DiagDesc, unsigned>::iterator I = DiagIDs.lower_bound(D); 93 if (I != DiagIDs.end() && I->first == D) 94 return I->second; 95 96 // If not, assign a new ID. 97 unsigned ID = DiagInfo.size()+diag::NUM_BUILTIN_DIAGNOSTICS; 98 DiagIDs.insert(std::make_pair(D, ID)); 99 DiagInfo.push_back(D); 100 101 // If this is a warning, and all warnings are supposed to map to errors, 102 // insert the mapping now. 103 if (L == Diagnostic::Warning && Diags.getWarningsAsErrors()) 104 Diags.setDiagnosticMapping((diag::kind)ID, diag::MAP_ERROR); 105 return ID; 106 } 107 }; 108 109 } // end diag namespace 110} // end clang namespace 111 112 113//===----------------------------------------------------------------------===// 114// Common Diagnostic implementation 115//===----------------------------------------------------------------------===// 116 117Diagnostic::Diagnostic(DiagnosticClient *client) : Client(client) { 118 IgnoreAllWarnings = false; 119 WarningsAsErrors = false; 120 WarnOnExtensions = false; 121 ErrorOnExtensions = false; 122 SuppressSystemWarnings = false; 123 // Clear all mappings, setting them to MAP_DEFAULT. 124 memset(DiagMappings, 0, sizeof(DiagMappings)); 125 126 ErrorOccurred = false; 127 NumDiagnostics = 0; 128 NumErrors = 0; 129 CustomDiagInfo = 0; 130} 131 132Diagnostic::~Diagnostic() { 133 delete CustomDiagInfo; 134} 135 136/// getCustomDiagID - Return an ID for a diagnostic with the specified message 137/// and level. If this is the first request for this diagnosic, it is 138/// registered and created, otherwise the existing ID is returned. 139unsigned Diagnostic::getCustomDiagID(Level L, const char *Message) { 140 if (CustomDiagInfo == 0) 141 CustomDiagInfo = new diag::CustomDiagInfo(); 142 return CustomDiagInfo->getOrCreateDiagID(L, Message, *this); 143} 144 145 146/// isBuiltinNoteWarningOrExtension - Return true if the unmapped diagnostic 147/// level of the specified diagnostic ID is a Note, Warning, or Extension. 148/// Note that this only works on builtin diagnostics, not custom ones. 149bool Diagnostic::isBuiltinNoteWarningOrExtension(unsigned DiagID) { 150 return DiagID < diag::NUM_BUILTIN_DIAGNOSTICS && 151 getBuiltinDiagClass(DiagID) < ERROR; 152} 153 154 155/// getDescription - Given a diagnostic ID, return a description of the 156/// issue. 157const char *Diagnostic::getDescription(unsigned DiagID) { 158 if (DiagID < diag::NUM_BUILTIN_DIAGNOSTICS) 159 return DiagnosticText[DiagID]; 160 else 161 return CustomDiagInfo->getDescription(DiagID); 162} 163 164/// getDiagnosticLevel - Based on the way the client configured the Diagnostic 165/// object, classify the specified diagnostic ID into a Level, consumable by 166/// the DiagnosticClient. 167Diagnostic::Level Diagnostic::getDiagnosticLevel(unsigned DiagID) const { 168 // Handle custom diagnostics, which cannot be mapped. 169 if (DiagID >= diag::NUM_BUILTIN_DIAGNOSTICS) 170 return CustomDiagInfo->getLevel(DiagID); 171 172 unsigned DiagClass = getBuiltinDiagClass(DiagID); 173 174 // Specific non-error diagnostics may be mapped to various levels from ignored 175 // to error. 176 if (DiagClass < ERROR) { 177 switch (getDiagnosticMapping((diag::kind)DiagID)) { 178 case diag::MAP_DEFAULT: break; 179 case diag::MAP_IGNORE: return Diagnostic::Ignored; 180 case diag::MAP_WARNING: DiagClass = WARNING; break; 181 case diag::MAP_ERROR: DiagClass = ERROR; break; 182 } 183 } 184 185 // Map diagnostic classes based on command line argument settings. 186 if (DiagClass == EXTENSION) { 187 if (ErrorOnExtensions) 188 DiagClass = ERROR; 189 else if (WarnOnExtensions) 190 DiagClass = WARNING; 191 else 192 return Ignored; 193 } else if (DiagClass == EXTWARN) { 194 DiagClass = ErrorOnExtensions ? ERROR : WARNING; 195 } 196 197 // If warnings are globally mapped to ignore or error, do it. 198 if (DiagClass == WARNING) { 199 if (IgnoreAllWarnings) 200 return Diagnostic::Ignored; 201 if (WarningsAsErrors) 202 DiagClass = ERROR; 203 } 204 205 switch (DiagClass) { 206 default: assert(0 && "Unknown diagnostic class!"); 207 case NOTE: return Diagnostic::Note; 208 case WARNING: return Diagnostic::Warning; 209 case ERROR: return Diagnostic::Error; 210 } 211} 212 213/// Report - Issue the message to the client. 214/// DiagID is a member of the diag::kind enum. 215void Diagnostic::Report(DiagnosticClient* C, 216 FullSourceLoc Loc, unsigned DiagID, 217 const std::string **Strs, unsigned NumStrs, 218 const SourceRange *Ranges, unsigned NumRanges) { 219 220 // Figure out the diagnostic level of this message. 221 Diagnostic::Level DiagLevel = getDiagnosticLevel(DiagID); 222 223 // If the client doesn't care about this message, don't issue it. 224 if (DiagLevel == Diagnostic::Ignored) 225 return; 226 227 // Set the diagnostic client if it isn't set already. 228 if (!C) C = Client; 229 230 // If this is not an error and we are in a system header, ignore it. We 231 // have to check on the original DiagID here, because we also want to 232 // ignore extensions and warnings in -Werror and -pedantic-errors modes, 233 // which *map* warnings/extensions to errors. 234 if (SuppressSystemWarnings && 235 DiagID < diag::NUM_BUILTIN_DIAGNOSTICS && 236 getBuiltinDiagClass(DiagID) != ERROR && 237 Loc.isValid() && Loc.getPhysicalLoc().isInSystemHeader()) 238 return; 239 240 if (DiagLevel >= Diagnostic::Error) { 241 ErrorOccurred = true; 242 243 if (C != 0 && C == Client) 244 ++NumErrors; 245 } 246 247 // Finally, report it. 248 249 if (C != 0) 250 C->HandleDiagnostic(*this, DiagLevel, Loc, (diag::kind)DiagID, 251 Strs, NumStrs, Ranges, NumRanges); 252 253 if (C != 0 && C == Client) 254 ++NumDiagnostics; 255} 256 257 258DiagnosticClient::~DiagnosticClient() {} 259 260std::string DiagnosticClient::FormatDiagnostic(Diagnostic &Diags, 261 Diagnostic::Level Level, 262 diag::kind ID, 263 const std::string **Strs, 264 unsigned NumStrs) { 265 std::string Msg = Diags.getDescription(ID); 266 267 // Replace all instances of %0 in Msg with 'Extra'. 268 for (unsigned i = 0; i < Msg.size() - 1; ++i) { 269 if (Msg[i] == '%' && isdigit(Msg[i + 1])) { 270 unsigned StrNo = Msg[i + 1] - '0'; 271 Msg = std::string(Msg.begin(), Msg.begin() + i) + 272 (StrNo < NumStrs ? *Strs[StrNo] : "<<<INTERNAL ERROR>>>") + 273 std::string(Msg.begin() + i + 2, Msg.end()); 274 } 275 } 276 277 return Msg; 278} 279