blob: 9428b218e54301cfdba15fe638d1681cd8f942a2 [file] [log] [blame]
Reid Spencer5f016e22007-07-11 17:01:13 +00001//===--- Diagnostic.cpp - C Language Family Diagnostic Handling -----------===//
2//
3// The LLVM Compiler Infrastructure
4//
Chris Lattner0bc735f2007-12-29 19:59:25 +00005// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
Reid Spencer5f016e22007-07-11 17:01:13 +00007//
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"
Chris Lattnerf4c83962008-11-19 06:51:40 +000016#include "llvm/ADT/SmallVector.h"
Chris Lattner30bc9652008-11-19 07:22:31 +000017#include "llvm/ADT/StringExtras.h"
Chris Lattner182745a2007-12-02 01:09:57 +000018#include <vector>
19#include <map>
Chris Lattner87cf5ac2008-03-10 17:04:53 +000020#include <cstring>
Reid Spencer5f016e22007-07-11 17:01:13 +000021using namespace clang;
22
Chris Lattner182745a2007-12-02 01:09:57 +000023//===----------------------------------------------------------------------===//
24// Builtin Diagnostic information
25//===----------------------------------------------------------------------===//
26
Reid Spencer5f016e22007-07-11 17:01:13 +000027/// Flag values for diagnostics.
28enum {
29 // Diagnostic classes.
30 NOTE = 0x01,
31 WARNING = 0x02,
32 EXTENSION = 0x03,
Daniel Dunbar4489fe12008-08-05 00:07:51 +000033 EXTWARN = 0x04,
34 ERROR = 0x05,
Reid Spencer5f016e22007-07-11 17:01:13 +000035 class_mask = 0x07
36};
37
38/// DiagnosticFlags - A set of flags, or'd together, that describe the
39/// diagnostic.
40static unsigned char DiagnosticFlags[] = {
41#define DIAG(ENUM,FLAGS,DESC) FLAGS,
42#include "clang/Basic/DiagnosticKinds.def"
43 0
44};
45
46/// getDiagClass - Return the class field of the diagnostic.
47///
Chris Lattner07506182007-11-30 22:53:43 +000048static unsigned getBuiltinDiagClass(unsigned DiagID) {
49 assert(DiagID < diag::NUM_BUILTIN_DIAGNOSTICS &&
50 "Diagnostic ID out of range!");
Reid Spencer5f016e22007-07-11 17:01:13 +000051 return DiagnosticFlags[DiagID] & class_mask;
52}
53
54/// DiagnosticText - An english message to print for the diagnostic. These
55/// should be localized.
56static const char * const DiagnosticText[] = {
57#define DIAG(ENUM,FLAGS,DESC) DESC,
58#include "clang/Basic/DiagnosticKinds.def"
59 0
60};
61
Chris Lattner182745a2007-12-02 01:09:57 +000062//===----------------------------------------------------------------------===//
63// Custom Diagnostic information
64//===----------------------------------------------------------------------===//
65
66namespace clang {
67 namespace diag {
68 class CustomDiagInfo {
69 typedef std::pair<Diagnostic::Level, std::string> DiagDesc;
70 std::vector<DiagDesc> DiagInfo;
71 std::map<DiagDesc, unsigned> DiagIDs;
72 public:
73
74 /// getDescription - Return the description of the specified custom
75 /// diagnostic.
76 const char *getDescription(unsigned DiagID) const {
77 assert(this && DiagID-diag::NUM_BUILTIN_DIAGNOSTICS < DiagInfo.size() &&
78 "Invalid diagnosic ID");
79 return DiagInfo[DiagID-diag::NUM_BUILTIN_DIAGNOSTICS].second.c_str();
80 }
81
82 /// getLevel - Return the level of the specified custom diagnostic.
83 Diagnostic::Level getLevel(unsigned DiagID) const {
84 assert(this && DiagID-diag::NUM_BUILTIN_DIAGNOSTICS < DiagInfo.size() &&
85 "Invalid diagnosic ID");
86 return DiagInfo[DiagID-diag::NUM_BUILTIN_DIAGNOSTICS].first;
87 }
88
Chris Lattnera1f23cc2008-10-17 21:24:47 +000089 unsigned getOrCreateDiagID(Diagnostic::Level L, const char *Message,
90 Diagnostic &Diags) {
Chris Lattner182745a2007-12-02 01:09:57 +000091 DiagDesc D(L, Message);
92 // Check to see if it already exists.
93 std::map<DiagDesc, unsigned>::iterator I = DiagIDs.lower_bound(D);
94 if (I != DiagIDs.end() && I->first == D)
95 return I->second;
96
97 // If not, assign a new ID.
98 unsigned ID = DiagInfo.size()+diag::NUM_BUILTIN_DIAGNOSTICS;
99 DiagIDs.insert(std::make_pair(D, ID));
100 DiagInfo.push_back(D);
Chris Lattnera1f23cc2008-10-17 21:24:47 +0000101
102 // If this is a warning, and all warnings are supposed to map to errors,
103 // insert the mapping now.
104 if (L == Diagnostic::Warning && Diags.getWarningsAsErrors())
105 Diags.setDiagnosticMapping((diag::kind)ID, diag::MAP_ERROR);
Chris Lattner182745a2007-12-02 01:09:57 +0000106 return ID;
107 }
108 };
109
110 } // end diag namespace
111} // end clang namespace
112
113
114//===----------------------------------------------------------------------===//
115// Common Diagnostic implementation
116//===----------------------------------------------------------------------===//
117
Ted Kremenekb4398aa2008-08-07 17:49:57 +0000118Diagnostic::Diagnostic(DiagnosticClient *client) : Client(client) {
Chris Lattner5b4681c2008-05-29 15:36:45 +0000119 IgnoreAllWarnings = false;
Reid Spencer5f016e22007-07-11 17:01:13 +0000120 WarningsAsErrors = false;
121 WarnOnExtensions = false;
122 ErrorOnExtensions = false;
Daniel Dunbar2fe09972008-09-12 18:10:20 +0000123 SuppressSystemWarnings = false;
Reid Spencer5f016e22007-07-11 17:01:13 +0000124 // Clear all mappings, setting them to MAP_DEFAULT.
125 memset(DiagMappings, 0, sizeof(DiagMappings));
126
127 ErrorOccurred = false;
128 NumDiagnostics = 0;
129 NumErrors = 0;
Chris Lattner182745a2007-12-02 01:09:57 +0000130 CustomDiagInfo = 0;
Chris Lattner0a14eee2008-11-18 07:04:44 +0000131 NumDiagArgs = -1;
Reid Spencer5f016e22007-07-11 17:01:13 +0000132}
133
Chris Lattner182745a2007-12-02 01:09:57 +0000134Diagnostic::~Diagnostic() {
135 delete CustomDiagInfo;
136}
137
138/// getCustomDiagID - Return an ID for a diagnostic with the specified message
139/// and level. If this is the first request for this diagnosic, it is
140/// registered and created, otherwise the existing ID is returned.
141unsigned Diagnostic::getCustomDiagID(Level L, const char *Message) {
142 if (CustomDiagInfo == 0)
143 CustomDiagInfo = new diag::CustomDiagInfo();
Chris Lattnera1f23cc2008-10-17 21:24:47 +0000144 return CustomDiagInfo->getOrCreateDiagID(L, Message, *this);
Chris Lattner182745a2007-12-02 01:09:57 +0000145}
146
147
Chris Lattner07506182007-11-30 22:53:43 +0000148/// isBuiltinNoteWarningOrExtension - Return true if the unmapped diagnostic
149/// level of the specified diagnostic ID is a Note, Warning, or Extension.
150/// Note that this only works on builtin diagnostics, not custom ones.
151bool Diagnostic::isBuiltinNoteWarningOrExtension(unsigned DiagID) {
152 return DiagID < diag::NUM_BUILTIN_DIAGNOSTICS &&
153 getBuiltinDiagClass(DiagID) < ERROR;
Reid Spencer5f016e22007-07-11 17:01:13 +0000154}
155
156
157/// getDescription - Given a diagnostic ID, return a description of the
158/// issue.
Chris Lattner0a14eee2008-11-18 07:04:44 +0000159const char *Diagnostic::getDescription(unsigned DiagID) const {
Chris Lattner07506182007-11-30 22:53:43 +0000160 if (DiagID < diag::NUM_BUILTIN_DIAGNOSTICS)
161 return DiagnosticText[DiagID];
162 else
Chris Lattner182745a2007-12-02 01:09:57 +0000163 return CustomDiagInfo->getDescription(DiagID);
Reid Spencer5f016e22007-07-11 17:01:13 +0000164}
165
166/// getDiagnosticLevel - Based on the way the client configured the Diagnostic
167/// object, classify the specified diagnostic ID into a Level, consumable by
168/// the DiagnosticClient.
169Diagnostic::Level Diagnostic::getDiagnosticLevel(unsigned DiagID) const {
Chris Lattner182745a2007-12-02 01:09:57 +0000170 // Handle custom diagnostics, which cannot be mapped.
171 if (DiagID >= diag::NUM_BUILTIN_DIAGNOSTICS)
172 return CustomDiagInfo->getLevel(DiagID);
Chris Lattner07506182007-11-30 22:53:43 +0000173
174 unsigned DiagClass = getBuiltinDiagClass(DiagID);
Reid Spencer5f016e22007-07-11 17:01:13 +0000175
176 // Specific non-error diagnostics may be mapped to various levels from ignored
177 // to error.
178 if (DiagClass < ERROR) {
179 switch (getDiagnosticMapping((diag::kind)DiagID)) {
180 case diag::MAP_DEFAULT: break;
Chris Lattner5b4681c2008-05-29 15:36:45 +0000181 case diag::MAP_IGNORE: return Diagnostic::Ignored;
Reid Spencer5f016e22007-07-11 17:01:13 +0000182 case diag::MAP_WARNING: DiagClass = WARNING; break;
183 case diag::MAP_ERROR: DiagClass = ERROR; break;
184 }
185 }
186
187 // Map diagnostic classes based on command line argument settings.
188 if (DiagClass == EXTENSION) {
189 if (ErrorOnExtensions)
190 DiagClass = ERROR;
191 else if (WarnOnExtensions)
192 DiagClass = WARNING;
193 else
194 return Ignored;
Daniel Dunbar4489fe12008-08-05 00:07:51 +0000195 } else if (DiagClass == EXTWARN) {
196 DiagClass = ErrorOnExtensions ? ERROR : WARNING;
Reid Spencer5f016e22007-07-11 17:01:13 +0000197 }
198
Chris Lattner5b4681c2008-05-29 15:36:45 +0000199 // If warnings are globally mapped to ignore or error, do it.
200 if (DiagClass == WARNING) {
201 if (IgnoreAllWarnings)
202 return Diagnostic::Ignored;
203 if (WarningsAsErrors)
204 DiagClass = ERROR;
205 }
Reid Spencer5f016e22007-07-11 17:01:13 +0000206
207 switch (DiagClass) {
208 default: assert(0 && "Unknown diagnostic class!");
209 case NOTE: return Diagnostic::Note;
210 case WARNING: return Diagnostic::Warning;
211 case ERROR: return Diagnostic::Error;
Reid Spencer5f016e22007-07-11 17:01:13 +0000212 }
213}
214
Chris Lattner0a14eee2008-11-18 07:04:44 +0000215/// ProcessDiag - This is the method used to report a diagnostic that is
216/// finally fully formed.
217void Diagnostic::ProcessDiag(const DiagnosticInfo &Info) {
Reid Spencer5f016e22007-07-11 17:01:13 +0000218 // Figure out the diagnostic level of this message.
Chris Lattner0a14eee2008-11-18 07:04:44 +0000219 Diagnostic::Level DiagLevel = getDiagnosticLevel(Info.getID());
Reid Spencer5f016e22007-07-11 17:01:13 +0000220
221 // If the client doesn't care about this message, don't issue it.
222 if (DiagLevel == Diagnostic::Ignored)
223 return;
Nico Weber7bfaaae2008-08-10 19:59:06 +0000224
Nico Weber7bfaaae2008-08-10 19:59:06 +0000225 // If this is not an error and we are in a system header, ignore it. We
Chris Lattner0a14eee2008-11-18 07:04:44 +0000226 // have to check on the original Diag ID here, because we also want to
Nico Weber7bfaaae2008-08-10 19:59:06 +0000227 // ignore extensions and warnings in -Werror and -pedantic-errors modes,
228 // which *map* warnings/extensions to errors.
Daniel Dunbar2fe09972008-09-12 18:10:20 +0000229 if (SuppressSystemWarnings &&
Chris Lattner0a14eee2008-11-18 07:04:44 +0000230 Info.getID() < diag::NUM_BUILTIN_DIAGNOSTICS &&
231 getBuiltinDiagClass(Info.getID()) != ERROR &&
232 Info.getLocation().isValid() &&
233 Info.getLocation().getPhysicalLoc().isInSystemHeader())
Chris Lattner7097d912008-02-03 09:00:04 +0000234 return;
Reid Spencer5f016e22007-07-11 17:01:13 +0000235
236 if (DiagLevel >= Diagnostic::Error) {
237 ErrorOccurred = true;
Nico Weber7bfaaae2008-08-10 19:59:06 +0000238
Chris Lattner0a14eee2008-11-18 07:04:44 +0000239 ++NumErrors;
Reid Spencer5f016e22007-07-11 17:01:13 +0000240 }
241
Reid Spencer5f016e22007-07-11 17:01:13 +0000242 // Finally, report it.
Chris Lattner0a14eee2008-11-18 07:04:44 +0000243 Client->HandleDiagnostic(DiagLevel, Info);
244 ++NumDiagnostics;
Reid Spencer5f016e22007-07-11 17:01:13 +0000245}
246
Nico Weber7bfaaae2008-08-10 19:59:06 +0000247
Reid Spencer5f016e22007-07-11 17:01:13 +0000248DiagnosticClient::~DiagnosticClient() {}
Nico Weber7bfaaae2008-08-10 19:59:06 +0000249
Chris Lattnerf4c83962008-11-19 06:51:40 +0000250
251/// FormatDiagnostic - Format this diagnostic into a string, substituting the
252/// formal arguments into the %0 slots. The result is appended onto the Str
253/// array.
254void DiagnosticInfo::
255FormatDiagnostic(llvm::SmallVectorImpl<char> &OutStr) const {
256 const char *DiagStr = getDiags()->getDescription(getID());
257 const char *DiagEnd = DiagStr+strlen(DiagStr);
Nico Weber7bfaaae2008-08-10 19:59:06 +0000258
Chris Lattnerf4c83962008-11-19 06:51:40 +0000259 while (DiagStr != DiagEnd) {
260 if (DiagStr[0] != '%') {
261 // Append non-%0 substrings to Str if we have one.
262 const char *StrEnd = std::find(DiagStr, DiagEnd, '%');
263 OutStr.append(DiagStr, StrEnd);
264 DiagStr = StrEnd;
265 } else if (DiagStr[1] == '%') {
266 OutStr.push_back('%'); // %% -> %.
267 DiagStr += 2;
268 } else {
269 assert(isdigit(DiagStr[1]) && "Must escape % with %%");
270 unsigned StrNo = DiagStr[1] - '0';
271
272 switch (getArgKind(StrNo)) {
273 case DiagnosticInfo::ak_std_string: {
274 const std::string &S = getArgStdStr(StrNo);
275 OutStr.append(S.begin(), S.end());
276 break;
277 }
278 case DiagnosticInfo::ak_c_string: {
279 const char *S = getArgCStr(StrNo);
280 OutStr.append(S, S + strlen(S));
281 break;
282 }
Chris Lattner30bc9652008-11-19 07:22:31 +0000283 case DiagnosticInfo::ak_sint: {
284 // FIXME: Optimize
285 std::string S = llvm::itostr(getArgSInt(StrNo));
286 OutStr.append(S.begin(), S.end());
287 break;
288 }
289 case DiagnosticInfo::ak_uint: {
290 // FIXME: Optimize
291 std::string S = llvm::utostr_32(getArgUInt(StrNo));
292 OutStr.append(S.begin(), S.end());
293 break;
294 }
Chris Lattnerf4c83962008-11-19 06:51:40 +0000295 }
296 DiagStr += 2;
Nico Weber7bfaaae2008-08-10 19:59:06 +0000297 }
298 }
Nico Weber7bfaaae2008-08-10 19:59:06 +0000299}