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 
     34 CXDiagnosticSetImpl::~CXDiagnosticSetImpl() {
     35   for (std::vector<CXDiagnosticImpl *>::iterator it = Diagnostics.begin(),
     36        et = Diagnostics.end();
     37        it != et; ++it) {
     38     delete *it;
     39   }
     40 }
     41 
     42 CXDiagnosticImpl::~CXDiagnosticImpl() {}
     43 
     44 namespace {
     45 class CXDiagnosticCustomNoteImpl : public CXDiagnosticImpl {
     46   std::string Message;
     47   CXSourceLocation Loc;
     48 public:
     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 
     89 class CXDiagnosticRenderer : public DiagnosticNoteRenderer {
     90 public:
     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 
    161 CXDiagnosticSetImpl *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 //-----------------------------------------------------------------------------
    211 extern "C" {
    212 
    213 unsigned 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 
    223 CXDiagnostic 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 
    240 CXDiagnosticSet 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 
    250 void clang_disposeDiagnostic(CXDiagnostic Diagnostic) {
    251   // No-op.  Kept as a legacy API.  CXDiagnostics are now managed
    252   // by the enclosing CXDiagnosticSet.
    253 }
    254 
    255 CXString 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 
    375 unsigned clang_defaultDiagnosticDisplayOptions() {
    376   return CXDiagnostic_DisplaySourceLocation | CXDiagnostic_DisplayColumn |
    377          CXDiagnostic_DisplayOption;
    378 }
    379 
    380 enum CXDiagnosticSeverity clang_getDiagnosticSeverity(CXDiagnostic Diag) {
    381   if (CXDiagnosticImpl *D = static_cast<CXDiagnosticImpl*>(Diag))
    382     return D->getSeverity();
    383   return CXDiagnostic_Ignored;
    384 }
    385 
    386 CXSourceLocation clang_getDiagnosticLocation(CXDiagnostic Diag) {
    387   if (CXDiagnosticImpl *D = static_cast<CXDiagnosticImpl*>(Diag))
    388     return D->getLocation();
    389   return clang_getNullLocation();
    390 }
    391 
    392 CXString clang_getDiagnosticSpelling(CXDiagnostic Diag) {
    393   if (CXDiagnosticImpl *D = static_cast<CXDiagnosticImpl *>(Diag))
    394     return D->getSpelling();
    395   return cxstring::createEmpty();
    396 }
    397 
    398 CXString 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 
    408 unsigned clang_getDiagnosticCategory(CXDiagnostic Diag) {
    409   if (CXDiagnosticImpl *D = static_cast<CXDiagnosticImpl *>(Diag))
    410     return D->getCategory();
    411   return 0;
    412 }
    413 
    414 CXString clang_getDiagnosticCategoryName(unsigned Category) {
    415   // Kept for backwards compatibility.
    416   return cxstring::createRef(DiagnosticIDs::getCategoryNameFromID(Category));
    417 }
    418 
    419 CXString clang_getDiagnosticCategoryText(CXDiagnostic Diag) {
    420   if (CXDiagnosticImpl *D = static_cast<CXDiagnosticImpl *>(Diag))
    421     return D->getCategoryText();
    422   return cxstring::createEmpty();
    423 }
    424 
    425 unsigned clang_getDiagnosticNumRanges(CXDiagnostic Diag) {
    426   if (CXDiagnosticImpl *D = static_cast<CXDiagnosticImpl *>(Diag))
    427     return D->getNumRanges();
    428   return 0;
    429 }
    430 
    431 CXSourceRange 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 
    438 unsigned clang_getDiagnosticNumFixIts(CXDiagnostic Diag) {
    439   if (CXDiagnosticImpl *D = static_cast<CXDiagnosticImpl *>(Diag))
    440     return D->getNumFixIts();
    441   return 0;
    442 }
    443 
    444 CXString 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 
    455 void clang_disposeDiagnosticSet(CXDiagnosticSet Diags) {
    456   if (CXDiagnosticSetImpl *D = static_cast<CXDiagnosticSetImpl *>(Diags)) {
    457     if (D->isExternallyManaged())
    458       delete D;
    459   }
    460 }
    461 
    462 CXDiagnostic 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 
    470 CXDiagnosticSet 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 
    478 unsigned 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