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