mirror of
https://github.com/intel/llvm.git
synced 2026-01-24 08:30:34 +08:00
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. llvm-svn: 94740
This commit is contained in:
@@ -23,16 +23,19 @@
|
||||
|
||||
namespace llvm {
|
||||
template <typename T> 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<char> &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
|
||||
|
||||
@@ -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<
|
||||
|
||||
@@ -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.
|
||||
///
|
||||
|
||||
@@ -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">;
|
||||
|
||||
@@ -252,6 +252,7 @@ def fconstant_string_class_EQ : Joined<"-fconstant-string-class=">, Group<f_Grou
|
||||
def fcreate_profile : Flag<"-fcreate-profile">, Group<f_Group>;
|
||||
def fdebug_pass_arguments : Flag<"-fdebug-pass-arguments">, Group<f_Group>;
|
||||
def fdebug_pass_structure : Flag<"-fdebug-pass-structure">, Group<f_Group>;
|
||||
def fdiagnostics_binary : Flag<"-fdiagnostics-binary">, Group<f_Group>, Flags<[HelpHidden]>;
|
||||
def fdiagnostics_fixit_info : Flag<"-fdiagnostics-fixit-info">, Group<f_Group>;
|
||||
def fdiagnostics_print_source_range_info : Flag<"-fdiagnostics-print-source-range-info">, Group<f_Group>;
|
||||
def fdiagnostics_show_option : Flag<"-fdiagnostics-show-option">, Group<f_Group>;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -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<FileID, unsigned> 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<SourceManager &>(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
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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<DiagnosticClient> DiagClient(
|
||||
new TextDiagnosticPrinter(llvm::errs(), Opts));
|
||||
llvm::OwningPtr<DiagnosticClient> 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)
|
||||
|
||||
@@ -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) {
|
||||
|
||||
16
clang/test/Index/code-complete-errors.c
Normal file
16
clang/test/Index/code-complete-errors.c
Normal file
@@ -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
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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<std::string> 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();
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user