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