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