CIndexDiagnostic.cpp revision 5694feb5ccd6eb862cb600b55753cecc13794471
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
28using namespace clang;
29using namespace clang::cxloc;
30using namespace clang::cxstring;
31using namespace clang::cxdiag;
32using namespace llvm;
33
34
35CXDiagnosticSetImpl::~CXDiagnosticSetImpl() {
36  for (std::vector<CXDiagnosticImpl *>::iterator it = Diagnostics.begin(),
37       et = Diagnostics.end();
38       it != et; ++it) {
39    delete *it;
40  }
41}
42
43CXDiagnosticImpl::~CXDiagnosticImpl() {}
44
45namespace {
46class CXDiagnosticCustomNoteImpl : public CXDiagnosticImpl {
47  std::string Message;
48  CXSourceLocation Loc;
49public:
50  CXDiagnosticCustomNoteImpl(StringRef Msg, CXSourceLocation L)
51    : CXDiagnosticImpl(CustomNoteDiagnosticKind),
52      Message(Msg), Loc(L) {}
53
54  virtual ~CXDiagnosticCustomNoteImpl() {}
55
56  CXDiagnosticSeverity getSeverity() const {
57    return CXDiagnostic_Note;
58  }
59
60  CXSourceLocation getLocation() const {
61    return Loc;
62  }
63
64  CXString getSpelling() const {
65    return createCXString(StringRef(Message), false);
66  }
67
68  CXString getDiagnosticOption(CXString *Disable) const {
69    if (Disable)
70      *Disable = createCXString("", false);
71    return createCXString("", false);
72  }
73
74  unsigned getCategory() const { return 0; }
75  CXString getCategoryText() const { return createCXString(""); }
76
77  unsigned getNumRanges() const { return 0; }
78  CXSourceRange getRange(unsigned Range) const { return clang_getNullRange(); }
79  unsigned getNumFixIts() const { return 0; }
80  CXString getFixIt(unsigned FixIt, CXSourceRange *ReplacementRange) const {
81    if (ReplacementRange)
82      *ReplacementRange = clang_getNullRange();
83    return createCXString("", false);
84  }
85};
86
87class CXDiagnosticRenderer : public DiagnosticNoteRenderer {
88public:
89  CXDiagnosticRenderer(const LangOptions &LangOpts,
90                       DiagnosticOptions *DiagOpts,
91                       CXDiagnosticSetImpl *mainSet)
92  : DiagnosticNoteRenderer(LangOpts, DiagOpts),
93    CurrentSet(mainSet), MainSet(mainSet) {}
94
95  virtual ~CXDiagnosticRenderer() {}
96
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
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
132  virtual void emitDiagnosticLoc(SourceLocation Loc, PresumedLoc PLoc,
133                                 DiagnosticsEngine::Level Level,
134                                 ArrayRef<CharSourceRange> Ranges,
135                                 const SourceManager &SM) {}
136
137  virtual void emitCodeContext(SourceLocation Loc,
138                               DiagnosticsEngine::Level Level,
139                               SmallVectorImpl<CharSourceRange>& Ranges,
140                               ArrayRef<FixItHint> Hints,
141                               const SourceManager &SM) {}
142
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
159CXDiagnosticSetImpl *cxdiag::lazyCreateDiags(CXTranslationUnit TU,
160                                             bool checkIfChanged) {
161  ASTUnit *AU = cxtu::getASTUnit(TU);
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    IntrusiveRefCntPtr<DiagnosticOptions> DOpts = new DiagnosticOptions;
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//-----------------------------------------------------------------------------
209extern "C" {
210
211unsigned clang_getNumDiagnostics(CXTranslationUnit Unit) {
212  if (!Unit->TUData)
213    return 0;
214  return lazyCreateDiags(Unit, /*checkIfChanged=*/true)->getNumDiagnostics();
215}
216
217CXDiagnostic 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
229CXDiagnosticSet clang_getDiagnosticSetFromTU(CXTranslationUnit Unit) {
230  if (!Unit->TUData)
231    return 0;
232  return static_cast<CXDiagnostic>(lazyCreateDiags(Unit));
233}
234
235void clang_disposeDiagnostic(CXDiagnostic Diagnostic) {
236  // No-op.  Kept as a legacy API.  CXDiagnostics are now managed
237  // by the enclosing CXDiagnosticSet.
238}
239
240CXString 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
360unsigned clang_defaultDiagnosticDisplayOptions() {
361  return CXDiagnostic_DisplaySourceLocation | CXDiagnostic_DisplayColumn |
362         CXDiagnostic_DisplayOption;
363}
364
365enum CXDiagnosticSeverity clang_getDiagnosticSeverity(CXDiagnostic Diag) {
366  if (CXDiagnosticImpl *D = static_cast<CXDiagnosticImpl*>(Diag))
367    return D->getSeverity();
368  return CXDiagnostic_Ignored;
369}
370
371CXSourceLocation clang_getDiagnosticLocation(CXDiagnostic Diag) {
372  if (CXDiagnosticImpl *D = static_cast<CXDiagnosticImpl*>(Diag))
373    return D->getLocation();
374  return clang_getNullLocation();
375}
376
377CXString clang_getDiagnosticSpelling(CXDiagnostic Diag) {
378  if (CXDiagnosticImpl *D = static_cast<CXDiagnosticImpl *>(Diag))
379    return D->getSpelling();
380  return createCXString("");
381}
382
383CXString 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
393unsigned clang_getDiagnosticCategory(CXDiagnostic Diag) {
394  if (CXDiagnosticImpl *D = static_cast<CXDiagnosticImpl *>(Diag))
395    return D->getCategory();
396  return 0;
397}
398
399CXString clang_getDiagnosticCategoryName(unsigned Category) {
400  // Kept for backwards compatibility.
401  return createCXString(DiagnosticIDs::getCategoryNameFromID(Category));
402}
403
404CXString clang_getDiagnosticCategoryText(CXDiagnostic Diag) {
405  if (CXDiagnosticImpl *D = static_cast<CXDiagnosticImpl *>(Diag))
406    return D->getCategoryText();
407  return createCXString("");
408}
409
410unsigned clang_getDiagnosticNumRanges(CXDiagnostic Diag) {
411  if (CXDiagnosticImpl *D = static_cast<CXDiagnosticImpl *>(Diag))
412    return D->getNumRanges();
413  return 0;
414}
415
416CXSourceRange 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
423unsigned clang_getDiagnosticNumFixIts(CXDiagnostic Diag) {
424  if (CXDiagnosticImpl *D = static_cast<CXDiagnosticImpl *>(Diag))
425    return D->getNumFixIts();
426  return 0;
427}
428
429CXString 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
440void clang_disposeDiagnosticSet(CXDiagnosticSet Diags) {
441  CXDiagnosticSetImpl *D = static_cast<CXDiagnosticSetImpl*>(Diags);
442  if (D->isExternallyManaged())
443    delete D;
444}
445
446CXDiagnostic 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
454CXDiagnosticSet 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
462unsigned 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