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