blob: ee0345b18d8a8c5038f7d93781761860356a086e [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"
Chris Lattner43b628c2008-11-19 07:32:16 +000015#include "clang/Basic/IdentifierTable.h"
Reid Spencer5f016e22007-07-11 17:01:13 +000016#include "clang/Basic/SourceLocation.h"
Chris Lattnerf4c83962008-11-19 06:51:40 +000017#include "llvm/ADT/SmallVector.h"
Chris Lattner30bc9652008-11-19 07:22:31 +000018#include "llvm/ADT/StringExtras.h"
Chris Lattner182745a2007-12-02 01:09:57 +000019#include <vector>
20#include <map>
Chris Lattner87cf5ac2008-03-10 17:04:53 +000021#include <cstring>
Reid Spencer5f016e22007-07-11 17:01:13 +000022using namespace clang;
23
Chris Lattner182745a2007-12-02 01:09:57 +000024//===----------------------------------------------------------------------===//
25// Builtin Diagnostic information
26//===----------------------------------------------------------------------===//
27
Reid Spencer5f016e22007-07-11 17:01:13 +000028/// Flag values for diagnostics.
29enum {
30 // Diagnostic classes.
31 NOTE = 0x01,
32 WARNING = 0x02,
33 EXTENSION = 0x03,
Daniel Dunbar4489fe12008-08-05 00:07:51 +000034 EXTWARN = 0x04,
35 ERROR = 0x05,
Reid Spencer5f016e22007-07-11 17:01:13 +000036 class_mask = 0x07
37};
38
Chris Lattner20c6b3b2009-01-27 18:30:58 +000039namespace clang {
40 namespace diag {
41 enum _kind{
42#define DIAG(ENUM,FLAGS,DESC) ENUM,
43#define LEXSTART
44#define PARSESTART
45#define ASTSTART
46#define SEMASTART
47#define ANALYSISSTART
48#include "clang/Basic/DiagnosticKinds.def"
49 NUM_BUILTIN_DIAGNOSTICS = DIAG_UPPER_LIMIT
50 };
51 }
52}
53
Reid Spencer5f016e22007-07-11 17:01:13 +000054/// DiagnosticFlags - A set of flags, or'd together, that describe the
55/// diagnostic.
Reid Spencer5f016e22007-07-11 17:01:13 +000056#define DIAG(ENUM,FLAGS,DESC) FLAGS,
Chris Lattner20c6b3b2009-01-27 18:30:58 +000057static unsigned char DiagnosticFlagsCommon[] = {
58#include "clang/Basic/DiagnosticCommonKinds.def"
Reid Spencer5f016e22007-07-11 17:01:13 +000059 0
60};
Chris Lattner20c6b3b2009-01-27 18:30:58 +000061static unsigned char DiagnosticFlagsLex[] = {
62#include "clang/Basic/DiagnosticLexKinds.def"
63 0
64};
65static unsigned char DiagnosticFlagsParse[] = {
66#include "clang/Basic/DiagnosticParseKinds.def"
67 0
68};
69static unsigned char DiagnosticFlagsAST[] = {
70#include "clang/Basic/DiagnosticASTKinds.def"
71 0
72};
73static unsigned char DiagnosticFlagsSema[] = {
74#include "clang/Basic/DiagnosticSemaKinds.def"
75 0
76};
77static unsigned char DiagnosticFlagsAnalysis[] = {
78#include "clang/Basic/DiagnosticAnalysisKinds.def"
79 0
80};
81#undef DIAG
Reid Spencer5f016e22007-07-11 17:01:13 +000082
83/// getDiagClass - Return the class field of the diagnostic.
84///
Chris Lattner07506182007-11-30 22:53:43 +000085static unsigned getBuiltinDiagClass(unsigned DiagID) {
86 assert(DiagID < diag::NUM_BUILTIN_DIAGNOSTICS &&
87 "Diagnostic ID out of range!");
Chris Lattner20c6b3b2009-01-27 18:30:58 +000088 unsigned res;
89 if (DiagID < DIAG_START_LEX)
90 res = DiagnosticFlagsCommon[DiagID];
91 else if (DiagID < DIAG_START_PARSE)
92 res = DiagnosticFlagsLex[DiagID - DIAG_START_LEX - 1];
93 else if (DiagID < DIAG_START_AST)
94 res = DiagnosticFlagsParse[DiagID - DIAG_START_PARSE - 1];
95 else if (DiagID < DIAG_START_SEMA)
96 res = DiagnosticFlagsAST[DiagID - DIAG_START_AST - 1];
97 else if (DiagID < DIAG_START_ANALYSIS)
98 res = DiagnosticFlagsSema[DiagID - DIAG_START_SEMA - 1];
99 else
100 res = DiagnosticFlagsAnalysis[DiagID - DIAG_START_ANALYSIS - 1];
101 return res & class_mask;
Reid Spencer5f016e22007-07-11 17:01:13 +0000102}
103
104/// DiagnosticText - An english message to print for the diagnostic. These
105/// should be localized.
Reid Spencer5f016e22007-07-11 17:01:13 +0000106#define DIAG(ENUM,FLAGS,DESC) DESC,
Chris Lattner20c6b3b2009-01-27 18:30:58 +0000107static const char * const DiagnosticTextCommon[] = {
108#include "clang/Basic/DiagnosticCommonKinds.def"
Reid Spencer5f016e22007-07-11 17:01:13 +0000109 0
110};
Chris Lattner20c6b3b2009-01-27 18:30:58 +0000111static const char * const DiagnosticTextLex[] = {
112#include "clang/Basic/DiagnosticLexKinds.def"
113 0
114};
115static const char * const DiagnosticTextParse[] = {
116#include "clang/Basic/DiagnosticParseKinds.def"
117 0
118};
119static const char * const DiagnosticTextAST[] = {
120#include "clang/Basic/DiagnosticASTKinds.def"
121 0
122};
123static const char * const DiagnosticTextSema[] = {
124#include "clang/Basic/DiagnosticSemaKinds.def"
125 0
126};
127static const char * const DiagnosticTextAnalysis[] = {
128#include "clang/Basic/DiagnosticAnalysisKinds.def"
129 0
130};
131#undef DIAG
Reid Spencer5f016e22007-07-11 17:01:13 +0000132
Chris Lattner182745a2007-12-02 01:09:57 +0000133//===----------------------------------------------------------------------===//
134// Custom Diagnostic information
135//===----------------------------------------------------------------------===//
136
137namespace clang {
138 namespace diag {
139 class CustomDiagInfo {
140 typedef std::pair<Diagnostic::Level, std::string> DiagDesc;
141 std::vector<DiagDesc> DiagInfo;
142 std::map<DiagDesc, unsigned> DiagIDs;
143 public:
144
145 /// getDescription - Return the description of the specified custom
146 /// diagnostic.
147 const char *getDescription(unsigned DiagID) const {
148 assert(this && DiagID-diag::NUM_BUILTIN_DIAGNOSTICS < DiagInfo.size() &&
149 "Invalid diagnosic ID");
150 return DiagInfo[DiagID-diag::NUM_BUILTIN_DIAGNOSTICS].second.c_str();
151 }
152
153 /// getLevel - Return the level of the specified custom diagnostic.
154 Diagnostic::Level getLevel(unsigned DiagID) const {
155 assert(this && DiagID-diag::NUM_BUILTIN_DIAGNOSTICS < DiagInfo.size() &&
156 "Invalid diagnosic ID");
157 return DiagInfo[DiagID-diag::NUM_BUILTIN_DIAGNOSTICS].first;
158 }
159
Chris Lattnera1f23cc2008-10-17 21:24:47 +0000160 unsigned getOrCreateDiagID(Diagnostic::Level L, const char *Message,
161 Diagnostic &Diags) {
Chris Lattner182745a2007-12-02 01:09:57 +0000162 DiagDesc D(L, Message);
163 // Check to see if it already exists.
164 std::map<DiagDesc, unsigned>::iterator I = DiagIDs.lower_bound(D);
165 if (I != DiagIDs.end() && I->first == D)
166 return I->second;
167
168 // If not, assign a new ID.
169 unsigned ID = DiagInfo.size()+diag::NUM_BUILTIN_DIAGNOSTICS;
170 DiagIDs.insert(std::make_pair(D, ID));
171 DiagInfo.push_back(D);
Chris Lattnera1f23cc2008-10-17 21:24:47 +0000172
173 // If this is a warning, and all warnings are supposed to map to errors,
174 // insert the mapping now.
175 if (L == Diagnostic::Warning && Diags.getWarningsAsErrors())
176 Diags.setDiagnosticMapping((diag::kind)ID, diag::MAP_ERROR);
Chris Lattner182745a2007-12-02 01:09:57 +0000177 return ID;
178 }
179 };
180
181 } // end diag namespace
182} // end clang namespace
183
184
185//===----------------------------------------------------------------------===//
186// Common Diagnostic implementation
187//===----------------------------------------------------------------------===//
188
Chris Lattner3fdf4b02008-11-23 09:21:17 +0000189static void DummyArgToStringFn(Diagnostic::ArgumentKind AK, intptr_t QT,
190 const char *Modifier, unsigned ML,
191 const char *Argument, unsigned ArgLen,
192 llvm::SmallVectorImpl<char> &Output) {
193 const char *Str = "<can't format argument>";
Chris Lattner22caddc2008-11-23 09:13:29 +0000194 Output.append(Str, Str+strlen(Str));
195}
196
197
Ted Kremenekb4398aa2008-08-07 17:49:57 +0000198Diagnostic::Diagnostic(DiagnosticClient *client) : Client(client) {
Chris Lattner5b4681c2008-05-29 15:36:45 +0000199 IgnoreAllWarnings = false;
Reid Spencer5f016e22007-07-11 17:01:13 +0000200 WarningsAsErrors = false;
201 WarnOnExtensions = false;
202 ErrorOnExtensions = false;
Daniel Dunbar2fe09972008-09-12 18:10:20 +0000203 SuppressSystemWarnings = false;
Reid Spencer5f016e22007-07-11 17:01:13 +0000204 // Clear all mappings, setting them to MAP_DEFAULT.
205 memset(DiagMappings, 0, sizeof(DiagMappings));
206
207 ErrorOccurred = false;
208 NumDiagnostics = 0;
209 NumErrors = 0;
Chris Lattner182745a2007-12-02 01:09:57 +0000210 CustomDiagInfo = 0;
Chris Lattner3cbfe2c2008-11-22 00:59:29 +0000211 CurDiagID = ~0U;
Chris Lattner22caddc2008-11-23 09:13:29 +0000212
Chris Lattner3fdf4b02008-11-23 09:21:17 +0000213 ArgToStringFn = DummyArgToStringFn;
Reid Spencer5f016e22007-07-11 17:01:13 +0000214}
215
Chris Lattner182745a2007-12-02 01:09:57 +0000216Diagnostic::~Diagnostic() {
217 delete CustomDiagInfo;
218}
219
220/// getCustomDiagID - Return an ID for a diagnostic with the specified message
221/// and level. If this is the first request for this diagnosic, it is
222/// registered and created, otherwise the existing ID is returned.
223unsigned Diagnostic::getCustomDiagID(Level L, const char *Message) {
224 if (CustomDiagInfo == 0)
225 CustomDiagInfo = new diag::CustomDiagInfo();
Chris Lattnera1f23cc2008-10-17 21:24:47 +0000226 return CustomDiagInfo->getOrCreateDiagID(L, Message, *this);
Chris Lattner182745a2007-12-02 01:09:57 +0000227}
228
229
Chris Lattner07506182007-11-30 22:53:43 +0000230/// isBuiltinNoteWarningOrExtension - Return true if the unmapped diagnostic
231/// level of the specified diagnostic ID is a Note, Warning, or Extension.
232/// Note that this only works on builtin diagnostics, not custom ones.
233bool Diagnostic::isBuiltinNoteWarningOrExtension(unsigned DiagID) {
234 return DiagID < diag::NUM_BUILTIN_DIAGNOSTICS &&
235 getBuiltinDiagClass(DiagID) < ERROR;
Reid Spencer5f016e22007-07-11 17:01:13 +0000236}
237
238
239/// getDescription - Given a diagnostic ID, return a description of the
240/// issue.
Chris Lattner0a14eee2008-11-18 07:04:44 +0000241const char *Diagnostic::getDescription(unsigned DiagID) const {
Chris Lattner07506182007-11-30 22:53:43 +0000242 if (DiagID < diag::NUM_BUILTIN_DIAGNOSTICS)
Chris Lattner20c6b3b2009-01-27 18:30:58 +0000243 {
244 if (DiagID < DIAG_START_LEX)
245 return DiagnosticTextCommon[DiagID];
246 else if (DiagID < DIAG_START_PARSE)
247 return DiagnosticTextLex[DiagID - DIAG_START_LEX - 1];
248 else if (DiagID < DIAG_START_AST)
249 return DiagnosticTextParse[DiagID - DIAG_START_PARSE - 1];
250 else if (DiagID < DIAG_START_SEMA)
251 return DiagnosticTextAST[DiagID - DIAG_START_AST - 1];
252 else if (DiagID < DIAG_START_ANALYSIS)
253 return DiagnosticTextSema[DiagID - DIAG_START_SEMA - 1];
254 else if (DiagID < DIAG_UPPER_LIMIT)
255 return DiagnosticTextAnalysis[DiagID - DIAG_START_ANALYSIS - 1];
256 }
257
258 return CustomDiagInfo->getDescription(DiagID);
Reid Spencer5f016e22007-07-11 17:01:13 +0000259}
260
261/// getDiagnosticLevel - Based on the way the client configured the Diagnostic
262/// object, classify the specified diagnostic ID into a Level, consumable by
263/// the DiagnosticClient.
264Diagnostic::Level Diagnostic::getDiagnosticLevel(unsigned DiagID) const {
Chris Lattner182745a2007-12-02 01:09:57 +0000265 // Handle custom diagnostics, which cannot be mapped.
266 if (DiagID >= diag::NUM_BUILTIN_DIAGNOSTICS)
267 return CustomDiagInfo->getLevel(DiagID);
Chris Lattner07506182007-11-30 22:53:43 +0000268
269 unsigned DiagClass = getBuiltinDiagClass(DiagID);
Reid Spencer5f016e22007-07-11 17:01:13 +0000270
271 // Specific non-error diagnostics may be mapped to various levels from ignored
272 // to error.
273 if (DiagClass < ERROR) {
274 switch (getDiagnosticMapping((diag::kind)DiagID)) {
275 case diag::MAP_DEFAULT: break;
Chris Lattner5b4681c2008-05-29 15:36:45 +0000276 case diag::MAP_IGNORE: return Diagnostic::Ignored;
Reid Spencer5f016e22007-07-11 17:01:13 +0000277 case diag::MAP_WARNING: DiagClass = WARNING; break;
278 case diag::MAP_ERROR: DiagClass = ERROR; break;
279 }
280 }
281
282 // Map diagnostic classes based on command line argument settings.
283 if (DiagClass == EXTENSION) {
284 if (ErrorOnExtensions)
285 DiagClass = ERROR;
286 else if (WarnOnExtensions)
287 DiagClass = WARNING;
288 else
289 return Ignored;
Daniel Dunbar4489fe12008-08-05 00:07:51 +0000290 } else if (DiagClass == EXTWARN) {
291 DiagClass = ErrorOnExtensions ? ERROR : WARNING;
Reid Spencer5f016e22007-07-11 17:01:13 +0000292 }
293
Chris Lattner5b4681c2008-05-29 15:36:45 +0000294 // If warnings are globally mapped to ignore or error, do it.
295 if (DiagClass == WARNING) {
296 if (IgnoreAllWarnings)
297 return Diagnostic::Ignored;
298 if (WarningsAsErrors)
299 DiagClass = ERROR;
300 }
Reid Spencer5f016e22007-07-11 17:01:13 +0000301
302 switch (DiagClass) {
303 default: assert(0 && "Unknown diagnostic class!");
304 case NOTE: return Diagnostic::Note;
305 case WARNING: return Diagnostic::Warning;
306 case ERROR: return Diagnostic::Error;
Reid Spencer5f016e22007-07-11 17:01:13 +0000307 }
308}
309
Chris Lattner0a14eee2008-11-18 07:04:44 +0000310/// ProcessDiag - This is the method used to report a diagnostic that is
311/// finally fully formed.
Chris Lattner3cbfe2c2008-11-22 00:59:29 +0000312void Diagnostic::ProcessDiag() {
313 DiagnosticInfo Info(this);
314
Reid Spencer5f016e22007-07-11 17:01:13 +0000315 // Figure out the diagnostic level of this message.
Chris Lattner0a14eee2008-11-18 07:04:44 +0000316 Diagnostic::Level DiagLevel = getDiagnosticLevel(Info.getID());
Reid Spencer5f016e22007-07-11 17:01:13 +0000317
318 // If the client doesn't care about this message, don't issue it.
319 if (DiagLevel == Diagnostic::Ignored)
320 return;
Nico Weber7bfaaae2008-08-10 19:59:06 +0000321
Nico Weber7bfaaae2008-08-10 19:59:06 +0000322 // If this is not an error and we are in a system header, ignore it. We
Chris Lattner0a14eee2008-11-18 07:04:44 +0000323 // have to check on the original Diag ID here, because we also want to
Nico Weber7bfaaae2008-08-10 19:59:06 +0000324 // ignore extensions and warnings in -Werror and -pedantic-errors modes,
325 // which *map* warnings/extensions to errors.
Daniel Dunbar2fe09972008-09-12 18:10:20 +0000326 if (SuppressSystemWarnings &&
Chris Lattner0a14eee2008-11-18 07:04:44 +0000327 Info.getID() < diag::NUM_BUILTIN_DIAGNOSTICS &&
328 getBuiltinDiagClass(Info.getID()) != ERROR &&
329 Info.getLocation().isValid() &&
Chris Lattnerdf7c17a2009-01-16 07:00:02 +0000330 Info.getLocation().getSpellingLoc().isInSystemHeader())
Chris Lattner7097d912008-02-03 09:00:04 +0000331 return;
Reid Spencer5f016e22007-07-11 17:01:13 +0000332
333 if (DiagLevel >= Diagnostic::Error) {
334 ErrorOccurred = true;
Nico Weber7bfaaae2008-08-10 19:59:06 +0000335
Chris Lattner0a14eee2008-11-18 07:04:44 +0000336 ++NumErrors;
Reid Spencer5f016e22007-07-11 17:01:13 +0000337 }
338
Reid Spencer5f016e22007-07-11 17:01:13 +0000339 // Finally, report it.
Chris Lattner0a14eee2008-11-18 07:04:44 +0000340 Client->HandleDiagnostic(DiagLevel, Info);
Ted Kremenekcabe6682009-01-23 20:28:53 +0000341 if (Client->IncludeInDiagnosticCounts()) ++NumDiagnostics;
Reid Spencer5f016e22007-07-11 17:01:13 +0000342}
343
Nico Weber7bfaaae2008-08-10 19:59:06 +0000344
Reid Spencer5f016e22007-07-11 17:01:13 +0000345DiagnosticClient::~DiagnosticClient() {}
Nico Weber7bfaaae2008-08-10 19:59:06 +0000346
Chris Lattnerf4c83962008-11-19 06:51:40 +0000347
Chris Lattneraf7ae4e2008-11-21 07:50:02 +0000348/// ModifierIs - Return true if the specified modifier matches specified string.
349template <std::size_t StrLen>
350static bool ModifierIs(const char *Modifier, unsigned ModifierLen,
351 const char (&Str)[StrLen]) {
352 return StrLen-1 == ModifierLen && !memcmp(Modifier, Str, StrLen-1);
353}
354
355/// HandleSelectModifier - Handle the integer 'select' modifier. This is used
356/// like this: %select{foo|bar|baz}2. This means that the integer argument
357/// "%2" has a value from 0-2. If the value is 0, the diagnostic prints 'foo'.
358/// If the value is 1, it prints 'bar'. If it has the value 2, it prints 'baz'.
359/// This is very useful for certain classes of variant diagnostics.
360static void HandleSelectModifier(unsigned ValNo,
361 const char *Argument, unsigned ArgumentLen,
362 llvm::SmallVectorImpl<char> &OutStr) {
363 const char *ArgumentEnd = Argument+ArgumentLen;
364
365 // Skip over 'ValNo' |'s.
366 while (ValNo) {
367 const char *NextVal = std::find(Argument, ArgumentEnd, '|');
368 assert(NextVal != ArgumentEnd && "Value for integer select modifier was"
369 " larger than the number of options in the diagnostic string!");
370 Argument = NextVal+1; // Skip this string.
371 --ValNo;
372 }
373
374 // Get the end of the value. This is either the } or the |.
375 const char *EndPtr = std::find(Argument, ArgumentEnd, '|');
376 // Add the value to the output string.
377 OutStr.append(Argument, EndPtr);
378}
379
380/// HandleIntegerSModifier - Handle the integer 's' modifier. This adds the
381/// letter 's' to the string if the value is not 1. This is used in cases like
382/// this: "you idiot, you have %4 parameter%s4!".
383static void HandleIntegerSModifier(unsigned ValNo,
384 llvm::SmallVectorImpl<char> &OutStr) {
385 if (ValNo != 1)
386 OutStr.push_back('s');
387}
388
389
Sebastian Redle4c452c2008-11-22 13:44:36 +0000390/// PluralNumber - Parse an unsigned integer and advance Start.
391static unsigned PluralNumber(const char *&Start, const char *End)
392{
393 // Programming 101: Parse a decimal number :-)
394 unsigned Val = 0;
395 while (Start != End && *Start >= '0' && *Start <= '9') {
396 Val *= 10;
397 Val += *Start - '0';
398 ++Start;
399 }
400 return Val;
401}
402
403/// TestPluralRange - Test if Val is in the parsed range. Modifies Start.
404static bool TestPluralRange(unsigned Val, const char *&Start, const char *End)
405{
406 if (*Start != '[') {
407 unsigned Ref = PluralNumber(Start, End);
408 return Ref == Val;
409 }
410
411 ++Start;
412 unsigned Low = PluralNumber(Start, End);
413 assert(*Start == ',' && "Bad plural expression syntax: expected ,");
414 ++Start;
415 unsigned High = PluralNumber(Start, End);
416 assert(*Start == ']' && "Bad plural expression syntax: expected )");
417 ++Start;
418 return Low <= Val && Val <= High;
419}
420
421/// EvalPluralExpr - Actual expression evaluator for HandlePluralModifier.
422static bool EvalPluralExpr(unsigned ValNo, const char *Start, const char *End)
423{
424 // Empty condition?
425 if (*Start == ':')
426 return true;
427
428 while (1) {
429 char C = *Start;
430 if (C == '%') {
431 // Modulo expression
432 ++Start;
433 unsigned Arg = PluralNumber(Start, End);
434 assert(*Start == '=' && "Bad plural expression syntax: expected =");
435 ++Start;
436 unsigned ValMod = ValNo % Arg;
437 if (TestPluralRange(ValMod, Start, End))
438 return true;
439 } else {
Sebastian Redle2065322008-11-27 07:28:14 +0000440 assert((C == '[' || (C >= '0' && C <= '9')) &&
Sebastian Redle4c452c2008-11-22 13:44:36 +0000441 "Bad plural expression syntax: unexpected character");
442 // Range expression
443 if (TestPluralRange(ValNo, Start, End))
444 return true;
445 }
446
447 // Scan for next or-expr part.
448 Start = std::find(Start, End, ',');
449 if(Start == End)
450 break;
451 ++Start;
452 }
453 return false;
454}
455
456/// HandlePluralModifier - Handle the integer 'plural' modifier. This is used
457/// for complex plural forms, or in languages where all plurals are complex.
458/// The syntax is: %plural{cond1:form1|cond2:form2|:form3}, where condn are
459/// conditions that are tested in order, the form corresponding to the first
460/// that applies being emitted. The empty condition is always true, making the
461/// last form a default case.
462/// Conditions are simple boolean expressions, where n is the number argument.
463/// Here are the rules.
464/// condition := expression | empty
465/// empty := -> always true
466/// expression := numeric [',' expression] -> logical or
467/// numeric := range -> true if n in range
468/// | '%' number '=' range -> true if n % number in range
469/// range := number
470/// | '[' number ',' number ']' -> ranges are inclusive both ends
471///
472/// Here are some examples from the GNU gettext manual written in this form:
473/// English:
474/// {1:form0|:form1}
475/// Latvian:
476/// {0:form2|%100=11,%10=0,%10=[2,9]:form1|:form0}
477/// Gaeilge:
478/// {1:form0|2:form1|:form2}
479/// Romanian:
480/// {1:form0|0,%100=[1,19]:form1|:form2}
481/// Lithuanian:
482/// {%10=0,%100=[10,19]:form2|%10=1:form0|:form1}
483/// Russian (requires repeated form):
484/// {%100=[11,14]:form2|%10=1:form0|%10=[2,4]:form1|:form2}
485/// Slovak
486/// {1:form0|[2,4]:form1|:form2}
487/// Polish (requires repeated form):
488/// {1:form0|%100=[10,20]:form2|%10=[2,4]:form1|:form2}
489static void HandlePluralModifier(unsigned ValNo,
490 const char *Argument, unsigned ArgumentLen,
491 llvm::SmallVectorImpl<char> &OutStr)
492{
493 const char *ArgumentEnd = Argument + ArgumentLen;
494 while (1) {
495 assert(Argument < ArgumentEnd && "Plural expression didn't match.");
496 const char *ExprEnd = Argument;
497 while (*ExprEnd != ':') {
498 assert(ExprEnd != ArgumentEnd && "Plural missing expression end");
499 ++ExprEnd;
500 }
501 if (EvalPluralExpr(ValNo, Argument, ExprEnd)) {
502 Argument = ExprEnd + 1;
503 ExprEnd = std::find(Argument, ArgumentEnd, '|');
504 OutStr.append(Argument, ExprEnd);
505 return;
506 }
507 Argument = std::find(Argument, ArgumentEnd - 1, '|') + 1;
508 }
509}
510
511
Chris Lattnerf4c83962008-11-19 06:51:40 +0000512/// FormatDiagnostic - Format this diagnostic into a string, substituting the
513/// formal arguments into the %0 slots. The result is appended onto the Str
514/// array.
515void DiagnosticInfo::
516FormatDiagnostic(llvm::SmallVectorImpl<char> &OutStr) const {
517 const char *DiagStr = getDiags()->getDescription(getID());
518 const char *DiagEnd = DiagStr+strlen(DiagStr);
Nico Weber7bfaaae2008-08-10 19:59:06 +0000519
Chris Lattnerf4c83962008-11-19 06:51:40 +0000520 while (DiagStr != DiagEnd) {
521 if (DiagStr[0] != '%') {
522 // Append non-%0 substrings to Str if we have one.
523 const char *StrEnd = std::find(DiagStr, DiagEnd, '%');
524 OutStr.append(DiagStr, StrEnd);
525 DiagStr = StrEnd;
Chris Lattneraf7ae4e2008-11-21 07:50:02 +0000526 continue;
Chris Lattnerf4c83962008-11-19 06:51:40 +0000527 } else if (DiagStr[1] == '%') {
528 OutStr.push_back('%'); // %% -> %.
529 DiagStr += 2;
Chris Lattneraf7ae4e2008-11-21 07:50:02 +0000530 continue;
531 }
532
533 // Skip the %.
534 ++DiagStr;
535
536 // This must be a placeholder for a diagnostic argument. The format for a
537 // placeholder is one of "%0", "%modifier0", or "%modifier{arguments}0".
538 // The digit is a number from 0-9 indicating which argument this comes from.
539 // The modifier is a string of digits from the set [-a-z]+, arguments is a
540 // brace enclosed string.
541 const char *Modifier = 0, *Argument = 0;
542 unsigned ModifierLen = 0, ArgumentLen = 0;
543
544 // Check to see if we have a modifier. If so eat it.
545 if (!isdigit(DiagStr[0])) {
546 Modifier = DiagStr;
547 while (DiagStr[0] == '-' ||
548 (DiagStr[0] >= 'a' && DiagStr[0] <= 'z'))
549 ++DiagStr;
550 ModifierLen = DiagStr-Modifier;
Chris Lattnerf4c83962008-11-19 06:51:40 +0000551
Chris Lattneraf7ae4e2008-11-21 07:50:02 +0000552 // If we have an argument, get it next.
553 if (DiagStr[0] == '{') {
554 ++DiagStr; // Skip {.
555 Argument = DiagStr;
556
557 for (; DiagStr[0] != '}'; ++DiagStr)
558 assert(DiagStr[0] && "Mismatched {}'s in diagnostic string!");
559 ArgumentLen = DiagStr-Argument;
560 ++DiagStr; // Skip }.
Chris Lattnerf4c83962008-11-19 06:51:40 +0000561 }
Chris Lattneraf7ae4e2008-11-21 07:50:02 +0000562 }
563
564 assert(isdigit(*DiagStr) && "Invalid format for argument in diagnostic");
Chris Lattner22caddc2008-11-23 09:13:29 +0000565 unsigned ArgNo = *DiagStr++ - '0';
Chris Lattneraf7ae4e2008-11-21 07:50:02 +0000566
Chris Lattner22caddc2008-11-23 09:13:29 +0000567 switch (getArgKind(ArgNo)) {
Chris Lattner08631c52008-11-23 21:45:46 +0000568 // ---- STRINGS ----
Chris Lattner3cbfe2c2008-11-22 00:59:29 +0000569 case Diagnostic::ak_std_string: {
Chris Lattner22caddc2008-11-23 09:13:29 +0000570 const std::string &S = getArgStdStr(ArgNo);
Chris Lattneraf7ae4e2008-11-21 07:50:02 +0000571 assert(ModifierLen == 0 && "No modifiers for strings yet");
572 OutStr.append(S.begin(), S.end());
573 break;
574 }
Chris Lattner3cbfe2c2008-11-22 00:59:29 +0000575 case Diagnostic::ak_c_string: {
Chris Lattner22caddc2008-11-23 09:13:29 +0000576 const char *S = getArgCStr(ArgNo);
Chris Lattneraf7ae4e2008-11-21 07:50:02 +0000577 assert(ModifierLen == 0 && "No modifiers for strings yet");
578 OutStr.append(S, S + strlen(S));
579 break;
580 }
Chris Lattner08631c52008-11-23 21:45:46 +0000581 // ---- INTEGERS ----
Chris Lattner3cbfe2c2008-11-22 00:59:29 +0000582 case Diagnostic::ak_sint: {
Chris Lattner22caddc2008-11-23 09:13:29 +0000583 int Val = getArgSInt(ArgNo);
Chris Lattneraf7ae4e2008-11-21 07:50:02 +0000584
585 if (ModifierIs(Modifier, ModifierLen, "select")) {
586 HandleSelectModifier((unsigned)Val, Argument, ArgumentLen, OutStr);
587 } else if (ModifierIs(Modifier, ModifierLen, "s")) {
588 HandleIntegerSModifier(Val, OutStr);
Sebastian Redle4c452c2008-11-22 13:44:36 +0000589 } else if (ModifierIs(Modifier, ModifierLen, "plural")) {
590 HandlePluralModifier((unsigned)Val, Argument, ArgumentLen, OutStr);
Chris Lattneraf7ae4e2008-11-21 07:50:02 +0000591 } else {
592 assert(ModifierLen == 0 && "Unknown integer modifier");
Chris Lattner30bc9652008-11-19 07:22:31 +0000593 // FIXME: Optimize
Chris Lattneraf7ae4e2008-11-21 07:50:02 +0000594 std::string S = llvm::itostr(Val);
Chris Lattner30bc9652008-11-19 07:22:31 +0000595 OutStr.append(S.begin(), S.end());
Chris Lattner30bc9652008-11-19 07:22:31 +0000596 }
Chris Lattneraf7ae4e2008-11-21 07:50:02 +0000597 break;
598 }
Chris Lattner3cbfe2c2008-11-22 00:59:29 +0000599 case Diagnostic::ak_uint: {
Chris Lattner22caddc2008-11-23 09:13:29 +0000600 unsigned Val = getArgUInt(ArgNo);
Chris Lattneraf7ae4e2008-11-21 07:50:02 +0000601
602 if (ModifierIs(Modifier, ModifierLen, "select")) {
603 HandleSelectModifier(Val, Argument, ArgumentLen, OutStr);
604 } else if (ModifierIs(Modifier, ModifierLen, "s")) {
605 HandleIntegerSModifier(Val, OutStr);
Sebastian Redle4c452c2008-11-22 13:44:36 +0000606 } else if (ModifierIs(Modifier, ModifierLen, "plural")) {
607 HandlePluralModifier((unsigned)Val, Argument, ArgumentLen, OutStr);
Chris Lattneraf7ae4e2008-11-21 07:50:02 +0000608 } else {
609 assert(ModifierLen == 0 && "Unknown integer modifier");
610
Chris Lattner30bc9652008-11-19 07:22:31 +0000611 // FIXME: Optimize
Chris Lattneraf7ae4e2008-11-21 07:50:02 +0000612 std::string S = llvm::utostr_32(Val);
Chris Lattner30bc9652008-11-19 07:22:31 +0000613 OutStr.append(S.begin(), S.end());
Chris Lattner30bc9652008-11-19 07:22:31 +0000614 }
Chris Lattner22caddc2008-11-23 09:13:29 +0000615 break;
Chris Lattneraf7ae4e2008-11-21 07:50:02 +0000616 }
Chris Lattner08631c52008-11-23 21:45:46 +0000617 // ---- NAMES and TYPES ----
618 case Diagnostic::ak_identifierinfo: {
619 OutStr.push_back('\'');
620 const IdentifierInfo *II = getArgIdentifier(ArgNo);
621 assert(ModifierLen == 0 && "No modifiers for strings yet");
622 OutStr.append(II->getName(), II->getName() + II->getLength());
623 OutStr.push_back('\'');
624 break;
625 }
Chris Lattner22caddc2008-11-23 09:13:29 +0000626 case Diagnostic::ak_qualtype:
Chris Lattner011bb4e2008-11-23 20:28:15 +0000627 case Diagnostic::ak_declarationname:
Chris Lattner22caddc2008-11-23 09:13:29 +0000628 OutStr.push_back('\'');
Chris Lattner3fdf4b02008-11-23 09:21:17 +0000629 getDiags()->ConvertArgToString(getArgKind(ArgNo), getRawArg(ArgNo),
630 Modifier, ModifierLen,
631 Argument, ArgumentLen, OutStr);
Chris Lattner22caddc2008-11-23 09:13:29 +0000632 OutStr.push_back('\'');
633 break;
Nico Weber7bfaaae2008-08-10 19:59:06 +0000634 }
635 }
Nico Weber7bfaaae2008-08-10 19:59:06 +0000636}
Ted Kremenekcabe6682009-01-23 20:28:53 +0000637
638/// IncludeInDiagnosticCounts - This method (whose default implementation
639/// returns true) indicates whether the diagnostics handled by this
640/// DiagnosticClient should be included in the number of diagnostics
641/// reported by Diagnostic.
642bool DiagnosticClient::IncludeInDiagnosticCounts() const { return true; }