1 /*===-- CIndexDiagnostics.cpp - Diagnostics C Interface ---------*- C++ -*-===*\
2 |*                                                                            *|
3 |*                     The LLVM Compiler Infrastructure                       *|
4 |*                                                                            *|
5 |* This file is distributed under the University of Illinois Open Source      *|
6 |* License. See LICENSE.TXT for details.                                      *|
7 |*                                                                            *|
8 |*===----------------------------------------------------------------------===*|
9 |*                                                                            *|
10 |* Implements the diagnostic functions of the Clang C interface.              *|
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/Frontend/ASTUnit.h"
20 #include "clang/Frontend/FrontendDiagnostic.h"
21 #include "clang/Frontend/DiagnosticRenderer.h"
22 #include "clang/Basic/DiagnosticOptions.h"
23 #include "llvm/ADT/SmallString.h"
24 #include "llvm/ADT/Twine.h"
25 #include "llvm/Support/MemoryBuffer.h"
26 #include "llvm/Support/raw_ostream.h"
27 
28 using namespace clang;
29 using namespace clang::cxloc;
30 using namespace clang::cxdiag;
31 using namespace llvm;
32 
~CXDiagnosticSetImpl()33 CXDiagnosticSetImpl::~CXDiagnosticSetImpl() {}
34 
35 void
appendDiagnostic(std::unique_ptr<CXDiagnosticImpl> D)36 CXDiagnosticSetImpl::appendDiagnostic(std::unique_ptr<CXDiagnosticImpl> D) {
37   Diagnostics.push_back(std::move(D));
38 }
39 
~CXDiagnosticImpl()40 CXDiagnosticImpl::~CXDiagnosticImpl() {}
41 
42 namespace {
43 class CXDiagnosticCustomNoteImpl : public CXDiagnosticImpl {
44   std::string Message;
45   CXSourceLocation Loc;
46 public:
CXDiagnosticCustomNoteImpl(StringRef Msg,CXSourceLocation L)47   CXDiagnosticCustomNoteImpl(StringRef Msg, CXSourceLocation L)
48     : CXDiagnosticImpl(CustomNoteDiagnosticKind),
49       Message(Msg), Loc(L) {}
50 
~CXDiagnosticCustomNoteImpl()51   virtual ~CXDiagnosticCustomNoteImpl() {}
52 
getSeverity() const53   CXDiagnosticSeverity getSeverity() const override {
54     return CXDiagnostic_Note;
55   }
56 
getLocation() const57   CXSourceLocation getLocation() const override {
58     return Loc;
59   }
60 
getSpelling() const61   CXString getSpelling() const override {
62     return cxstring::createRef(Message.c_str());
63   }
64 
getDiagnosticOption(CXString * Disable) const65   CXString getDiagnosticOption(CXString *Disable) const override {
66     if (Disable)
67       *Disable = cxstring::createEmpty();
68     return cxstring::createEmpty();
69   }
70 
getCategory() const71   unsigned getCategory() const override { return 0; }
getCategoryText() const72   CXString getCategoryText() const override { return cxstring::createEmpty(); }
73 
getNumRanges() const74   unsigned getNumRanges() const override { return 0; }
getRange(unsigned Range) const75   CXSourceRange getRange(unsigned Range) const override {
76     return clang_getNullRange();
77   }
getNumFixIts() const78   unsigned getNumFixIts() const override { return 0; }
getFixIt(unsigned FixIt,CXSourceRange * ReplacementRange) const79   CXString getFixIt(unsigned FixIt,
80                     CXSourceRange *ReplacementRange) const override {
81     if (ReplacementRange)
82       *ReplacementRange = clang_getNullRange();
83     return cxstring::createEmpty();
84   }
85 };
86 
87 class CXDiagnosticRenderer : public DiagnosticNoteRenderer {
88 public:
CXDiagnosticRenderer(const LangOptions & LangOpts,DiagnosticOptions * DiagOpts,CXDiagnosticSetImpl * mainSet)89   CXDiagnosticRenderer(const LangOptions &LangOpts,
90                        DiagnosticOptions *DiagOpts,
91                        CXDiagnosticSetImpl *mainSet)
92   : DiagnosticNoteRenderer(LangOpts, DiagOpts),
93     CurrentSet(mainSet), MainSet(mainSet) {}
94 
~CXDiagnosticRenderer()95   virtual ~CXDiagnosticRenderer() {}
96 
beginDiagnostic(DiagOrStoredDiag D,DiagnosticsEngine::Level Level)97   void beginDiagnostic(DiagOrStoredDiag D,
98                        DiagnosticsEngine::Level Level) override {
99 
100     const StoredDiagnostic *SD = D.dyn_cast<const StoredDiagnostic*>();
101     if (!SD)
102       return;
103 
104     if (Level != DiagnosticsEngine::Note)
105       CurrentSet = MainSet;
106 
107     auto Owner = llvm::make_unique<CXStoredDiagnostic>(*SD, LangOpts);
108     CXStoredDiagnostic &CD = *Owner;
109     CurrentSet->appendDiagnostic(std::move(Owner));
110 
111     if (Level != DiagnosticsEngine::Note)
112       CurrentSet = &CD.getChildDiagnostics();
113   }
114 
emitDiagnosticMessage(SourceLocation Loc,PresumedLoc PLoc,DiagnosticsEngine::Level Level,StringRef Message,ArrayRef<CharSourceRange> Ranges,const SourceManager * SM,DiagOrStoredDiag D)115   void emitDiagnosticMessage(SourceLocation Loc, PresumedLoc PLoc,
116                              DiagnosticsEngine::Level Level,
117                              StringRef Message,
118                              ArrayRef<CharSourceRange> Ranges,
119                              const SourceManager *SM,
120                              DiagOrStoredDiag D) override {
121     if (!D.isNull())
122       return;
123 
124     CXSourceLocation L;
125     if (SM)
126       L = translateSourceLocation(*SM, LangOpts, Loc);
127     else
128       L = clang_getNullLocation();
129     CurrentSet->appendDiagnostic(
130         llvm::make_unique<CXDiagnosticCustomNoteImpl>(Message, L));
131   }
132 
emitDiagnosticLoc(SourceLocation Loc,PresumedLoc PLoc,DiagnosticsEngine::Level Level,ArrayRef<CharSourceRange> Ranges,const SourceManager & SM)133   void emitDiagnosticLoc(SourceLocation Loc, PresumedLoc PLoc,
134                          DiagnosticsEngine::Level Level,
135                          ArrayRef<CharSourceRange> Ranges,
136                          const SourceManager &SM) override {}
137 
emitCodeContext(SourceLocation Loc,DiagnosticsEngine::Level Level,SmallVectorImpl<CharSourceRange> & Ranges,ArrayRef<FixItHint> Hints,const SourceManager & SM)138   void emitCodeContext(SourceLocation Loc,
139                        DiagnosticsEngine::Level Level,
140                        SmallVectorImpl<CharSourceRange>& Ranges,
141                        ArrayRef<FixItHint> Hints,
142                        const SourceManager &SM) override {}
143 
emitNote(SourceLocation Loc,StringRef Message,const SourceManager * SM)144   void emitNote(SourceLocation Loc, StringRef Message,
145                 const SourceManager *SM) override {
146     CXSourceLocation L;
147     if (SM)
148       L = translateSourceLocation(*SM, LangOpts, Loc);
149     else
150       L = clang_getNullLocation();
151     CurrentSet->appendDiagnostic(
152         llvm::make_unique<CXDiagnosticCustomNoteImpl>(Message, L));
153   }
154 
155   CXDiagnosticSetImpl *CurrentSet;
156   CXDiagnosticSetImpl *MainSet;
157 };
158 }
159 
lazyCreateDiags(CXTranslationUnit TU,bool checkIfChanged)160 CXDiagnosticSetImpl *cxdiag::lazyCreateDiags(CXTranslationUnit TU,
161                                              bool checkIfChanged) {
162   ASTUnit *AU = cxtu::getASTUnit(TU);
163 
164   if (TU->Diagnostics && checkIfChanged) {
165     // In normal use, ASTUnit's diagnostics should not change unless we reparse.
166     // Currently they can only change by using the internal testing flag
167     // '-error-on-deserialized-decl' which will error during deserialization of
168     // a declaration. What will happen is:
169     //
170     //  -c-index-test gets a CXTranslationUnit
171     //  -checks the diagnostics, the diagnostics set is lazily created,
172     //     no errors are reported
173     //  -later does an operation, like annotation of tokens, that triggers
174     //     -error-on-deserialized-decl, that will emit a diagnostic error,
175     //     that ASTUnit will catch and add to its stored diagnostics vector.
176     //  -c-index-test wants to check whether an error occurred after performing
177     //     the operation but can only query the lazily created set.
178     //
179     // We check here if a new diagnostic was appended since the last time the
180     // diagnostic set was created, in which case we reset it.
181 
182     CXDiagnosticSetImpl *
183       Set = static_cast<CXDiagnosticSetImpl*>(TU->Diagnostics);
184     if (AU->stored_diag_size() != Set->getNumDiagnostics()) {
185       // Diagnostics in the ASTUnit were updated, reset the associated
186       // diagnostics.
187       delete Set;
188       TU->Diagnostics = nullptr;
189     }
190   }
191 
192   if (!TU->Diagnostics) {
193     CXDiagnosticSetImpl *Set = new CXDiagnosticSetImpl();
194     TU->Diagnostics = Set;
195     IntrusiveRefCntPtr<DiagnosticOptions> DOpts = new DiagnosticOptions;
196     CXDiagnosticRenderer Renderer(AU->getASTContext().getLangOpts(),
197                                   &*DOpts, Set);
198 
199     for (ASTUnit::stored_diag_iterator it = AU->stored_diag_begin(),
200          ei = AU->stored_diag_end(); it != ei; ++it) {
201       Renderer.emitStoredDiagnostic(*it);
202     }
203   }
204   return static_cast<CXDiagnosticSetImpl*>(TU->Diagnostics);
205 }
206 
207 //-----------------------------------------------------------------------------
208 // C Interface Routines
209 //-----------------------------------------------------------------------------
210 extern "C" {
211 
clang_getNumDiagnostics(CXTranslationUnit Unit)212 unsigned clang_getNumDiagnostics(CXTranslationUnit Unit) {
213   if (cxtu::isNotUsableTU(Unit)) {
214     LOG_BAD_TU(Unit);
215     return 0;
216   }
217   if (!cxtu::getASTUnit(Unit))
218     return 0;
219   return lazyCreateDiags(Unit, /*checkIfChanged=*/true)->getNumDiagnostics();
220 }
221 
clang_getDiagnostic(CXTranslationUnit Unit,unsigned Index)222 CXDiagnostic clang_getDiagnostic(CXTranslationUnit Unit, unsigned Index) {
223   if (cxtu::isNotUsableTU(Unit)) {
224     LOG_BAD_TU(Unit);
225     return nullptr;
226   }
227 
228   CXDiagnosticSet D = clang_getDiagnosticSetFromTU(Unit);
229   if (!D)
230     return nullptr;
231 
232   CXDiagnosticSetImpl *Diags = static_cast<CXDiagnosticSetImpl*>(D);
233   if (Index >= Diags->getNumDiagnostics())
234     return nullptr;
235 
236   return Diags->getDiagnostic(Index);
237 }
238 
clang_getDiagnosticSetFromTU(CXTranslationUnit Unit)239 CXDiagnosticSet clang_getDiagnosticSetFromTU(CXTranslationUnit Unit) {
240   if (cxtu::isNotUsableTU(Unit)) {
241     LOG_BAD_TU(Unit);
242     return nullptr;
243   }
244   if (!cxtu::getASTUnit(Unit))
245     return nullptr;
246   return static_cast<CXDiagnostic>(lazyCreateDiags(Unit));
247 }
248 
clang_disposeDiagnostic(CXDiagnostic Diagnostic)249 void clang_disposeDiagnostic(CXDiagnostic Diagnostic) {
250   // No-op.  Kept as a legacy API.  CXDiagnostics are now managed
251   // by the enclosing CXDiagnosticSet.
252 }
253 
clang_formatDiagnostic(CXDiagnostic Diagnostic,unsigned Options)254 CXString clang_formatDiagnostic(CXDiagnostic Diagnostic, unsigned Options) {
255   if (!Diagnostic)
256     return cxstring::createEmpty();
257 
258   CXDiagnosticSeverity Severity = clang_getDiagnosticSeverity(Diagnostic);
259 
260   SmallString<256> Str;
261   llvm::raw_svector_ostream Out(Str);
262 
263   if (Options & CXDiagnostic_DisplaySourceLocation) {
264     // Print source location (file:line), along with optional column
265     // and source ranges.
266     CXFile File;
267     unsigned Line, Column;
268     clang_getSpellingLocation(clang_getDiagnosticLocation(Diagnostic),
269                               &File, &Line, &Column, nullptr);
270     if (File) {
271       CXString FName = clang_getFileName(File);
272       Out << clang_getCString(FName) << ":" << Line << ":";
273       clang_disposeString(FName);
274       if (Options & CXDiagnostic_DisplayColumn)
275         Out << Column << ":";
276 
277       if (Options & CXDiagnostic_DisplaySourceRanges) {
278         unsigned N = clang_getDiagnosticNumRanges(Diagnostic);
279         bool PrintedRange = false;
280         for (unsigned I = 0; I != N; ++I) {
281           CXFile StartFile, EndFile;
282           CXSourceRange Range = clang_getDiagnosticRange(Diagnostic, I);
283 
284           unsigned StartLine, StartColumn, EndLine, EndColumn;
285           clang_getSpellingLocation(clang_getRangeStart(Range),
286                                     &StartFile, &StartLine, &StartColumn,
287                                     nullptr);
288           clang_getSpellingLocation(clang_getRangeEnd(Range),
289                                     &EndFile, &EndLine, &EndColumn, nullptr);
290 
291           if (StartFile != EndFile || StartFile != File)
292             continue;
293 
294           Out << "{" << StartLine << ":" << StartColumn << "-"
295               << EndLine << ":" << EndColumn << "}";
296           PrintedRange = true;
297         }
298         if (PrintedRange)
299           Out << ":";
300       }
301 
302       Out << " ";
303     }
304   }
305 
306   /* Print warning/error/etc. */
307   switch (Severity) {
308   case CXDiagnostic_Ignored: llvm_unreachable("impossible");
309   case CXDiagnostic_Note: Out << "note: "; break;
310   case CXDiagnostic_Warning: Out << "warning: "; break;
311   case CXDiagnostic_Error: Out << "error: "; break;
312   case CXDiagnostic_Fatal: Out << "fatal error: "; break;
313   }
314 
315   CXString Text = clang_getDiagnosticSpelling(Diagnostic);
316   if (clang_getCString(Text))
317     Out << clang_getCString(Text);
318   else
319     Out << "<no diagnostic text>";
320   clang_disposeString(Text);
321 
322   if (Options & (CXDiagnostic_DisplayOption | CXDiagnostic_DisplayCategoryId |
323                  CXDiagnostic_DisplayCategoryName)) {
324     bool NeedBracket = true;
325     bool NeedComma = false;
326 
327     if (Options & CXDiagnostic_DisplayOption) {
328       CXString OptionName = clang_getDiagnosticOption(Diagnostic, nullptr);
329       if (const char *OptionText = clang_getCString(OptionName)) {
330         if (OptionText[0]) {
331           Out << " [" << OptionText;
332           NeedBracket = false;
333           NeedComma = true;
334         }
335       }
336       clang_disposeString(OptionName);
337     }
338 
339     if (Options & (CXDiagnostic_DisplayCategoryId |
340                    CXDiagnostic_DisplayCategoryName)) {
341       if (unsigned CategoryID = clang_getDiagnosticCategory(Diagnostic)) {
342         if (Options & CXDiagnostic_DisplayCategoryId) {
343           if (NeedBracket)
344             Out << " [";
345           if (NeedComma)
346             Out << ", ";
347           Out << CategoryID;
348           NeedBracket = false;
349           NeedComma = true;
350         }
351 
352         if (Options & CXDiagnostic_DisplayCategoryName) {
353           CXString CategoryName = clang_getDiagnosticCategoryText(Diagnostic);
354           if (NeedBracket)
355             Out << " [";
356           if (NeedComma)
357             Out << ", ";
358           Out << clang_getCString(CategoryName);
359           NeedBracket = false;
360           NeedComma = true;
361           clang_disposeString(CategoryName);
362         }
363       }
364     }
365 
366     (void) NeedComma; // Silence dead store warning.
367     if (!NeedBracket)
368       Out << "]";
369   }
370 
371   return cxstring::createDup(Out.str());
372 }
373 
clang_defaultDiagnosticDisplayOptions()374 unsigned clang_defaultDiagnosticDisplayOptions() {
375   return CXDiagnostic_DisplaySourceLocation | CXDiagnostic_DisplayColumn |
376          CXDiagnostic_DisplayOption;
377 }
378 
clang_getDiagnosticSeverity(CXDiagnostic Diag)379 enum CXDiagnosticSeverity clang_getDiagnosticSeverity(CXDiagnostic Diag) {
380   if (CXDiagnosticImpl *D = static_cast<CXDiagnosticImpl*>(Diag))
381     return D->getSeverity();
382   return CXDiagnostic_Ignored;
383 }
384 
clang_getDiagnosticLocation(CXDiagnostic Diag)385 CXSourceLocation clang_getDiagnosticLocation(CXDiagnostic Diag) {
386   if (CXDiagnosticImpl *D = static_cast<CXDiagnosticImpl*>(Diag))
387     return D->getLocation();
388   return clang_getNullLocation();
389 }
390 
clang_getDiagnosticSpelling(CXDiagnostic Diag)391 CXString clang_getDiagnosticSpelling(CXDiagnostic Diag) {
392   if (CXDiagnosticImpl *D = static_cast<CXDiagnosticImpl *>(Diag))
393     return D->getSpelling();
394   return cxstring::createEmpty();
395 }
396 
clang_getDiagnosticOption(CXDiagnostic Diag,CXString * Disable)397 CXString clang_getDiagnosticOption(CXDiagnostic Diag, CXString *Disable) {
398   if (Disable)
399     *Disable = cxstring::createEmpty();
400 
401   if (CXDiagnosticImpl *D = static_cast<CXDiagnosticImpl *>(Diag))
402     return D->getDiagnosticOption(Disable);
403 
404   return cxstring::createEmpty();
405 }
406 
clang_getDiagnosticCategory(CXDiagnostic Diag)407 unsigned clang_getDiagnosticCategory(CXDiagnostic Diag) {
408   if (CXDiagnosticImpl *D = static_cast<CXDiagnosticImpl *>(Diag))
409     return D->getCategory();
410   return 0;
411 }
412 
clang_getDiagnosticCategoryName(unsigned Category)413 CXString clang_getDiagnosticCategoryName(unsigned Category) {
414   // Kept for backward compatibility.
415   return cxstring::createRef(DiagnosticIDs::getCategoryNameFromID(Category));
416 }
417 
clang_getDiagnosticCategoryText(CXDiagnostic Diag)418 CXString clang_getDiagnosticCategoryText(CXDiagnostic Diag) {
419   if (CXDiagnosticImpl *D = static_cast<CXDiagnosticImpl *>(Diag))
420     return D->getCategoryText();
421   return cxstring::createEmpty();
422 }
423 
clang_getDiagnosticNumRanges(CXDiagnostic Diag)424 unsigned clang_getDiagnosticNumRanges(CXDiagnostic Diag) {
425   if (CXDiagnosticImpl *D = static_cast<CXDiagnosticImpl *>(Diag))
426     return D->getNumRanges();
427   return 0;
428 }
429 
clang_getDiagnosticRange(CXDiagnostic Diag,unsigned Range)430 CXSourceRange clang_getDiagnosticRange(CXDiagnostic Diag, unsigned Range) {
431   CXDiagnosticImpl *D = static_cast<CXDiagnosticImpl *>(Diag);
432   if (!D || Range >= D->getNumRanges())
433     return clang_getNullRange();
434   return D->getRange(Range);
435 }
436 
clang_getDiagnosticNumFixIts(CXDiagnostic Diag)437 unsigned clang_getDiagnosticNumFixIts(CXDiagnostic Diag) {
438   if (CXDiagnosticImpl *D = static_cast<CXDiagnosticImpl *>(Diag))
439     return D->getNumFixIts();
440   return 0;
441 }
442 
clang_getDiagnosticFixIt(CXDiagnostic Diag,unsigned FixIt,CXSourceRange * ReplacementRange)443 CXString clang_getDiagnosticFixIt(CXDiagnostic Diag, unsigned FixIt,
444                                   CXSourceRange *ReplacementRange) {
445   CXDiagnosticImpl *D = static_cast<CXDiagnosticImpl *>(Diag);
446   if (!D || FixIt >= D->getNumFixIts()) {
447     if (ReplacementRange)
448       *ReplacementRange = clang_getNullRange();
449     return cxstring::createEmpty();
450   }
451   return D->getFixIt(FixIt, ReplacementRange);
452 }
453 
clang_disposeDiagnosticSet(CXDiagnosticSet Diags)454 void clang_disposeDiagnosticSet(CXDiagnosticSet Diags) {
455   if (CXDiagnosticSetImpl *D = static_cast<CXDiagnosticSetImpl *>(Diags)) {
456     if (D->isExternallyManaged())
457       delete D;
458   }
459 }
460 
clang_getDiagnosticInSet(CXDiagnosticSet Diags,unsigned Index)461 CXDiagnostic clang_getDiagnosticInSet(CXDiagnosticSet Diags,
462                                       unsigned Index) {
463   if (CXDiagnosticSetImpl *D = static_cast<CXDiagnosticSetImpl*>(Diags))
464     if (Index < D->getNumDiagnostics())
465       return D->getDiagnostic(Index);
466   return nullptr;
467 }
468 
clang_getChildDiagnostics(CXDiagnostic Diag)469 CXDiagnosticSet clang_getChildDiagnostics(CXDiagnostic Diag) {
470   if (CXDiagnosticImpl *D = static_cast<CXDiagnosticImpl *>(Diag)) {
471     CXDiagnosticSetImpl &ChildDiags = D->getChildDiagnostics();
472     return ChildDiags.empty() ? nullptr : (CXDiagnosticSet) &ChildDiags;
473   }
474   return nullptr;
475 }
476 
clang_getNumDiagnosticsInSet(CXDiagnosticSet Diags)477 unsigned clang_getNumDiagnosticsInSet(CXDiagnosticSet Diags) {
478   if (CXDiagnosticSetImpl *D = static_cast<CXDiagnosticSetImpl*>(Diags))
479     return D->getNumDiagnostics();
480   return 0;
481 }
482 
483 } // end extern "C"
484