Diagnostic.cpp revision 615f5177095e62b36bf88f1b1b7b644295e4097b
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 ERROR = 0x04, 33 class_mask = 0x07 34}; 35 36/// DiagnosticFlags - A set of flags, or'd together, that describe the 37/// diagnostic. 38static unsigned char DiagnosticFlags[] = { 39#define DIAG(ENUM,FLAGS,DESC) FLAGS, 40#include "clang/Basic/DiagnosticKinds.def" 41 0 42}; 43 44/// getDiagClass - Return the class field of the diagnostic. 45/// 46static unsigned getBuiltinDiagClass(unsigned DiagID) { 47 assert(DiagID < diag::NUM_BUILTIN_DIAGNOSTICS && 48 "Diagnostic ID out of range!"); 49 return DiagnosticFlags[DiagID] & class_mask; 50} 51 52/// DiagnosticText - An english message to print for the diagnostic. These 53/// should be localized. 54static const char * const DiagnosticText[] = { 55#define DIAG(ENUM,FLAGS,DESC) DESC, 56#include "clang/Basic/DiagnosticKinds.def" 57 0 58}; 59 60//===----------------------------------------------------------------------===// 61// Custom Diagnostic information 62//===----------------------------------------------------------------------===// 63 64namespace clang { 65 namespace diag { 66 class CustomDiagInfo { 67 typedef std::pair<Diagnostic::Level, std::string> DiagDesc; 68 std::vector<DiagDesc> DiagInfo; 69 std::map<DiagDesc, unsigned> DiagIDs; 70 public: 71 72 /// getDescription - Return the description of the specified custom 73 /// diagnostic. 74 const char *getDescription(unsigned DiagID) const { 75 assert(this && DiagID-diag::NUM_BUILTIN_DIAGNOSTICS < DiagInfo.size() && 76 "Invalid diagnosic ID"); 77 return DiagInfo[DiagID-diag::NUM_BUILTIN_DIAGNOSTICS].second.c_str(); 78 } 79 80 /// getLevel - Return the level of the specified custom diagnostic. 81 Diagnostic::Level getLevel(unsigned DiagID) const { 82 assert(this && DiagID-diag::NUM_BUILTIN_DIAGNOSTICS < DiagInfo.size() && 83 "Invalid diagnosic ID"); 84 return DiagInfo[DiagID-diag::NUM_BUILTIN_DIAGNOSTICS].first; 85 } 86 87 unsigned getOrCreateDiagID(Diagnostic::Level L, const char *Message) { 88 DiagDesc D(L, Message); 89 // Check to see if it already exists. 90 std::map<DiagDesc, unsigned>::iterator I = DiagIDs.lower_bound(D); 91 if (I != DiagIDs.end() && I->first == D) 92 return I->second; 93 94 // If not, assign a new ID. 95 unsigned ID = DiagInfo.size()+diag::NUM_BUILTIN_DIAGNOSTICS; 96 DiagIDs.insert(std::make_pair(D, ID)); 97 DiagInfo.push_back(D); 98 return ID; 99 } 100 }; 101 102 } // end diag namespace 103} // end clang namespace 104 105 106//===----------------------------------------------------------------------===// 107// Common Diagnostic implementation 108//===----------------------------------------------------------------------===// 109 110Diagnostic::Diagnostic(DiagnosticClient &client) : Client(client) { 111 WarningsAsErrors = false; 112 WarnOnExtensions = false; 113 ErrorOnExtensions = false; 114 // Clear all mappings, setting them to MAP_DEFAULT. 115 memset(DiagMappings, 0, sizeof(DiagMappings)); 116 117 ErrorOccurred = false; 118 NumDiagnostics = 0; 119 NumErrors = 0; 120 CustomDiagInfo = 0; 121} 122 123Diagnostic::~Diagnostic() { 124 delete CustomDiagInfo; 125} 126 127/// getCustomDiagID - Return an ID for a diagnostic with the specified message 128/// and level. If this is the first request for this diagnosic, it is 129/// registered and created, otherwise the existing ID is returned. 130unsigned Diagnostic::getCustomDiagID(Level L, const char *Message) { 131 if (CustomDiagInfo == 0) 132 CustomDiagInfo = new diag::CustomDiagInfo(); 133 return CustomDiagInfo->getOrCreateDiagID(L, Message); 134} 135 136 137/// isBuiltinNoteWarningOrExtension - Return true if the unmapped diagnostic 138/// level of the specified diagnostic ID is a Note, Warning, or Extension. 139/// Note that this only works on builtin diagnostics, not custom ones. 140bool Diagnostic::isBuiltinNoteWarningOrExtension(unsigned DiagID) { 141 return DiagID < diag::NUM_BUILTIN_DIAGNOSTICS && 142 getBuiltinDiagClass(DiagID) < ERROR; 143} 144 145 146/// getDescription - Given a diagnostic ID, return a description of the 147/// issue. 148const char *Diagnostic::getDescription(unsigned DiagID) { 149 if (DiagID < diag::NUM_BUILTIN_DIAGNOSTICS) 150 return DiagnosticText[DiagID]; 151 else 152 return CustomDiagInfo->getDescription(DiagID); 153} 154 155/// getDiagnosticLevel - Based on the way the client configured the Diagnostic 156/// object, classify the specified diagnostic ID into a Level, consumable by 157/// the DiagnosticClient. 158Diagnostic::Level Diagnostic::getDiagnosticLevel(unsigned DiagID) const { 159 // Handle custom diagnostics, which cannot be mapped. 160 if (DiagID >= diag::NUM_BUILTIN_DIAGNOSTICS) 161 return CustomDiagInfo->getLevel(DiagID); 162 163 unsigned DiagClass = getBuiltinDiagClass(DiagID); 164 165 // Specific non-error diagnostics may be mapped to various levels from ignored 166 // to error. 167 if (DiagClass < ERROR) { 168 switch (getDiagnosticMapping((diag::kind)DiagID)) { 169 case diag::MAP_DEFAULT: break; 170 case diag::MAP_IGNORE: return Ignored; 171 case diag::MAP_WARNING: DiagClass = WARNING; break; 172 case diag::MAP_ERROR: DiagClass = ERROR; break; 173 } 174 } 175 176 // Map diagnostic classes based on command line argument settings. 177 if (DiagClass == EXTENSION) { 178 if (ErrorOnExtensions) 179 DiagClass = ERROR; 180 else if (WarnOnExtensions) 181 DiagClass = WARNING; 182 else 183 return Ignored; 184 } 185 186 // If warnings are to be treated as errors, indicate this as such. 187 if (DiagClass == WARNING && WarningsAsErrors) 188 DiagClass = ERROR; 189 190 switch (DiagClass) { 191 default: assert(0 && "Unknown diagnostic class!"); 192 case NOTE: return Diagnostic::Note; 193 case WARNING: return Diagnostic::Warning; 194 case ERROR: return Diagnostic::Error; 195 } 196} 197 198/// Report - Issue the message to the client. If the client wants us to stop 199/// compilation, return true, otherwise return false. DiagID is a member of 200/// the diag::kind enum. 201void Diagnostic::Report(DiagnosticClient* C, 202 FullSourceLoc Pos, unsigned DiagID, 203 const std::string *Strs, unsigned NumStrs, 204 const SourceRange *Ranges, unsigned NumRanges) { 205 206 // Figure out the diagnostic level of this message. 207 Diagnostic::Level DiagLevel = getDiagnosticLevel(DiagID); 208 209 // If the client doesn't care about this message, don't issue it. 210 if (DiagLevel == Diagnostic::Ignored) 211 return; 212 213 // If this is not an error and we are in a system header, ignore it. We have 214 // to check on the original class here, because we also want to ignore 215 // extensions and warnings in -Werror and -pedantic-errors modes, which *map* 216 // warnings/extensions to errors. 217 if (DiagID < diag::NUM_BUILTIN_DIAGNOSTICS && 218 getBuiltinDiagClass(DiagID) != ERROR && 219 Client.isInSystemHeader(Pos)) 220 return; 221 222 if (DiagLevel >= Diagnostic::Error) { 223 ErrorOccurred = true; 224 ++NumErrors; 225 } 226 227 // Finally, report it. 228 229 if (!C) C = &Client; 230 231 C->HandleDiagnostic(*this, DiagLevel, Pos, (diag::kind)DiagID, 232 Strs, NumStrs, Ranges, NumRanges); 233 ++NumDiagnostics; 234} 235 236DiagnosticClient::~DiagnosticClient() {} 237