|  | /*===-- 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<CXDiagnosticImpl> 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<const StoredDiagnostic*>(); | 
|  | if (!SD) | 
|  | return; | 
|  |  | 
|  | if (Level != DiagnosticsEngine::Note) | 
|  | CurrentSet = MainSet; | 
|  |  | 
|  | auto Owner = llvm::make_unique<CXStoredDiagnostic>(*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<CharSourceRange> 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<CXDiagnosticCustomNoteImpl>(Message, L)); | 
|  | } | 
|  |  | 
|  | void emitDiagnosticLoc(FullSourceLoc Loc, PresumedLoc PLoc, | 
|  | DiagnosticsEngine::Level Level, | 
|  | ArrayRef<CharSourceRange> Ranges) override {} | 
|  |  | 
|  | void emitCodeContext(FullSourceLoc Loc, DiagnosticsEngine::Level Level, | 
|  | SmallVectorImpl<CharSourceRange> &Ranges, | 
|  | ArrayRef<FixItHint> 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<CXDiagnosticCustomNoteImpl>(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<CXDiagnosticSetImpl*>(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<DiagnosticOptions> 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<CXDiagnosticSetImpl*>(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<CXDiagnosticSetImpl*>(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<CXDiagnostic>(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 << "<no diagnostic text>"; | 
|  | 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<CXDiagnosticImpl*>(Diag)) | 
|  | return D->getSeverity(); | 
|  | return CXDiagnostic_Ignored; | 
|  | } | 
|  |  | 
|  | CXSourceLocation clang_getDiagnosticLocation(CXDiagnostic Diag) { | 
|  | if (CXDiagnosticImpl *D = static_cast<CXDiagnosticImpl*>(Diag)) | 
|  | return D->getLocation(); | 
|  | return clang_getNullLocation(); | 
|  | } | 
|  |  | 
|  | CXString clang_getDiagnosticSpelling(CXDiagnostic Diag) { | 
|  | if (CXDiagnosticImpl *D = static_cast<CXDiagnosticImpl *>(Diag)) | 
|  | return D->getSpelling(); | 
|  | return cxstring::createEmpty(); | 
|  | } | 
|  |  | 
|  | CXString clang_getDiagnosticOption(CXDiagnostic Diag, CXString *Disable) { | 
|  | if (Disable) | 
|  | *Disable = cxstring::createEmpty(); | 
|  |  | 
|  | if (CXDiagnosticImpl *D = static_cast<CXDiagnosticImpl *>(Diag)) | 
|  | return D->getDiagnosticOption(Disable); | 
|  |  | 
|  | return cxstring::createEmpty(); | 
|  | } | 
|  |  | 
|  | unsigned clang_getDiagnosticCategory(CXDiagnostic Diag) { | 
|  | if (CXDiagnosticImpl *D = static_cast<CXDiagnosticImpl *>(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<CXDiagnosticImpl *>(Diag)) | 
|  | return D->getCategoryText(); | 
|  | return cxstring::createEmpty(); | 
|  | } | 
|  |  | 
|  | unsigned clang_getDiagnosticNumRanges(CXDiagnostic Diag) { | 
|  | if (CXDiagnosticImpl *D = static_cast<CXDiagnosticImpl *>(Diag)) | 
|  | return D->getNumRanges(); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | CXSourceRange clang_getDiagnosticRange(CXDiagnostic Diag, unsigned Range) { | 
|  | CXDiagnosticImpl *D = static_cast<CXDiagnosticImpl *>(Diag); | 
|  | if (!D || Range >= D->getNumRanges()) | 
|  | return clang_getNullRange(); | 
|  | return D->getRange(Range); | 
|  | } | 
|  |  | 
|  | unsigned clang_getDiagnosticNumFixIts(CXDiagnostic Diag) { | 
|  | if (CXDiagnosticImpl *D = static_cast<CXDiagnosticImpl *>(Diag)) | 
|  | return D->getNumFixIts(); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | CXString clang_getDiagnosticFixIt(CXDiagnostic Diag, unsigned FixIt, | 
|  | CXSourceRange *ReplacementRange) { | 
|  | CXDiagnosticImpl *D = static_cast<CXDiagnosticImpl *>(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<CXDiagnosticSetImpl *>(Diags)) { | 
|  | if (D->isExternallyManaged()) | 
|  | delete D; | 
|  | } | 
|  | } | 
|  |  | 
|  | CXDiagnostic clang_getDiagnosticInSet(CXDiagnosticSet Diags, | 
|  | unsigned Index) { | 
|  | if (CXDiagnosticSetImpl *D = static_cast<CXDiagnosticSetImpl*>(Diags)) | 
|  | if (Index < D->getNumDiagnostics()) | 
|  | return D->getDiagnostic(Index); | 
|  | return nullptr; | 
|  | } | 
|  |  | 
|  | CXDiagnosticSet clang_getChildDiagnostics(CXDiagnostic Diag) { | 
|  | if (CXDiagnosticImpl *D = static_cast<CXDiagnosticImpl *>(Diag)) { | 
|  | CXDiagnosticSetImpl &ChildDiags = D->getChildDiagnostics(); | 
|  | return ChildDiags.empty() ? nullptr : (CXDiagnosticSet) &ChildDiags; | 
|  | } | 
|  | return nullptr; | 
|  | } | 
|  |  | 
|  | unsigned clang_getNumDiagnosticsInSet(CXDiagnosticSet Diags) { | 
|  | if (CXDiagnosticSetImpl *D = static_cast<CXDiagnosticSetImpl*>(Diags)) | 
|  | return D->getNumDiagnostics(); | 
|  | return 0; | 
|  | } |