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