[docs] Add LICENSE.txt to the root of the mono-repo
[llvm-project.git] / clang / tools / libclang / CIndexDiagnostic.cpp
blob34792d5bdfaaff0de5aad64f220717b70d7e0ede
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 = D.dyn_cast<const StoredDiagnostic*>();
96 if (!SD)
97 return;
99 if (Level != DiagnosticsEngine::Note)
100 CurrentSet = MainSet;
102 auto Owner = std::make_unique<CXStoredDiagnostic>(*SD, LangOpts);
103 CXStoredDiagnostic &CD = *Owner;
104 CurrentSet->appendDiagnostic(std::move(Owner));
106 if (Level != DiagnosticsEngine::Note)
107 CurrentSet = &CD.getChildDiagnostics();
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;
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));
126 void emitDiagnosticLoc(FullSourceLoc Loc, PresumedLoc PLoc,
127 DiagnosticsEngine::Level Level,
128 ArrayRef<CharSourceRange> Ranges) override {}
130 void emitCodeContext(FullSourceLoc Loc, DiagnosticsEngine::Level Level,
131 SmallVectorImpl<CharSourceRange> &Ranges,
132 ArrayRef<FixItHint> Hints) override {}
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));
144 CXDiagnosticSetImpl *CurrentSet;
145 CXDiagnosticSetImpl *MainSet;
149 CXDiagnosticSetImpl *cxdiag::lazyCreateDiags(CXTranslationUnit TU,
150 bool checkIfChanged) {
151 ASTUnit *AU = cxtu::getASTUnit(TU);
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:
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.
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.
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;
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);
188 for (ASTUnit::stored_diag_iterator it = AU->stored_diag_begin(),
189 ei = AU->stored_diag_end(); it != ei; ++it) {
190 Renderer.emitStoredDiagnostic(*it);
193 return static_cast<CXDiagnosticSetImpl*>(TU->Diagnostics);
196 //-----------------------------------------------------------------------------
197 // C Interface Routines
198 //-----------------------------------------------------------------------------
199 unsigned clang_getNumDiagnostics(CXTranslationUnit Unit) {
200 if (cxtu::isNotUsableTU(Unit)) {
201 LOG_BAD_TU(Unit);
202 return 0;
204 if (!cxtu::getASTUnit(Unit))
205 return 0;
206 return lazyCreateDiags(Unit, /*checkIfChanged=*/true)->getNumDiagnostics();
209 CXDiagnostic clang_getDiagnostic(CXTranslationUnit Unit, unsigned Index) {
210 if (cxtu::isNotUsableTU(Unit)) {
211 LOG_BAD_TU(Unit);
212 return nullptr;
215 CXDiagnosticSet D = clang_getDiagnosticSetFromTU(Unit);
216 if (!D)
217 return nullptr;
219 CXDiagnosticSetImpl *Diags = static_cast<CXDiagnosticSetImpl*>(D);
220 if (Index >= Diags->getNumDiagnostics())
221 return nullptr;
223 return Diags->getDiagnostic(Index);
226 CXDiagnosticSet clang_getDiagnosticSetFromTU(CXTranslationUnit Unit) {
227 if (cxtu::isNotUsableTU(Unit)) {
228 LOG_BAD_TU(Unit);
229 return nullptr;
231 if (!cxtu::getASTUnit(Unit))
232 return nullptr;
233 return static_cast<CXDiagnostic>(lazyCreateDiags(Unit));
236 void clang_disposeDiagnostic(CXDiagnostic Diagnostic) {
237 // No-op. Kept as a legacy API. CXDiagnostics are now managed
238 // by the enclosing CXDiagnosticSet.
241 CXString clang_formatDiagnostic(CXDiagnostic Diagnostic, unsigned Options) {
242 if (!Diagnostic)
243 return cxstring::createEmpty();
245 CXDiagnosticSeverity Severity = clang_getDiagnosticSeverity(Diagnostic);
247 SmallString<256> Str;
248 llvm::raw_svector_ostream Out(Str);
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 << ":";
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);
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);
278 if (StartFile != EndFile || StartFile != File)
279 continue;
281 Out << "{" << StartLine << ":" << StartColumn << "-"
282 << EndLine << ":" << EndColumn << "}";
283 PrintedRange = true;
285 if (PrintedRange)
286 Out << ":";
289 Out << " ";
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;
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);
309 if (Options & (CXDiagnostic_DisplayOption | CXDiagnostic_DisplayCategoryId |
310 CXDiagnostic_DisplayCategoryName)) {
311 bool NeedBracket = true;
312 bool NeedComma = false;
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;
323 clang_disposeString(OptionName);
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;
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);
353 (void) NeedComma; // Silence dead store warning.
354 if (!NeedBracket)
355 Out << "]";
358 return cxstring::createDup(Out.str());
361 unsigned clang_defaultDiagnosticDisplayOptions() {
362 return CXDiagnostic_DisplaySourceLocation | CXDiagnostic_DisplayColumn |
363 CXDiagnostic_DisplayOption;
366 enum CXDiagnosticSeverity clang_getDiagnosticSeverity(CXDiagnostic Diag) {
367 if (CXDiagnosticImpl *D = static_cast<CXDiagnosticImpl*>(Diag))
368 return D->getSeverity();
369 return CXDiagnostic_Ignored;
372 CXSourceLocation clang_getDiagnosticLocation(CXDiagnostic Diag) {
373 if (CXDiagnosticImpl *D = static_cast<CXDiagnosticImpl*>(Diag))
374 return D->getLocation();
375 return clang_getNullLocation();
378 CXString clang_getDiagnosticSpelling(CXDiagnostic Diag) {
379 if (CXDiagnosticImpl *D = static_cast<CXDiagnosticImpl *>(Diag))
380 return D->getSpelling();
381 return cxstring::createEmpty();
384 CXString clang_getDiagnosticOption(CXDiagnostic Diag, CXString *Disable) {
385 if (Disable)
386 *Disable = cxstring::createEmpty();
388 if (CXDiagnosticImpl *D = static_cast<CXDiagnosticImpl *>(Diag))
389 return D->getDiagnosticOption(Disable);
391 return cxstring::createEmpty();
394 unsigned clang_getDiagnosticCategory(CXDiagnostic Diag) {
395 if (CXDiagnosticImpl *D = static_cast<CXDiagnosticImpl *>(Diag))
396 return D->getCategory();
397 return 0;
400 CXString clang_getDiagnosticCategoryName(unsigned Category) {
401 // Kept for backward compatibility.
402 return cxstring::createRef(DiagnosticIDs::getCategoryNameFromID(Category));
405 CXString clang_getDiagnosticCategoryText(CXDiagnostic Diag) {
406 if (CXDiagnosticImpl *D = static_cast<CXDiagnosticImpl *>(Diag))
407 return D->getCategoryText();
408 return cxstring::createEmpty();
411 unsigned clang_getDiagnosticNumRanges(CXDiagnostic Diag) {
412 if (CXDiagnosticImpl *D = static_cast<CXDiagnosticImpl *>(Diag))
413 return D->getNumRanges();
414 return 0;
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);
424 unsigned clang_getDiagnosticNumFixIts(CXDiagnostic Diag) {
425 if (CXDiagnosticImpl *D = static_cast<CXDiagnosticImpl *>(Diag))
426 return D->getNumFixIts();
427 return 0;
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();
438 return D->getFixIt(FixIt, ReplacementRange);
441 void clang_disposeDiagnosticSet(CXDiagnosticSet Diags) {
442 if (CXDiagnosticSetImpl *D = static_cast<CXDiagnosticSetImpl *>(Diags)) {
443 if (D->isExternallyManaged())
444 delete D;
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;
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;
461 return nullptr;
464 unsigned clang_getNumDiagnosticsInSet(CXDiagnosticSet Diags) {
465 if (CXDiagnosticSetImpl *D = static_cast<CXDiagnosticSetImpl*>(Diags))
466 return D->getNumDiagnostics();
467 return 0;