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