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