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
=
96 dyn_cast_if_present
<const StoredDiagnostic
*>(D
);
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
{
119 if (Loc
.hasManager())
120 L
= translateSourceLocation(Loc
.getManager(), LangOpts
, Loc
);
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
{
137 if (Loc
.hasManager())
138 L
= translateSourceLocation(Loc
.getManager(), LangOpts
, Loc
);
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
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(),
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
)) {
205 if (!cxtu::getASTUnit(Unit
))
207 return lazyCreateDiags(Unit
, /*checkIfChanged=*/true)->getNumDiagnostics();
210 CXDiagnostic
clang_getDiagnostic(CXTranslationUnit Unit
, unsigned Index
) {
211 if (cxtu::isNotUsableTU(Unit
)) {
216 CXDiagnosticSet D
= clang_getDiagnosticSetFromTU(Unit
);
220 CXDiagnosticSetImpl
*Diags
= static_cast<CXDiagnosticSetImpl
*>(D
);
221 if (Index
>= Diags
->getNumDiagnostics())
224 return Diags
->getDiagnostic(Index
);
227 CXDiagnosticSet
clang_getDiagnosticSetFromTU(CXTranslationUnit Unit
) {
228 if (cxtu::isNotUsableTU(Unit
)) {
232 if (!cxtu::getASTUnit(Unit
))
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
) {
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.
255 unsigned Line
, Column
;
256 clang_getSpellingLocation(clang_getDiagnosticLocation(Diagnostic
),
257 &File
, &Line
, &Column
, nullptr);
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
,
276 clang_getSpellingLocation(clang_getRangeEnd(Range
),
277 &EndFile
, &EndLine
, &EndColumn
, nullptr);
279 if (StartFile
!= EndFile
|| StartFile
!= File
)
282 Out
<< "{" << StartLine
<< ":" << StartColumn
<< "-"
283 << EndLine
<< ":" << EndColumn
<< "}";
294 /* Print warning/error/etc. */
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
);
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
)) {
319 Out
<< " [" << OptionText
;
324 clang_disposeString(OptionName
);
327 if (Options
& (CXDiagnostic_DisplayCategoryId
|
328 CXDiagnostic_DisplayCategoryName
)) {
329 if (unsigned CategoryID
= clang_getDiagnosticCategory(Diagnostic
)) {
330 if (Options
& CXDiagnostic_DisplayCategoryId
) {
340 if (Options
& CXDiagnostic_DisplayCategoryName
) {
341 CXString CategoryName
= clang_getDiagnosticCategoryText(Diagnostic
);
346 Out
<< clang_getCString(CategoryName
);
349 clang_disposeString(CategoryName
);
354 (void) NeedComma
; // Silence dead store warning.
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
) {
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();
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();
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();
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())
449 CXDiagnostic
clang_getDiagnosticInSet(CXDiagnosticSet Diags
,
451 if (CXDiagnosticSetImpl
*D
= static_cast<CXDiagnosticSetImpl
*>(Diags
))
452 if (Index
< D
->getNumDiagnostics())
453 return D
->getDiagnostic(Index
);
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
;
465 unsigned clang_getNumDiagnosticsInSet(CXDiagnosticSet Diags
) {
466 if (CXDiagnosticSetImpl
*D
= static_cast<CXDiagnosticSetImpl
*>(Diags
))
467 return D
->getNumDiagnostics();