1 /*===-- CIndexDiagnostics.cpp - Diagnostics C Interface ---------*- C++ -*-===*\
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 |* Implements the diagnostic functions of the Clang C interface. *|
11 |* *|
12 \*===----------------------------------------------------------------------===*/
13 #include "CIndexDiagnostic.h"
14 #include "CIndexer.h"
15 #include "CXTranslationUnit.h"
16 #include "CXSourceLocation.h"
17 #include "CXString.h"
18
19 #include "clang/Frontend/ASTUnit.h"
20 #include "clang/Frontend/FrontendDiagnostic.h"
21 #include "clang/Frontend/DiagnosticRenderer.h"
22 #include "clang/Basic/DiagnosticOptions.h"
23 #include "llvm/ADT/SmallString.h"
24 #include "llvm/ADT/Twine.h"
25 #include "llvm/Support/MemoryBuffer.h"
26 #include "llvm/Support/raw_ostream.h"
27
28 using namespace clang;
29 using namespace clang::cxloc;
30 using namespace clang::cxdiag;
31 using namespace llvm;
32
33
~CXDiagnosticSetImpl()34 CXDiagnosticSetImpl::~CXDiagnosticSetImpl() {
35 for (std::vector<CXDiagnosticImpl *>::iterator it = Diagnostics.begin(),
36 et = Diagnostics.end();
37 it != et; ++it) {
38 delete *it;
39 }
40 }
41
~CXDiagnosticImpl()42 CXDiagnosticImpl::~CXDiagnosticImpl() {}
43
44 namespace {
45 class CXDiagnosticCustomNoteImpl : public CXDiagnosticImpl {
46 std::string Message;
47 CXSourceLocation Loc;
48 public:
CXDiagnosticCustomNoteImpl(StringRef Msg,CXSourceLocation L)49 CXDiagnosticCustomNoteImpl(StringRef Msg, CXSourceLocation L)
50 : CXDiagnosticImpl(CustomNoteDiagnosticKind),
51 Message(Msg), Loc(L) {}
52
~CXDiagnosticCustomNoteImpl()53 virtual ~CXDiagnosticCustomNoteImpl() {}
54
getSeverity() const55 CXDiagnosticSeverity getSeverity() const {
56 return CXDiagnostic_Note;
57 }
58
getLocation() const59 CXSourceLocation getLocation() const {
60 return Loc;
61 }
62
getSpelling() const63 CXString getSpelling() const {
64 return cxstring::createRef(Message.c_str());
65 }
66
getDiagnosticOption(CXString * Disable) const67 CXString getDiagnosticOption(CXString *Disable) const {
68 if (Disable)
69 *Disable = cxstring::createEmpty();
70 return cxstring::createEmpty();
71 }
72
getCategory() const73 unsigned getCategory() const { return 0; }
getCategoryText() const74 CXString getCategoryText() const { return cxstring::createEmpty(); }
75
getNumRanges() const76 unsigned getNumRanges() const { return 0; }
getRange(unsigned Range) const77 CXSourceRange getRange(unsigned Range) const { return clang_getNullRange(); }
getNumFixIts() const78 unsigned getNumFixIts() const { return 0; }
getFixIt(unsigned FixIt,CXSourceRange * ReplacementRange) const79 CXString getFixIt(unsigned FixIt, CXSourceRange *ReplacementRange) const {
80 if (ReplacementRange)
81 *ReplacementRange = clang_getNullRange();
82 return cxstring::createEmpty();
83 }
84 };
85
86 class CXDiagnosticRenderer : public DiagnosticNoteRenderer {
87 public:
CXDiagnosticRenderer(const LangOptions & LangOpts,DiagnosticOptions * DiagOpts,CXDiagnosticSetImpl * mainSet)88 CXDiagnosticRenderer(const LangOptions &LangOpts,
89 DiagnosticOptions *DiagOpts,
90 CXDiagnosticSetImpl *mainSet)
91 : DiagnosticNoteRenderer(LangOpts, DiagOpts),
92 CurrentSet(mainSet), MainSet(mainSet) {}
93
~CXDiagnosticRenderer()94 virtual ~CXDiagnosticRenderer() {}
95
beginDiagnostic(DiagOrStoredDiag D,DiagnosticsEngine::Level Level)96 virtual void beginDiagnostic(DiagOrStoredDiag D,
97 DiagnosticsEngine::Level Level) {
98
99 const StoredDiagnostic *SD = D.dyn_cast<const StoredDiagnostic*>();
100 if (!SD)
101 return;
102
103 if (Level != DiagnosticsEngine::Note)
104 CurrentSet = MainSet;
105
106 CXStoredDiagnostic *CD = new CXStoredDiagnostic(*SD, LangOpts);
107 CurrentSet->appendDiagnostic(CD);
108
109 if (Level != DiagnosticsEngine::Note)
110 CurrentSet = &CD->getChildDiagnostics();
111 }
112
emitDiagnosticMessage(SourceLocation Loc,PresumedLoc PLoc,DiagnosticsEngine::Level Level,StringRef Message,ArrayRef<CharSourceRange> Ranges,const SourceManager * SM,DiagOrStoredDiag D)113 virtual void emitDiagnosticMessage(SourceLocation Loc, PresumedLoc PLoc,
114 DiagnosticsEngine::Level Level,
115 StringRef Message,
116 ArrayRef<CharSourceRange> Ranges,
117 const SourceManager *SM,
118 DiagOrStoredDiag D) {
119 if (!D.isNull())
120 return;
121
122 CXSourceLocation L;
123 if (SM)
124 L = translateSourceLocation(*SM, LangOpts, Loc);
125 else
126 L = clang_getNullLocation();
127 CXDiagnosticImpl *CD = new CXDiagnosticCustomNoteImpl(Message, L);
128 CurrentSet->appendDiagnostic(CD);
129 }
130
emitDiagnosticLoc(SourceLocation Loc,PresumedLoc PLoc,DiagnosticsEngine::Level Level,ArrayRef<CharSourceRange> Ranges,const SourceManager & SM)131 virtual void emitDiagnosticLoc(SourceLocation Loc, PresumedLoc PLoc,
132 DiagnosticsEngine::Level Level,
133 ArrayRef<CharSourceRange> Ranges,
134 const SourceManager &SM) {}
135
emitCodeContext(SourceLocation Loc,DiagnosticsEngine::Level Level,SmallVectorImpl<CharSourceRange> & Ranges,ArrayRef<FixItHint> Hints,const SourceManager & SM)136 virtual void emitCodeContext(SourceLocation Loc,
137 DiagnosticsEngine::Level Level,
138 SmallVectorImpl<CharSourceRange>& Ranges,
139 ArrayRef<FixItHint> Hints,
140 const SourceManager &SM) {}
141
emitNote(SourceLocation Loc,StringRef Message,const SourceManager * SM)142 virtual void emitNote(SourceLocation Loc, StringRef Message,
143 const SourceManager *SM) {
144 CXSourceLocation L;
145 if (SM)
146 L = translateSourceLocation(*SM, LangOpts, Loc);
147 else
148 L = clang_getNullLocation();
149 CurrentSet->appendDiagnostic(new CXDiagnosticCustomNoteImpl(Message,
150 L));
151 }
152
153 CXDiagnosticSetImpl *CurrentSet;
154 CXDiagnosticSetImpl *MainSet;
155 };
156 }
157
lazyCreateDiags(CXTranslationUnit TU,bool checkIfChanged)158 CXDiagnosticSetImpl *cxdiag::lazyCreateDiags(CXTranslationUnit TU,
159 bool checkIfChanged) {
160 ASTUnit *AU = cxtu::getASTUnit(TU);
161
162 if (TU->Diagnostics && checkIfChanged) {
163 // In normal use, ASTUnit's diagnostics should not change unless we reparse.
164 // Currently they can only change by using the internal testing flag
165 // '-error-on-deserialized-decl' which will error during deserialization of
166 // a declaration. What will happen is:
167 //
168 // -c-index-test gets a CXTranslationUnit
169 // -checks the diagnostics, the diagnostics set is lazily created,
170 // no errors are reported
171 // -later does an operation, like annotation of tokens, that triggers
172 // -error-on-deserialized-decl, that will emit a diagnostic error,
173 // that ASTUnit will catch and add to its stored diagnostics vector.
174 // -c-index-test wants to check whether an error occurred after performing
175 // the operation but can only query the lazily created set.
176 //
177 // We check here if a new diagnostic was appended since the last time the
178 // diagnostic set was created, in which case we reset it.
179
180 CXDiagnosticSetImpl *
181 Set = static_cast<CXDiagnosticSetImpl*>(TU->Diagnostics);
182 if (AU->stored_diag_size() != Set->getNumDiagnostics()) {
183 // Diagnostics in the ASTUnit were updated, reset the associated
184 // diagnostics.
185 delete Set;
186 TU->Diagnostics = 0;
187 }
188 }
189
190 if (!TU->Diagnostics) {
191 CXDiagnosticSetImpl *Set = new CXDiagnosticSetImpl();
192 TU->Diagnostics = Set;
193 IntrusiveRefCntPtr<DiagnosticOptions> DOpts = new DiagnosticOptions;
194 CXDiagnosticRenderer Renderer(AU->getASTContext().getLangOpts(),
195 &*DOpts, Set);
196
197 for (ASTUnit::stored_diag_iterator it = AU->stored_diag_begin(),
198 ei = AU->stored_diag_end(); it != ei; ++it) {
199 Renderer.emitStoredDiagnostic(*it);
200 }
201 }
202 return static_cast<CXDiagnosticSetImpl*>(TU->Diagnostics);
203 }
204
205 //-----------------------------------------------------------------------------
206 // C Interface Routines
207 //-----------------------------------------------------------------------------
208 extern "C" {
209
clang_getNumDiagnostics(CXTranslationUnit Unit)210 unsigned clang_getNumDiagnostics(CXTranslationUnit Unit) {
211 if (!cxtu::getASTUnit(Unit))
212 return 0;
213 return lazyCreateDiags(Unit, /*checkIfChanged=*/true)->getNumDiagnostics();
214 }
215
clang_getDiagnostic(CXTranslationUnit Unit,unsigned Index)216 CXDiagnostic clang_getDiagnostic(CXTranslationUnit Unit, unsigned Index) {
217 CXDiagnosticSet D = clang_getDiagnosticSetFromTU(Unit);
218 if (!D)
219 return 0;
220
221 CXDiagnosticSetImpl *Diags = static_cast<CXDiagnosticSetImpl*>(D);
222 if (Index >= Diags->getNumDiagnostics())
223 return 0;
224
225 return Diags->getDiagnostic(Index);
226 }
227
clang_getDiagnosticSetFromTU(CXTranslationUnit Unit)228 CXDiagnosticSet clang_getDiagnosticSetFromTU(CXTranslationUnit Unit) {
229 if (!cxtu::getASTUnit(Unit))
230 return 0;
231 return static_cast<CXDiagnostic>(lazyCreateDiags(Unit));
232 }
233
clang_disposeDiagnostic(CXDiagnostic Diagnostic)234 void clang_disposeDiagnostic(CXDiagnostic Diagnostic) {
235 // No-op. Kept as a legacy API. CXDiagnostics are now managed
236 // by the enclosing CXDiagnosticSet.
237 }
238
clang_formatDiagnostic(CXDiagnostic Diagnostic,unsigned Options)239 CXString clang_formatDiagnostic(CXDiagnostic Diagnostic, unsigned Options) {
240 if (!Diagnostic)
241 return cxstring::createEmpty();
242
243 CXDiagnosticSeverity Severity = clang_getDiagnosticSeverity(Diagnostic);
244
245 SmallString<256> Str;
246 llvm::raw_svector_ostream Out(Str);
247
248 if (Options & CXDiagnostic_DisplaySourceLocation) {
249 // Print source location (file:line), along with optional column
250 // and source ranges.
251 CXFile File;
252 unsigned Line, Column;
253 clang_getSpellingLocation(clang_getDiagnosticLocation(Diagnostic),
254 &File, &Line, &Column, 0);
255 if (File) {
256 CXString FName = clang_getFileName(File);
257 Out << clang_getCString(FName) << ":" << Line << ":";
258 clang_disposeString(FName);
259 if (Options & CXDiagnostic_DisplayColumn)
260 Out << Column << ":";
261
262 if (Options & CXDiagnostic_DisplaySourceRanges) {
263 unsigned N = clang_getDiagnosticNumRanges(Diagnostic);
264 bool PrintedRange = false;
265 for (unsigned I = 0; I != N; ++I) {
266 CXFile StartFile, EndFile;
267 CXSourceRange Range = clang_getDiagnosticRange(Diagnostic, I);
268
269 unsigned StartLine, StartColumn, EndLine, EndColumn;
270 clang_getSpellingLocation(clang_getRangeStart(Range),
271 &StartFile, &StartLine, &StartColumn,
272 0);
273 clang_getSpellingLocation(clang_getRangeEnd(Range),
274 &EndFile, &EndLine, &EndColumn, 0);
275
276 if (StartFile != EndFile || StartFile != File)
277 continue;
278
279 Out << "{" << StartLine << ":" << StartColumn << "-"
280 << EndLine << ":" << EndColumn << "}";
281 PrintedRange = true;
282 }
283 if (PrintedRange)
284 Out << ":";
285 }
286
287 Out << " ";
288 }
289 }
290
291 /* Print warning/error/etc. */
292 switch (Severity) {
293 case CXDiagnostic_Ignored: llvm_unreachable("impossible");
294 case CXDiagnostic_Note: Out << "note: "; break;
295 case CXDiagnostic_Warning: Out << "warning: "; break;
296 case CXDiagnostic_Error: Out << "error: "; break;
297 case CXDiagnostic_Fatal: Out << "fatal error: "; break;
298 }
299
300 CXString Text = clang_getDiagnosticSpelling(Diagnostic);
301 if (clang_getCString(Text))
302 Out << clang_getCString(Text);
303 else
304 Out << "<no diagnostic text>";
305 clang_disposeString(Text);
306
307 if (Options & (CXDiagnostic_DisplayOption | CXDiagnostic_DisplayCategoryId |
308 CXDiagnostic_DisplayCategoryName)) {
309 bool NeedBracket = true;
310 bool NeedComma = false;
311
312 if (Options & CXDiagnostic_DisplayOption) {
313 CXString OptionName = clang_getDiagnosticOption(Diagnostic, 0);
314 if (const char *OptionText = clang_getCString(OptionName)) {
315 if (OptionText[0]) {
316 Out << " [" << OptionText;
317 NeedBracket = false;
318 NeedComma = true;
319 }
320 }
321 clang_disposeString(OptionName);
322 }
323
324 if (Options & (CXDiagnostic_DisplayCategoryId |
325 CXDiagnostic_DisplayCategoryName)) {
326 if (unsigned CategoryID = clang_getDiagnosticCategory(Diagnostic)) {
327 if (Options & CXDiagnostic_DisplayCategoryId) {
328 if (NeedBracket)
329 Out << " [";
330 if (NeedComma)
331 Out << ", ";
332 Out << CategoryID;
333 NeedBracket = false;
334 NeedComma = true;
335 }
336
337 if (Options & CXDiagnostic_DisplayCategoryName) {
338 CXString CategoryName = clang_getDiagnosticCategoryText(Diagnostic);
339 if (NeedBracket)
340 Out << " [";
341 if (NeedComma)
342 Out << ", ";
343 Out << clang_getCString(CategoryName);
344 NeedBracket = false;
345 NeedComma = true;
346 clang_disposeString(CategoryName);
347 }
348 }
349 }
350
351 (void) NeedComma; // Silence dead store warning.
352 if (!NeedBracket)
353 Out << "]";
354 }
355
356 return cxstring::createDup(Out.str());
357 }
358
clang_defaultDiagnosticDisplayOptions()359 unsigned clang_defaultDiagnosticDisplayOptions() {
360 return CXDiagnostic_DisplaySourceLocation | CXDiagnostic_DisplayColumn |
361 CXDiagnostic_DisplayOption;
362 }
363
clang_getDiagnosticSeverity(CXDiagnostic Diag)364 enum CXDiagnosticSeverity clang_getDiagnosticSeverity(CXDiagnostic Diag) {
365 if (CXDiagnosticImpl *D = static_cast<CXDiagnosticImpl*>(Diag))
366 return D->getSeverity();
367 return CXDiagnostic_Ignored;
368 }
369
clang_getDiagnosticLocation(CXDiagnostic Diag)370 CXSourceLocation clang_getDiagnosticLocation(CXDiagnostic Diag) {
371 if (CXDiagnosticImpl *D = static_cast<CXDiagnosticImpl*>(Diag))
372 return D->getLocation();
373 return clang_getNullLocation();
374 }
375
clang_getDiagnosticSpelling(CXDiagnostic Diag)376 CXString clang_getDiagnosticSpelling(CXDiagnostic Diag) {
377 if (CXDiagnosticImpl *D = static_cast<CXDiagnosticImpl *>(Diag))
378 return D->getSpelling();
379 return cxstring::createEmpty();
380 }
381
clang_getDiagnosticOption(CXDiagnostic Diag,CXString * Disable)382 CXString clang_getDiagnosticOption(CXDiagnostic Diag, CXString *Disable) {
383 if (Disable)
384 *Disable = cxstring::createEmpty();
385
386 if (CXDiagnosticImpl *D = static_cast<CXDiagnosticImpl *>(Diag))
387 return D->getDiagnosticOption(Disable);
388
389 return cxstring::createEmpty();
390 }
391
clang_getDiagnosticCategory(CXDiagnostic Diag)392 unsigned clang_getDiagnosticCategory(CXDiagnostic Diag) {
393 if (CXDiagnosticImpl *D = static_cast<CXDiagnosticImpl *>(Diag))
394 return D->getCategory();
395 return 0;
396 }
397
clang_getDiagnosticCategoryName(unsigned Category)398 CXString clang_getDiagnosticCategoryName(unsigned Category) {
399 // Kept for backwards compatibility.
400 return cxstring::createRef(DiagnosticIDs::getCategoryNameFromID(Category));
401 }
402
clang_getDiagnosticCategoryText(CXDiagnostic Diag)403 CXString clang_getDiagnosticCategoryText(CXDiagnostic Diag) {
404 if (CXDiagnosticImpl *D = static_cast<CXDiagnosticImpl *>(Diag))
405 return D->getCategoryText();
406 return cxstring::createEmpty();
407 }
408
clang_getDiagnosticNumRanges(CXDiagnostic Diag)409 unsigned clang_getDiagnosticNumRanges(CXDiagnostic Diag) {
410 if (CXDiagnosticImpl *D = static_cast<CXDiagnosticImpl *>(Diag))
411 return D->getNumRanges();
412 return 0;
413 }
414
clang_getDiagnosticRange(CXDiagnostic Diag,unsigned Range)415 CXSourceRange clang_getDiagnosticRange(CXDiagnostic Diag, unsigned Range) {
416 CXDiagnosticImpl *D = static_cast<CXDiagnosticImpl *>(Diag);
417 if (!D || Range >= D->getNumRanges())
418 return clang_getNullRange();
419 return D->getRange(Range);
420 }
421
clang_getDiagnosticNumFixIts(CXDiagnostic Diag)422 unsigned clang_getDiagnosticNumFixIts(CXDiagnostic Diag) {
423 if (CXDiagnosticImpl *D = static_cast<CXDiagnosticImpl *>(Diag))
424 return D->getNumFixIts();
425 return 0;
426 }
427
clang_getDiagnosticFixIt(CXDiagnostic Diag,unsigned FixIt,CXSourceRange * ReplacementRange)428 CXString clang_getDiagnosticFixIt(CXDiagnostic Diag, unsigned FixIt,
429 CXSourceRange *ReplacementRange) {
430 CXDiagnosticImpl *D = static_cast<CXDiagnosticImpl *>(Diag);
431 if (!D || FixIt >= D->getNumFixIts()) {
432 if (ReplacementRange)
433 *ReplacementRange = clang_getNullRange();
434 return cxstring::createEmpty();
435 }
436 return D->getFixIt(FixIt, ReplacementRange);
437 }
438
clang_disposeDiagnosticSet(CXDiagnosticSet Diags)439 void clang_disposeDiagnosticSet(CXDiagnosticSet Diags) {
440 CXDiagnosticSetImpl *D = static_cast<CXDiagnosticSetImpl*>(Diags);
441 if (D->isExternallyManaged())
442 delete D;
443 }
444
clang_getDiagnosticInSet(CXDiagnosticSet Diags,unsigned Index)445 CXDiagnostic clang_getDiagnosticInSet(CXDiagnosticSet Diags,
446 unsigned Index) {
447 if (CXDiagnosticSetImpl *D = static_cast<CXDiagnosticSetImpl*>(Diags))
448 if (Index < D->getNumDiagnostics())
449 return D->getDiagnostic(Index);
450 return 0;
451 }
452
clang_getChildDiagnostics(CXDiagnostic Diag)453 CXDiagnosticSet clang_getChildDiagnostics(CXDiagnostic Diag) {
454 if (CXDiagnosticImpl *D = static_cast<CXDiagnosticImpl *>(Diag)) {
455 CXDiagnosticSetImpl &ChildDiags = D->getChildDiagnostics();
456 return ChildDiags.empty() ? 0 : (CXDiagnosticSet) &ChildDiags;
457 }
458 return 0;
459 }
460
clang_getNumDiagnosticsInSet(CXDiagnosticSet Diags)461 unsigned clang_getNumDiagnosticsInSet(CXDiagnosticSet Diags) {
462 if (CXDiagnosticSetImpl *D = static_cast<CXDiagnosticSetImpl*>(Diags))
463 return D->getNumDiagnostics();
464 return 0;
465 }
466
467 } // end extern "C"
468