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