[libclang] Migrate away from PointerUnion::dyn_cast (NFC) (#125381)
[llvm-project.git] / clang / tools / libclang / CIndexDiagnostic.cpp
blob92271d9c37f862def5b623883d564a6c7b1ab56a
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.
11 //===----------------------------------------------------------------------===//
13 #include "CIndexDiagnostic.h"
14 #include "CIndexer.h"
15 #include "CXTranslationUnit.h"
16 #include "CXSourceLocation.h"
17 #include "CXString.h"
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"
25 using namespace clang;
26 using namespace clang::cxloc;
27 using namespace clang::cxdiag;
28 using namespace llvm;
30 CXDiagnosticSetImpl::~CXDiagnosticSetImpl() {}
32 void
33 CXDiagnosticSetImpl::appendDiagnostic(std::unique_ptr<CXDiagnosticImpl> D) {
34 Diagnostics.push_back(std::move(D));
37 CXDiagnosticImpl::~CXDiagnosticImpl() {}
39 namespace {
40 class CXDiagnosticCustomNoteImpl : public CXDiagnosticImpl {
41 std::string Message;
42 CXSourceLocation Loc;
43 public:
44 CXDiagnosticCustomNoteImpl(StringRef Msg, CXSourceLocation L)
45 : CXDiagnosticImpl(CustomNoteDiagnosticKind), Message(std::string(Msg)),
46 Loc(L) {}
48 ~CXDiagnosticCustomNoteImpl() override {}
50 CXDiagnosticSeverity getSeverity() const override {
51 return CXDiagnostic_Note;
54 CXSourceLocation getLocation() const override { return Loc; }
56 CXString getSpelling() const override {
57 return cxstring::createRef(Message.c_str());
60 CXString getDiagnosticOption(CXString *Disable) const override {
61 if (Disable)
62 *Disable = cxstring::createEmpty();
63 return cxstring::createEmpty();
66 unsigned getCategory() const override { return 0; }
67 CXString getCategoryText() const override { return cxstring::createEmpty(); }
69 unsigned getNumRanges() const override { return 0; }
70 CXSourceRange getRange(unsigned Range) const override {
71 return clang_getNullRange();
73 unsigned getNumFixIts() const override { return 0; }
74 CXString getFixIt(unsigned FixIt,
75 CXSourceRange *ReplacementRange) const override {
76 if (ReplacementRange)
77 *ReplacementRange = clang_getNullRange();
78 return cxstring::createEmpty();
80 };
82 class CXDiagnosticRenderer : public DiagnosticNoteRenderer {
83 public:
84 CXDiagnosticRenderer(const LangOptions &LangOpts,
85 DiagnosticOptions *DiagOpts,
86 CXDiagnosticSetImpl *mainSet)
87 : DiagnosticNoteRenderer(LangOpts, DiagOpts),
88 CurrentSet(mainSet), MainSet(mainSet) {}
90 ~CXDiagnosticRenderer() override {}
92 void beginDiagnostic(DiagOrStoredDiag D,
93 DiagnosticsEngine::Level Level) override {
95 const StoredDiagnostic *SD =
96 dyn_cast_if_present<const StoredDiagnostic *>(D);
97 if (!SD)
98 return;
100 if (Level != DiagnosticsEngine::Note)
101 CurrentSet = MainSet;
103 auto Owner = std::make_unique<CXStoredDiagnostic>(*SD, LangOpts);
104 CXStoredDiagnostic &CD = *Owner;
105 CurrentSet->appendDiagnostic(std::move(Owner));
107 if (Level != DiagnosticsEngine::Note)
108 CurrentSet = &CD.getChildDiagnostics();
111 void emitDiagnosticMessage(FullSourceLoc Loc, PresumedLoc PLoc,
112 DiagnosticsEngine::Level Level, StringRef Message,
113 ArrayRef<CharSourceRange> Ranges,
114 DiagOrStoredDiag D) override {
115 if (!D.isNull())
116 return;
118 CXSourceLocation L;
119 if (Loc.hasManager())
120 L = translateSourceLocation(Loc.getManager(), LangOpts, Loc);
121 else
122 L = clang_getNullLocation();
123 CurrentSet->appendDiagnostic(
124 std::make_unique<CXDiagnosticCustomNoteImpl>(Message, L));
127 void emitDiagnosticLoc(FullSourceLoc Loc, PresumedLoc PLoc,
128 DiagnosticsEngine::Level Level,
129 ArrayRef<CharSourceRange> Ranges) override {}
131 void emitCodeContext(FullSourceLoc Loc, DiagnosticsEngine::Level Level,
132 SmallVectorImpl<CharSourceRange> &Ranges,
133 ArrayRef<FixItHint> Hints) override {}
135 void emitNote(FullSourceLoc Loc, StringRef Message) override {
136 CXSourceLocation L;
137 if (Loc.hasManager())
138 L = translateSourceLocation(Loc.getManager(), LangOpts, Loc);
139 else
140 L = clang_getNullLocation();
141 CurrentSet->appendDiagnostic(
142 std::make_unique<CXDiagnosticCustomNoteImpl>(Message, L));
145 CXDiagnosticSetImpl *CurrentSet;
146 CXDiagnosticSetImpl *MainSet;
150 CXDiagnosticSetImpl *cxdiag::lazyCreateDiags(CXTranslationUnit TU,
151 bool checkIfChanged) {
152 ASTUnit *AU = cxtu::getASTUnit(TU);
154 if (TU->Diagnostics && checkIfChanged) {
155 // In normal use, ASTUnit's diagnostics should not change unless we reparse.
156 // Currently they can only change by using the internal testing flag
157 // '-error-on-deserialized-decl' which will error during deserialization of
158 // a declaration. What will happen is:
160 // -c-index-test gets a CXTranslationUnit
161 // -checks the diagnostics, the diagnostics set is lazily created,
162 // no errors are reported
163 // -later does an operation, like annotation of tokens, that triggers
164 // -error-on-deserialized-decl, that will emit a diagnostic error,
165 // that ASTUnit will catch and add to its stored diagnostics vector.
166 // -c-index-test wants to check whether an error occurred after performing
167 // the operation but can only query the lazily created set.
169 // We check here if a new diagnostic was appended since the last time the
170 // diagnostic set was created, in which case we reset it.
172 CXDiagnosticSetImpl *
173 Set = static_cast<CXDiagnosticSetImpl*>(TU->Diagnostics);
174 if (AU->stored_diag_size() != Set->getNumDiagnostics()) {
175 // Diagnostics in the ASTUnit were updated, reset the associated
176 // diagnostics.
177 delete Set;
178 TU->Diagnostics = nullptr;
182 if (!TU->Diagnostics) {
183 CXDiagnosticSetImpl *Set = new CXDiagnosticSetImpl();
184 TU->Diagnostics = Set;
185 IntrusiveRefCntPtr<DiagnosticOptions> DOpts = new DiagnosticOptions;
186 CXDiagnosticRenderer Renderer(AU->getASTContext().getLangOpts(),
187 &*DOpts, Set);
189 for (ASTUnit::stored_diag_iterator it = AU->stored_diag_begin(),
190 ei = AU->stored_diag_end(); it != ei; ++it) {
191 Renderer.emitStoredDiagnostic(*it);
194 return static_cast<CXDiagnosticSetImpl*>(TU->Diagnostics);
197 //-----------------------------------------------------------------------------
198 // C Interface Routines
199 //-----------------------------------------------------------------------------
200 unsigned clang_getNumDiagnostics(CXTranslationUnit Unit) {
201 if (cxtu::isNotUsableTU(Unit)) {
202 LOG_BAD_TU(Unit);
203 return 0;
205 if (!cxtu::getASTUnit(Unit))
206 return 0;
207 return lazyCreateDiags(Unit, /*checkIfChanged=*/true)->getNumDiagnostics();
210 CXDiagnostic clang_getDiagnostic(CXTranslationUnit Unit, unsigned Index) {
211 if (cxtu::isNotUsableTU(Unit)) {
212 LOG_BAD_TU(Unit);
213 return nullptr;
216 CXDiagnosticSet D = clang_getDiagnosticSetFromTU(Unit);
217 if (!D)
218 return nullptr;
220 CXDiagnosticSetImpl *Diags = static_cast<CXDiagnosticSetImpl*>(D);
221 if (Index >= Diags->getNumDiagnostics())
222 return nullptr;
224 return Diags->getDiagnostic(Index);
227 CXDiagnosticSet clang_getDiagnosticSetFromTU(CXTranslationUnit Unit) {
228 if (cxtu::isNotUsableTU(Unit)) {
229 LOG_BAD_TU(Unit);
230 return nullptr;
232 if (!cxtu::getASTUnit(Unit))
233 return nullptr;
234 return static_cast<CXDiagnostic>(lazyCreateDiags(Unit));
237 void clang_disposeDiagnostic(CXDiagnostic Diagnostic) {
238 // No-op. Kept as a legacy API. CXDiagnostics are now managed
239 // by the enclosing CXDiagnosticSet.
242 CXString clang_formatDiagnostic(CXDiagnostic Diagnostic, unsigned Options) {
243 if (!Diagnostic)
244 return cxstring::createEmpty();
246 CXDiagnosticSeverity Severity = clang_getDiagnosticSeverity(Diagnostic);
248 SmallString<256> Str;
249 llvm::raw_svector_ostream Out(Str);
251 if (Options & CXDiagnostic_DisplaySourceLocation) {
252 // Print source location (file:line), along with optional column
253 // and source ranges.
254 CXFile File;
255 unsigned Line, Column;
256 clang_getSpellingLocation(clang_getDiagnosticLocation(Diagnostic),
257 &File, &Line, &Column, nullptr);
258 if (File) {
259 CXString FName = clang_getFileName(File);
260 Out << clang_getCString(FName) << ":" << Line << ":";
261 clang_disposeString(FName);
262 if (Options & CXDiagnostic_DisplayColumn)
263 Out << Column << ":";
265 if (Options & CXDiagnostic_DisplaySourceRanges) {
266 unsigned N = clang_getDiagnosticNumRanges(Diagnostic);
267 bool PrintedRange = false;
268 for (unsigned I = 0; I != N; ++I) {
269 CXFile StartFile, EndFile;
270 CXSourceRange Range = clang_getDiagnosticRange(Diagnostic, I);
272 unsigned StartLine, StartColumn, EndLine, EndColumn;
273 clang_getSpellingLocation(clang_getRangeStart(Range),
274 &StartFile, &StartLine, &StartColumn,
275 nullptr);
276 clang_getSpellingLocation(clang_getRangeEnd(Range),
277 &EndFile, &EndLine, &EndColumn, nullptr);
279 if (StartFile != EndFile || StartFile != File)
280 continue;
282 Out << "{" << StartLine << ":" << StartColumn << "-"
283 << EndLine << ":" << EndColumn << "}";
284 PrintedRange = true;
286 if (PrintedRange)
287 Out << ":";
290 Out << " ";
294 /* Print warning/error/etc. */
295 switch (Severity) {
296 case CXDiagnostic_Ignored: llvm_unreachable("impossible");
297 case CXDiagnostic_Note: Out << "note: "; break;
298 case CXDiagnostic_Warning: Out << "warning: "; break;
299 case CXDiagnostic_Error: Out << "error: "; break;
300 case CXDiagnostic_Fatal: Out << "fatal error: "; break;
303 CXString Text = clang_getDiagnosticSpelling(Diagnostic);
304 if (clang_getCString(Text))
305 Out << clang_getCString(Text);
306 else
307 Out << "<no diagnostic text>";
308 clang_disposeString(Text);
310 if (Options & (CXDiagnostic_DisplayOption | CXDiagnostic_DisplayCategoryId |
311 CXDiagnostic_DisplayCategoryName)) {
312 bool NeedBracket = true;
313 bool NeedComma = false;
315 if (Options & CXDiagnostic_DisplayOption) {
316 CXString OptionName = clang_getDiagnosticOption(Diagnostic, nullptr);
317 if (const char *OptionText = clang_getCString(OptionName)) {
318 if (OptionText[0]) {
319 Out << " [" << OptionText;
320 NeedBracket = false;
321 NeedComma = true;
324 clang_disposeString(OptionName);
327 if (Options & (CXDiagnostic_DisplayCategoryId |
328 CXDiagnostic_DisplayCategoryName)) {
329 if (unsigned CategoryID = clang_getDiagnosticCategory(Diagnostic)) {
330 if (Options & CXDiagnostic_DisplayCategoryId) {
331 if (NeedBracket)
332 Out << " [";
333 if (NeedComma)
334 Out << ", ";
335 Out << CategoryID;
336 NeedBracket = false;
337 NeedComma = true;
340 if (Options & CXDiagnostic_DisplayCategoryName) {
341 CXString CategoryName = clang_getDiagnosticCategoryText(Diagnostic);
342 if (NeedBracket)
343 Out << " [";
344 if (NeedComma)
345 Out << ", ";
346 Out << clang_getCString(CategoryName);
347 NeedBracket = false;
348 NeedComma = true;
349 clang_disposeString(CategoryName);
354 (void) NeedComma; // Silence dead store warning.
355 if (!NeedBracket)
356 Out << "]";
359 return cxstring::createDup(Out.str());
362 unsigned clang_defaultDiagnosticDisplayOptions() {
363 return CXDiagnostic_DisplaySourceLocation | CXDiagnostic_DisplayColumn |
364 CXDiagnostic_DisplayOption;
367 enum CXDiagnosticSeverity clang_getDiagnosticSeverity(CXDiagnostic Diag) {
368 if (CXDiagnosticImpl *D = static_cast<CXDiagnosticImpl*>(Diag))
369 return D->getSeverity();
370 return CXDiagnostic_Ignored;
373 CXSourceLocation clang_getDiagnosticLocation(CXDiagnostic Diag) {
374 if (CXDiagnosticImpl *D = static_cast<CXDiagnosticImpl*>(Diag))
375 return D->getLocation();
376 return clang_getNullLocation();
379 CXString clang_getDiagnosticSpelling(CXDiagnostic Diag) {
380 if (CXDiagnosticImpl *D = static_cast<CXDiagnosticImpl *>(Diag))
381 return D->getSpelling();
382 return cxstring::createEmpty();
385 CXString clang_getDiagnosticOption(CXDiagnostic Diag, CXString *Disable) {
386 if (Disable)
387 *Disable = cxstring::createEmpty();
389 if (CXDiagnosticImpl *D = static_cast<CXDiagnosticImpl *>(Diag))
390 return D->getDiagnosticOption(Disable);
392 return cxstring::createEmpty();
395 unsigned clang_getDiagnosticCategory(CXDiagnostic Diag) {
396 if (CXDiagnosticImpl *D = static_cast<CXDiagnosticImpl *>(Diag))
397 return D->getCategory();
398 return 0;
401 CXString clang_getDiagnosticCategoryName(unsigned Category) {
402 // Kept for backward compatibility.
403 return cxstring::createRef(DiagnosticIDs::getCategoryNameFromID(Category));
406 CXString clang_getDiagnosticCategoryText(CXDiagnostic Diag) {
407 if (CXDiagnosticImpl *D = static_cast<CXDiagnosticImpl *>(Diag))
408 return D->getCategoryText();
409 return cxstring::createEmpty();
412 unsigned clang_getDiagnosticNumRanges(CXDiagnostic Diag) {
413 if (CXDiagnosticImpl *D = static_cast<CXDiagnosticImpl *>(Diag))
414 return D->getNumRanges();
415 return 0;
418 CXSourceRange clang_getDiagnosticRange(CXDiagnostic Diag, unsigned Range) {
419 CXDiagnosticImpl *D = static_cast<CXDiagnosticImpl *>(Diag);
420 if (!D || Range >= D->getNumRanges())
421 return clang_getNullRange();
422 return D->getRange(Range);
425 unsigned clang_getDiagnosticNumFixIts(CXDiagnostic Diag) {
426 if (CXDiagnosticImpl *D = static_cast<CXDiagnosticImpl *>(Diag))
427 return D->getNumFixIts();
428 return 0;
431 CXString clang_getDiagnosticFixIt(CXDiagnostic Diag, unsigned FixIt,
432 CXSourceRange *ReplacementRange) {
433 CXDiagnosticImpl *D = static_cast<CXDiagnosticImpl *>(Diag);
434 if (!D || FixIt >= D->getNumFixIts()) {
435 if (ReplacementRange)
436 *ReplacementRange = clang_getNullRange();
437 return cxstring::createEmpty();
439 return D->getFixIt(FixIt, ReplacementRange);
442 void clang_disposeDiagnosticSet(CXDiagnosticSet Diags) {
443 if (CXDiagnosticSetImpl *D = static_cast<CXDiagnosticSetImpl *>(Diags)) {
444 if (D->isExternallyManaged())
445 delete D;
449 CXDiagnostic clang_getDiagnosticInSet(CXDiagnosticSet Diags,
450 unsigned Index) {
451 if (CXDiagnosticSetImpl *D = static_cast<CXDiagnosticSetImpl*>(Diags))
452 if (Index < D->getNumDiagnostics())
453 return D->getDiagnostic(Index);
454 return nullptr;
457 CXDiagnosticSet clang_getChildDiagnostics(CXDiagnostic Diag) {
458 if (CXDiagnosticImpl *D = static_cast<CXDiagnosticImpl *>(Diag)) {
459 CXDiagnosticSetImpl &ChildDiags = D->getChildDiagnostics();
460 return ChildDiags.empty() ? nullptr : (CXDiagnosticSet) &ChildDiags;
462 return nullptr;
465 unsigned clang_getNumDiagnosticsInSet(CXDiagnosticSet Diags) {
466 if (CXDiagnosticSetImpl *D = static_cast<CXDiagnosticSetImpl*>(Diags))
467 return D->getNumDiagnostics();
468 return 0;