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