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