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