Diagnostic.cpp revision 6948ae68efb71fc0ef50fbf240c3cc2ce8b97b2d
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 NumDiagArgs = -1; 131} 132 133Diagnostic::~Diagnostic() { 134 delete CustomDiagInfo; 135} 136 137/// getCustomDiagID - Return an ID for a diagnostic with the specified message 138/// and level. If this is the first request for this diagnosic, it is 139/// registered and created, otherwise the existing ID is returned. 140unsigned Diagnostic::getCustomDiagID(Level L, const char *Message) { 141 if (CustomDiagInfo == 0) 142 CustomDiagInfo = new diag::CustomDiagInfo(); 143 return CustomDiagInfo->getOrCreateDiagID(L, Message, *this); 144} 145 146 147/// isBuiltinNoteWarningOrExtension - Return true if the unmapped diagnostic 148/// level of the specified diagnostic ID is a Note, Warning, or Extension. 149/// Note that this only works on builtin diagnostics, not custom ones. 150bool Diagnostic::isBuiltinNoteWarningOrExtension(unsigned DiagID) { 151 return DiagID < diag::NUM_BUILTIN_DIAGNOSTICS && 152 getBuiltinDiagClass(DiagID) < ERROR; 153} 154 155 156/// getDescription - Given a diagnostic ID, return a description of the 157/// issue. 158const char *Diagnostic::getDescription(unsigned DiagID) const { 159 if (DiagID < diag::NUM_BUILTIN_DIAGNOSTICS) 160 return DiagnosticText[DiagID]; 161 else 162 return CustomDiagInfo->getDescription(DiagID); 163} 164 165/// getDiagnosticLevel - Based on the way the client configured the Diagnostic 166/// object, classify the specified diagnostic ID into a Level, consumable by 167/// the DiagnosticClient. 168Diagnostic::Level Diagnostic::getDiagnosticLevel(unsigned DiagID) const { 169 // Handle custom diagnostics, which cannot be mapped. 170 if (DiagID >= diag::NUM_BUILTIN_DIAGNOSTICS) 171 return CustomDiagInfo->getLevel(DiagID); 172 173 unsigned DiagClass = getBuiltinDiagClass(DiagID); 174 175 // Specific non-error diagnostics may be mapped to various levels from ignored 176 // to error. 177 if (DiagClass < ERROR) { 178 switch (getDiagnosticMapping((diag::kind)DiagID)) { 179 case diag::MAP_DEFAULT: break; 180 case diag::MAP_IGNORE: return Diagnostic::Ignored; 181 case diag::MAP_WARNING: DiagClass = WARNING; break; 182 case diag::MAP_ERROR: DiagClass = ERROR; break; 183 } 184 } 185 186 // Map diagnostic classes based on command line argument settings. 187 if (DiagClass == EXTENSION) { 188 if (ErrorOnExtensions) 189 DiagClass = ERROR; 190 else if (WarnOnExtensions) 191 DiagClass = WARNING; 192 else 193 return Ignored; 194 } else if (DiagClass == EXTWARN) { 195 DiagClass = ErrorOnExtensions ? ERROR : WARNING; 196 } 197 198 // If warnings are globally mapped to ignore or error, do it. 199 if (DiagClass == WARNING) { 200 if (IgnoreAllWarnings) 201 return Diagnostic::Ignored; 202 if (WarningsAsErrors) 203 DiagClass = ERROR; 204 } 205 206 switch (DiagClass) { 207 default: assert(0 && "Unknown diagnostic class!"); 208 case NOTE: return Diagnostic::Note; 209 case WARNING: return Diagnostic::Warning; 210 case ERROR: return Diagnostic::Error; 211 } 212} 213 214/// ProcessDiag - This is the method used to report a diagnostic that is 215/// finally fully formed. 216void Diagnostic::ProcessDiag(const DiagnosticInfo &Info) { 217 // Figure out the diagnostic level of this message. 218 Diagnostic::Level DiagLevel = getDiagnosticLevel(Info.getID()); 219 220 // If the client doesn't care about this message, don't issue it. 221 if (DiagLevel == Diagnostic::Ignored) 222 return; 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 Diag ID 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 Info.getID() < diag::NUM_BUILTIN_DIAGNOSTICS && 230 getBuiltinDiagClass(Info.getID()) != ERROR && 231 Info.getLocation().isValid() && 232 Info.getLocation().getPhysicalLoc().isInSystemHeader()) 233 return; 234 235 if (DiagLevel >= Diagnostic::Error) { 236 ErrorOccurred = true; 237 238 ++NumErrors; 239 } 240 241 // Finally, report it. 242 Client->HandleDiagnostic(DiagLevel, Info); 243 ++NumDiagnostics; 244} 245 246 247DiagnosticClient::~DiagnosticClient() {} 248 249std::string DiagnosticClient::FormatDiagnostic(const DiagnosticInfo &Info) { 250 std::string Msg = Info.getDiags()->getDescription(Info.getID()); 251 252 // Replace all instances of %0 in Msg with 'Extra'. This is a pretty horrible 253 // and inefficient way to do this, we could improve this a lot if we care. 254 for (unsigned i = 0; i < Msg.size() - 1; ++i) { 255 if (Msg[i] == '%' && isdigit(Msg[i + 1])) { 256 unsigned StrNo = Msg[i + 1] - '0'; 257 Msg = std::string(Msg.begin(), Msg.begin() + i) + 258 Info.getArgStr(StrNo) + 259 std::string(Msg.begin() + i + 2, Msg.end()); 260 } 261 } 262 263 return Msg; 264} 265