Clang Project

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