From: Douglas Gregor Date: Thu, 28 Jan 2010 06:00:51 +0000 (+0000) Subject: Introduce serialization and deserialization of diagnostic information X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=d93256e55673a17d18543397ec462416acb13792;p=clang Introduce serialization and deserialization of diagnostic information so that CIndex can report diagnostics through the normal mechanisms even when executing Clang in a separate process. This applies both when performing code completion and when using ASTs as an intermediary for clang_createTranslationUnitFromSourceFile(). The serialized format is not perfect at the moment, because it does not encapsulate macro-instantiation information. Instead, it maps all source locations back to the instantiation location. However, it does maintain source-range and fix-it information. To get perfect fidelity from the serialized format would require serializing a large chunk of the source manager; at present, it isn't clear if this code will live long enough for that to matter. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@94740 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/clang/Basic/Diagnostic.h b/include/clang/Basic/Diagnostic.h index c5d6d7c713..1f7cd07c60 100644 --- a/include/clang/Basic/Diagnostic.h +++ b/include/clang/Basic/Diagnostic.h @@ -23,16 +23,19 @@ namespace llvm { template class SmallVectorImpl; + class raw_ostream; } namespace clang { class DeclContext; class DiagnosticBuilder; class DiagnosticClient; + class FileManager; class IdentifierInfo; class LangOptions; class PartialDiagnostic; class Preprocessor; + class SourceManager; class SourceRange; // Import the diagnostic enums themselves. @@ -400,6 +403,13 @@ public: /// \brief Clear out the current diagnostic. void Clear() { CurDiagID = ~0U; } + /// Deserialize - Deserialize the first diagnostic within the memory + /// [Memory, MemoryEnd), producing a new diagnostic builder describing the + /// deserialized diagnostic. If the memory does not contain a + /// diagnostic, returns a diagnostic builder with no diagnostic ID. + DiagnosticBuilder Deserialize(FileManager &FM, SourceManager &SM, + const char *&Memory, const char *MemoryEnd); + private: /// getDiagnosticMappingInfo - Return the mapping info currently set for the /// specified builtin diagnostic. This returns the high bit encoding, or zero @@ -568,6 +578,9 @@ public: /// been emitted. ~DiagnosticBuilder() { Emit(); } + /// isActive - Determine whether this diagnostic is still active. + bool isActive() const { return DiagObj != 0; } + /// Operator bool: conversion of DiagnosticBuilder to bool always returns /// true. This allows is to be used in boolean error contexts like: /// return Diag(...); @@ -786,6 +799,12 @@ public: /// output buffer using the arguments stored in this diagnostic. void FormatDiagnostic(const char *DiagStr, const char *DiagEnd, llvm::SmallVectorImpl &OutStr) const; + + /// Serialize - Serialize the given diagnostic (with its diagnostic + /// level) to the given stream. Serialization is a lossy operation, + /// since the specific diagnostic ID and any macro-instantiation + /// information is lost. + void Serialize(Diagnostic::Level DiagLevel, llvm::raw_ostream &OS) const; }; /// DiagnosticClient - This is an abstract interface implemented by clients of diff --git a/include/clang/Basic/DiagnosticFrontendKinds.td b/include/clang/Basic/DiagnosticFrontendKinds.td index 59287b3def..01104fc543 100644 --- a/include/clang/Basic/DiagnosticFrontendKinds.td +++ b/include/clang/Basic/DiagnosticFrontendKinds.td @@ -19,6 +19,8 @@ def err_fe_invalid_code_complete_file : Error< "cannot locate code-completion file %0">, DefaultFatal; def err_fe_stdout_binary : Error<"unable to change standard output to binary">, DefaultFatal; +def err_fe_stderr_binary : Error<"unable to change standard error to binary">, + DefaultFatal; def err_fe_dependency_file_requires_MT : Error< "-dependency-file requires at least one -MT option">; def err_fe_incompatible_options : Error< diff --git a/include/clang/Basic/SourceManager.h b/include/clang/Basic/SourceManager.h index 59984e7ee8..15ece68510 100644 --- a/include/clang/Basic/SourceManager.h +++ b/include/clang/Basic/SourceManager.h @@ -669,6 +669,9 @@ public: ::const_iterator fileinfo_iterator; fileinfo_iterator fileinfo_begin() const { return FileInfos.begin(); } fileinfo_iterator fileinfo_end() const { return FileInfos.end(); } + bool hasFileInfo(const FileEntry *File) const { + return FileInfos.find(File) != FileInfos.end(); + } /// PrintStats - Print statistics to stderr. /// diff --git a/include/clang/Driver/CC1Options.td b/include/clang/Driver/CC1Options.td index 2511dfb767..6d8ec114c1 100644 --- a/include/clang/Driver/CC1Options.td +++ b/include/clang/Driver/CC1Options.td @@ -165,6 +165,7 @@ def fno_caret_diagnostics : Flag<"-fno-caret-diagnostics">, HelpText<"Do not include source line and caret with diagnostics">; def fno_diagnostics_fixit_info : Flag<"-fno-diagnostics-fixit-info">, HelpText<"Do not include fixit information in diagnostics">; +def fdiagnostics_binary : Flag<"-fdiagnostics-binary">; def w : Flag<"-w">, HelpText<"Suppress all warnings">; def pedantic : Flag<"-pedantic">; def pedantic_errors : Flag<"-pedantic-errors">; diff --git a/include/clang/Driver/Options.td b/include/clang/Driver/Options.td index b384f90051..4b99956da2 100644 --- a/include/clang/Driver/Options.td +++ b/include/clang/Driver/Options.td @@ -252,6 +252,7 @@ def fconstant_string_class_EQ : Joined<"-fconstant-string-class=">, Group, Group; def fdebug_pass_arguments : Flag<"-fdebug-pass-arguments">, Group; def fdebug_pass_structure : Flag<"-fdebug-pass-structure">, Group; +def fdiagnostics_binary : Flag<"-fdiagnostics-binary">, Group, Flags<[HelpHidden]>; def fdiagnostics_fixit_info : Flag<"-fdiagnostics-fixit-info">, Group; def fdiagnostics_print_source_range_info : Flag<"-fdiagnostics-print-source-range-info">, Group; def fdiagnostics_show_option : Flag<"-fdiagnostics-show-option">, Group; diff --git a/include/clang/Frontend/DiagnosticOptions.h b/include/clang/Frontend/DiagnosticOptions.h index 13039bb506..b37c18057f 100644 --- a/include/clang/Frontend/DiagnosticOptions.h +++ b/include/clang/Frontend/DiagnosticOptions.h @@ -31,9 +31,12 @@ public: unsigned ShowOptionNames : 1; /// Show the diagnostic name for mappable /// diagnostics. unsigned ShowColors : 1; /// Show diagnostics with ANSI color sequences. - unsigned VerifyDiagnostics; /// Check that diagnostics match the expected + unsigned VerifyDiagnostics: 1; /// Check that diagnostics match the expected /// diagnostics, indicated by markers in the /// input source file. + unsigned BinaryOutput : 1; /// Emit diagnostics via the diagnostic + /// binary serialization mechanism, to be + /// deserialized by, e.g., the CIndex library. /// The distance between tab stops. unsigned TabStop; @@ -66,6 +69,7 @@ public: ShowOptionNames = 0; ShowSourceRanges = 0; VerifyDiagnostics = 0; + BinaryOutput = 0; } }; diff --git a/lib/Basic/Diagnostic.cpp b/lib/Basic/Diagnostic.cpp index abbf6f9b6e..b2f13bba8d 100644 --- a/lib/Basic/Diagnostic.cpp +++ b/lib/Basic/Diagnostic.cpp @@ -21,8 +21,10 @@ #include "clang/Analysis/AnalysisDiagnostic.h" #include "clang/Driver/DriverDiagnostic.h" +#include "clang/Basic/FileManager.h" #include "clang/Basic/IdentifierTable.h" #include "clang/Basic/SourceLocation.h" +#include "clang/Basic/SourceManager.h" #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringExtras.h" #include "llvm/Support/raw_ostream.h" @@ -385,6 +387,123 @@ Diagnostic::getDiagnosticLevel(unsigned DiagID, unsigned DiagClass) const { return Result; } +static bool ReadUnsigned(const char *&Memory, const char *MemoryEnd, + unsigned &Value) { + if (Memory + sizeof(unsigned) > MemoryEnd) + return true; + + memmove(&Value, Memory, sizeof(unsigned)); + Memory += sizeof(unsigned); + return false; +} + +static bool ReadSourceLocation(FileManager &FM, SourceManager &SM, + const char *&Memory, const char *MemoryEnd, + SourceLocation &Location) { + // Read the filename. + unsigned FileNameLen = 0; + if (ReadUnsigned(Memory, MemoryEnd, FileNameLen) || + Memory + FileNameLen > MemoryEnd) + return true; + + llvm::StringRef FileName(Memory, FileNameLen); + Memory += FileNameLen; + + // Read the line, column. + unsigned Line = 0, Column = 0; + if (ReadUnsigned(Memory, MemoryEnd, Line) || + ReadUnsigned(Memory, MemoryEnd, Column)) + return true; + + if (FileName.empty()) { + Location = SourceLocation(); + return false; + } + + const FileEntry *File = FM.getFile(FileName); + if (!File) + return true; + + // Make sure that this file has an entry in the source manager. + if (!SM.hasFileInfo(File)) + SM.createFileID(File, SourceLocation(), SrcMgr::C_User); + + Location = SM.getLocation(File, Line, Column); + return false; +} + +DiagnosticBuilder Diagnostic::Deserialize(FileManager &FM, SourceManager &SM, + const char *&Memory, + const char *MemoryEnd) { + if (Memory == MemoryEnd) + return DiagnosticBuilder(0); + + // Read the severity level. + unsigned Level = 0; + if (ReadUnsigned(Memory, MemoryEnd, Level) || Level > Fatal) + return DiagnosticBuilder(0); + + // Read the source location. + SourceLocation Location; + if (ReadSourceLocation(FM, SM, Memory, MemoryEnd, Location)) + return DiagnosticBuilder(0); + + // Read the diagnostic text. + if (Memory == MemoryEnd) + return DiagnosticBuilder(0); + + unsigned MessageLen = 0; + if (ReadUnsigned(Memory, MemoryEnd, MessageLen) || + Memory + MessageLen > MemoryEnd) + return DiagnosticBuilder(0); + + llvm::StringRef Message(Memory, MessageLen); + Memory += MessageLen; + + // At this point, we have enough information to form a diagnostic. Do so. + unsigned DiagID = getCustomDiagID((enum Level)Level, Message); + DiagnosticBuilder DB = Report(FullSourceLoc(Location, SM), DiagID); + if (Memory == MemoryEnd) + return DB; + + // Read the source ranges. + unsigned NumSourceRanges = 0; + if (ReadUnsigned(Memory, MemoryEnd, NumSourceRanges)) + return DB; + for (unsigned I = 0; I != NumSourceRanges; ++I) { + SourceLocation Begin, End; + if (ReadSourceLocation(FM, SM, Memory, MemoryEnd, Begin) || + ReadSourceLocation(FM, SM, Memory, MemoryEnd, End)) + return DB; + + DB << SourceRange(Begin, End); + } + + // Read the fix-it hints. + unsigned NumFixIts = 0; + if (ReadUnsigned(Memory, MemoryEnd, NumFixIts)) + return DB; + for (unsigned I = 0; I != NumFixIts; ++I) { + SourceLocation RemoveBegin, RemoveEnd, InsertionLoc; + unsigned InsertLen = 0; + if (ReadSourceLocation(FM, SM, Memory, MemoryEnd, RemoveBegin) || + ReadSourceLocation(FM, SM, Memory, MemoryEnd, RemoveEnd) || + ReadSourceLocation(FM, SM, Memory, MemoryEnd, InsertionLoc) || + ReadUnsigned(Memory, MemoryEnd, InsertLen) || + Memory + InsertLen > MemoryEnd) + return DB; + + CodeModificationHint Hint; + Hint.RemoveRange = SourceRange(RemoveBegin, RemoveEnd); + Hint.InsertionLoc = InsertionLoc; + Hint.CodeToInsert.assign(Memory, Memory + InsertLen); + Memory += InsertLen; + DB << Hint; + } + + return DB; +} + struct WarningOption { const char *Name; const short *Members; @@ -917,6 +1036,104 @@ FormatDiagnostic(const char *DiagStr, const char *DiagEnd, } } +static void WriteUnsigned(llvm::raw_ostream &OS, unsigned Value) { + OS.write((const char *)&Value, sizeof(unsigned)); +} + +static void WriteString(llvm::raw_ostream &OS, llvm::StringRef String) { + WriteUnsigned(OS, String.size()); + OS.write(String.data(), String.size()); +} + +static void WriteSourceLocation(llvm::raw_ostream &OS, + SourceManager *SM, + SourceLocation Location) { + if (!SM || Location.isInvalid()) { + // If we don't have a source manager or this location is invalid, + // just write an invalid location. + WriteUnsigned(OS, 0); + WriteUnsigned(OS, 0); + WriteUnsigned(OS, 0); + return; + } + + Location = SM->getInstantiationLoc(Location); + std::pair Decomposed = SM->getDecomposedLoc(Location); + + WriteString(OS, SM->getFileEntryForID(Decomposed.first)->getName()); + WriteUnsigned(OS, SM->getLineNumber(Decomposed.first, Decomposed.second)); + WriteUnsigned(OS, SM->getColumnNumber(Decomposed.first, Decomposed.second)); +} + +void DiagnosticInfo::Serialize(Diagnostic::Level DiagLevel, + llvm::raw_ostream &OS) const { + SourceManager *SM = 0; + if (getLocation().isValid()) + SM = &const_cast(getLocation().getManager()); + + // Write the diagnostic level and location. + WriteUnsigned(OS, (unsigned)DiagLevel); + WriteSourceLocation(OS, SM, getLocation()); + + // Write the diagnostic message. + llvm::SmallString<64> Message; + FormatDiagnostic(Message); + WriteString(OS, Message); + + // Count the number of ranges that don't point into macros, since + // only simple file ranges serialize well. + unsigned NumNonMacroRanges = 0; + for (unsigned I = 0, N = getNumRanges(); I != N; ++I) { + SourceRange R = getRange(I); + if (R.getBegin().isMacroID() || R.getEnd().isMacroID()) + continue; + + ++NumNonMacroRanges; + } + + // Write the ranges. + WriteUnsigned(OS, NumNonMacroRanges); + if (NumNonMacroRanges) { + for (unsigned I = 0, N = getNumRanges(); I != N; ++I) { + SourceRange R = getRange(I); + if (R.getBegin().isMacroID() || R.getEnd().isMacroID()) + continue; + + WriteSourceLocation(OS, SM, R.getBegin()); + WriteSourceLocation(OS, SM, R.getEnd()); + } + } + + // Determine if all of the fix-its involve rewrites with simple file + // locations (not in macro instantiations). If so, we can write + // fix-it information. + unsigned NumFixIts = getNumCodeModificationHints(); + for (unsigned I = 0; I != NumFixIts; ++I) { + const CodeModificationHint &Hint = getCodeModificationHint(I); + if (Hint.RemoveRange.isValid() && + (Hint.RemoveRange.getBegin().isMacroID() || + Hint.RemoveRange.getEnd().isMacroID())) { + NumFixIts = 0; + break; + } + + if (Hint.InsertionLoc.isValid() && Hint.InsertionLoc.isMacroID()) { + NumFixIts = 0; + break; + } + } + + // Write the fix-its. + WriteUnsigned(OS, NumFixIts); + for (unsigned I = 0; I != NumFixIts; ++I) { + const CodeModificationHint &Hint = getCodeModificationHint(I); + WriteSourceLocation(OS, SM, Hint.RemoveRange.getBegin()); + WriteSourceLocation(OS, SM, Hint.RemoveRange.getEnd()); + WriteSourceLocation(OS, SM, Hint.InsertionLoc); + WriteString(OS, Hint.CodeToInsert); + } +} + /// IncludeInDiagnosticCounts - This method (whose default implementation /// returns true) indicates whether the diagnostics handled by this /// DiagnosticClient should be included in the number of diagnostics diff --git a/lib/Driver/Tools.cpp b/lib/Driver/Tools.cpp index 92507d2659..565c13c275 100644 --- a/lib/Driver/Tools.cpp +++ b/lib/Driver/Tools.cpp @@ -1058,6 +1058,9 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA, options::OPT_fno_diagnostics_fixit_info)) CmdArgs.push_back("-fno-diagnostics-fixit-info"); + if (Args.hasArg(options::OPT_fdiagnostics_binary)) + CmdArgs.push_back("-fdiagnostics-binary"); + // Enable -fdiagnostics-show-option by default. if (Args.hasFlag(options::OPT_fdiagnostics_show_option, options::OPT_fno_diagnostics_show_option)) diff --git a/lib/Frontend/CompilerInstance.cpp b/lib/Frontend/CompilerInstance.cpp index 19c740d17a..6b0fdb8a34 100644 --- a/lib/Frontend/CompilerInstance.cpp +++ b/lib/Frontend/CompilerInstance.cpp @@ -83,6 +83,23 @@ void CompilerInstance::setCodeCompletionConsumer(CodeCompleteConsumer *Value) { } // Diagnostics +namespace { + class BinaryDiagnosticSerializer : public DiagnosticClient { + llvm::raw_ostream &OS; + SourceManager *SourceMgr; + public: + explicit BinaryDiagnosticSerializer(llvm::raw_ostream &OS) + : OS(OS), SourceMgr(0) { } + + virtual void HandleDiagnostic(Diagnostic::Level DiagLevel, + const DiagnosticInfo &Info); + }; +} + +void BinaryDiagnosticSerializer::HandleDiagnostic(Diagnostic::Level DiagLevel, + const DiagnosticInfo &Info) { + Info.Serialize(DiagLevel, OS); +} static void SetUpBuildDumpLog(const DiagnosticOptions &DiagOpts, unsigned argc, char **argv, @@ -122,8 +139,23 @@ Diagnostic *CompilerInstance::createDiagnostics(const DiagnosticOptions &Opts, // Create the diagnostic client for reporting errors or for // implementing -verify. - llvm::OwningPtr DiagClient( - new TextDiagnosticPrinter(llvm::errs(), Opts)); + llvm::OwningPtr DiagClient; + if (Opts.BinaryOutput) { + if (llvm::sys::Program::ChangeStderrToBinary()) { + // We weren't able to set standard error to binary, which is a + // bit of a problem. So, just create a text diagnostic printer + // to complain about this problem, and pretend that the user + // didn't try to use binary output. + DiagClient.reset(new TextDiagnosticPrinter(llvm::errs(), Opts)); + Diags->setClient(DiagClient.take()); + Diags->Report(diag::err_fe_stderr_binary); + return Diags.take(); + } else { + DiagClient.reset(new BinaryDiagnosticSerializer(llvm::errs())); + } + } else { + DiagClient.reset(new TextDiagnosticPrinter(llvm::errs(), Opts)); + } // Chain in -verify checker, if requested. if (Opts.VerifyDiagnostics) diff --git a/lib/Frontend/CompilerInvocation.cpp b/lib/Frontend/CompilerInvocation.cpp index 0bca4754ec..06fc15b597 100644 --- a/lib/Frontend/CompilerInvocation.cpp +++ b/lib/Frontend/CompilerInvocation.cpp @@ -220,6 +220,8 @@ static void DiagnosticOptsToArgs(const DiagnosticOptions &Opts, Res.push_back("-fcolor-diagnostics"); if (Opts.VerifyDiagnostics) Res.push_back("-verify"); + if (Opts.BinaryOutput) + Res.push_back("-fdiagnostics-binary"); if (Opts.ShowOptionNames) Res.push_back("-fdiagnostics-show-option"); if (Opts.TabStop != DiagnosticOptions::DefaultTabStop) { @@ -808,6 +810,7 @@ static void ParseDiagnosticArgs(DiagnosticOptions &Opts, ArgList &Args, Opts.ShowOptionNames = Args.hasArg(OPT_fdiagnostics_show_option); Opts.ShowSourceRanges = Args.hasArg(OPT_fdiagnostics_print_source_range_info); Opts.VerifyDiagnostics = Args.hasArg(OPT_verify); + Opts.BinaryOutput = Args.hasArg(OPT_fdiagnostics_binary); Opts.TabStop = getLastArgIntValue(Args, OPT_ftabstop, DiagnosticOptions::DefaultTabStop, Diags); if (Opts.TabStop == 0 || Opts.TabStop > DiagnosticOptions::MaxTabStop) { diff --git a/test/Index/code-complete-errors.c b/test/Index/code-complete-errors.c new file mode 100644 index 0000000000..43bc5d8a1a --- /dev/null +++ b/test/Index/code-complete-errors.c @@ -0,0 +1,16 @@ +_Complex cd; // CHECK: code-complete-errors.c:1:1: warning: plain '_Complex' requires a type specifier; assuming '_Complex double' + +struct s { + int x, y;; +}; + +struct s s0 = { y: 5 }; // CHECK: code-complete-errors.c:7:20: warning: use of GNU old-style field designator extension + +int f(int *ptr1, float *ptr2) { + return ptr1 != ptr2; // CHECK: code-complete-errors.c:10:15: warning: comparison of distinct pointer types ('int *' and 'float *') +} + +void g() { } + +// RUN: c-index-test -code-completion-at=%s:13:12 %s 2> %t +// RUN: FileCheck -check-prefix=CHECK %s < %t diff --git a/tools/CIndex/CIndex.cpp b/tools/CIndex/CIndex.cpp index 57b463cba0..ee73946dc1 100644 --- a/tools/CIndex/CIndex.cpp +++ b/tools/CIndex/CIndex.cpp @@ -1044,6 +1044,13 @@ clang_createTranslationUnitFromSourceFile(CXIndex CIdx, argv.push_back(arg); } + // Generate a temporary name for the diagnostics file. + char tmpFileResults[L_tmpnam]; + char *tmpResultsFileName = tmpnam(tmpFileResults); + llvm::sys::Path DiagnosticsFile(tmpResultsFileName); + TemporaryFiles.push_back(DiagnosticsFile); + argv.push_back("-fdiagnostics-binary"); + // Add the null terminator. argv.push_back(NULL); @@ -1051,7 +1058,8 @@ clang_createTranslationUnitFromSourceFile(CXIndex CIdx, llvm::sys::Path DevNull; // leave empty, causes redirection to /dev/null // on Unix or NUL (Windows). std::string ErrMsg; - const llvm::sys::Path *Redirects[] = { &DevNull, &DevNull, &DevNull, NULL }; + const llvm::sys::Path *Redirects[] = { &DevNull, &DevNull, &DiagnosticsFile, + NULL }; llvm::sys::Program::ExecuteAndWait(ClangPath, &argv[0], /* env */ NULL, /* redirects */ &Redirects[0], /* secondsToWait */ 0, /* memoryLimits */ 0, &ErrMsg); @@ -1078,6 +1086,9 @@ clang_createTranslationUnitFromSourceFile(CXIndex CIdx, if (ATU) ATU->unlinkTemporaryFile(); + ReportSerializedDiagnostics(DiagnosticsFile, *Diags, + num_unsaved_files, unsaved_files); + for (unsigned i = 0, e = TemporaryFiles.size(); i != e; ++i) TemporaryFiles[i].eraseFromDisk(); diff --git a/tools/CIndex/CIndexCodeCompletion.cpp b/tools/CIndex/CIndexCodeCompletion.cpp index 25b1417c97..fe4eb8af11 100644 --- a/tools/CIndex/CIndexCodeCompletion.cpp +++ b/tools/CIndex/CIndexCodeCompletion.cpp @@ -231,7 +231,8 @@ CXCodeCompleteResults *clang_codeComplete(CXIndex CIdx, argv.push_back("-no-code-completion-debug-printer"); argv.push_back("-Xclang"); argv.push_back("-code-completion-macros"); - + argv.push_back("-fdiagnostics-binary"); + // Remap any unsaved files to temporary files. std::vector RemapArgs; if (RemapFiles(num_unsaved_files, unsaved_files, RemapArgs, TemporaryFiles)) @@ -267,17 +268,24 @@ CXCodeCompleteResults *clang_codeComplete(CXIndex CIdx, // Add the null terminator. argv.push_back(NULL); - // Generate a temporary name for the AST file. + // Generate a temporary name for the code-completion results file. char tmpFile[L_tmpnam]; char *tmpFileName = tmpnam(tmpFile); llvm::sys::Path ResultsFile(tmpFileName); TemporaryFiles.push_back(ResultsFile); + // Generate a temporary name for the diagnostics file. + char tmpFileResults[L_tmpnam]; + char *tmpResultsFileName = tmpnam(tmpFileResults); + llvm::sys::Path DiagnosticsFile(tmpResultsFileName); + TemporaryFiles.push_back(DiagnosticsFile); + // Invoke 'clang'. llvm::sys::Path DevNull; // leave empty, causes redirection to /dev/null // on Unix or NUL (Windows). std::string ErrMsg; - const llvm::sys::Path *Redirects[] = { &DevNull, &ResultsFile, &DevNull, 0 }; + const llvm::sys::Path *Redirects[] = { &DevNull, &ResultsFile, + &DiagnosticsFile, 0 }; llvm::sys::Program::ExecuteAndWait(ClangPath, &argv[0], /* env */ NULL, /* redirects */ &Redirects[0], /* secondsToWait */ 0, @@ -331,7 +339,8 @@ CXCodeCompleteResults *clang_codeComplete(CXIndex CIdx, Results->Buffer = F; } - // FIXME: Parse the (redirected) standard error to emit diagnostics. + ReportSerializedDiagnostics(DiagnosticsFile, *Diags, + num_unsaved_files, unsaved_files); for (unsigned i = 0, e = TemporaryFiles.size(); i != e; ++i) TemporaryFiles[i].eraseFromDisk(); diff --git a/tools/CIndex/CIndexDiagnostic.cpp b/tools/CIndex/CIndexDiagnostic.cpp index 9a21f383bd..eb039dff6e 100644 --- a/tools/CIndex/CIndexDiagnostic.cpp +++ b/tools/CIndex/CIndexDiagnostic.cpp @@ -14,6 +14,9 @@ #include "CIndexer.h" #include "CXSourceLocation.h" +#include "clang/Frontend/FrontendDiagnostic.h" +#include "llvm/Support/MemoryBuffer.h" + using namespace clang; using namespace clang::cxloc; @@ -196,3 +199,47 @@ CXString clang_getDiagnosticFixItReplacement(CXDiagnostic Diag, } } // end extern "C" + +void clang::ReportSerializedDiagnostics(const llvm::sys::Path &DiagnosticsPath, + Diagnostic &Diags, + unsigned num_unsaved_files, + struct CXUnsavedFile *unsaved_files) { + using llvm::MemoryBuffer; + using llvm::StringRef; + MemoryBuffer *F = MemoryBuffer::getFile(DiagnosticsPath.c_str()); + if (!F) + return; + + // Enter the unsaved files into the file manager. + SourceManager SourceMgr; + FileManager FileMgr; + for (unsigned I = 0; I != num_unsaved_files; ++I) { + const FileEntry *File = FileMgr.getVirtualFile(unsaved_files[I].Filename, + unsaved_files[I].Length, + 0); + if (!File) { + Diags.Report(diag::err_fe_remap_missing_from_file) + << unsaved_files[I].Filename; + return; + } + + MemoryBuffer *Buffer + = MemoryBuffer::getMemBuffer(unsaved_files[I].Contents, + unsaved_files[I].Contents + unsaved_files[I].Length); + if (!Buffer) + return; + + SourceMgr.overrideFileContents(File, Buffer); + } + + // Parse the diagnostics, emitting them one by one until we've + // exhausted the data. + StringRef Buffer = F->getBuffer(); + const char *Memory = Buffer.data(), *MemoryEnd = Memory + Buffer.size(); + while (Memory != MemoryEnd) { + DiagnosticBuilder DB = Diags.Deserialize(FileMgr, SourceMgr, + Memory, MemoryEnd); + if (!DB.isActive()) + return; + } +} diff --git a/tools/CIndex/CIndexDiagnostic.h b/tools/CIndex/CIndexDiagnostic.h index f9620415cb..62d219cea9 100644 --- a/tools/CIndex/CIndexDiagnostic.h +++ b/tools/CIndex/CIndexDiagnostic.h @@ -1,4 +1,4 @@ -/*===-- CIndexDiagnostic.h - Diagnostics C Interface --------------*- C -*-===*\ +/*===-- CIndexDiagnostic.h - Diagnostics C Interface ------------*- C++ -*-===*\ |* *| |* The LLVM Compiler Infrastructure *| |* *| @@ -17,8 +17,13 @@ #include "clang/Basic/Diagnostic.h" #include "clang/Basic/LangOptions.h" +namespace llvm { namespace sys { +class Path; +} } + namespace clang { +class Diagnostic; class Preprocessor; /** @@ -43,7 +48,15 @@ public: virtual void HandleDiagnostic(Diagnostic::Level DiagLevel, const DiagnosticInfo &Info); }; - + +/// \brief Given the path to a file that contains binary, serialized +/// diagnostics produced by Clang, emit those diagnostics via the +/// given diagnostic engine. +void ReportSerializedDiagnostics(const llvm::sys::Path &DiagnosticsPath, + Diagnostic &Diags, + unsigned num_unsaved_files, + struct CXUnsavedFile *unsaved_files); + } // end namespace clang #endif // LLVM_CLANG_CINDEX_DIAGNOSTIC_H