Diagnostic.cpp revision 2fe0997427d92388e66e7573f4b043e7ba285ef0
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 DiagDesc D(L, Message); 90 // Check to see if it already exists. 91 std::map<DiagDesc, unsigned>::iterator I = DiagIDs.lower_bound(D); 92 if (I != DiagIDs.end() && I->first == D) 93 return I->second; 94 95 // If not, assign a new ID. 96 unsigned ID = DiagInfo.size()+diag::NUM_BUILTIN_DIAGNOSTICS; 97 DiagIDs.insert(std::make_pair(D, ID)); 98 DiagInfo.push_back(D); 99 return ID; 100 } 101 }; 102 103 } // end diag namespace 104} // end clang namespace 105 106 107//===----------------------------------------------------------------------===// 108// Common Diagnostic implementation 109//===----------------------------------------------------------------------===// 110 111Diagnostic::Diagnostic(DiagnosticClient *client) : Client(client) { 112 IgnoreAllWarnings = false; 113 WarningsAsErrors = false; 114 WarnOnExtensions = false; 115 ErrorOnExtensions = false; 116 SuppressSystemWarnings = false; 117 // Clear all mappings, setting them to MAP_DEFAULT. 118 memset(DiagMappings, 0, sizeof(DiagMappings)); 119 120 ErrorOccurred = false; 121 NumDiagnostics = 0; 122 NumErrors = 0; 123 CustomDiagInfo = 0; 124} 125 126Diagnostic::~Diagnostic() { 127 delete CustomDiagInfo; 128} 129 130/// getCustomDiagID - Return an ID for a diagnostic with the specified message 131/// and level. If this is the first request for this diagnosic, it is 132/// registered and created, otherwise the existing ID is returned. 133unsigned Diagnostic::getCustomDiagID(Level L, const char *Message) { 134 if (CustomDiagInfo == 0) 135 CustomDiagInfo = new diag::CustomDiagInfo(); 136 return CustomDiagInfo->getOrCreateDiagID(L, Message); 137} 138 139 140/// isBuiltinNoteWarningOrExtension - Return true if the unmapped diagnostic 141/// level of the specified diagnostic ID is a Note, Warning, or Extension. 142/// Note that this only works on builtin diagnostics, not custom ones. 143bool Diagnostic::isBuiltinNoteWarningOrExtension(unsigned DiagID) { 144 return DiagID < diag::NUM_BUILTIN_DIAGNOSTICS && 145 getBuiltinDiagClass(DiagID) < ERROR; 146} 147 148 149/// getDescription - Given a diagnostic ID, return a description of the 150/// issue. 151const char *Diagnostic::getDescription(unsigned DiagID) { 152 if (DiagID < diag::NUM_BUILTIN_DIAGNOSTICS) 153 return DiagnosticText[DiagID]; 154 else 155 return CustomDiagInfo->getDescription(DiagID); 156} 157 158/// getDiagnosticLevel - Based on the way the client configured the Diagnostic 159/// object, classify the specified diagnostic ID into a Level, consumable by 160/// the DiagnosticClient. 161Diagnostic::Level Diagnostic::getDiagnosticLevel(unsigned DiagID) const { 162 // Handle custom diagnostics, which cannot be mapped. 163 if (DiagID >= diag::NUM_BUILTIN_DIAGNOSTICS) 164 return CustomDiagInfo->getLevel(DiagID); 165 166 unsigned DiagClass = getBuiltinDiagClass(DiagID); 167 168 // Specific non-error diagnostics may be mapped to various levels from ignored 169 // to error. 170 if (DiagClass < ERROR) { 171 switch (getDiagnosticMapping((diag::kind)DiagID)) { 172 case diag::MAP_DEFAULT: break; 173 case diag::MAP_IGNORE: return Diagnostic::Ignored; 174 case diag::MAP_WARNING: DiagClass = WARNING; break; 175 case diag::MAP_ERROR: DiagClass = ERROR; break; 176 } 177 } 178 179 // Map diagnostic classes based on command line argument settings. 180 if (DiagClass == EXTENSION) { 181 if (ErrorOnExtensions) 182 DiagClass = ERROR; 183 else if (WarnOnExtensions) 184 DiagClass = WARNING; 185 else 186 return Ignored; 187 } else if (DiagClass == EXTWARN) { 188 DiagClass = ErrorOnExtensions ? ERROR : WARNING; 189 } 190 191 // If warnings are globally mapped to ignore or error, do it. 192 if (DiagClass == WARNING) { 193 if (IgnoreAllWarnings) 194 return Diagnostic::Ignored; 195 if (WarningsAsErrors) 196 DiagClass = ERROR; 197 } 198 199 switch (DiagClass) { 200 default: assert(0 && "Unknown diagnostic class!"); 201 case NOTE: return Diagnostic::Note; 202 case WARNING: return Diagnostic::Warning; 203 case ERROR: return Diagnostic::Error; 204 } 205} 206 207/// Report - Issue the message to the client. 208/// DiagID is a member of the diag::kind enum. 209void Diagnostic::Report(DiagnosticClient* C, 210 FullSourceLoc Loc, unsigned DiagID, 211 const std::string *Strs, unsigned NumStrs, 212 const SourceRange *Ranges, unsigned NumRanges) { 213 214 // Figure out the diagnostic level of this message. 215 Diagnostic::Level DiagLevel = getDiagnosticLevel(DiagID); 216 217 // If the client doesn't care about this message, don't issue it. 218 if (DiagLevel == Diagnostic::Ignored) 219 return; 220 221 // Set the diagnostic client if it isn't set already. 222 if (!C) C = Client; 223 224 // If this is not an error and we are in a system header, ignore it. We 225 // have to check on the original DiagID here, because we also want to 226 // ignore extensions and warnings in -Werror and -pedantic-errors modes, 227 // which *map* warnings/extensions to errors. 228 if (SuppressSystemWarnings && 229 DiagID < diag::NUM_BUILTIN_DIAGNOSTICS && 230 getBuiltinDiagClass(DiagID) != ERROR && 231 Loc.isValid() && Loc.isFileID() && Loc.isInSystemHeader()) 232 return; 233 234 if (DiagLevel >= Diagnostic::Error) { 235 ErrorOccurred = true; 236 237 if (C != 0 && C == Client) 238 ++NumErrors; 239 } 240 241 // Finally, report it. 242 243 if (C != 0) 244 C->HandleDiagnostic(*this, DiagLevel, Loc, (diag::kind)DiagID, 245 Strs, NumStrs, Ranges, NumRanges); 246 247 if (C != 0 && C == Client) 248 ++NumDiagnostics; 249} 250 251 252DiagnosticClient::~DiagnosticClient() {} 253 254std::string DiagnosticClient::FormatDiagnostic(Diagnostic &Diags, 255 Diagnostic::Level Level, 256 diag::kind ID, 257 const std::string *Strs, 258 unsigned NumStrs) { 259 std::string Msg = Diags.getDescription(ID); 260 261 // Replace all instances of %0 in Msg with 'Extra'. 262 for (unsigned i = 0; i < Msg.size() - 1; ++i) { 263 if (Msg[i] == '%' && isdigit(Msg[i + 1])) { 264 unsigned StrNo = Msg[i + 1] - '0'; 265 Msg = std::string(Msg.begin(), Msg.begin() + i) + 266 (StrNo < NumStrs ? Strs[StrNo] : "<<<INTERNAL ERROR>>>") + 267 std::string(Msg.begin() + i + 2, Msg.end()); 268 } 269 } 270 271 return Msg; 272} 273