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