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