CIndexDiagnostic.cpp revision 36d592718ff342f762e32cbde73d1113f88cb275
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 "llvm/ADT/SmallString.h"
22#include "llvm/ADT/Twine.h"
23#include "llvm/Support/MemoryBuffer.h"
24#include "llvm/Support/raw_ostream.h"
25
26using namespace clang;
27using namespace clang::cxloc;
28using namespace clang::cxstring;
29using namespace clang::cxdiag;
30using namespace llvm;
31
32
33CXDiagnosticSetImpl::~CXDiagnosticSetImpl() {
34  for (std::vector<CXDiagnosticImpl *>::iterator it = Diagnostics.begin(),
35       et = Diagnostics.end();
36       it != et; ++it) {
37    delete *it;
38  }
39}
40
41CXDiagnosticImpl::~CXDiagnosticImpl() {}
42
43CXDiagnosticSetImpl *cxdiag::lazyCreateDiags(CXTranslationUnit TU,
44                                             bool checkIfChanged) {
45  ASTUnit *AU = static_cast<ASTUnit *>(TU->TUData);
46
47  if (TU->Diagnostics && checkIfChanged) {
48    // In normal use, ASTUnit's diagnostics should not change unless we reparse.
49    // Currently they can only change by using the internal testing flag
50    // '-error-on-deserialized-decl' which will error during deserialization of
51    // a declaration. What will happen is:
52    //
53    //  -c-index-test gets a CXTranslationUnit
54    //  -checks the diagnostics, the diagnostics set is lazily created,
55    //     no errors are reported
56    //  -later does an operation, like annotation of tokens, that triggers
57    //     -error-on-deserialized-decl, that will emit a diagnostic error,
58    //     that ASTUnit will catch and add to its stored diagnostics vector.
59    //  -c-index-test wants to check whether an error occurred after performing
60    //     the operation but can only query the lazily created set.
61    //
62    // We check here if a new diagnostic was appended since the last time the
63    // diagnostic set was created, in which case we reset it.
64
65    CXDiagnosticSetImpl *
66      Set = static_cast<CXDiagnosticSetImpl*>(TU->Diagnostics);
67    if (AU->stored_diag_size() != Set->getNumDiagnostics()) {
68      // Diagnostics in the ASTUnit were updated, reset the associated
69      // diagnostics.
70      delete Set;
71      TU->Diagnostics = 0;
72    }
73  }
74
75  if (!TU->Diagnostics) {
76    CXDiagnosticSetImpl *Set = new CXDiagnosticSetImpl();
77    TU->Diagnostics = Set;
78
79    for (ASTUnit::stored_diag_iterator it = AU->stored_diag_begin(),
80         ei = AU->stored_diag_end(); it != ei; ++it) {
81      CXStoredDiagnostic *D =
82        new CXStoredDiagnostic(*it, AU->getASTContext().getLangOptions());
83      Set->appendDiagnostic(D);
84    }
85  }
86  return static_cast<CXDiagnosticSetImpl*>(TU->Diagnostics);
87}
88
89//-----------------------------------------------------------------------------
90// C Interface Routines
91//-----------------------------------------------------------------------------
92extern "C" {
93
94unsigned clang_getNumDiagnostics(CXTranslationUnit Unit) {
95  if (!Unit->TUData)
96    return 0;
97  return lazyCreateDiags(Unit, /*checkIfChanged=*/true)->getNumDiagnostics();
98}
99
100CXDiagnostic clang_getDiagnostic(CXTranslationUnit Unit, unsigned Index) {
101  CXDiagnosticSet D = clang_getDiagnosticSetFromTU(Unit);
102  if (!D)
103    return 0;
104
105  CXDiagnosticSetImpl *Diags = static_cast<CXDiagnosticSetImpl*>(D);
106  if (Index >= Diags->getNumDiagnostics())
107    return 0;
108
109  return Diags->getDiagnostic(Index);
110}
111
112CXDiagnosticSet clang_getDiagnosticSetFromTU(CXTranslationUnit Unit) {
113  if (!Unit->TUData)
114    return 0;
115  return static_cast<CXDiagnostic>(lazyCreateDiags(Unit));
116}
117
118void clang_disposeDiagnostic(CXDiagnostic Diagnostic) {
119  // No-op.  Kept as a legacy API.  CXDiagnostics are now managed
120  // by the enclosing CXDiagnosticSet.
121}
122
123CXString clang_formatDiagnostic(CXDiagnostic Diagnostic, unsigned Options) {
124  if (!Diagnostic)
125    return createCXString("");
126
127  CXDiagnosticSeverity Severity = clang_getDiagnosticSeverity(Diagnostic);
128
129  SmallString<256> Str;
130  llvm::raw_svector_ostream Out(Str);
131
132  if (Options & CXDiagnostic_DisplaySourceLocation) {
133    // Print source location (file:line), along with optional column
134    // and source ranges.
135    CXFile File;
136    unsigned Line, Column;
137    clang_getSpellingLocation(clang_getDiagnosticLocation(Diagnostic),
138                              &File, &Line, &Column, 0);
139    if (File) {
140      CXString FName = clang_getFileName(File);
141      Out << clang_getCString(FName) << ":" << Line << ":";
142      clang_disposeString(FName);
143      if (Options & CXDiagnostic_DisplayColumn)
144        Out << Column << ":";
145
146      if (Options & CXDiagnostic_DisplaySourceRanges) {
147        unsigned N = clang_getDiagnosticNumRanges(Diagnostic);
148        bool PrintedRange = false;
149        for (unsigned I = 0; I != N; ++I) {
150          CXFile StartFile, EndFile;
151          CXSourceRange Range = clang_getDiagnosticRange(Diagnostic, I);
152
153          unsigned StartLine, StartColumn, EndLine, EndColumn;
154          clang_getSpellingLocation(clang_getRangeStart(Range),
155                                    &StartFile, &StartLine, &StartColumn,
156                                    0);
157          clang_getSpellingLocation(clang_getRangeEnd(Range),
158                                    &EndFile, &EndLine, &EndColumn, 0);
159
160          if (StartFile != EndFile || StartFile != File)
161            continue;
162
163          Out << "{" << StartLine << ":" << StartColumn << "-"
164              << EndLine << ":" << EndColumn << "}";
165          PrintedRange = true;
166        }
167        if (PrintedRange)
168          Out << ":";
169      }
170
171      Out << " ";
172    }
173  }
174
175  /* Print warning/error/etc. */
176  switch (Severity) {
177  case CXDiagnostic_Ignored: llvm_unreachable("impossible");
178  case CXDiagnostic_Note: Out << "note: "; break;
179  case CXDiagnostic_Warning: Out << "warning: "; break;
180  case CXDiagnostic_Error: Out << "error: "; break;
181  case CXDiagnostic_Fatal: Out << "fatal error: "; break;
182  }
183
184  CXString Text = clang_getDiagnosticSpelling(Diagnostic);
185  if (clang_getCString(Text))
186    Out << clang_getCString(Text);
187  else
188    Out << "<no diagnostic text>";
189  clang_disposeString(Text);
190
191  if (Options & (CXDiagnostic_DisplayOption | CXDiagnostic_DisplayCategoryId |
192                 CXDiagnostic_DisplayCategoryName)) {
193    bool NeedBracket = true;
194    bool NeedComma = false;
195
196    if (Options & CXDiagnostic_DisplayOption) {
197      CXString OptionName = clang_getDiagnosticOption(Diagnostic, 0);
198      if (const char *OptionText = clang_getCString(OptionName)) {
199        if (OptionText[0]) {
200          Out << " [" << OptionText;
201          NeedBracket = false;
202          NeedComma = true;
203        }
204      }
205      clang_disposeString(OptionName);
206    }
207
208    if (Options & (CXDiagnostic_DisplayCategoryId |
209                   CXDiagnostic_DisplayCategoryName)) {
210      if (unsigned CategoryID = clang_getDiagnosticCategory(Diagnostic)) {
211        if (Options & CXDiagnostic_DisplayCategoryId) {
212          if (NeedBracket)
213            Out << " [";
214          if (NeedComma)
215            Out << ", ";
216          Out << CategoryID;
217          NeedBracket = false;
218          NeedComma = true;
219        }
220
221        if (Options & CXDiagnostic_DisplayCategoryName) {
222          CXString CategoryName = clang_getDiagnosticCategoryName(CategoryID);
223          if (NeedBracket)
224            Out << " [";
225          if (NeedComma)
226            Out << ", ";
227          Out << clang_getCString(CategoryName);
228          NeedBracket = false;
229          NeedComma = true;
230          clang_disposeString(CategoryName);
231        }
232      }
233    }
234
235    if (!NeedBracket)
236      Out << "]";
237  }
238
239  return createCXString(Out.str(), true);
240}
241
242unsigned clang_defaultDiagnosticDisplayOptions() {
243  return CXDiagnostic_DisplaySourceLocation | CXDiagnostic_DisplayColumn |
244         CXDiagnostic_DisplayOption;
245}
246
247enum CXDiagnosticSeverity clang_getDiagnosticSeverity(CXDiagnostic Diag) {
248  if (CXDiagnosticImpl *D = static_cast<CXDiagnosticImpl*>(Diag))
249    return D->getSeverity();
250  return CXDiagnostic_Ignored;
251}
252
253CXSourceLocation clang_getDiagnosticLocation(CXDiagnostic Diag) {
254  if (CXDiagnosticImpl *D = static_cast<CXDiagnosticImpl*>(Diag))
255    return D->getLocation();
256  return clang_getNullLocation();
257}
258
259CXString clang_getDiagnosticSpelling(CXDiagnostic Diag) {
260  if (CXDiagnosticImpl *D = static_cast<CXDiagnosticImpl *>(Diag))
261    return D->getSpelling();
262  return createCXString("");
263}
264
265CXString clang_getDiagnosticOption(CXDiagnostic Diag, CXString *Disable) {
266  if (Disable)
267    *Disable = createCXString("");
268
269  if (CXDiagnosticImpl *D = static_cast<CXDiagnosticImpl *>(Diag))
270    return D->getDiagnosticOption(Disable);
271
272  return createCXString("");
273}
274
275unsigned clang_getDiagnosticCategory(CXDiagnostic Diag) {
276  if (CXDiagnosticImpl *D = static_cast<CXDiagnosticImpl *>(Diag))
277    return D->getCategory();
278  return 0;
279}
280
281CXString clang_getDiagnosticCategoryName(unsigned Category) {
282  return createCXString(DiagnosticIDs::getCategoryNameFromID(Category));
283}
284
285unsigned clang_getDiagnosticNumRanges(CXDiagnostic Diag) {
286  if (CXDiagnosticImpl *D = static_cast<CXDiagnosticImpl *>(Diag))
287    return D->getNumRanges();
288  return 0;
289}
290
291CXSourceRange clang_getDiagnosticRange(CXDiagnostic Diag, unsigned Range) {
292  CXDiagnosticImpl *D = static_cast<CXDiagnosticImpl *>(Diag);
293  if (!D || Range >= D->getNumRanges())
294    return clang_getNullRange();
295  return D->getRange(Range);
296}
297
298unsigned clang_getDiagnosticNumFixIts(CXDiagnostic Diag) {
299  if (CXDiagnosticImpl *D = static_cast<CXDiagnosticImpl *>(Diag))
300    return D->getNumFixIts();
301  return 0;
302}
303
304CXString clang_getDiagnosticFixIt(CXDiagnostic Diag, unsigned FixIt,
305                                  CXSourceRange *ReplacementRange) {
306  CXDiagnosticImpl *D = static_cast<CXDiagnosticImpl *>(Diag);
307  if (!D || FixIt >= D->getNumFixIts()) {
308    if (ReplacementRange)
309      *ReplacementRange = clang_getNullRange();
310    return createCXString("");
311  }
312  return D->getFixIt(FixIt, ReplacementRange);
313}
314
315void clang_disposeDiagnosticSet(CXDiagnosticSet Diags) {
316  CXDiagnosticSetImpl *D = static_cast<CXDiagnosticSetImpl*>(Diags);
317  if (D->isExternallyManaged())
318    delete D;
319}
320
321CXDiagnostic clang_getDiagnosticInSet(CXDiagnosticSet Diags,
322                                      unsigned Index) {
323  if (CXDiagnosticSetImpl *D = static_cast<CXDiagnosticSetImpl*>(Diags))
324    if (Index < D->getNumDiagnostics())
325      return D->getDiagnostic(Index);
326  return 0;
327}
328
329CXDiagnosticSet clang_getChildDiagnostics(CXDiagnostic Diag) {
330  if (CXDiagnosticImpl *D = static_cast<CXDiagnosticImpl *>(Diag)) {
331    CXDiagnosticSetImpl &ChildDiags = D->getChildDiagnostics();
332    return ChildDiags.empty() ? 0 : (CXDiagnosticSet) &ChildDiags;
333  }
334  return 0;
335}
336
337unsigned clang_getNumDiagnosticsInSet(CXDiagnosticSet Diags) {
338  if (CXDiagnosticSetImpl *D = static_cast<CXDiagnosticSetImpl*>(Diags))
339    return D->getNumDiagnostics();
340  return 0;
341}
342
343} // end extern "C"
344