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