/*===-- CIndexDiagnostics.cpp - Diagnostics C Interface ---------*- C++ -*-===*\ |* *| |* The LLVM Compiler Infrastructure *| |* *| |* This file is distributed under the University of Illinois Open Source *| |* License. See LICENSE.TXT for details. *| |* *| |*===----------------------------------------------------------------------===*| |* *| |* Implements the diagnostic functions of the Clang C interface. *| |* *| \*===----------------------------------------------------------------------===*/ #include "CIndexDiagnostic.h" #include "CIndexer.h" #include "CXTranslationUnit.h" #include "CXSourceLocation.h" #include "CXString.h" #include "clang/Basic/DiagnosticOptions.h" #include "clang/Frontend/ASTUnit.h" #include "clang/Frontend/DiagnosticRenderer.h" #include "clang/Frontend/FrontendDiagnostic.h" #include "llvm/ADT/SmallString.h" #include "llvm/Support/raw_ostream.h" using namespace clang; using namespace clang::cxloc; using namespace clang::cxdiag; using namespace llvm; CXDiagnosticSetImpl::~CXDiagnosticSetImpl() {} void CXDiagnosticSetImpl::appendDiagnostic(std::unique_ptr D) { Diagnostics.push_back(std::move(D)); } CXDiagnosticImpl::~CXDiagnosticImpl() {} namespace { class CXDiagnosticCustomNoteImpl : public CXDiagnosticImpl { std::string Message; CXSourceLocation Loc; public: CXDiagnosticCustomNoteImpl(StringRef Msg, CXSourceLocation L) : CXDiagnosticImpl(CustomNoteDiagnosticKind), Message(Msg), Loc(L) {} ~CXDiagnosticCustomNoteImpl() override {} CXDiagnosticSeverity getSeverity() const override { return CXDiagnostic_Note; } CXSourceLocation getLocation() const override { return Loc; } CXString getSpelling() const override { return cxstring::createRef(Message.c_str()); } CXString getDiagnosticOption(CXString *Disable) const override { if (Disable) *Disable = cxstring::createEmpty(); return cxstring::createEmpty(); } unsigned getCategory() const override { return 0; } CXString getCategoryText() const override { return cxstring::createEmpty(); } unsigned getNumRanges() const override { return 0; } CXSourceRange getRange(unsigned Range) const override { return clang_getNullRange(); } unsigned getNumFixIts() const override { return 0; } CXString getFixIt(unsigned FixIt, CXSourceRange *ReplacementRange) const override { if (ReplacementRange) *ReplacementRange = clang_getNullRange(); return cxstring::createEmpty(); } }; class CXDiagnosticRenderer : public DiagnosticNoteRenderer { public: CXDiagnosticRenderer(const LangOptions &LangOpts, DiagnosticOptions *DiagOpts, CXDiagnosticSetImpl *mainSet) : DiagnosticNoteRenderer(LangOpts, DiagOpts), CurrentSet(mainSet), MainSet(mainSet) {} ~CXDiagnosticRenderer() override {} void beginDiagnostic(DiagOrStoredDiag D, DiagnosticsEngine::Level Level) override { const StoredDiagnostic *SD = D.dyn_cast(); if (!SD) return; if (Level != DiagnosticsEngine::Note) CurrentSet = MainSet; auto Owner = llvm::make_unique(*SD, LangOpts); CXStoredDiagnostic &CD = *Owner; CurrentSet->appendDiagnostic(std::move(Owner)); if (Level != DiagnosticsEngine::Note) CurrentSet = &CD.getChildDiagnostics(); } void emitDiagnosticMessage(FullSourceLoc Loc, PresumedLoc PLoc, DiagnosticsEngine::Level Level, StringRef Message, ArrayRef Ranges, DiagOrStoredDiag D) override { if (!D.isNull()) return; CXSourceLocation L; if (Loc.hasManager()) L = translateSourceLocation(Loc.getManager(), LangOpts, Loc); else L = clang_getNullLocation(); CurrentSet->appendDiagnostic( llvm::make_unique(Message, L)); } void emitDiagnosticLoc(FullSourceLoc Loc, PresumedLoc PLoc, DiagnosticsEngine::Level Level, ArrayRef Ranges) override {} void emitCodeContext(FullSourceLoc Loc, DiagnosticsEngine::Level Level, SmallVectorImpl &Ranges, ArrayRef Hints) override {} void emitNote(FullSourceLoc Loc, StringRef Message) override { CXSourceLocation L; if (Loc.hasManager()) L = translateSourceLocation(Loc.getManager(), LangOpts, Loc); else L = clang_getNullLocation(); CurrentSet->appendDiagnostic( llvm::make_unique(Message, L)); } CXDiagnosticSetImpl *CurrentSet; CXDiagnosticSetImpl *MainSet; }; } CXDiagnosticSetImpl *cxdiag::lazyCreateDiags(CXTranslationUnit TU, bool checkIfChanged) { ASTUnit *AU = cxtu::getASTUnit(TU); if (TU->Diagnostics && checkIfChanged) { // In normal use, ASTUnit's diagnostics should not change unless we reparse. // Currently they can only change by using the internal testing flag // '-error-on-deserialized-decl' which will error during deserialization of // a declaration. What will happen is: // // -c-index-test gets a CXTranslationUnit // -checks the diagnostics, the diagnostics set is lazily created, // no errors are reported // -later does an operation, like annotation of tokens, that triggers // -error-on-deserialized-decl, that will emit a diagnostic error, // that ASTUnit will catch and add to its stored diagnostics vector. // -c-index-test wants to check whether an error occurred after performing // the operation but can only query the lazily created set. // // We check here if a new diagnostic was appended since the last time the // diagnostic set was created, in which case we reset it. CXDiagnosticSetImpl * Set = static_cast(TU->Diagnostics); if (AU->stored_diag_size() != Set->getNumDiagnostics()) { // Diagnostics in the ASTUnit were updated, reset the associated // diagnostics. delete Set; TU->Diagnostics = nullptr; } } if (!TU->Diagnostics) { CXDiagnosticSetImpl *Set = new CXDiagnosticSetImpl(); TU->Diagnostics = Set; IntrusiveRefCntPtr DOpts = new DiagnosticOptions; CXDiagnosticRenderer Renderer(AU->getASTContext().getLangOpts(), &*DOpts, Set); for (ASTUnit::stored_diag_iterator it = AU->stored_diag_begin(), ei = AU->stored_diag_end(); it != ei; ++it) { Renderer.emitStoredDiagnostic(*it); } } return static_cast(TU->Diagnostics); } //----------------------------------------------------------------------------- // C Interface Routines //----------------------------------------------------------------------------- unsigned clang_getNumDiagnostics(CXTranslationUnit Unit) { if (cxtu::isNotUsableTU(Unit)) { LOG_BAD_TU(Unit); return 0; } if (!cxtu::getASTUnit(Unit)) return 0; return lazyCreateDiags(Unit, /*checkIfChanged=*/true)->getNumDiagnostics(); } CXDiagnostic clang_getDiagnostic(CXTranslationUnit Unit, unsigned Index) { if (cxtu::isNotUsableTU(Unit)) { LOG_BAD_TU(Unit); return nullptr; } CXDiagnosticSet D = clang_getDiagnosticSetFromTU(Unit); if (!D) return nullptr; CXDiagnosticSetImpl *Diags = static_cast(D); if (Index >= Diags->getNumDiagnostics()) return nullptr; return Diags->getDiagnostic(Index); } CXDiagnosticSet clang_getDiagnosticSetFromTU(CXTranslationUnit Unit) { if (cxtu::isNotUsableTU(Unit)) { LOG_BAD_TU(Unit); return nullptr; } if (!cxtu::getASTUnit(Unit)) return nullptr; return static_cast(lazyCreateDiags(Unit)); } void clang_disposeDiagnostic(CXDiagnostic Diagnostic) { // No-op. Kept as a legacy API. CXDiagnostics are now managed // by the enclosing CXDiagnosticSet. } CXString clang_formatDiagnostic(CXDiagnostic Diagnostic, unsigned Options) { if (!Diagnostic) return cxstring::createEmpty(); CXDiagnosticSeverity Severity = clang_getDiagnosticSeverity(Diagnostic); SmallString<256> Str; llvm::raw_svector_ostream Out(Str); if (Options & CXDiagnostic_DisplaySourceLocation) { // Print source location (file:line), along with optional column // and source ranges. CXFile File; unsigned Line, Column; clang_getSpellingLocation(clang_getDiagnosticLocation(Diagnostic), &File, &Line, &Column, nullptr); if (File) { CXString FName = clang_getFileName(File); Out << clang_getCString(FName) << ":" << Line << ":"; clang_disposeString(FName); if (Options & CXDiagnostic_DisplayColumn) Out << Column << ":"; if (Options & CXDiagnostic_DisplaySourceRanges) { unsigned N = clang_getDiagnosticNumRanges(Diagnostic); bool PrintedRange = false; for (unsigned I = 0; I != N; ++I) { CXFile StartFile, EndFile; CXSourceRange Range = clang_getDiagnosticRange(Diagnostic, I); unsigned StartLine, StartColumn, EndLine, EndColumn; clang_getSpellingLocation(clang_getRangeStart(Range), &StartFile, &StartLine, &StartColumn, nullptr); clang_getSpellingLocation(clang_getRangeEnd(Range), &EndFile, &EndLine, &EndColumn, nullptr); if (StartFile != EndFile || StartFile != File) continue; Out << "{" << StartLine << ":" << StartColumn << "-" << EndLine << ":" << EndColumn << "}"; PrintedRange = true; } if (PrintedRange) Out << ":"; } Out << " "; } } /* Print warning/error/etc. */ switch (Severity) { case CXDiagnostic_Ignored: llvm_unreachable("impossible"); case CXDiagnostic_Note: Out << "note: "; break; case CXDiagnostic_Warning: Out << "warning: "; break; case CXDiagnostic_Error: Out << "error: "; break; case CXDiagnostic_Fatal: Out << "fatal error: "; break; } CXString Text = clang_getDiagnosticSpelling(Diagnostic); if (clang_getCString(Text)) Out << clang_getCString(Text); else Out << ""; clang_disposeString(Text); if (Options & (CXDiagnostic_DisplayOption | CXDiagnostic_DisplayCategoryId | CXDiagnostic_DisplayCategoryName)) { bool NeedBracket = true; bool NeedComma = false; if (Options & CXDiagnostic_DisplayOption) { CXString OptionName = clang_getDiagnosticOption(Diagnostic, nullptr); if (const char *OptionText = clang_getCString(OptionName)) { if (OptionText[0]) { Out << " [" << OptionText; NeedBracket = false; NeedComma = true; } } clang_disposeString(OptionName); } if (Options & (CXDiagnostic_DisplayCategoryId | CXDiagnostic_DisplayCategoryName)) { if (unsigned CategoryID = clang_getDiagnosticCategory(Diagnostic)) { if (Options & CXDiagnostic_DisplayCategoryId) { if (NeedBracket) Out << " ["; if (NeedComma) Out << ", "; Out << CategoryID; NeedBracket = false; NeedComma = true; } if (Options & CXDiagnostic_DisplayCategoryName) { CXString CategoryName = clang_getDiagnosticCategoryText(Diagnostic); if (NeedBracket) Out << " ["; if (NeedComma) Out << ", "; Out << clang_getCString(CategoryName); NeedBracket = false; NeedComma = true; clang_disposeString(CategoryName); } } } (void) NeedComma; // Silence dead store warning. if (!NeedBracket) Out << "]"; } return cxstring::createDup(Out.str()); } unsigned clang_defaultDiagnosticDisplayOptions() { return CXDiagnostic_DisplaySourceLocation | CXDiagnostic_DisplayColumn | CXDiagnostic_DisplayOption; } enum CXDiagnosticSeverity clang_getDiagnosticSeverity(CXDiagnostic Diag) { if (CXDiagnosticImpl *D = static_cast(Diag)) return D->getSeverity(); return CXDiagnostic_Ignored; } CXSourceLocation clang_getDiagnosticLocation(CXDiagnostic Diag) { if (CXDiagnosticImpl *D = static_cast(Diag)) return D->getLocation(); return clang_getNullLocation(); } CXString clang_getDiagnosticSpelling(CXDiagnostic Diag) { if (CXDiagnosticImpl *D = static_cast(Diag)) return D->getSpelling(); return cxstring::createEmpty(); } CXString clang_getDiagnosticOption(CXDiagnostic Diag, CXString *Disable) { if (Disable) *Disable = cxstring::createEmpty(); if (CXDiagnosticImpl *D = static_cast(Diag)) return D->getDiagnosticOption(Disable); return cxstring::createEmpty(); } unsigned clang_getDiagnosticCategory(CXDiagnostic Diag) { if (CXDiagnosticImpl *D = static_cast(Diag)) return D->getCategory(); return 0; } CXString clang_getDiagnosticCategoryName(unsigned Category) { // Kept for backward compatibility. return cxstring::createRef(DiagnosticIDs::getCategoryNameFromID(Category)); } CXString clang_getDiagnosticCategoryText(CXDiagnostic Diag) { if (CXDiagnosticImpl *D = static_cast(Diag)) return D->getCategoryText(); return cxstring::createEmpty(); } unsigned clang_getDiagnosticNumRanges(CXDiagnostic Diag) { if (CXDiagnosticImpl *D = static_cast(Diag)) return D->getNumRanges(); return 0; } CXSourceRange clang_getDiagnosticRange(CXDiagnostic Diag, unsigned Range) { CXDiagnosticImpl *D = static_cast(Diag); if (!D || Range >= D->getNumRanges()) return clang_getNullRange(); return D->getRange(Range); } unsigned clang_getDiagnosticNumFixIts(CXDiagnostic Diag) { if (CXDiagnosticImpl *D = static_cast(Diag)) return D->getNumFixIts(); return 0; } CXString clang_getDiagnosticFixIt(CXDiagnostic Diag, unsigned FixIt, CXSourceRange *ReplacementRange) { CXDiagnosticImpl *D = static_cast(Diag); if (!D || FixIt >= D->getNumFixIts()) { if (ReplacementRange) *ReplacementRange = clang_getNullRange(); return cxstring::createEmpty(); } return D->getFixIt(FixIt, ReplacementRange); } void clang_disposeDiagnosticSet(CXDiagnosticSet Diags) { if (CXDiagnosticSetImpl *D = static_cast(Diags)) { if (D->isExternallyManaged()) delete D; } } CXDiagnostic clang_getDiagnosticInSet(CXDiagnosticSet Diags, unsigned Index) { if (CXDiagnosticSetImpl *D = static_cast(Diags)) if (Index < D->getNumDiagnostics()) return D->getDiagnostic(Index); return nullptr; } CXDiagnosticSet clang_getChildDiagnostics(CXDiagnostic Diag) { if (CXDiagnosticImpl *D = static_cast(Diag)) { CXDiagnosticSetImpl &ChildDiags = D->getChildDiagnostics(); return ChildDiags.empty() ? nullptr : (CXDiagnosticSet) &ChildDiags; } return nullptr; } unsigned clang_getNumDiagnosticsInSet(CXDiagnosticSet Diags) { if (CXDiagnosticSetImpl *D = static_cast(Diags)) return D->getNumDiagnostics(); return 0; }