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 
~CXDiagnosticSetImpl()30 CXDiagnosticSetImpl::~CXDiagnosticSetImpl() {}
31 
32 void
appendDiagnostic(std::unique_ptr<CXDiagnosticImpl> D)33 CXDiagnosticSetImpl::appendDiagnostic(std::unique_ptr<CXDiagnosticImpl> D) {
34   Diagnostics.push_back(std::move(D));
35 }
36 
~CXDiagnosticImpl()37 CXDiagnosticImpl::~CXDiagnosticImpl() {}
38 
39 namespace {
40 class CXDiagnosticCustomNoteImpl : public CXDiagnosticImpl {
41   std::string Message;
42   CXSourceLocation Loc;
43 public:
CXDiagnosticCustomNoteImpl(StringRef Msg,CXSourceLocation L)44   CXDiagnosticCustomNoteImpl(StringRef Msg, CXSourceLocation L)
45       : CXDiagnosticImpl(CustomNoteDiagnosticKind), Message(std::string(Msg)),
46         Loc(L) {}
47 
~CXDiagnosticCustomNoteImpl()48   ~CXDiagnosticCustomNoteImpl() override {}
49 
getSeverity() const50   CXDiagnosticSeverity getSeverity() const override {
51     return CXDiagnostic_Note;
52   }
53 
getLocation() const54   CXSourceLocation getLocation() const override { return Loc; }
55 
getSpelling() const56   CXString getSpelling() const override {
57     return cxstring::createRef(Message.c_str());
58   }
59 
getDiagnosticOption(CXString * Disable) const60   CXString getDiagnosticOption(CXString *Disable) const override {
61     if (Disable)
62       *Disable = cxstring::createEmpty();
63     return cxstring::createEmpty();
64   }
65 
getCategory() const66   unsigned getCategory() const override { return 0; }
getCategoryText() const67   CXString getCategoryText() const override { return cxstring::createEmpty(); }
68 
getNumRanges() const69   unsigned getNumRanges() const override { return 0; }
getRange(unsigned Range) const70   CXSourceRange getRange(unsigned Range) const override {
71     return clang_getNullRange();
72   }
getNumFixIts() const73   unsigned getNumFixIts() const override { return 0; }
getFixIt(unsigned FixIt,CXSourceRange * ReplacementRange) const74   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:
CXDiagnosticRenderer(const LangOptions & LangOpts,DiagnosticOptions * DiagOpts,CXDiagnosticSetImpl * mainSet)84   CXDiagnosticRenderer(const LangOptions &LangOpts,
85                        DiagnosticOptions *DiagOpts,
86                        CXDiagnosticSetImpl *mainSet)
87   : DiagnosticNoteRenderer(LangOpts, DiagOpts),
88     CurrentSet(mainSet), MainSet(mainSet) {}
89 
~CXDiagnosticRenderer()90   ~CXDiagnosticRenderer() override {}
91 
beginDiagnostic(DiagOrStoredDiag D,DiagnosticsEngine::Level Level)92   void beginDiagnostic(DiagOrStoredDiag D,
93                        DiagnosticsEngine::Level Level) override {
94 
95     const StoredDiagnostic *SD = D.dyn_cast<const StoredDiagnostic*>();
96     if (!SD)
97       return;
98 
99     if (Level != DiagnosticsEngine::Note)
100       CurrentSet = MainSet;
101 
102     auto Owner = std::make_unique<CXStoredDiagnostic>(*SD, LangOpts);
103     CXStoredDiagnostic &CD = *Owner;
104     CurrentSet->appendDiagnostic(std::move(Owner));
105 
106     if (Level != DiagnosticsEngine::Note)
107       CurrentSet = &CD.getChildDiagnostics();
108   }
109 
emitDiagnosticMessage(FullSourceLoc Loc,PresumedLoc PLoc,DiagnosticsEngine::Level Level,StringRef Message,ArrayRef<CharSourceRange> Ranges,DiagOrStoredDiag D)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;
116 
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));
124   }
125 
emitDiagnosticLoc(FullSourceLoc Loc,PresumedLoc PLoc,DiagnosticsEngine::Level Level,ArrayRef<CharSourceRange> Ranges)126   void emitDiagnosticLoc(FullSourceLoc Loc, PresumedLoc PLoc,
127                          DiagnosticsEngine::Level Level,
128                          ArrayRef<CharSourceRange> Ranges) override {}
129 
emitCodeContext(FullSourceLoc Loc,DiagnosticsEngine::Level Level,SmallVectorImpl<CharSourceRange> & Ranges,ArrayRef<FixItHint> Hints)130   void emitCodeContext(FullSourceLoc Loc, DiagnosticsEngine::Level Level,
131                        SmallVectorImpl<CharSourceRange> &Ranges,
132                        ArrayRef<FixItHint> Hints) override {}
133 
emitNote(FullSourceLoc Loc,StringRef Message)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));
142   }
143 
144   CXDiagnosticSetImpl *CurrentSet;
145   CXDiagnosticSetImpl *MainSet;
146 };
147 }
148 
lazyCreateDiags(CXTranslationUnit TU,bool checkIfChanged)149 CXDiagnosticSetImpl *cxdiag::lazyCreateDiags(CXTranslationUnit TU,
150                                              bool checkIfChanged) {
151   ASTUnit *AU = cxtu::getASTUnit(TU);
152 
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:
158     //
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.
167     //
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.
170 
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;
178     }
179   }
180 
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);
187 
188     for (ASTUnit::stored_diag_iterator it = AU->stored_diag_begin(),
189          ei = AU->stored_diag_end(); it != ei; ++it) {
190       Renderer.emitStoredDiagnostic(*it);
191     }
192   }
193   return static_cast<CXDiagnosticSetImpl*>(TU->Diagnostics);
194 }
195 
196 //-----------------------------------------------------------------------------
197 // C Interface Routines
198 //-----------------------------------------------------------------------------
clang_getNumDiagnostics(CXTranslationUnit Unit)199 unsigned clang_getNumDiagnostics(CXTranslationUnit Unit) {
200   if (cxtu::isNotUsableTU(Unit)) {
201     LOG_BAD_TU(Unit);
202     return 0;
203   }
204   if (!cxtu::getASTUnit(Unit))
205     return 0;
206   return lazyCreateDiags(Unit, /*checkIfChanged=*/true)->getNumDiagnostics();
207 }
208 
clang_getDiagnostic(CXTranslationUnit Unit,unsigned Index)209 CXDiagnostic clang_getDiagnostic(CXTranslationUnit Unit, unsigned Index) {
210   if (cxtu::isNotUsableTU(Unit)) {
211     LOG_BAD_TU(Unit);
212     return nullptr;
213   }
214 
215   CXDiagnosticSet D = clang_getDiagnosticSetFromTU(Unit);
216   if (!D)
217     return nullptr;
218 
219   CXDiagnosticSetImpl *Diags = static_cast<CXDiagnosticSetImpl*>(D);
220   if (Index >= Diags->getNumDiagnostics())
221     return nullptr;
222 
223   return Diags->getDiagnostic(Index);
224 }
225 
clang_getDiagnosticSetFromTU(CXTranslationUnit Unit)226 CXDiagnosticSet clang_getDiagnosticSetFromTU(CXTranslationUnit Unit) {
227   if (cxtu::isNotUsableTU(Unit)) {
228     LOG_BAD_TU(Unit);
229     return nullptr;
230   }
231   if (!cxtu::getASTUnit(Unit))
232     return nullptr;
233   return static_cast<CXDiagnostic>(lazyCreateDiags(Unit));
234 }
235 
clang_disposeDiagnostic(CXDiagnostic Diagnostic)236 void clang_disposeDiagnostic(CXDiagnostic Diagnostic) {
237   // No-op.  Kept as a legacy API.  CXDiagnostics are now managed
238   // by the enclosing CXDiagnosticSet.
239 }
240 
clang_formatDiagnostic(CXDiagnostic Diagnostic,unsigned Options)241 CXString clang_formatDiagnostic(CXDiagnostic Diagnostic, unsigned Options) {
242   if (!Diagnostic)
243     return cxstring::createEmpty();
244 
245   CXDiagnosticSeverity Severity = clang_getDiagnosticSeverity(Diagnostic);
246 
247   SmallString<256> Str;
248   llvm::raw_svector_ostream Out(Str);
249 
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 << ":";
263 
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);
270 
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);
277 
278           if (StartFile != EndFile || StartFile != File)
279             continue;
280 
281           Out << "{" << StartLine << ":" << StartColumn << "-"
282               << EndLine << ":" << EndColumn << "}";
283           PrintedRange = true;
284         }
285         if (PrintedRange)
286           Out << ":";
287       }
288 
289       Out << " ";
290     }
291   }
292 
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;
300   }
301 
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);
308 
309   if (Options & (CXDiagnostic_DisplayOption | CXDiagnostic_DisplayCategoryId |
310                  CXDiagnostic_DisplayCategoryName)) {
311     bool NeedBracket = true;
312     bool NeedComma = false;
313 
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;
321         }
322       }
323       clang_disposeString(OptionName);
324     }
325 
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;
337         }
338 
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);
349         }
350       }
351     }
352 
353     (void) NeedComma; // Silence dead store warning.
354     if (!NeedBracket)
355       Out << "]";
356   }
357 
358   return cxstring::createDup(Out.str());
359 }
360 
clang_defaultDiagnosticDisplayOptions()361 unsigned clang_defaultDiagnosticDisplayOptions() {
362   return CXDiagnostic_DisplaySourceLocation | CXDiagnostic_DisplayColumn |
363          CXDiagnostic_DisplayOption;
364 }
365 
clang_getDiagnosticSeverity(CXDiagnostic Diag)366 enum CXDiagnosticSeverity clang_getDiagnosticSeverity(CXDiagnostic Diag) {
367   if (CXDiagnosticImpl *D = static_cast<CXDiagnosticImpl*>(Diag))
368     return D->getSeverity();
369   return CXDiagnostic_Ignored;
370 }
371 
clang_getDiagnosticLocation(CXDiagnostic Diag)372 CXSourceLocation clang_getDiagnosticLocation(CXDiagnostic Diag) {
373   if (CXDiagnosticImpl *D = static_cast<CXDiagnosticImpl*>(Diag))
374     return D->getLocation();
375   return clang_getNullLocation();
376 }
377 
clang_getDiagnosticSpelling(CXDiagnostic Diag)378 CXString clang_getDiagnosticSpelling(CXDiagnostic Diag) {
379   if (CXDiagnosticImpl *D = static_cast<CXDiagnosticImpl *>(Diag))
380     return D->getSpelling();
381   return cxstring::createEmpty();
382 }
383 
clang_getDiagnosticOption(CXDiagnostic Diag,CXString * Disable)384 CXString clang_getDiagnosticOption(CXDiagnostic Diag, CXString *Disable) {
385   if (Disable)
386     *Disable = cxstring::createEmpty();
387 
388   if (CXDiagnosticImpl *D = static_cast<CXDiagnosticImpl *>(Diag))
389     return D->getDiagnosticOption(Disable);
390 
391   return cxstring::createEmpty();
392 }
393 
clang_getDiagnosticCategory(CXDiagnostic Diag)394 unsigned clang_getDiagnosticCategory(CXDiagnostic Diag) {
395   if (CXDiagnosticImpl *D = static_cast<CXDiagnosticImpl *>(Diag))
396     return D->getCategory();
397   return 0;
398 }
399 
clang_getDiagnosticCategoryName(unsigned Category)400 CXString clang_getDiagnosticCategoryName(unsigned Category) {
401   // Kept for backward compatibility.
402   return cxstring::createRef(DiagnosticIDs::getCategoryNameFromID(Category));
403 }
404 
clang_getDiagnosticCategoryText(CXDiagnostic Diag)405 CXString clang_getDiagnosticCategoryText(CXDiagnostic Diag) {
406   if (CXDiagnosticImpl *D = static_cast<CXDiagnosticImpl *>(Diag))
407     return D->getCategoryText();
408   return cxstring::createEmpty();
409 }
410 
clang_getDiagnosticNumRanges(CXDiagnostic Diag)411 unsigned clang_getDiagnosticNumRanges(CXDiagnostic Diag) {
412   if (CXDiagnosticImpl *D = static_cast<CXDiagnosticImpl *>(Diag))
413     return D->getNumRanges();
414   return 0;
415 }
416 
clang_getDiagnosticRange(CXDiagnostic Diag,unsigned Range)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);
422 }
423 
clang_getDiagnosticNumFixIts(CXDiagnostic Diag)424 unsigned clang_getDiagnosticNumFixIts(CXDiagnostic Diag) {
425   if (CXDiagnosticImpl *D = static_cast<CXDiagnosticImpl *>(Diag))
426     return D->getNumFixIts();
427   return 0;
428 }
429 
clang_getDiagnosticFixIt(CXDiagnostic Diag,unsigned FixIt,CXSourceRange * ReplacementRange)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();
437   }
438   return D->getFixIt(FixIt, ReplacementRange);
439 }
440 
clang_disposeDiagnosticSet(CXDiagnosticSet Diags)441 void clang_disposeDiagnosticSet(CXDiagnosticSet Diags) {
442   if (CXDiagnosticSetImpl *D = static_cast<CXDiagnosticSetImpl *>(Diags)) {
443     if (D->isExternallyManaged())
444       delete D;
445   }
446 }
447 
clang_getDiagnosticInSet(CXDiagnosticSet Diags,unsigned Index)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;
454 }
455 
clang_getChildDiagnostics(CXDiagnostic Diag)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;
460   }
461   return nullptr;
462 }
463 
clang_getNumDiagnosticsInSet(CXDiagnosticSet Diags)464 unsigned clang_getNumDiagnosticsInSet(CXDiagnosticSet Diags) {
465   if (CXDiagnosticSetImpl *D = static_cast<CXDiagnosticSetImpl*>(Diags))
466     return D->getNumDiagnostics();
467   return 0;
468 }
469