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