1 //===- CIndexDiagnostic.cpp - Diagnostics C Interface ---------------------===//
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
7 //===----------------------------------------------------------------------===//
9 // Implements the diagnostic functions of the Clang C interface.
11 //===----------------------------------------------------------------------===//
13 #include "CIndexDiagnostic.h"
15 #include "CXTranslationUnit.h"
16 #include "CXSourceLocation.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
;
30 CXDiagnosticSetImpl::~CXDiagnosticSetImpl() {}
33 CXDiagnosticSetImpl::appendDiagnostic(std::unique_ptr
<CXDiagnosticImpl
> D
) {
34 Diagnostics
.push_back(std::move(D
));
37 CXDiagnosticImpl::~CXDiagnosticImpl() {}
40 class CXDiagnosticCustomNoteImpl
: public CXDiagnosticImpl
{
44 CXDiagnosticCustomNoteImpl(StringRef Msg
, CXSourceLocation L
)
45 : CXDiagnosticImpl(CustomNoteDiagnosticKind
), Message(std::string(Msg
)),
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
{
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
{
77 *ReplacementRange
= clang_getNullRange();
78 return cxstring::createEmpty();
82 class CXDiagnosticRenderer
: public DiagnosticNoteRenderer
{
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
*>();
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
{
118 if (Loc
.hasManager())
119 L
= translateSourceLocation(Loc
.getManager(), LangOpts
, Loc
);
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
{
136 if (Loc
.hasManager())
137 L
= translateSourceLocation(Loc
.getManager(), LangOpts
, Loc
);
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
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(),
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
)) {
204 if (!cxtu::getASTUnit(Unit
))
206 return lazyCreateDiags(Unit
, /*checkIfChanged=*/true)->getNumDiagnostics();
209 CXDiagnostic
clang_getDiagnostic(CXTranslationUnit Unit
, unsigned Index
) {
210 if (cxtu::isNotUsableTU(Unit
)) {
215 CXDiagnosticSet D
= clang_getDiagnosticSetFromTU(Unit
);
219 CXDiagnosticSetImpl
*Diags
= static_cast<CXDiagnosticSetImpl
*>(D
);
220 if (Index
>= Diags
->getNumDiagnostics())
223 return Diags
->getDiagnostic(Index
);
226 CXDiagnosticSet
clang_getDiagnosticSetFromTU(CXTranslationUnit Unit
) {
227 if (cxtu::isNotUsableTU(Unit
)) {
231 if (!cxtu::getASTUnit(Unit
))
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
) {
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.
254 unsigned Line
, Column
;
255 clang_getSpellingLocation(clang_getDiagnosticLocation(Diagnostic
),
256 &File
, &Line
, &Column
, nullptr);
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
,
275 clang_getSpellingLocation(clang_getRangeEnd(Range
),
276 &EndFile
, &EndLine
, &EndColumn
, nullptr);
278 if (StartFile
!= EndFile
|| StartFile
!= File
)
281 Out
<< "{" << StartLine
<< ":" << StartColumn
<< "-"
282 << EndLine
<< ":" << EndColumn
<< "}";
293 /* Print warning/error/etc. */
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
);
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
)) {
318 Out
<< " [" << OptionText
;
323 clang_disposeString(OptionName
);
326 if (Options
& (CXDiagnostic_DisplayCategoryId
|
327 CXDiagnostic_DisplayCategoryName
)) {
328 if (unsigned CategoryID
= clang_getDiagnosticCategory(Diagnostic
)) {
329 if (Options
& CXDiagnostic_DisplayCategoryId
) {
339 if (Options
& CXDiagnostic_DisplayCategoryName
) {
340 CXString CategoryName
= clang_getDiagnosticCategoryText(Diagnostic
);
345 Out
<< clang_getCString(CategoryName
);
348 clang_disposeString(CategoryName
);
353 (void) NeedComma
; // Silence dead store warning.
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
) {
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();
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();
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();
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())
448 CXDiagnostic
clang_getDiagnosticInSet(CXDiagnosticSet Diags
,
450 if (CXDiagnosticSetImpl
*D
= static_cast<CXDiagnosticSetImpl
*>(Diags
))
451 if (Index
< D
->getNumDiagnostics())
452 return D
->getDiagnostic(Index
);
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
;
464 unsigned clang_getNumDiagnosticsInSet(CXDiagnosticSet Diags
) {
465 if (CXDiagnosticSetImpl
*D
= static_cast<CXDiagnosticSetImpl
*>(Diags
))
466 return D
->getNumDiagnostics();