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