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