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