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