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