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 "llvm/ADT/SmallString.h"
     22 #include "llvm/ADT/Twine.h"
     23 #include "llvm/Support/MemoryBuffer.h"
     24 #include "llvm/Support/raw_ostream.h"
     25 
     26 using namespace clang;
     27 using namespace clang::cxloc;
     28 using namespace clang::cxstring;
     29 using namespace llvm;
     30 
     31 //-----------------------------------------------------------------------------
     32 // C Interface Routines
     33 //-----------------------------------------------------------------------------
     34 extern "C" {
     35 
     36 unsigned clang_getNumDiagnostics(CXTranslationUnit Unit) {
     37   ASTUnit *CXXUnit = static_cast<ASTUnit *>(Unit->TUData);
     38   return CXXUnit? CXXUnit->stored_diag_size() : 0;
     39 }
     40 
     41 CXDiagnostic clang_getDiagnostic(CXTranslationUnit Unit, unsigned Index) {
     42   ASTUnit *CXXUnit = static_cast<ASTUnit *>(Unit->TUData);
     43   if (!CXXUnit || Index >= CXXUnit->stored_diag_size())
     44     return 0;
     45 
     46   return new CXStoredDiagnostic(CXXUnit->stored_diag_begin()[Index],
     47                                 CXXUnit->getASTContext().getLangOptions());
     48 }
     49 
     50 void clang_disposeDiagnostic(CXDiagnostic Diagnostic) {
     51   CXStoredDiagnostic *Stored = static_cast<CXStoredDiagnostic *>(Diagnostic);
     52   delete Stored;
     53 }
     54 
     55 CXString clang_formatDiagnostic(CXDiagnostic Diagnostic, unsigned Options) {
     56   if (!Diagnostic)
     57     return createCXString("");
     58 
     59   CXDiagnosticSeverity Severity = clang_getDiagnosticSeverity(Diagnostic);
     60 
     61   llvm::SmallString<256> Str;
     62   llvm::raw_svector_ostream Out(Str);
     63 
     64   if (Options & CXDiagnostic_DisplaySourceLocation) {
     65     // Print source location (file:line), along with optional column
     66     // and source ranges.
     67     CXFile File;
     68     unsigned Line, Column;
     69     clang_getSpellingLocation(clang_getDiagnosticLocation(Diagnostic),
     70                               &File, &Line, &Column, 0);
     71     if (File) {
     72       CXString FName = clang_getFileName(File);
     73       Out << clang_getCString(FName) << ":" << Line << ":";
     74       clang_disposeString(FName);
     75       if (Options & CXDiagnostic_DisplayColumn)
     76         Out << Column << ":";
     77 
     78       if (Options & CXDiagnostic_DisplaySourceRanges) {
     79         unsigned N = clang_getDiagnosticNumRanges(Diagnostic);
     80         bool PrintedRange = false;
     81         for (unsigned I = 0; I != N; ++I) {
     82           CXFile StartFile, EndFile;
     83           CXSourceRange Range = clang_getDiagnosticRange(Diagnostic, I);
     84 
     85           unsigned StartLine, StartColumn, EndLine, EndColumn;
     86           clang_getSpellingLocation(clang_getRangeStart(Range),
     87                                     &StartFile, &StartLine, &StartColumn,
     88                                     0);
     89           clang_getSpellingLocation(clang_getRangeEnd(Range),
     90                                     &EndFile, &EndLine, &EndColumn, 0);
     91 
     92           if (StartFile != EndFile || StartFile != File)
     93             continue;
     94 
     95           Out << "{" << StartLine << ":" << StartColumn << "-"
     96               << EndLine << ":" << EndColumn << "}";
     97           PrintedRange = true;
     98         }
     99         if (PrintedRange)
    100           Out << ":";
    101       }
    102 
    103       Out << " ";
    104     }
    105   }
    106 
    107   /* Print warning/error/etc. */
    108   switch (Severity) {
    109   case CXDiagnostic_Ignored: llvm_unreachable("impossible");
    110   case CXDiagnostic_Note: Out << "note: "; break;
    111   case CXDiagnostic_Warning: Out << "warning: "; break;
    112   case CXDiagnostic_Error: Out << "error: "; break;
    113   case CXDiagnostic_Fatal: Out << "fatal error: "; break;
    114   }
    115 
    116   CXString Text = clang_getDiagnosticSpelling(Diagnostic);
    117   if (clang_getCString(Text))
    118     Out << clang_getCString(Text);
    119   else
    120     Out << "<no diagnostic text>";
    121   clang_disposeString(Text);
    122 
    123   if (Options & (CXDiagnostic_DisplayOption | CXDiagnostic_DisplayCategoryId |
    124                  CXDiagnostic_DisplayCategoryName)) {
    125     bool NeedBracket = true;
    126     bool NeedComma = false;
    127 
    128     if (Options & CXDiagnostic_DisplayOption) {
    129       CXString OptionName = clang_getDiagnosticOption(Diagnostic, 0);
    130       if (const char *OptionText = clang_getCString(OptionName)) {
    131         if (OptionText[0]) {
    132           Out << " [" << OptionText;
    133           NeedBracket = false;
    134           NeedComma = true;
    135         }
    136       }
    137       clang_disposeString(OptionName);
    138     }
    139 
    140     if (Options & (CXDiagnostic_DisplayCategoryId |
    141                    CXDiagnostic_DisplayCategoryName)) {
    142       if (unsigned CategoryID = clang_getDiagnosticCategory(Diagnostic)) {
    143         if (Options & CXDiagnostic_DisplayCategoryId) {
    144           if (NeedBracket)
    145             Out << " [";
    146           if (NeedComma)
    147             Out << ", ";
    148           Out << CategoryID;
    149           NeedBracket = false;
    150           NeedComma = true;
    151         }
    152 
    153         if (Options & CXDiagnostic_DisplayCategoryName) {
    154           CXString CategoryName = clang_getDiagnosticCategoryName(CategoryID);
    155           if (NeedBracket)
    156             Out << " [";
    157           if (NeedComma)
    158             Out << ", ";
    159           Out << clang_getCString(CategoryName);
    160           NeedBracket = false;
    161           NeedComma = true;
    162           clang_disposeString(CategoryName);
    163         }
    164       }
    165     }
    166 
    167     if (!NeedBracket)
    168       Out << "]";
    169   }
    170 
    171   return createCXString(Out.str(), true);
    172 }
    173 
    174 unsigned clang_defaultDiagnosticDisplayOptions() {
    175   return CXDiagnostic_DisplaySourceLocation | CXDiagnostic_DisplayColumn |
    176          CXDiagnostic_DisplayOption;
    177 }
    178 
    179 enum CXDiagnosticSeverity clang_getDiagnosticSeverity(CXDiagnostic Diag) {
    180   CXStoredDiagnostic *StoredDiag = static_cast<CXStoredDiagnostic *>(Diag);
    181   if (!StoredDiag)
    182     return CXDiagnostic_Ignored;
    183 
    184   switch (StoredDiag->Diag.getLevel()) {
    185   case DiagnosticsEngine::Ignored: return CXDiagnostic_Ignored;
    186   case DiagnosticsEngine::Note:    return CXDiagnostic_Note;
    187   case DiagnosticsEngine::Warning: return CXDiagnostic_Warning;
    188   case DiagnosticsEngine::Error:   return CXDiagnostic_Error;
    189   case DiagnosticsEngine::Fatal:   return CXDiagnostic_Fatal;
    190   }
    191 
    192   llvm_unreachable("Invalid diagnostic level");
    193   return CXDiagnostic_Ignored;
    194 }
    195 
    196 CXSourceLocation clang_getDiagnosticLocation(CXDiagnostic Diag) {
    197   CXStoredDiagnostic *StoredDiag = static_cast<CXStoredDiagnostic *>(Diag);
    198   if (!StoredDiag || StoredDiag->Diag.getLocation().isInvalid())
    199     return clang_getNullLocation();
    200 
    201   return translateSourceLocation(StoredDiag->Diag.getLocation().getManager(),
    202                                  StoredDiag->LangOpts,
    203                                  StoredDiag->Diag.getLocation());
    204 }
    205 
    206 CXString clang_getDiagnosticSpelling(CXDiagnostic Diag) {
    207   CXStoredDiagnostic *StoredDiag = static_cast<CXStoredDiagnostic *>(Diag);
    208   if (!StoredDiag)
    209     return createCXString("");
    210 
    211   return createCXString(StoredDiag->Diag.getMessage(), false);
    212 }
    213 
    214 CXString clang_getDiagnosticOption(CXDiagnostic Diag, CXString *Disable) {
    215   if (Disable)
    216     *Disable = createCXString("");
    217 
    218   CXStoredDiagnostic *StoredDiag = static_cast<CXStoredDiagnostic *>(Diag);
    219   if (!StoredDiag)
    220     return createCXString("");
    221 
    222   unsigned ID = StoredDiag->Diag.getID();
    223   StringRef Option = DiagnosticIDs::getWarningOptionForDiag(ID);
    224   if (!Option.empty()) {
    225     if (Disable)
    226       *Disable = createCXString((Twine("-Wno-") + Option).str());
    227     return createCXString((Twine("-W") + Option).str());
    228   }
    229 
    230   if (ID == diag::fatal_too_many_errors) {
    231     if (Disable)
    232       *Disable = createCXString("-ferror-limit=0");
    233     return createCXString("-ferror-limit=");
    234   }
    235 
    236   bool EnabledByDefault;
    237   if (DiagnosticIDs::isBuiltinExtensionDiag(ID, EnabledByDefault) &&
    238       !EnabledByDefault)
    239     return createCXString("-pedantic");
    240 
    241   return createCXString("");
    242 }
    243 
    244 unsigned clang_getDiagnosticCategory(CXDiagnostic Diag) {
    245   CXStoredDiagnostic *StoredDiag = static_cast<CXStoredDiagnostic *>(Diag);
    246   if (!StoredDiag)
    247     return 0;
    248 
    249   return DiagnosticIDs::getCategoryNumberForDiag(StoredDiag->Diag.getID());
    250 }
    251 
    252 CXString clang_getDiagnosticCategoryName(unsigned Category) {
    253   return createCXString(DiagnosticIDs::getCategoryNameFromID(Category));
    254 }
    255 
    256 unsigned clang_getDiagnosticNumRanges(CXDiagnostic Diag) {
    257   CXStoredDiagnostic *StoredDiag = static_cast<CXStoredDiagnostic *>(Diag);
    258   if (!StoredDiag || StoredDiag->Diag.getLocation().isInvalid())
    259     return 0;
    260 
    261   return StoredDiag->Diag.range_size();
    262 }
    263 
    264 CXSourceRange clang_getDiagnosticRange(CXDiagnostic Diag, unsigned Range) {
    265   CXStoredDiagnostic *StoredDiag = static_cast<CXStoredDiagnostic *>(Diag);
    266   if (!StoredDiag || Range >= StoredDiag->Diag.range_size() ||
    267       StoredDiag->Diag.getLocation().isInvalid())
    268     return clang_getNullRange();
    269 
    270   return translateSourceRange(StoredDiag->Diag.getLocation().getManager(),
    271                               StoredDiag->LangOpts,
    272                               StoredDiag->Diag.range_begin()[Range]);
    273 }
    274 
    275 unsigned clang_getDiagnosticNumFixIts(CXDiagnostic Diag) {
    276   CXStoredDiagnostic *StoredDiag = static_cast<CXStoredDiagnostic *>(Diag);
    277   if (!StoredDiag)
    278     return 0;
    279 
    280   return StoredDiag->Diag.fixit_size();
    281 }
    282 
    283 CXString clang_getDiagnosticFixIt(CXDiagnostic Diagnostic, unsigned FixIt,
    284                                   CXSourceRange *ReplacementRange) {
    285   CXStoredDiagnostic *StoredDiag
    286     = static_cast<CXStoredDiagnostic *>(Diagnostic);
    287   if (!StoredDiag || FixIt >= StoredDiag->Diag.fixit_size() ||
    288       StoredDiag->Diag.getLocation().isInvalid()) {
    289     if (ReplacementRange)
    290       *ReplacementRange = clang_getNullRange();
    291 
    292     return createCXString("");
    293   }
    294 
    295   const FixItHint &Hint = StoredDiag->Diag.fixit_begin()[FixIt];
    296   if (ReplacementRange) {
    297     // Create a range that covers the entire replacement (or
    298     // removal) range, adjusting the end of the range to point to
    299     // the end of the token.
    300     *ReplacementRange
    301         = translateSourceRange(StoredDiag->Diag.getLocation().getManager(),
    302                                 StoredDiag->LangOpts,
    303                                 Hint.RemoveRange);
    304   }
    305 
    306   return createCXString(Hint.CodeToInsert);
    307 }
    308 
    309 } // end extern "C"
    310