blob: c3ff10d427934ae289197b0d45986b1427409571 [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"
16#include <cassert>
Chris Lattner182745a2007-12-02 01:09:57 +000017#include <vector>
18#include <map>
Chris Lattner87cf5ac2008-03-10 17:04:53 +000019#include <cstring>
Reid Spencer5f016e22007-07-11 17:01:13 +000020using namespace clang;
21
Chris Lattner182745a2007-12-02 01:09:57 +000022//===----------------------------------------------------------------------===//
23// Builtin Diagnostic information
24//===----------------------------------------------------------------------===//
25
Reid Spencer5f016e22007-07-11 17:01:13 +000026/// Flag values for diagnostics.
27enum {
28 // Diagnostic classes.
29 NOTE = 0x01,
30 WARNING = 0x02,
31 EXTENSION = 0x03,
Daniel Dunbar4489fe12008-08-05 00:07:51 +000032 EXTWARN = 0x04,
33 ERROR = 0x05,
Reid Spencer5f016e22007-07-11 17:01:13 +000034 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///
Chris Lattner07506182007-11-30 22:53:43 +000047static unsigned getBuiltinDiagClass(unsigned DiagID) {
48 assert(DiagID < diag::NUM_BUILTIN_DIAGNOSTICS &&
49 "Diagnostic ID out of range!");
Reid Spencer5f016e22007-07-11 17:01:13 +000050 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
Chris Lattner182745a2007-12-02 01:09:57 +000061//===----------------------------------------------------------------------===//
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
Chris Lattnera1f23cc2008-10-17 21:24:47 +000088 unsigned getOrCreateDiagID(Diagnostic::Level L, const char *Message,
89 Diagnostic &Diags) {
Chris Lattner182745a2007-12-02 01:09:57 +000090 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);
Chris Lattnera1f23cc2008-10-17 21:24:47 +0000100
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);
Chris Lattner182745a2007-12-02 01:09:57 +0000105 return ID;
106 }
107 };
108
109 } // end diag namespace
110} // end clang namespace
111
112
113//===----------------------------------------------------------------------===//
114// Common Diagnostic implementation
115//===----------------------------------------------------------------------===//
116
Ted Kremenekb4398aa2008-08-07 17:49:57 +0000117Diagnostic::Diagnostic(DiagnosticClient *client) : Client(client) {
Chris Lattner5b4681c2008-05-29 15:36:45 +0000118 IgnoreAllWarnings = false;
Reid Spencer5f016e22007-07-11 17:01:13 +0000119 WarningsAsErrors = false;
120 WarnOnExtensions = false;
121 ErrorOnExtensions = false;
Daniel Dunbar2fe09972008-09-12 18:10:20 +0000122 SuppressSystemWarnings = false;
Reid Spencer5f016e22007-07-11 17:01:13 +0000123 // Clear all mappings, setting them to MAP_DEFAULT.
124 memset(DiagMappings, 0, sizeof(DiagMappings));
125
126 ErrorOccurred = false;
127 NumDiagnostics = 0;
128 NumErrors = 0;
Chris Lattner182745a2007-12-02 01:09:57 +0000129 CustomDiagInfo = 0;
Reid Spencer5f016e22007-07-11 17:01:13 +0000130}
131
Chris Lattner182745a2007-12-02 01:09:57 +0000132Diagnostic::~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();
Chris Lattnera1f23cc2008-10-17 21:24:47 +0000142 return CustomDiagInfo->getOrCreateDiagID(L, Message, *this);
Chris Lattner182745a2007-12-02 01:09:57 +0000143}
144
145
Chris Lattner07506182007-11-30 22:53:43 +0000146/// 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;
Reid Spencer5f016e22007-07-11 17:01:13 +0000152}
153
154
155/// getDescription - Given a diagnostic ID, return a description of the
156/// issue.
157const char *Diagnostic::getDescription(unsigned DiagID) {
Chris Lattner07506182007-11-30 22:53:43 +0000158 if (DiagID < diag::NUM_BUILTIN_DIAGNOSTICS)
159 return DiagnosticText[DiagID];
160 else
Chris Lattner182745a2007-12-02 01:09:57 +0000161 return CustomDiagInfo->getDescription(DiagID);
Reid Spencer5f016e22007-07-11 17:01:13 +0000162}
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 {
Chris Lattner182745a2007-12-02 01:09:57 +0000168 // Handle custom diagnostics, which cannot be mapped.
169 if (DiagID >= diag::NUM_BUILTIN_DIAGNOSTICS)
170 return CustomDiagInfo->getLevel(DiagID);
Chris Lattner07506182007-11-30 22:53:43 +0000171
172 unsigned DiagClass = getBuiltinDiagClass(DiagID);
Reid Spencer5f016e22007-07-11 17:01:13 +0000173
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;
Chris Lattner5b4681c2008-05-29 15:36:45 +0000179 case diag::MAP_IGNORE: return Diagnostic::Ignored;
Reid Spencer5f016e22007-07-11 17:01:13 +0000180 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;
Daniel Dunbar4489fe12008-08-05 00:07:51 +0000193 } else if (DiagClass == EXTWARN) {
194 DiagClass = ErrorOnExtensions ? ERROR : WARNING;
Reid Spencer5f016e22007-07-11 17:01:13 +0000195 }
196
Chris Lattner5b4681c2008-05-29 15:36:45 +0000197 // 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 }
Reid Spencer5f016e22007-07-11 17:01:13 +0000204
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;
Reid Spencer5f016e22007-07-11 17:01:13 +0000210 }
211}
212
Nico Weber7bfaaae2008-08-10 19:59:06 +0000213/// Report - Issue the message to the client.
214/// DiagID is a member of the diag::kind enum.
Ted Kremenek615f5172008-03-31 18:23:15 +0000215void Diagnostic::Report(DiagnosticClient* C,
Nico Weber7bfaaae2008-08-10 19:59:06 +0000216 FullSourceLoc Loc, unsigned DiagID,
Reid Spencer5f016e22007-07-11 17:01:13 +0000217 const std::string *Strs, unsigned NumStrs,
218 const SourceRange *Ranges, unsigned NumRanges) {
Chris Lattner7097d912008-02-03 09:00:04 +0000219
Reid Spencer5f016e22007-07-11 17:01:13 +0000220 // 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;
Nico Weber7bfaaae2008-08-10 19:59:06 +0000226
Ted Kremenekd0734e52008-04-14 21:21:38 +0000227 // Set the diagnostic client if it isn't set already.
Ted Kremenekb4398aa2008-08-07 17:49:57 +0000228 if (!C) C = Client;
Chris Lattner7097d912008-02-03 09:00:04 +0000229
Nico Weber7bfaaae2008-08-10 19:59:06 +0000230 // 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.
Daniel Dunbar2fe09972008-09-12 18:10:20 +0000234 if (SuppressSystemWarnings &&
235 DiagID < diag::NUM_BUILTIN_DIAGNOSTICS &&
Chris Lattner7097d912008-02-03 09:00:04 +0000236 getBuiltinDiagClass(DiagID) != ERROR &&
Nico Weber7bfaaae2008-08-10 19:59:06 +0000237 Loc.isValid() && Loc.isFileID() && Loc.isInSystemHeader())
Chris Lattner7097d912008-02-03 09:00:04 +0000238 return;
Reid Spencer5f016e22007-07-11 17:01:13 +0000239
240 if (DiagLevel >= Diagnostic::Error) {
241 ErrorOccurred = true;
Nico Weber7bfaaae2008-08-10 19:59:06 +0000242
243 if (C != 0 && C == Client)
Ted Kremenek06bdb3a2008-04-14 19:56:12 +0000244 ++NumErrors;
Reid Spencer5f016e22007-07-11 17:01:13 +0000245 }
246
Reid Spencer5f016e22007-07-11 17:01:13 +0000247 // Finally, report it.
Nico Weber7bfaaae2008-08-10 19:59:06 +0000248
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)
Ted Kremenek06bdb3a2008-04-14 19:56:12 +0000254 ++NumDiagnostics;
Reid Spencer5f016e22007-07-11 17:01:13 +0000255}
256
Nico Weber7bfaaae2008-08-10 19:59:06 +0000257
Reid Spencer5f016e22007-07-11 17:01:13 +0000258DiagnosticClient::~DiagnosticClient() {}
Nico Weber7bfaaae2008-08-10 19:59:06 +0000259
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}