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