From: Ted Kremenek Date: Thu, 10 Nov 2011 08:43:12 +0000 (+0000) Subject: serialized diagnostics: implement full deserialization of clang diagnostics via the... X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=153221717e39ce41323d5bc6b8b8bf130923c1bd;p=clang serialized diagnostics: implement full deserialization of clang diagnostics via the libclang API. I've tested it on simple cases and it works. Test cases to follow as well as a few tweaks. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@144269 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/clang-c/Index.h b/include/clang-c/Index.h index 10fa53853a..941381d79e 100644 --- a/include/clang-c/Index.h +++ b/include/clang-c/Index.h @@ -521,6 +521,86 @@ enum CXDiagnosticSeverity { */ typedef void *CXDiagnostic; +/** + * \brief A group of CXDiagnostics. + */ +typedef void *CXDiagnosticSet; + +/** + * \brief Determine the number of diagnostics in a CXDiagnosticSet. + */ +CINDEX_LINKAGE unsigned clang_getNumDiagnosticsInSet(CXDiagnosticSet Diags); + +/** + * \brief Retrieve a diagnostic associated with the given CXDiagnosticSet. + * + * \param Unit the CXDiagnosticSet to query. + * \param Index the zero-based diagnostic number to retrieve. + * + * \returns the requested diagnostic. This diagnostic must be freed + * via a call to \c clang_disposeDiagnostic(). + */ +CINDEX_LINKAGE CXDiagnostic clang_getDiagnosticInSet(CXDiagnosticSet Diags, + unsigned Index); + + +/** + * \brief Describes the kind of error that occurred (if any) in a call to + * \c clang_loadDiagnostics. + */ +enum CXLoadDiag_Error { + /** + * \brief Indicates that no error occurred. + */ + CXLoadDiag_None = 0, + + /** + * \brief Indicates that an unknown error occurred while attempting to + * deserialize diagnostics. + */ + CXLoadDiag_Unknown = 1, + + /** + * \brief Indicates that the file containing the serialized diagnostics + * could not be opened. + */ + CXLoadDiag_CannotLoad = 2, + + /** + * \brief Indicates that the serialized diagnostics file is invalid or + * corrupt. + */ + CXLoadDiag_InvalidFile = 3 +}; + +/** + * \brief Deserialize a set of diagnostics from a Clang diagnostics bitcode + * file. + * + * \param The name of the file to deserialize. + * \param A pointer to a enum value recording if there was a problem + * deserializing the diagnostics. + * \param A pointer to a CXString for recording the error string + * if the file was not successfully loaded. + * + * \returns A loaded CXDiagnosticSet if successful, and NULL otherwise. These + * diagnostics should be released using clang_disposeDiagnosticSet(). + */ +CINDEX_LINKAGE CXDiagnosticSet clang_loadDiagnostics(const char *file, + enum CXLoadDiag_Error *error, + CXString *errorString); + +/** + * \brief Release a CXDiagnosticSet and all of its contained diagnostics. + */ +CINDEX_LINKAGE void clang_disposeDiagnosticSet(CXDiagnosticSet Diags); + +/** + * \brief Retrieve the child diagnostics of a CXDiagnostic. This + * CXDiagnosticSet does not need to be released by clang_diposeDiagnosticSet. + */ +CINDEX_LINKAGE CXDiagnosticSet clang_getChildDiagnostics(CXDiagnostic D); + /** * \brief Determine the number of diagnostics produced for the given * translation unit. diff --git a/include/clang/Frontend/SerializedDiagnosticPrinter.h b/include/clang/Frontend/SerializedDiagnosticPrinter.h index d063df90ae..af4390fb12 100644 --- a/include/clang/Frontend/SerializedDiagnosticPrinter.h +++ b/include/clang/Frontend/SerializedDiagnosticPrinter.h @@ -39,7 +39,9 @@ enum RecordIDs { RECORD_DIAG_FLAG, RECORD_CATEGORY, RECORD_FILENAME, - RECORD_FIXIT + RECORD_FIXIT, + RECORD_FIRST = RECORD_VERSION, + RECORD_LAST = RECORD_FIXIT }; /// \brief Returns a DiagnosticConsumer that serializes diagnostics to diff --git a/lib/Frontend/SerializedDiagnosticPrinter.cpp b/lib/Frontend/SerializedDiagnosticPrinter.cpp index 5984567e2c..974ec535ed 100644 --- a/lib/Frontend/SerializedDiagnosticPrinter.cpp +++ b/lib/Frontend/SerializedDiagnosticPrinter.cpp @@ -16,6 +16,7 @@ #include "clang/Basic/FileManager.h" #include "clang/Basic/Diagnostic.h" #include "clang/Basic/Version.h" +#include "clang/Lex/Lexer.h" #include "clang/Frontend/SerializedDiagnosticPrinter.h" using namespace clang; @@ -47,7 +48,8 @@ typedef llvm::SmallVectorImpl RecordDataImpl; class SDiagsWriter : public DiagnosticConsumer { public: SDiagsWriter(DiagnosticsEngine &diags, llvm::raw_ostream *os) - : Stream(Buffer), OS(os), Diags(diags), inNonNoteDiagnostic(false) + : LangOpts(0), Stream(Buffer), OS(os), Diags(diags), + inNonNoteDiagnostic(false) { EmitPreamble(); }; @@ -59,6 +61,11 @@ public: void EndSourceFile(); + void BeginSourceFile(const LangOptions &LO, + const Preprocessor *PP) { + LangOpts = &LO; + } + DiagnosticConsumer *clone(DiagnosticsEngine &Diags) const { // It makes no sense to clone this. return 0; @@ -88,7 +95,8 @@ private: unsigned getEmitFile(SourceLocation Loc); /// \brief Add SourceLocation information the specified record. - void AddLocToRecord(SourceLocation Loc, RecordDataImpl &Record); + void AddLocToRecord(SourceLocation Loc, RecordDataImpl &Record, + unsigned TokSize = 0); /// \brief Add CharSourceRange information the specified record. void AddCharSourceRangeToRecord(CharSourceRange R, RecordDataImpl &Record); @@ -96,6 +104,8 @@ private: /// \brief The version of the diagnostics file. enum { Version = 1 }; + const LangOptions *LangOpts; + /// \brief The byte buffer for the serialized content. std::vector Buffer; @@ -181,13 +191,14 @@ static void EmitRecordID(unsigned ID, const char *Name, } void SDiagsWriter::AddLocToRecord(SourceLocation Loc, - RecordDataImpl &Record) { + RecordDataImpl &Record, + unsigned TokSize) { if (Loc.isInvalid()) { // Emit a "sentinel" location. - Record.push_back((unsigned) 0); // File. - Record.push_back(~(unsigned)0); // Line. - Record.push_back(~(unsigned)0); // Column. - Record.push_back(~(unsigned)0); // Offset. + Record.push_back((unsigned)0); // File. + Record.push_back((unsigned)0); // Line. + Record.push_back((unsigned)0); // Column. + Record.push_back((unsigned)0); // Offset. return; } @@ -195,7 +206,7 @@ void SDiagsWriter::AddLocToRecord(SourceLocation Loc, Loc = SM.getSpellingLoc(Loc); Record.push_back(getEmitFile(Loc)); Record.push_back(SM.getSpellingLineNumber(Loc)); - Record.push_back(SM.getSpellingColumnNumber(Loc)); + Record.push_back(SM.getSpellingColumnNumber(Loc)+TokSize); std::pair LocInfo = SM.getDecomposedLoc(Loc); FileID FID = LocInfo.first; @@ -206,7 +217,13 @@ void SDiagsWriter::AddLocToRecord(SourceLocation Loc, void SDiagsWriter::AddCharSourceRangeToRecord(CharSourceRange Range, RecordDataImpl &Record) { AddLocToRecord(Range.getBegin(), Record); - AddLocToRecord(Range.getEnd(), Record); + unsigned TokSize = 0; + if (Range.isTokenRange()) + TokSize = Lexer::MeasureTokenLength(Range.getEnd(), + Diags.getSourceManager(), + *LangOpts); + + AddLocToRecord(Range.getEnd(), Record, TokSize); } unsigned SDiagsWriter::getEmitFile(SourceLocation Loc) { diff --git a/tools/c-index-test/c-index-test.c b/tools/c-index-test/c-index-test.c index dd691d94b1..3a3bfd734e 100644 --- a/tools/c-index-test/c-index-test.c +++ b/tools/c-index-test/c-index-test.c @@ -2430,6 +2430,155 @@ int write_pch_file(const char *filename, int argc, const char *argv[]) { return result; } +/******************************************************************************/ +/* Serialized diagnostics. */ +/******************************************************************************/ + +static const char *getDiagnosticCodeStr(enum CXLoadDiag_Error error) { + switch (error) { + case CXLoadDiag_CannotLoad: return "Cannot Load File"; + case CXLoadDiag_None: break; + case CXLoadDiag_Unknown: return "Unknown"; + case CXLoadDiag_InvalidFile: return "Invalid File"; + } + return "None"; +} + +static const char *getSeverityString(enum CXDiagnosticSeverity severity) { + switch (severity) { + case CXDiagnostic_Note: return "note"; + case CXDiagnostic_Error: return "error"; + case CXDiagnostic_Fatal: return "fatal"; + case CXDiagnostic_Ignored: return "ignored"; + case CXDiagnostic_Warning: return "warning"; + } + return "unknown"; +} + +static void printIndent(unsigned indent) { + while (indent > 0) { + fprintf(stderr, " "); + --indent; + } +} + +static void printLocation(CXSourceLocation L) { + CXFile File; + CXString FileName; + unsigned line, column, offset; + + clang_getExpansionLocation(L, &File, &line, &column, &offset); + FileName = clang_getFileName(File); + + fprintf(stderr, "%s:%d:%d", clang_getCString(FileName), line, column); + clang_disposeString(FileName); +} + +static void printRanges(CXDiagnostic D, unsigned indent) { + unsigned i, n = clang_getDiagnosticNumRanges(D); + + for (i = 0; i < n; ++i) { + CXSourceLocation Start, End; + CXSourceRange SR = clang_getDiagnosticRange(D, i); + Start = clang_getRangeStart(SR); + End = clang_getRangeEnd(SR); + + printIndent(indent); + fprintf(stderr, "Range: "); + printLocation(Start); + fprintf(stderr, " "); + printLocation(End); + fprintf(stderr, "\n"); + } +} + +static void printFixIts(CXDiagnostic D, unsigned indent) { + unsigned i, n = clang_getDiagnosticNumFixIts(D); + for (i = 0 ; i < n; ++i) { + CXSourceRange ReplacementRange; + CXString text; + text = clang_getDiagnosticFixIt(D, i, &ReplacementRange); + + printIndent(indent); + fprintf(stderr, "FIXIT: ("); + printLocation(clang_getRangeStart(ReplacementRange)); + fprintf(stderr, " - "); + printLocation(clang_getRangeEnd(ReplacementRange)); + fprintf(stderr, "): \"%s\"\n", clang_getCString(text)); + clang_disposeString(text); + } +} + +static void printDiagnosticSet(CXDiagnosticSet Diags, unsigned indent) { + if (!Diags) + return; + + fprintf(stderr, "\n"); + + unsigned i = 0; + unsigned n = clang_getNumDiagnosticsInSet(Diags); + for (i = 0; i < n; ++i) { + CXSourceLocation DiagLoc; + CXDiagnostic D; + CXFile File; + CXString FileName, DiagSpelling, DiagOption; + unsigned line, column, offset; + const char *DiagOptionStr = 0; + + D = clang_getDiagnosticInSet(Diags, i); + DiagLoc = clang_getDiagnosticLocation(D); + clang_getExpansionLocation(DiagLoc, &File, &line, &column, &offset); + FileName = clang_getFileName(File); + DiagSpelling = clang_getDiagnosticSpelling(D); + + printIndent(indent); + + fprintf(stderr, "%s:%d:%d: %s: %s", + clang_getCString(FileName), + line, + column, + getSeverityString(clang_getDiagnosticSeverity(D)), + clang_getCString(DiagSpelling)); + + DiagOption = clang_getDiagnosticOption(D, 0); + DiagOptionStr = clang_getCString(DiagOption); + if (DiagOptionStr) { + fprintf(stderr, " [%s]", DiagOptionStr); + } + + fprintf(stderr, "\n"); + + printRanges(D, indent); + printFixIts(D, indent); + + // Print subdiagnostics. + printDiagnosticSet(clang_getChildDiagnostics(D), indent+2); + + clang_disposeString(FileName); + clang_disposeString(DiagSpelling); + clang_disposeString(DiagOption); + } +} + +static int read_diagnostics(const char *filename) { + enum CXLoadDiag_Error error; + CXString errorString; + CXDiagnosticSet Diags = 0; + + Diags = clang_loadDiagnostics(filename, &error, &errorString); + if (!Diags) { + fprintf(stderr, "Trouble deserializing file (%s): %s\n", + getDiagnosticCodeStr(error), + clang_getCString(errorString)); + clang_disposeString(errorString); + return 1; + } + + printDiagnosticSet(Diags, 0); + clang_disposeDiagnosticSet(Diags); + return 0; +} + /******************************************************************************/ /* Command line processing. */ /******************************************************************************/ @@ -2475,7 +2624,9 @@ static void print_usage(void) { " c-index-test -test-print-typekind {}*\n" " c-index-test -print-usr [ {}]*\n" " c-index-test -print-usr-file \n" - " c-index-test -write-pch \n\n"); + " c-index-test -write-pch \n"); + fprintf(stderr, + " c-index-test -read-diagnostics \n\n"); fprintf(stderr, " values:\n%s", " all - load all symbols, including those from PCH\n" @@ -2492,6 +2643,8 @@ static void print_usage(void) { int cindextest_main(int argc, const char **argv) { clang_enableStackTraces(); + if (argc > 2 && strcmp(argv[1], "-read-diagnostics") == 0) + return read_diagnostics(argv[2]); if (argc > 2 && strstr(argv[1], "-code-completion-at=") == argv[1]) return perform_code_completion(argc, argv, 0); if (argc > 2 && strstr(argv[1], "-code-completion-timing=") == argv[1]) diff --git a/tools/libclang/CIndex.cpp b/tools/libclang/CIndex.cpp index 0f93931034..f9a873819a 100644 --- a/tools/libclang/CIndex.cpp +++ b/tools/libclang/CIndex.cpp @@ -58,6 +58,7 @@ CXTranslationUnit cxtu::MakeCXTranslationUnit(ASTUnit *TU) { CXTranslationUnit D = new CXTranslationUnitImpl(); D->TUData = TU; D->StringPool = createCXStringPool(); + D->Diagnostics = 0; return D; } @@ -2562,6 +2563,7 @@ void clang_disposeTranslationUnit(CXTranslationUnit CTUnit) { delete static_cast(CTUnit->TUData); disposeCXStringPool(CTUnit->StringPool); + delete static_cast(CTUnit->Diagnostics); delete CTUnit; } } @@ -2582,6 +2584,11 @@ static void clang_reparseTranslationUnit_Impl(void *UserData) { ReparseTranslationUnitInfo *RTUI = static_cast(UserData); CXTranslationUnit TU = RTUI->TU; + + // Reset the associated diagnostics. + delete static_cast(TU->Diagnostics); + TU->Diagnostics = 0; + unsigned num_unsaved_files = RTUI->num_unsaved_files; struct CXUnsavedFile *unsaved_files = RTUI->unsaved_files; unsigned options = RTUI->options; diff --git a/tools/libclang/CIndexDiagnostic.cpp b/tools/libclang/CIndexDiagnostic.cpp index 1beb993863..436ea37038 100644 --- a/tools/libclang/CIndexDiagnostic.cpp +++ b/tools/libclang/CIndexDiagnostic.cpp @@ -28,27 +28,58 @@ using namespace clang::cxloc; using namespace clang::cxstring; using namespace llvm; + +CXDiagnosticSetImpl::~CXDiagnosticSetImpl() { + for (std::vector::iterator it = Diagnostics.begin(), + et = Diagnostics.end(); + it != et; ++it) { + delete *it; + } +} + +CXDiagnosticImpl::~CXDiagnosticImpl() {} + +static CXDiagnosticSetImpl *lazyCreateDiags(CXTranslationUnit TU) { + if (!TU->Diagnostics) { + ASTUnit *AU = static_cast(TU->TUData); + CXDiagnosticSetImpl *Set = new CXDiagnosticSetImpl(); + TU->Diagnostics = Set; + + for (ASTUnit::stored_diag_iterator it = AU->stored_diag_begin(), + ei = AU->stored_diag_end(); it != ei; ++it) { + CXStoredDiagnostic *D = + new CXStoredDiagnostic(*it, AU->getASTContext().getLangOptions()); + Set->appendDiagnostic(D); + } + } + return static_cast(TU->Diagnostics); +} + //----------------------------------------------------------------------------- // C Interface Routines //----------------------------------------------------------------------------- extern "C" { unsigned clang_getNumDiagnostics(CXTranslationUnit Unit) { - ASTUnit *CXXUnit = static_cast(Unit->TUData); - return CXXUnit? CXXUnit->stored_diag_size() : 0; + if (!Unit->TUData) + return 0; + return lazyCreateDiags(Unit)->getNumDiagnostics(); } CXDiagnostic clang_getDiagnostic(CXTranslationUnit Unit, unsigned Index) { - ASTUnit *CXXUnit = static_cast(Unit->TUData); - if (!CXXUnit || Index >= CXXUnit->stored_diag_size()) + if (!Unit->TUData) + return 0; + + CXDiagnosticSetImpl *Diags = lazyCreateDiags(Unit); + if (Index >= Diags->getNumDiagnostics()) return 0; - return new CXStoredDiagnostic(CXXUnit->stored_diag_begin()[Index], - CXXUnit->getASTContext().getLangOptions()); + return Diags->getDiagnostic(Index); } void clang_disposeDiagnostic(CXDiagnostic Diagnostic) { - delete static_cast(Diagnostic); + // No-op. Kept as a legacy API. CXDiagnostics are now managed + // by the enclosing CXDiagnosticSet. } CXString clang_formatDiagnostic(CXDiagnostic Diagnostic, unsigned Options) { @@ -243,4 +274,32 @@ CXString clang_getDiagnosticFixIt(CXDiagnostic Diag, unsigned FixIt, return D->getFixIt(FixIt, ReplacementRange); } +void clang_disposeDiagnosticSet(CXDiagnosticSet Diags) { + 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 0; +} + +CXDiagnosticSet clang_getChildDiagnostics(CXDiagnostic Diag) { + if (CXDiagnosticImpl *D = static_cast(Diag)) { + CXDiagnosticSetImpl &ChildDiags = D->getChildDiagnostics(); + return ChildDiags.empty() ? 0 : (CXDiagnosticSet) &ChildDiags; + } + return 0; +} + +unsigned clang_getNumDiagnosticsInSet(CXDiagnosticSet Diags) { + if (CXDiagnosticSetImpl *D = static_cast(Diags)) + return D->getNumDiagnostics(); + return 0; +} + } // end extern "C" diff --git a/tools/libclang/CIndexDiagnostic.h b/tools/libclang/CIndexDiagnostic.h index 07b6b15968..cc00cfe346 100644 --- a/tools/libclang/CIndexDiagnostic.h +++ b/tools/libclang/CIndexDiagnostic.h @@ -14,15 +14,47 @@ #define LLVM_CLANG_CINDEX_DIAGNOSTIC_H #include "clang-c/Index.h" +#include +#include namespace clang { class LangOptions; class StoredDiagnostic; +class CXDiagnosticImpl; + +class CXDiagnosticSetImpl { + std::vector Diagnostics; + const bool IsExternallyManaged; +public: + CXDiagnosticSetImpl(bool isManaged = false) + : IsExternallyManaged(isManaged) {} + + virtual ~CXDiagnosticSetImpl(); + + size_t getNumDiagnostics() const { + return Diagnostics.size(); + } + + CXDiagnosticImpl *getDiagnostic(unsigned i) const { + assert(i < getNumDiagnostics()); + return Diagnostics[i]; + } + + void appendDiagnostic(CXDiagnosticImpl *D) { + Diagnostics.push_back(D); + } + + bool empty() const { + return Diagnostics.empty(); + } + + bool isExternallyManaged() const { return IsExternallyManaged; } +}; class CXDiagnosticImpl { public: - enum Kind { StoredDiagnosticKind, SerializedDiagnosticKind }; + enum Kind { StoredDiagnosticKind, LoadedDiagnosticKind }; virtual ~CXDiagnosticImpl(); @@ -55,9 +87,18 @@ public: CXSourceRange *ReplacementRange) const = 0; Kind getKind() const { return K; } - + + CXDiagnosticSetImpl &getChildDiagnostics() { + return ChildDiags; + } + protected: CXDiagnosticImpl(Kind k) : K(k) {} + CXDiagnosticSetImpl ChildDiags; + + void append(CXDiagnosticImpl *D) { + ChildDiags.appendDiagnostic(D); + } private: Kind K; diff --git a/tools/libclang/CMakeLists.txt b/tools/libclang/CMakeLists.txt index e4a5527112..742448d2ea 100644 --- a/tools/libclang/CMakeLists.txt +++ b/tools/libclang/CMakeLists.txt @@ -29,6 +29,8 @@ set(SOURCES CIndexer.h CXCursor.cpp CXCursor.h + CXLoadedDiagnostic.cpp + CXLoadedDiagnostic.h CXSourceLocation.cpp CXSourceLocation.h CXStoredDiagnostic.cpp diff --git a/tools/libclang/CXLoadedDiagnostic.cpp b/tools/libclang/CXLoadedDiagnostic.cpp new file mode 100644 index 0000000000..a36976a770 --- /dev/null +++ b/tools/libclang/CXLoadedDiagnostic.cpp @@ -0,0 +1,655 @@ +/*===-- CXLoadedDiagnostic.cpp - Handling of persisent diags -*- C++ -*-===*\ +|* *| +|* The LLVM Compiler Infrastructure *| +|* *| +|* This file is distributed under the University of Illinois Open Source *| +|* License. See LICENSE.TXT for details. *| +|* *| +|*===----------------------------------------------------------------------===*| +|* *| +|* Implements handling of persisent diagnostics. *| +|* *| +\*===----------------------------------------------------------------------===*/ + +#include "CXLoadedDiagnostic.h" +#include "CXString.h" +#include "clang/Basic/Diagnostic.h" +#include "clang/Basic/FileManager.h" +#include "clang/Frontend/SerializedDiagnosticPrinter.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/ADT/Twine.h" +#include "llvm/ADT/Optional.h" +#include "clang/Basic/LLVM.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Bitcode/BitstreamReader.h" +#include "llvm/Support/MemoryBuffer.h" +#include + +using namespace clang; +using namespace clang::cxstring; + +//===----------------------------------------------------------------------===// +// Extend CXDiagnosticSetImpl which contains strings for diagnostics. +//===----------------------------------------------------------------------===// + +typedef llvm::DenseMap Strings; + +namespace { +class CXLoadedDiagnosticSetImpl : public CXDiagnosticSetImpl { +public: + CXLoadedDiagnosticSetImpl() : CXDiagnosticSetImpl(true), FakeFiles(FO) {} + virtual ~CXLoadedDiagnosticSetImpl() {} + + llvm::StringRef makeString(const char *blob, unsigned blobLen); + + llvm::BumpPtrAllocator Alloc; + Strings Categories; + Strings WarningFlags; + Strings FileNames; + + FileSystemOptions FO; + FileManager FakeFiles; + llvm::DenseMap Files; +}; +} + +llvm::StringRef CXLoadedDiagnosticSetImpl::makeString(const char *blob, + unsigned bloblen) { + char *mem = Alloc.Allocate(bloblen); + memcpy(mem, blob, bloblen); + return llvm::StringRef(mem, bloblen); +} + +//===----------------------------------------------------------------------===// +// Cleanup. +//===----------------------------------------------------------------------===// + +CXLoadedDiagnostic::~CXLoadedDiagnostic() {} + +//===----------------------------------------------------------------------===// +// Public CXLoadedDiagnostic methods. +//===----------------------------------------------------------------------===// + +CXDiagnosticSeverity CXLoadedDiagnostic::getSeverity() const { + // FIXME: possibly refactor with logic in CXStoredDiagnostic. + switch (severity) { + case DiagnosticsEngine::Ignored: return CXDiagnostic_Ignored; + case DiagnosticsEngine::Note: return CXDiagnostic_Note; + case DiagnosticsEngine::Warning: return CXDiagnostic_Warning; + case DiagnosticsEngine::Error: return CXDiagnostic_Error; + case DiagnosticsEngine::Fatal: return CXDiagnostic_Fatal; + } + + llvm_unreachable("Invalid diagnostic level"); + return CXDiagnostic_Ignored; +} + +static CXSourceLocation makeLocation(const CXLoadedDiagnostic::Location *DLoc) { + // The lowest bit of ptr_data[0] is always set to 1 to indicate this + // is a persistent diagnostic. + uintptr_t V = (uintptr_t) DLoc; + V |= 0x1; + CXSourceLocation Loc = { { (void*) V, 0 }, 0 }; + return Loc; +} + +CXSourceLocation CXLoadedDiagnostic::getLocation() const { + // The lowest bit of ptr_data[0] is always set to 1 to indicate this + // is a persistent diagnostic. + return makeLocation(&DiagLoc); +} + +CXString CXLoadedDiagnostic::getSpelling() const { + return cxstring::createCXString(Spelling, false); +} + +CXString CXLoadedDiagnostic::getDiagnosticOption(CXString *Disable) const { + if (DiagOption.empty()) + return createCXString(""); + + // FIXME: possibly refactor with logic in CXStoredDiagnostic. + if (Disable) + *Disable = createCXString((Twine("-Wno-") + DiagOption).str()); + return createCXString((Twine("-W") + DiagOption).str()); +} + +unsigned CXLoadedDiagnostic::getCategory() const { + return category; +} + +unsigned CXLoadedDiagnostic::getNumRanges() const { + return Ranges.size(); +} + +CXSourceRange CXLoadedDiagnostic::getRange(unsigned Range) const { + assert(Range < Ranges.size()); + return Ranges[Range]; +} + +unsigned CXLoadedDiagnostic::getNumFixIts() const { + return FixIts.size(); +} + +CXString CXLoadedDiagnostic::getFixIt(unsigned FixIt, + CXSourceRange *ReplacementRange) const { + assert(FixIt < FixIts.size()); + if (ReplacementRange) + *ReplacementRange = FixIts[FixIt].first; + return FixIts[FixIt].second; +} + +void CXLoadedDiagnostic::decodeLocation(CXSourceLocation location, + CXFile *file, + unsigned int *line, + unsigned int *column, + unsigned int *offset) { + + + // CXSourceLocation consists of the following fields: + // + // void *ptr_data[2]; + // unsigned int_data; + // + // The lowest bit of ptr_data[0] is always set to 1 to indicate this + // is a persistent diagnostic. + // + // For now, do the unoptimized approach and store the data in a side + // data structure. We can optimize this case later. + + uintptr_t V = (uintptr_t) location.ptr_data[0]; + assert((V & 0x1) == 1); + V &= ~(uintptr_t)1; + + const Location &Loc = *((Location*)V); + + if (file) + *file = Loc.file; + if (line) + *line = Loc.line; + if (column) + *column = Loc.column; + if (offset) + *offset = Loc.offset; +} + +//===----------------------------------------------------------------------===// +// Deserialize diagnostics. +//===----------------------------------------------------------------------===// + +enum { MaxSupportedVersion = 1 }; +typedef SmallVector RecordData; +enum LoadResult { Failure = 1, Success = 0 }; +enum StreamResult { Read_EndOfStream, + Read_BlockBegin, + Read_Failure, + Read_Record, + Read_BlockEnd }; + +namespace { +class DiagLoader { + enum CXLoadDiag_Error *error; + CXString *errorString; + + void reportBad(enum CXLoadDiag_Error code, llvm::StringRef err) { + if (error) + *error = code; + if (errorString) + *errorString = createCXString(err); + } + + void reportInvalidFile(llvm::StringRef err) { + return reportBad(CXLoadDiag_InvalidFile, err); + } + + LoadResult readMetaBlock(llvm::BitstreamCursor &Stream); + + LoadResult readDiagnosticBlock(llvm::BitstreamCursor &Stream, + CXDiagnosticSetImpl &Diags, + CXLoadedDiagnosticSetImpl &TopDiags); + + StreamResult readToNextRecordOrBlock(llvm::BitstreamCursor &Stream, + llvm::StringRef errorContext, + unsigned &BlockOrRecordID, + const bool atTopLevel = false); + + + LoadResult readString(CXLoadedDiagnosticSetImpl &TopDiags, + Strings &strings, llvm::StringRef errorContext, + RecordData &Record, + const char *BlobStart, + unsigned BlobLen); + + LoadResult readString(CXLoadedDiagnosticSetImpl &TopDiags, + llvm::StringRef &RetStr, + llvm::StringRef errorContext, + RecordData &Record, + const char *BlobStart, + unsigned BlobLen); + + LoadResult readRange(CXLoadedDiagnosticSetImpl &TopDiags, + RecordData &Record, unsigned RecStartIdx, + CXSourceRange &SR); + + LoadResult readLocation(CXLoadedDiagnosticSetImpl &TopDiags, + RecordData &Record, unsigned &offset, + CXLoadedDiagnostic::Location &Loc); + +public: + DiagLoader(enum CXLoadDiag_Error *e, CXString *es) + : error(e), errorString(es) { + if (error) + *error = CXLoadDiag_None; + if (errorString) + *errorString = createCXString(""); + } + + CXDiagnosticSet load(const char *file); +}; +} + +CXDiagnosticSet DiagLoader::load(const char *file) { + // Open the diagnostics file. + std::string ErrStr; + FileSystemOptions FO; + FileManager FileMgr(FO); + + llvm::OwningPtr Buffer; + Buffer.reset(FileMgr.getBufferForFile(file)); + + if (!Buffer) { + reportBad(CXLoadDiag_CannotLoad, ErrStr); + return 0; + } + + llvm::BitstreamReader StreamFile; + StreamFile.init((const unsigned char *)Buffer->getBufferStart(), + (const unsigned char *)Buffer->getBufferEnd()); + + llvm::BitstreamCursor Stream; + Stream.init(StreamFile); + + // Sniff for the signature. + if (Stream.Read(8) != 'D' || + Stream.Read(8) != 'I' || + Stream.Read(8) != 'A' || + Stream.Read(8) != 'G') { + reportBad(CXLoadDiag_InvalidFile, + "Bad header in diagnostics file"); + return 0; + } + + llvm::OwningPtr + Diags(new CXLoadedDiagnosticSetImpl()); + + while (true) { + unsigned BlockID = 0; + StreamResult Res = readToNextRecordOrBlock(Stream, "Top-level", + BlockID, true); + switch (Res) { + case Read_EndOfStream: + return (CXDiagnosticSet) Diags.take(); + case Read_Failure: + return 0; + case Read_Record: + llvm_unreachable("Top-level does not have records"); + return 0; + case Read_BlockEnd: + continue; + case Read_BlockBegin: + break; + } + + switch (BlockID) { + case serialized_diags::BLOCK_META: + if (readMetaBlock(Stream)) + return 0; + break; + case serialized_diags::BLOCK_DIAG: + if (readDiagnosticBlock(Stream, *Diags.get(), *Diags.get())) + return 0; + break; + default: + if (!Stream.SkipBlock()) { + reportInvalidFile("Malformed block at top-level of diagnostics file"); + return 0; + } + break; + } + } +} + +StreamResult DiagLoader::readToNextRecordOrBlock(llvm::BitstreamCursor &Stream, + llvm::StringRef errorContext, + unsigned &blockOrRecordID, + const bool atTopLevel) { + + blockOrRecordID = 0; + + while (!Stream.AtEndOfStream()) { + unsigned Code = Stream.ReadCode(); + + // Handle the top-level specially. + if (atTopLevel) { + if (Code == llvm::bitc::ENTER_SUBBLOCK) { + unsigned BlockID = Stream.ReadSubBlockID(); + if (BlockID == llvm::bitc::BLOCKINFO_BLOCK_ID) { + if (Stream.ReadBlockInfoBlock()) { + reportInvalidFile("Malformed BlockInfoBlock in diagnostics file"); + return Read_Failure; + } + continue; + } + blockOrRecordID = BlockID; + return Read_BlockBegin; + } + reportInvalidFile("Only blocks can appear at the top of a " + "diagnostic file"); + return Read_Failure; + } + + switch ((llvm::bitc::FixedAbbrevIDs)Code) { + case llvm::bitc::ENTER_SUBBLOCK: + blockOrRecordID = Stream.ReadSubBlockID(); + return Read_BlockBegin; + + case llvm::bitc::END_BLOCK: + if (Stream.ReadBlockEnd()) { + reportInvalidFile("Cannot read end of block"); + return Read_Failure; + } + return Read_BlockEnd; + + case llvm::bitc::DEFINE_ABBREV: + Stream.ReadAbbrevRecord(); + continue; + + case llvm::bitc::UNABBREV_RECORD: + reportInvalidFile("Diagnostics file should have no unabbreviated " + "records"); + return Read_Failure; + + default: + // We found a record. + blockOrRecordID = Code; + return Read_Record; + } + } + + if (atTopLevel) + return Read_EndOfStream; + + reportInvalidFile(Twine("Premature end of diagnostics file within ").str() + + errorContext.str()); + return Read_Failure; +} + +LoadResult DiagLoader::readMetaBlock(llvm::BitstreamCursor &Stream) { + if (Stream.EnterSubBlock(clang::serialized_diags::BLOCK_META)) { + reportInvalidFile("Malformed metadata block"); + return Failure; + } + + bool versionChecked = false; + + while (true) { + unsigned blockOrCode = 0; + StreamResult Res = readToNextRecordOrBlock(Stream, "Metadata Block", + blockOrCode); + + switch(Res) { + case Read_EndOfStream: + llvm_unreachable("EndOfStream handled by readToNextRecordOrBlock"); + case Read_Failure: + return Failure; + case Read_Record: + break; + case Read_BlockBegin: + if (Stream.SkipBlock()) { + reportInvalidFile("Malformed metadata block"); + return Failure; + } + case Read_BlockEnd: + if (!versionChecked) { + reportInvalidFile("Diagnostics file does not contain version" + " information"); + return Failure; + } + return Success; + } + + RecordData Record; + const char *Blob; + unsigned BlobLen; + unsigned recordID = Stream.ReadRecord(blockOrCode, Record, &Blob, &BlobLen); + + if (recordID == serialized_diags::RECORD_VERSION) { + if (Record.size() < 1) { + reportInvalidFile("malformed VERSION identifier in diagnostics file"); + return Failure; + } + if (Record[0] > MaxSupportedVersion) { + reportInvalidFile("diagnosics file is a newer version than the one " + "supported"); + return Failure; + } + versionChecked = true; + } + } +} + +LoadResult DiagLoader::readString(CXLoadedDiagnosticSetImpl &TopDiags, + llvm::StringRef &RetStr, + llvm::StringRef errorContext, + RecordData &Record, + const char *BlobStart, + unsigned BlobLen) { + + // Basic buffer overflow check. + if (BlobLen > 65536) { + reportInvalidFile(std::string("Out-of-bounds string in ") + + std::string(errorContext)); + return Failure; + } + + if (Record.size() < 1 || BlobLen == 0) { + reportInvalidFile(std::string("Corrupted ") + std::string(errorContext) + + std::string(" entry")); + return Failure; + } + + RetStr = TopDiags.makeString(BlobStart, BlobLen); + return Success; +} + +LoadResult DiagLoader::readString(CXLoadedDiagnosticSetImpl &TopDiags, + Strings &strings, + llvm::StringRef errorContext, + RecordData &Record, + const char *BlobStart, + unsigned BlobLen) { + llvm::StringRef RetStr; + if (readString(TopDiags, RetStr, errorContext, Record, BlobStart, BlobLen)) + return Failure; + strings[Record[0]] = RetStr; + return Success; +} + +LoadResult DiagLoader::readLocation(CXLoadedDiagnosticSetImpl &TopDiags, + RecordData &Record, unsigned &offset, + CXLoadedDiagnostic::Location &Loc) { + if (Record.size() < offset + 3) { + reportInvalidFile("Corrupted source location"); + return Failure; + } + + unsigned fileID = Record[offset++]; + if (fileID == 0) { + // Sentinel value. + Loc.file = 0; + Loc.line = 0; + Loc.column = 0; + Loc.offset = 0; + return Success; + } + + const FileEntry *FE = TopDiags.Files[fileID]; + if (!FE) { + reportInvalidFile("Corrupted file entry in source location"); + return Failure; + } + Loc.file = (void*) FE; + Loc.line = Record[offset++]; + Loc.column = Record[offset++]; + Loc.offset = Record[offset++]; + return Success; +} + +LoadResult DiagLoader::readRange(CXLoadedDiagnosticSetImpl &TopDiags, + RecordData &Record, + unsigned int RecStartIdx, + CXSourceRange &SR) { + CXLoadedDiagnostic::Location *Start, *End; + Start = TopDiags.Alloc.Allocate(); + End = TopDiags.Alloc.Allocate(); + + if (readLocation(TopDiags, Record, RecStartIdx, *Start)) + return Failure; + if (readLocation(TopDiags, Record, RecStartIdx, *End)) + return Failure; + + CXSourceLocation startLoc = makeLocation(Start); + CXSourceLocation endLoc = makeLocation(End); + SR = clang_getRange(startLoc, endLoc); + return Success; +} + +LoadResult DiagLoader::readDiagnosticBlock(llvm::BitstreamCursor &Stream, + CXDiagnosticSetImpl &Diags, + CXLoadedDiagnosticSetImpl &TopDiags){ + + if (Stream.EnterSubBlock(clang::serialized_diags::BLOCK_DIAG)) { + reportInvalidFile("malformed diagnostic block"); + return Failure; + } + + llvm::OwningPtr D(new CXLoadedDiagnostic()); + RecordData Record; + + while (true) { + unsigned blockOrCode = 0; + StreamResult Res = readToNextRecordOrBlock(Stream, "Diagnostic Block", + blockOrCode); + switch (Res) { + case Read_EndOfStream: + llvm_unreachable("EndOfStream handled in readToNextRecordOrBlock"); + return Failure; + case Read_Failure: + return Failure; + case Read_BlockBegin: { + // The only blocks we care about are subdiagnostics. + if (blockOrCode != serialized_diags::BLOCK_DIAG) { + if (!Stream.SkipBlock()) { + reportInvalidFile("Invalid subblock in Diagnostics block"); + return Failure; + } + } else if (readDiagnosticBlock(Stream, D->getChildDiagnostics(), + TopDiags)) { + return Failure; + } + + continue; + } + case Read_BlockEnd: + Diags.appendDiagnostic(D.take()); + return Success; + case Read_Record: + break; + } + + // Read the record. + Record.clear(); + const char *BlobStart = 0; + unsigned BlobLen = 0; + unsigned recID = Stream.ReadRecord(blockOrCode, Record, + BlobStart, BlobLen); + + if (recID < serialized_diags::RECORD_FIRST || + recID > serialized_diags::RECORD_LAST) + continue; + + switch ((serialized_diags::RecordIDs)recID) { + case serialized_diags::RECORD_VERSION: + continue; + case serialized_diags::RECORD_CATEGORY: + if (readString(TopDiags, TopDiags.Categories, "category", Record, + BlobStart, BlobLen)) + return Failure; + continue; + + case serialized_diags::RECORD_DIAG_FLAG: + if (readString(TopDiags, TopDiags.WarningFlags, "warning flag", Record, + BlobStart, BlobLen)) + return Failure; + continue; + + case serialized_diags::RECORD_FILENAME: { + if (readString(TopDiags, TopDiags.FileNames, "filename", Record, + BlobStart, BlobLen)) + return Failure; + + if (Record.size() < 3) { + reportInvalidFile("Invalid file entry"); + return Failure; + } + + const FileEntry *FE = + TopDiags.FakeFiles.getVirtualFile(TopDiags.FileNames[Record[0]], + /* size */ Record[1], + /* time */ Record[2]); + + TopDiags.Files[Record[0]] = FE; + continue; + } + + case serialized_diags::RECORD_SOURCE_RANGE: { + CXSourceRange SR; + if (readRange(TopDiags, Record, 0, SR)) + return Failure; + D->Ranges.push_back(SR); + continue; + } + + case serialized_diags::RECORD_FIXIT: { + CXSourceRange SR; + if (readRange(TopDiags, Record, 0, SR)) + return Failure; + llvm::StringRef RetStr; + if (readString(TopDiags, RetStr, "FIXIT", Record, BlobStart, BlobLen)) + return Failure; + D->FixIts.push_back(std::make_pair(SR, createCXString(RetStr, false))); + continue; + } + + case serialized_diags::RECORD_DIAG: { + D->severity = Record[0]; + unsigned offset = 1; + if (readLocation(TopDiags, Record, offset, D->DiagLoc)) + return Failure; + D->category = Record[offset++]; + unsigned diagFlag = Record[offset++]; + D->DiagOption = diagFlag ? TopDiags.WarningFlags[diagFlag] : ""; + D->Spelling = TopDiags.makeString(BlobStart, BlobLen); + continue; + } + } + } +} + +extern "C" { +CXDiagnosticSet clang_loadDiagnostics(const char *file, + enum CXLoadDiag_Error *error, + CXString *errorString) { + DiagLoader L(error, errorString); + return L.load(file); +} +} // end extern 'C'. diff --git a/tools/libclang/CXLoadedDiagnostic.h b/tools/libclang/CXLoadedDiagnostic.h new file mode 100644 index 0000000000..9eff144901 --- /dev/null +++ b/tools/libclang/CXLoadedDiagnostic.h @@ -0,0 +1,90 @@ +/*===-- CXLoadedDiagnostic.h - Handling of persisent diags ------*- C++ -*-===*\ +|* *| +|* The LLVM Compiler Infrastructure *| +|* *| +|* This file is distributed under the University of Illinois Open Source *| +|* License. See LICENSE.TXT for details. *| +|* *| +|*===----------------------------------------------------------------------===*| +|* *| +|* Implements handling of persisent diagnostics. *| +|* *| +\*===----------------------------------------------------------------------===*/ + +#ifndef LLVM_CLANG_CINDEX_LOADED_DIAGNOSTIC_H +#define LLVM_CLANG_CINDEX_LOADED_DIAGNOSTIC_H + +#include "CIndexDiagnostic.h" +#include "llvm/ADT/StringRef.h" +#include "clang/Basic/LLVM.h" +#include +#include + +namespace clang { +class CXLoadedDiagnostic : public CXDiagnosticImpl { +public: + CXLoadedDiagnostic() : CXDiagnosticImpl(LoadedDiagnosticKind), + severity(0), category(0) {} + + virtual ~CXLoadedDiagnostic(); + + /// \brief Return the severity of the diagnostic. + virtual CXDiagnosticSeverity getSeverity() const; + + /// \brief Return the location of the diagnostic. + virtual CXSourceLocation getLocation() const; + + /// \brief Return the spelling of the diagnostic. + virtual CXString getSpelling() const; + + /// \brief Return the text for the diagnostic option. + virtual CXString getDiagnosticOption(CXString *Disable) const; + + /// \brief Return the category of the diagnostic. + virtual unsigned getCategory() const; + + /// \brief Return the number of source ranges for the diagnostic. + virtual unsigned getNumRanges() const; + + /// \brief Return the source ranges for the diagnostic. + virtual CXSourceRange getRange(unsigned Range) const; + + /// \brief Return the number of FixIts. + virtual unsigned getNumFixIts() const; + + /// \brief Return the FixIt information (source range and inserted text). + virtual CXString getFixIt(unsigned FixIt, + CXSourceRange *ReplacementRange) const; + + static bool classof(const CXDiagnosticImpl *D) { + return D->getKind() == LoadedDiagnosticKind; + } + + /// \brief Decode the CXSourceLocation into file, line, column, and offset. + static void decodeLocation(CXSourceLocation location, + CXFile *file, + unsigned *line, + unsigned *column, + unsigned *offset); + + struct Location { + CXFile file; + unsigned line; + unsigned column; + unsigned offset; + + Location() : line(0), column(0), offset(0) {} + }; + + Location DiagLoc; + + std::vector Ranges; + std::vector > FixIts; + llvm::StringRef Spelling; + llvm::StringRef DiagOption; + unsigned severity; + unsigned category; +}; +} + +#endif diff --git a/tools/libclang/CXSourceLocation.cpp b/tools/libclang/CXSourceLocation.cpp index d8d4c96e48..a6bf8fcf1c 100644 --- a/tools/libclang/CXSourceLocation.cpp +++ b/tools/libclang/CXSourceLocation.cpp @@ -17,6 +17,7 @@ #include "CXString.h" #include "CXSourceLocation.h" #include "CXTranslationUnit.h" +#include "CXLoadedDiagnostic.h" using namespace clang; using namespace clang::cxstring; @@ -54,6 +55,13 @@ CXSourceRange clang_getNullRange() { } CXSourceRange clang_getRange(CXSourceLocation begin, CXSourceLocation end) { + if (!isASTUnitSourceLocation(begin)) { + if (isASTUnitSourceLocation(end)) + return clang_getNullRange(); + CXSourceRange Result = { { begin.ptr_data[0], end.ptr_data[0] }, 0, 0 }; + return Result; + } + if (begin.ptr_data[0] != end.ptr_data[0] || begin.ptr_data[1] != end.ptr_data[1]) return clang_getNullRange(); @@ -64,7 +72,6 @@ CXSourceRange clang_getRange(CXSourceLocation begin, CXSourceLocation end) { return Result; } - unsigned clang_equalRanges(CXSourceRange range1, CXSourceRange range2) { return range1.ptr_data[0] == range2.ptr_data[0] && range1.ptr_data[1] == range2.ptr_data[1] @@ -78,12 +85,24 @@ int clang_Range_isNull(CXSourceRange range) { CXSourceLocation clang_getRangeStart(CXSourceRange range) { + // Special decoding for CXSourceLocations for CXLoadedDiagnostics. + if ((uintptr_t)range.ptr_data[0] & 0x1) { + CXSourceLocation Result = { { range.ptr_data[0], 0 }, 0 }; + return Result; + } + CXSourceLocation Result = { { range.ptr_data[0], range.ptr_data[1] }, range.begin_int_data }; return Result; } CXSourceLocation clang_getRangeEnd(CXSourceRange range) { + // Special decoding for CXSourceLocations for CXLoadedDiagnostics. + if ((uintptr_t)range.ptr_data[0] & 0x1) { + CXSourceLocation Result = { { range.ptr_data[1], 0 }, 0 }; + return Result; + } + CXSourceLocation Result = { { range.ptr_data[0], range.ptr_data[1] }, range.end_int_data }; return Result; @@ -182,41 +201,40 @@ void clang_getExpansionLocation(CXSourceLocation location, unsigned *column, unsigned *offset) { - if (isASTUnitSourceLocation(location)) { - SourceLocation Loc = SourceLocation::getFromRawEncoding(location.int_data); - - if (!location.ptr_data[0] || Loc.isInvalid()) { - createNullLocation(file, line, column, offset); - return; - } + if (!isASTUnitSourceLocation(location)) { + CXLoadedDiagnostic::decodeLocation(location, file, line, column, offset); + return; + } + + SourceLocation Loc = SourceLocation::getFromRawEncoding(location.int_data); + + if (!location.ptr_data[0] || Loc.isInvalid()) { + createNullLocation(file, line, column, offset); + return; + } + + const SourceManager &SM = + *static_cast(location.ptr_data[0]); + SourceLocation ExpansionLoc = SM.getExpansionLoc(Loc); - const SourceManager &SM = - *static_cast(location.ptr_data[0]); - SourceLocation ExpansionLoc = SM.getExpansionLoc(Loc); - - // Check that the FileID is invalid on the expansion location. - // This can manifest in invalid code. - FileID fileID = SM.getFileID(ExpansionLoc); - bool Invalid = false; - const SrcMgr::SLocEntry &sloc = SM.getSLocEntry(fileID, &Invalid); - if (Invalid || !sloc.isFile()) { - createNullLocation(file, line, column, offset); - return; - } - - if (file) - *file = (void *)SM.getFileEntryForSLocEntry(sloc); - if (line) - *line = SM.getExpansionLineNumber(ExpansionLoc); - if (column) - *column = SM.getExpansionColumnNumber(ExpansionLoc); - if (offset) - *offset = SM.getDecomposedLoc(ExpansionLoc).second; + // Check that the FileID is invalid on the expansion location. + // This can manifest in invalid code. + FileID fileID = SM.getFileID(ExpansionLoc); + bool Invalid = false; + const SrcMgr::SLocEntry &sloc = SM.getSLocEntry(fileID, &Invalid); + if (Invalid || !sloc.isFile()) { + createNullLocation(file, line, column, offset); return; } - // FIXME: - createNullLocation(file, line, column, offset); + if (file) + *file = (void *)SM.getFileEntryForSLocEntry(sloc); + if (line) + *line = SM.getExpansionLineNumber(ExpansionLoc); + if (column) + *column = SM.getExpansionColumnNumber(ExpansionLoc); + if (offset) + *offset = SM.getDecomposedLoc(ExpansionLoc).second; } void clang_getPresumedLocation(CXSourceLocation location, @@ -224,28 +242,29 @@ void clang_getPresumedLocation(CXSourceLocation location, unsigned *line, unsigned *column) { - if (isASTUnitSourceLocation(location)) { - SourceLocation Loc = SourceLocation::getFromRawEncoding(location.int_data); - - if (!location.ptr_data[0] || Loc.isInvalid()) - createNullLocation(filename, line, column); - else { - const SourceManager &SM = - *static_cast(location.ptr_data[0]); - PresumedLoc PreLoc = SM.getPresumedLoc(Loc); - - if (filename) - *filename = createCXString(PreLoc.getFilename()); - if (line) - *line = PreLoc.getLine(); - if (column) - *column = PreLoc.getColumn(); - } + if (!isASTUnitSourceLocation(location)) { + // Other SourceLocation implementations do not support presumed locations + // at this time. + createNullLocation(filename, line, column); return; } - - // FIXME: - createNullLocation(filename, line, column); + + SourceLocation Loc = SourceLocation::getFromRawEncoding(location.int_data); + + if (!location.ptr_data[0] || Loc.isInvalid()) + createNullLocation(filename, line, column); + else { + const SourceManager &SM = + *static_cast(location.ptr_data[0]); + PresumedLoc PreLoc = SM.getPresumedLoc(Loc); + + if (filename) + *filename = createCXString(PreLoc.getFilename()); + if (line) + *line = PreLoc.getLine(); + if (column) + *column = PreLoc.getColumn(); + } } void clang_getInstantiationLocation(CXSourceLocation location, @@ -263,44 +282,44 @@ void clang_getSpellingLocation(CXSourceLocation location, unsigned *column, unsigned *offset) { - if (isASTUnitSourceLocation(location)) { - SourceLocation Loc = SourceLocation::getFromRawEncoding(location.int_data); - - if (!location.ptr_data[0] || Loc.isInvalid()) - return createNullLocation(file, line, column, offset); - - const SourceManager &SM = - *static_cast(location.ptr_data[0]); - SourceLocation SpellLoc = Loc; - if (SpellLoc.isMacroID()) { - SourceLocation SimpleSpellingLoc = SM.getImmediateSpellingLoc(SpellLoc); - if (SimpleSpellingLoc.isFileID() && - SM.getFileEntryForID(SM.getDecomposedLoc(SimpleSpellingLoc).first)) - SpellLoc = SimpleSpellingLoc; - else - SpellLoc = SM.getExpansionLoc(SpellLoc); - } - - std::pair LocInfo = SM.getDecomposedLoc(SpellLoc); - FileID FID = LocInfo.first; - unsigned FileOffset = LocInfo.second; - - if (FID.isInvalid()) - return createNullLocation(file, line, column, offset); - - if (file) - *file = (void *)SM.getFileEntryForID(FID); - if (line) - *line = SM.getLineNumber(FID, FileOffset); - if (column) - *column = SM.getColumnNumber(FID, FileOffset); - if (offset) - *offset = FileOffset; + if (!isASTUnitSourceLocation(location)) { + CXLoadedDiagnostic::decodeLocation(location, file, line, + column, offset); return; } - - // FIXME: - createNullLocation(file, line, column, offset); + + SourceLocation Loc = SourceLocation::getFromRawEncoding(location.int_data); + + if (!location.ptr_data[0] || Loc.isInvalid()) + return createNullLocation(file, line, column, offset); + + const SourceManager &SM = + *static_cast(location.ptr_data[0]); + SourceLocation SpellLoc = Loc; + if (SpellLoc.isMacroID()) { + SourceLocation SimpleSpellingLoc = SM.getImmediateSpellingLoc(SpellLoc); + if (SimpleSpellingLoc.isFileID() && + SM.getFileEntryForID(SM.getDecomposedLoc(SimpleSpellingLoc).first)) + SpellLoc = SimpleSpellingLoc; + else + SpellLoc = SM.getExpansionLoc(SpellLoc); + } + + std::pair LocInfo = SM.getDecomposedLoc(SpellLoc); + FileID FID = LocInfo.first; + unsigned FileOffset = LocInfo.second; + + if (FID.isInvalid()) + return createNullLocation(file, line, column, offset); + + if (file) + *file = (void *)SM.getFileEntryForID(FID); + if (line) + *line = SM.getLineNumber(FID, FileOffset); + if (column) + *column = SM.getColumnNumber(FID, FileOffset); + if (offset) + *offset = FileOffset; } } // end extern "C" diff --git a/tools/libclang/CXStoredDiagnostic.cpp b/tools/libclang/CXStoredDiagnostic.cpp index 117e5398be..fb5d33d584 100644 --- a/tools/libclang/CXStoredDiagnostic.cpp +++ b/tools/libclang/CXStoredDiagnostic.cpp @@ -28,9 +28,6 @@ using namespace clang; using namespace clang::cxloc; using namespace clang::cxstring; -// Needed for vtable of CXPersisetntDiagnostic. -CXDiagnosticImpl::~CXDiagnosticImpl() {} - CXDiagnosticSeverity CXStoredDiagnostic::getSeverity() const { switch (Diag.getLevel()) { case DiagnosticsEngine::Ignored: return CXDiagnostic_Ignored; diff --git a/tools/libclang/CXTranslationUnit.h b/tools/libclang/CXTranslationUnit.h index 47d7146116..b2b1b84037 100644 --- a/tools/libclang/CXTranslationUnit.h +++ b/tools/libclang/CXTranslationUnit.h @@ -18,6 +18,7 @@ extern "C" { struct CXTranslationUnitImpl { void *TUData; void *StringPool; + void *Diagnostics; }; } @@ -27,7 +28,7 @@ namespace clang { namespace cxtu { CXTranslationUnitImpl *MakeCXTranslationUnit(ASTUnit *TU); - + class CXTUOwner { CXTranslationUnitImpl *TU; diff --git a/tools/libclang/libclang.exports b/tools/libclang/libclang.exports index e224f3baa7..a82bd999d5 100644 --- a/tools/libclang/libclang.exports +++ b/tools/libclang/libclang.exports @@ -32,6 +32,7 @@ clang_disposeCXCursorSet clang_disposeCXTUResourceUsage clang_disposeCodeCompleteResults clang_disposeDiagnostic +clang_disposeDiagnosticSet clang_disposeIndex clang_disposeOverriddenCursors clang_disposeString @@ -53,6 +54,7 @@ clang_getCXTUResourceUsage clang_getCXXAccessSpecifier clang_getCanonicalCursor clang_getCanonicalType +clang_getChildDiagnostics clang_getClangVersion clang_getCompletionAnnotation clang_getCompletionAvailability @@ -86,6 +88,7 @@ clang_getDiagnostic clang_getDiagnosticCategory clang_getDiagnosticCategoryName clang_getDiagnosticFixIt +clang_getDiagnosticInSet clang_getDiagnosticLocation clang_getDiagnosticNumFixIts clang_getDiagnosticNumRanges @@ -108,6 +111,7 @@ clang_getNullLocation clang_getNullRange clang_getNumCompletionChunks clang_getNumDiagnostics +clang_getNumDiagnosticsInSet clang_getNumOverloadedDecls clang_getOverloadedDecl clang_getOverriddenCursors @@ -150,6 +154,7 @@ clang_isTranslationUnit clang_isUnexposed clang_isVirtualBase clang_isVolatileQualifiedType +clang_loadDiagnostics clang_parseTranslationUnit clang_remap_dispose clang_remap_getFilenames