469 lines · cpp
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-exception6//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 32void33CXDiagnosticSetImpl::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, DiagnosticOptions &DiagOpts,85 CXDiagnosticSetImpl *mainSet)86 : DiagnosticNoteRenderer(LangOpts, DiagOpts), CurrentSet(mainSet),87 MainSet(mainSet) {}88 89 ~CXDiagnosticRenderer() override {}90 91 void beginDiagnostic(DiagOrStoredDiag D,92 DiagnosticsEngine::Level Level) override {93 94 const StoredDiagnostic *SD =95 dyn_cast_if_present<const StoredDiagnostic *>(D);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 else121 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 else139 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 flag156 // '-error-on-deserialized-decl' which will error during deserialization of157 // a declaration. What will happen is:158 //159 // -c-index-test gets a CXTranslationUnit160 // -checks the diagnostics, the diagnostics set is lazily created,161 // no errors are reported162 // -later does an operation, like annotation of tokens, that triggers163 // -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 performing166 // 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 the169 // 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 associated175 // 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 DiagnosticOptions DOpts;185 CXDiagnosticRenderer Renderer(AU->getASTContext().getLangOpts(), DOpts,186 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 Routines198//-----------------------------------------------------------------------------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 managed238 // 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 column252 // 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 else306 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