xref: /llvm-project/clang/tools/libclang/CIndexDiagnostic.cpp (revision 563c7c5539f05e7f8cbb42565c1f24466019f38b)
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 
30 CXDiagnosticSetImpl::~CXDiagnosticSetImpl() {}
31 
32 void
33 CXDiagnosticSetImpl::appendDiagnostic(std::unique_ptr<CXDiagnosticImpl> D) {
34   Diagnostics.push_back(std::move(D));
35 }
36 
37 CXDiagnosticImpl::~CXDiagnosticImpl() {}
38 
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) {}
47 
48   ~CXDiagnosticCustomNoteImpl() override {}
49 
50   CXDiagnosticSeverity getSeverity() const override {
51     return CXDiagnostic_Note;
52   }
53 
54   CXSourceLocation getLocation() const override { return Loc; }
55 
56   CXString getSpelling() const override {
57     return cxstring::createRef(Message.c_str());
58   }
59 
60   CXString getDiagnosticOption(CXString *Disable) const override {
61     if (Disable)
62       *Disable = cxstring::createEmpty();
63     return cxstring::createEmpty();
64   }
65 
66   unsigned getCategory() const override { return 0; }
67   CXString getCategoryText() const override { return cxstring::createEmpty(); }
68 
69   unsigned getNumRanges() const override { return 0; }
70   CXSourceRange getRange(unsigned Range) const override {
71     return clang_getNullRange();
72   }
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();
79   }
80 };
81 
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) {}
89 
90   ~CXDiagnosticRenderer() override {}
91 
92   void beginDiagnostic(DiagOrStoredDiag D,
93                        DiagnosticsEngine::Level Level) override {
94 
95     const StoredDiagnostic *SD =
96         dyn_cast_if_present<const StoredDiagnostic *>(D);
97     if (!SD)
98       return;
99 
100     if (Level != DiagnosticsEngine::Note)
101       CurrentSet = MainSet;
102 
103     auto Owner = std::make_unique<CXStoredDiagnostic>(*SD, LangOpts);
104     CXStoredDiagnostic &CD = *Owner;
105     CurrentSet->appendDiagnostic(std::move(Owner));
106 
107     if (Level != DiagnosticsEngine::Note)
108       CurrentSet = &CD.getChildDiagnostics();
109   }
110 
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;
117 
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));
125   }
126 
127   void emitDiagnosticLoc(FullSourceLoc Loc, PresumedLoc PLoc,
128                          DiagnosticsEngine::Level Level,
129                          ArrayRef<CharSourceRange> Ranges) override {}
130 
131   void emitCodeContext(FullSourceLoc Loc, DiagnosticsEngine::Level Level,
132                        SmallVectorImpl<CharSourceRange> &Ranges,
133                        ArrayRef<FixItHint> Hints) override {}
134 
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));
143   }
144 
145   CXDiagnosticSetImpl *CurrentSet;
146   CXDiagnosticSetImpl *MainSet;
147 };
148 }
149 
150 CXDiagnosticSetImpl *cxdiag::lazyCreateDiags(CXTranslationUnit TU,
151                                              bool checkIfChanged) {
152   ASTUnit *AU = cxtu::getASTUnit(TU);
153 
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:
159     //
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.
168     //
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.
171 
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;
179     }
180   }
181 
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);
188 
189     for (ASTUnit::stored_diag_iterator it = AU->stored_diag_begin(),
190          ei = AU->stored_diag_end(); it != ei; ++it) {
191       Renderer.emitStoredDiagnostic(*it);
192     }
193   }
194   return static_cast<CXDiagnosticSetImpl*>(TU->Diagnostics);
195 }
196 
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;
204   }
205   if (!cxtu::getASTUnit(Unit))
206     return 0;
207   return lazyCreateDiags(Unit, /*checkIfChanged=*/true)->getNumDiagnostics();
208 }
209 
210 CXDiagnostic clang_getDiagnostic(CXTranslationUnit Unit, unsigned Index) {
211   if (cxtu::isNotUsableTU(Unit)) {
212     LOG_BAD_TU(Unit);
213     return nullptr;
214   }
215 
216   CXDiagnosticSet D = clang_getDiagnosticSetFromTU(Unit);
217   if (!D)
218     return nullptr;
219 
220   CXDiagnosticSetImpl *Diags = static_cast<CXDiagnosticSetImpl*>(D);
221   if (Index >= Diags->getNumDiagnostics())
222     return nullptr;
223 
224   return Diags->getDiagnostic(Index);
225 }
226 
227 CXDiagnosticSet clang_getDiagnosticSetFromTU(CXTranslationUnit Unit) {
228   if (cxtu::isNotUsableTU(Unit)) {
229     LOG_BAD_TU(Unit);
230     return nullptr;
231   }
232   if (!cxtu::getASTUnit(Unit))
233     return nullptr;
234   return static_cast<CXDiagnostic>(lazyCreateDiags(Unit));
235 }
236 
237 void clang_disposeDiagnostic(CXDiagnostic Diagnostic) {
238   // No-op.  Kept as a legacy API.  CXDiagnostics are now managed
239   // by the enclosing CXDiagnosticSet.
240 }
241 
242 CXString clang_formatDiagnostic(CXDiagnostic Diagnostic, unsigned Options) {
243   if (!Diagnostic)
244     return cxstring::createEmpty();
245 
246   CXDiagnosticSeverity Severity = clang_getDiagnosticSeverity(Diagnostic);
247 
248   SmallString<256> Str;
249   llvm::raw_svector_ostream Out(Str);
250 
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 << ":";
264 
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);
271 
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);
278 
279           if (StartFile != EndFile || StartFile != File)
280             continue;
281 
282           Out << "{" << StartLine << ":" << StartColumn << "-"
283               << EndLine << ":" << EndColumn << "}";
284           PrintedRange = true;
285         }
286         if (PrintedRange)
287           Out << ":";
288       }
289 
290       Out << " ";
291     }
292   }
293 
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;
301   }
302 
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);
309 
310   if (Options & (CXDiagnostic_DisplayOption | CXDiagnostic_DisplayCategoryId |
311                  CXDiagnostic_DisplayCategoryName)) {
312     bool NeedBracket = true;
313     bool NeedComma = false;
314 
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;
322         }
323       }
324       clang_disposeString(OptionName);
325     }
326 
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;
338         }
339 
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);
350         }
351       }
352     }
353 
354     (void) NeedComma; // Silence dead store warning.
355     if (!NeedBracket)
356       Out << "]";
357   }
358 
359   return cxstring::createDup(Out.str());
360 }
361 
362 unsigned clang_defaultDiagnosticDisplayOptions() {
363   return CXDiagnostic_DisplaySourceLocation | CXDiagnostic_DisplayColumn |
364          CXDiagnostic_DisplayOption;
365 }
366 
367 enum CXDiagnosticSeverity clang_getDiagnosticSeverity(CXDiagnostic Diag) {
368   if (CXDiagnosticImpl *D = static_cast<CXDiagnosticImpl*>(Diag))
369     return D->getSeverity();
370   return CXDiagnostic_Ignored;
371 }
372 
373 CXSourceLocation clang_getDiagnosticLocation(CXDiagnostic Diag) {
374   if (CXDiagnosticImpl *D = static_cast<CXDiagnosticImpl*>(Diag))
375     return D->getLocation();
376   return clang_getNullLocation();
377 }
378 
379 CXString clang_getDiagnosticSpelling(CXDiagnostic Diag) {
380   if (CXDiagnosticImpl *D = static_cast<CXDiagnosticImpl *>(Diag))
381     return D->getSpelling();
382   return cxstring::createEmpty();
383 }
384 
385 CXString clang_getDiagnosticOption(CXDiagnostic Diag, CXString *Disable) {
386   if (Disable)
387     *Disable = cxstring::createEmpty();
388 
389   if (CXDiagnosticImpl *D = static_cast<CXDiagnosticImpl *>(Diag))
390     return D->getDiagnosticOption(Disable);
391 
392   return cxstring::createEmpty();
393 }
394 
395 unsigned clang_getDiagnosticCategory(CXDiagnostic Diag) {
396   if (CXDiagnosticImpl *D = static_cast<CXDiagnosticImpl *>(Diag))
397     return D->getCategory();
398   return 0;
399 }
400 
401 CXString clang_getDiagnosticCategoryName(unsigned Category) {
402   // Kept for backward compatibility.
403   return cxstring::createRef(DiagnosticIDs::getCategoryNameFromID(Category));
404 }
405 
406 CXString clang_getDiagnosticCategoryText(CXDiagnostic Diag) {
407   if (CXDiagnosticImpl *D = static_cast<CXDiagnosticImpl *>(Diag))
408     return D->getCategoryText();
409   return cxstring::createEmpty();
410 }
411 
412 unsigned clang_getDiagnosticNumRanges(CXDiagnostic Diag) {
413   if (CXDiagnosticImpl *D = static_cast<CXDiagnosticImpl *>(Diag))
414     return D->getNumRanges();
415   return 0;
416 }
417 
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);
423 }
424 
425 unsigned clang_getDiagnosticNumFixIts(CXDiagnostic Diag) {
426   if (CXDiagnosticImpl *D = static_cast<CXDiagnosticImpl *>(Diag))
427     return D->getNumFixIts();
428   return 0;
429 }
430 
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();
438   }
439   return D->getFixIt(FixIt, ReplacementRange);
440 }
441 
442 void clang_disposeDiagnosticSet(CXDiagnosticSet Diags) {
443   if (CXDiagnosticSetImpl *D = static_cast<CXDiagnosticSetImpl *>(Diags)) {
444     if (D->isExternallyManaged())
445       delete D;
446   }
447 }
448 
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;
455 }
456 
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;
461   }
462   return nullptr;
463 }
464 
465 unsigned clang_getNumDiagnosticsInSet(CXDiagnosticSet Diags) {
466   if (CXDiagnosticSetImpl *D = static_cast<CXDiagnosticSetImpl*>(Diags))
467     return D->getNumDiagnostics();
468   return 0;
469 }
470