[driver] Remove the 'swift-fixit' symlink and introduce '-emit-fixits-path' frontend option that

writes compiler fixits as source edits.

Driver option '-fixit-code' adds '-emit-fixits-path' for all the frontend invocations.

Swift SVN r27208
This commit is contained in:
Argyrios Kyrtzidis
2015-04-10 17:33:29 +00:00
parent 4e6efcb617
commit 8b250d6d35
22 changed files with 176 additions and 258 deletions

View File

@@ -46,6 +46,8 @@ ERROR(error_no_frontend_args, frontend, none,
ERROR(error_no_such_file_or_directory,frontend,none,
"no such file or directory: '%0'", (StringRef))
ERROR(cannot_open_file,frontend,none,
"cannot open file '%0' (%1)", (StringRef, StringRef))
ERROR(cannot_open_serialized_file,frontend,none,
"cannot open file '%0' for diagnostics emission (%1)", (StringRef, StringRef))
ERROR(error_open_input_file,frontend,none,

View File

@@ -68,9 +68,6 @@ public:
/// Invoke swift-update with the compiler frontend options.
UpdateCode,
/// Invoke swift-fixit with the compiler frontend options.
FixCode,
};
/// The mode in which the driver should invoke the frontend.
@@ -99,6 +96,9 @@ public:
/// Whether the compiler picked the current module name, rather than the user.
bool ModuleNameIsFallback = false;
// Whether the driver should generate compiler fixits as source edits.
bool ShouldGenerateFixitEdits = false;
/// The number of threads for multi-threaded compilation.
unsigned numThreads = 0;
@@ -121,7 +121,6 @@ public:
enum class DriverKind {
Interactive, // swift
Batch, // swiftc
FixCode, // swift-fixit
AutolinkExtract, // swift-autolink-extract
};

View File

@@ -112,6 +112,9 @@ public:
/// The path to which we should output a Swift reference dependencies file.
std::string ReferenceDependenciesFilePath;
/// The path to which we should output a fixits as source edits.
std::string FixitsOutputPath;
/// Arguments which should be passed in immediate mode.
std::vector<std::string> ImmediateArgv;

View File

@@ -49,6 +49,10 @@ def serialize_diagnostics_path
: Separate<["-"], "serialize-diagnostics-path">, MetaVarName<"<path>">,
HelpText<"Output serialized diagnostics to <path>">;
def emit_fixits_path
: Separate<["-"], "emit-fixits-path">, MetaVarName<"<path>">,
HelpText<"Output compiler fixits as source edits to <path>">;
def verify : Flag<["-"], "verify">,
HelpText<"Verify diagnostics against expected-{error|warning|note} "
"annotations">;

View File

@@ -84,7 +84,7 @@ def driver_use_frontend_path : Separate<["-"], "driver-use-frontend-path">,
HelpText<"Use the given executable to perform compilations">;
def driver_mode : Joined<["--"], "driver-mode=">, Flags<[HelpHidden]>,
HelpText<"Set the driver mode to either 'swift' or 'swiftc' or 'swift-fixit'">;
HelpText<"Set the driver mode to either 'swift' or 'swiftc'">;
def help : Flag<["-", "--"], "help">,
Flags<[FrontendOption, AutolinkExtractOption]>,
@@ -336,8 +336,8 @@ def update_code : Flag<["-"], "update-code">,
Flags<[HelpHidden, NoInteractiveOption, DoesNotAffectIncrementalBuild]>;
def fixit_code : Flag<["-"], "fixit-code">,
HelpText<"Get compiler fixits as code edits">, ModeOpt,
Flags<[HelpHidden, NoInteractiveOption, DoesNotAffectIncrementalBuild]>;
HelpText<"Get compiler fixits as code edits">,
Flags<[NoInteractiveOption, DoesNotAffectIncrementalBuild]>;
// No Output Modes
def parse : Flag<["-"], "parse">,

View File

@@ -93,8 +93,6 @@ void Driver::parseDriverKind(ArrayRef<const char *> Args) {
llvm::StringSwitch<Optional<DriverKind>>(DriverName)
.Case("swift", DriverKind::Interactive)
.Case("swiftc", DriverKind::Batch)
.Case("swift-fixit", DriverKind::FixCode)
.Case("swift-update", DriverKind::FixCode) // FIXME: remove once fully transitioned.
.Case("swift-autolink-extract", DriverKind::AutolinkExtract)
.Default(None);
@@ -309,7 +307,9 @@ std::unique_ptr<Compilation> Driver::buildCompilation(
// For updating code we need to go through all the files and pick up changes,
// even if they have compiler errors.
if (OI.CompilerMode == OutputInfo::Mode::UpdateCode)
// Also for getting bulk fixits.
if (OI.CompilerMode == OutputInfo::Mode::UpdateCode ||
OI.ShouldGenerateFixitEdits)
C->setContinueBuildingAfterErrors();
if (OFM)
@@ -674,10 +674,6 @@ void Driver::buildOutputInfo(const ToolChain &TC, const DerivedArgList &Args,
OI.CompilerMode = OutputInfo::Mode::UpdateCode;
OI.CompilerOutputType = types::TY_Remapping;
OI.LinkAction = LinkKind::None;
} else if (Args.hasArg(options::OPT_fixit_code)) {
OI.CompilerMode = OutputInfo::Mode::FixCode;
OI.CompilerOutputType = types::TY_Remapping;
OI.LinkAction = LinkKind::None;
} else {
diagnoseOutputModeArg(Diags, OutputModeArg, !Inputs.empty(), Args,
driverKind == DriverKind::Interactive, Name);
@@ -823,6 +819,10 @@ void Driver::buildOutputInfo(const ToolChain &TC, const DerivedArgList &Args,
}
}
if (Args.hasArg(options::OPT_fixit_code)) {
OI.ShouldGenerateFixitEdits = true;
}
{
if (const Arg *A = Args.getLastArg(options::OPT_sdk)) {
OI.SDKPath = A->getValue();
@@ -1032,7 +1032,6 @@ void Driver::buildActions(const ToolChain &TC,
ActionList CompileActions;
switch (OI.CompilerMode) {
case OutputInfo::Mode::StandardCompile:
case OutputInfo::Mode::FixCode:
case OutputInfo::Mode::UpdateCode: {
for (const InputPair &Input : Inputs) {
types::ID InputType = Input.first;
@@ -1631,6 +1630,26 @@ Job *Driver::buildJobsForAction(Compilation &C, const Action *A,
}
}
if (OI.ShouldGenerateFixitEdits && isa<CompileJobAction>(JA)) {
StringRef OFMFixitsOutputPath;
if (OutputMap) {
auto iter = OutputMap->find(types::TY_Remapping);
if (iter != OutputMap->end())
OFMFixitsOutputPath = iter->second;
}
if (!OFMFixitsOutputPath.empty()) {
Output->setAdditionalOutputForType(types::ID::TY_Remapping,
OFMFixitsOutputPath);
} else {
llvm::SmallString<128> Path(Output->getPrimaryOutputFilenames()[0]);
bool isTempFile = C.isTemporaryFile(Path);
llvm::sys::path::replace_extension(Path, "remap");
Output->setAdditionalOutputForType(types::ID::TY_Remapping, Path);
if (isTempFile)
C.addTemporaryFile(Path);
}
}
if (isa<CompileJobAction>(JA)) {
// Choose the serialized diagnostics output path.
if (C.getArgs().hasArg(options::OPT_serialize_diagnostics)) {
@@ -1837,7 +1856,6 @@ void Driver::printHelp(bool ShowHidden) const {
ExcludedFlagsBitmask |= options::NoInteractiveOption;
break;
case DriverKind::Batch:
case DriverKind::FixCode:
case DriverKind::AutolinkExtract:
ExcludedFlagsBitmask |= options::NoBatchOption;
break;

View File

@@ -156,10 +156,6 @@ Job *Swift::constructJob(const JobAction &JA, std::unique_ptr<JobList> Inputs,
SmallString<128> SwiftUpdatePath = llvm::sys::path::parent_path(Exec);
llvm::sys::path::append(SwiftUpdatePath, "swift-update");
Exec = Args.MakeArgString(SwiftUpdatePath.str());
} else if (OI.CompilerMode == OutputInfo::Mode::FixCode) {
SmallString<128> SwiftFixPath = llvm::sys::path::parent_path(Exec);
llvm::sys::path::append(SwiftFixPath, "swift-fixit");
Exec = Args.MakeArgString(SwiftFixPath.str());
} else {
// Invoke ourselves in -frontend mode.
Arguments.push_back("-frontend");
@@ -230,7 +226,6 @@ Job *Swift::constructJob(const JobAction &JA, std::unique_ptr<JobList> Inputs,
case OutputInfo::Mode::REPL:
FrontendModeOption = "-repl";
break;
case OutputInfo::Mode::FixCode:
case OutputInfo::Mode::UpdateCode:
// Make sure that adding '-update-code' will permit accepting all arguments
// '-c' accepts.
@@ -248,7 +243,6 @@ Job *Swift::constructJob(const JobAction &JA, std::unique_ptr<JobList> Inputs,
// Add input arguments.
switch (OI.CompilerMode) {
case OutputInfo::Mode::StandardCompile:
case OutputInfo::Mode::FixCode:
case OutputInfo::Mode::UpdateCode: {
if (isa<BackendJobAction>(JA)) {
assert(Inputs->size() == 1 && "The Swift backend expects one input!");
@@ -316,7 +310,6 @@ Job *Swift::constructJob(const JobAction &JA, std::unique_ptr<JobList> Inputs,
switch (OI.CompilerMode) {
case OutputInfo::Mode::StandardCompile:
case OutputInfo::Mode::SingleCompile:
case OutputInfo::Mode::FixCode:
case OutputInfo::Mode::UpdateCode:
break;
case OutputInfo::Mode::Immediate:
@@ -365,6 +358,13 @@ Job *Swift::constructJob(const JobAction &JA, std::unique_ptr<JobList> Inputs,
Arguments.push_back(ReferenceDependenciesPath.c_str());
}
const std::string &FixitsPath =
Output->getAdditionalOutputForType(types::TY_Remapping);
if (!FixitsPath.empty()) {
Arguments.push_back("-emit-fixits-path");
Arguments.push_back(FixitsPath.c_str());
}
if (OI.numThreads > 0) {
Arguments.push_back("-num-threads");
Arguments.push_back(Args.MakeArgString(Twine(OI.numThreads)));

View File

@@ -472,6 +472,10 @@ static bool ParseFrontendArgs(FrontendOptions &Opts, ArgList &Args,
OPT_emit_objc_header_path,
"h", false);
if (const Arg *A = Args.getLastArg(OPT_emit_fixits_path)) {
Opts.FixitsOutputPath = A->getValue();
}
bool IsSIB =
Opts.RequestedAction == FrontendOptions::EmitSIB ||
Opts.RequestedAction == FrontendOptions::EmitSIBGen;

View File

@@ -101,9 +101,9 @@
// UPDATE-CODE: -c{{ }}
// UPDATE-CODE: -o {{.+}}.remap
// FIXIT-CODE: bin/swift-fixit
// FIXIT-CODE: bin/swift
// FIXIT-CODE: -c{{ }}
// FIXIT-CODE: -o {{.+}}.remap
// FIXIT-CODE: -emit-fixits-path {{.+}}.remap
// NO-REFERENCE-DEPENDENCIES: bin/swift

View File

@@ -0,0 +1,8 @@
func foo1() {
class Base {}
class Derived : Base {}
var b : Base
b as Derived
undeclared_foo1
}

View File

@@ -0,0 +1,8 @@
func foo1() {
class Base {}
class Derived : Base {}
var b : Base
b as! Derived
undeclared_foo1
}

View File

@@ -0,0 +1,8 @@
func foo2() {
class Base {}
class Derived : Base {}
var b : Base
b as Derived
undeclared_foo2
}

View File

@@ -0,0 +1,8 @@
func foo2() {
class Base {}
class Derived : Base {}
var b : Base
b as! Derived
undeclared_foo2
}

View File

@@ -1,9 +1,9 @@
// RUN: %swift-fixit -c -target %target-triple %s -o %t.remap -serialize-diagnostics-path %t.dia -emit-module-doc-path %t.doc -emit-module-path %t.mod
// RUN: not %swift -parse -target %target-triple %s -emit-fixits-path %t.remap -serialize-diagnostics-path %t.dia
// RUN: c-arcmt-test %t.remap | arcmt-test -verify-transformed-files %s.result
// RUN: c-index-test -read-diagnostics %t.dia > %t.deserialized_diagnostics.txt 2>&1
// RUN: FileCheck --input-file=%t.deserialized_diagnostics.txt %s
// CHECK: Number of diagnostics: 0
// CHECK: Number of diagnostics: 2
class Base {}
class Derived : Base {}

View File

@@ -1,9 +1,9 @@
// RUN: %swift-fixit -c -target %target-triple %s -o %t.remap -serialize-diagnostics-path %t.dia -emit-module-doc-path %t.doc -emit-module-path %t.mod
// RUN: not %swift -parse -target %target-triple %s -emit-fixits-path %t.remap -serialize-diagnostics-path %t.dia
// RUN: c-arcmt-test %t.remap | arcmt-test -verify-transformed-files %s.result
// RUN: c-index-test -read-diagnostics %t.dia > %t.deserialized_diagnostics.txt 2>&1
// RUN: FileCheck --input-file=%t.deserialized_diagnostics.txt %s
// CHECK: Number of diagnostics: 0
// CHECK: Number of diagnostics: 2
class Base {}
class Derived : Base {}

View File

@@ -0,0 +1,8 @@
// RUN: mkdir -p %t.out
// RUN: echo "{\"%S/Inputs/t1.swift\": {\"remap\": \"%t.out/t1.remap\"}, \"%S/Inputs/t2.swift\": {\"remap\": \"%t.out/t2.remap\"}}" > %t.json
// RUN: not %swiftc_driver -target %target-triple -module-name Blah -fixit-code %S/Inputs/t1.swift %S/Inputs/t2.swift -emit-module -emit-module-path %t.mod -emit-objc-header -emit-objc-header-path %t.h -j 1 -output-file-map %t.json 2> %t.err.txt
// RUN: c-arcmt-test %t.out/t1.remap %t.out/t2.remap | arcmt-test -verify-transformed-files %S/Inputs/t1.swift.result %S/Inputs/t2.swift.result
// RUN: FileCheck --input-file=%t.err.txt %s
// CHECK: error: use of unresolved identifier 'undeclared_foo1'
// CHECK: error: use of unresolved identifier 'undeclared_foo2'

View File

@@ -1,18 +0,0 @@
// RUN: not %swift-fixit -target %target-triple %s -o %t.remap -serialize-diagnostics-path %t.dia
// RUN: c-arcmt-test %t.remap | arcmt-test -verify-transformed-files %s.result
// RUN: c-index-test -read-diagnostics %t.dia > %t.deserialized_diagnostics.txt 2>&1
// RUN: FileCheck --input-file=%t.deserialized_diagnostics.txt %s
// CHECK: error: no such module
// CHECK: error: use of unresolved identifier
// CHECK: Number of diagnostics: 2
import NotFoundModule
class Base {}
class Derived : Base {}
var b : Base
b as Derived
undeclared()

View File

@@ -1,18 +0,0 @@
// RUN: not %swift-fixit -target %target-triple %s -o %t.remap -serialize-diagnostics-path %t.dia
// RUN: c-arcmt-test %t.remap | arcmt-test -verify-transformed-files %s.result
// RUN: c-index-test -read-diagnostics %t.dia > %t.deserialized_diagnostics.txt 2>&1
// RUN: FileCheck --input-file=%t.deserialized_diagnostics.txt %s
// CHECK: error: no such module
// CHECK: error: use of unresolved identifier
// CHECK: Number of diagnostics: 2
import NotFoundModule
class Base {}
class Derived : Base {}
var b : Base
b as! Derived
undeclared()

View File

@@ -1,7 +1,6 @@
add_swift_executable(swift
driver.cpp
autolink_extract_main.cpp
fixit_main.cpp
frontend_main.cpp
LINK_LIBRARIES
swiftIDE
@@ -20,10 +19,6 @@ add_custom_command(TARGET swift POST_BUILD
COMMAND "${CMAKE_COMMAND}" "-E" "create_symlink" "swift" "swiftc"
WORKING_DIRECTORY "${SWIFT_RUNTIME_OUTPUT_INTDIR}")
add_custom_command(TARGET swift POST_BUILD
COMMAND "${CMAKE_COMMAND}" "-E" "create_symlink" "swift" "swift-fixit"
WORKING_DIRECTORY "${SWIFT_RUNTIME_OUTPUT_INTDIR}")
add_custom_command(TARGET swift POST_BUILD
COMMAND "${CMAKE_COMMAND}" "-E" "create_symlink" "swift" "swift-autolink-extract"
WORKING_DIRECTORY "${SWIFT_RUNTIME_OUTPUT_INTDIR}")
@@ -54,9 +49,6 @@ swift_install_in_component(compiler
swift_install_in_component(compiler
FILES "${SWIFT_RUNTIME_OUTPUT_INTDIR}/swiftc"
DESTINATION "bin")
swift_install_in_component(compiler
FILES "${SWIFT_RUNTIME_OUTPUT_INTDIR}/swift-fixit"
DESTINATION "bin")
if(SWIFT_HOST_VARIANT STREQUAL "LINUX")
# No need for this on other platforms.

View File

@@ -48,10 +48,6 @@ std::string getExecutablePath(const char *FirstArg) {
extern int frontend_main(ArrayRef<const char *> Args, const char *Argv0,
void *MainAddr);
/// Run 'swift-fixit'.
extern int fixit_main(ArrayRef<const char *> Args, const char *Argv0,
void *MainAddr);
/// Run 'swift-autolink-extract'.
extern int autolink_extract_main(ArrayRef<const char *> Args, const char *Argv0,
void *MainAddr);
@@ -93,10 +89,6 @@ int main(int argc_, const char **argv_) {
Driver TheDriver(Path, llvm::sys::path::stem(argv[0]), argv, Diags);
switch (TheDriver.getDriverKind()) {
case Driver::DriverKind::FixCode:
return fixit_main(TheDriver.getArgsWithoutProgramNameAndDriverMode(argv),
argv[0],
(void *)(intptr_t)getExecutablePath);
case Driver::DriverKind::AutolinkExtract:
return autolink_extract_main(
TheDriver.getArgsWithoutProgramNameAndDriverMode(argv),

View File

@@ -1,177 +0,0 @@
//===--- fixit_main.cpp - Get Swift compiler fixits ------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2015 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See http://swift.org/LICENSE.txt for license information
// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
// This tool turns Swift compiler fixits into file edits.
//===----------------------------------------------------------------------===//
#include "swift/AST/DiagnosticsFrontend.h"
#include "swift/Basic/SourceManager.h"
#include "swift/Driver/FrontendUtil.h"
#include "swift/Frontend/Frontend.h"
#include "swift/Frontend/PrintingDiagnosticConsumer.h"
#include "swift/Frontend/SerializedDiagnosticConsumer.h"
#include "llvm/Support/FileSystem.h"
using namespace swift;
namespace {
/// If there is an error with fixits it writes the fixits in json format and
/// filters out the diagnostic.
class JSONFixitWriter : public DiagnosticConsumer {
llvm::raw_ostream &OS;
SmallVector<DiagnosticConsumer *, 2> Consumers;
bool HasUnfixedError = false;
bool PrevErrorFilteredOut = false;
public:
explicit JSONFixitWriter(llvm::raw_ostream &OS) : OS(OS) {
OS << "[\n";
}
~JSONFixitWriter() {
OS << "]\n";
}
/// \brief Add an additional DiagnosticConsumer to receive diagnostics.
void addConsumer(DiagnosticConsumer *Consumer) {
Consumers.push_back(Consumer);
}
void addConsumers(std::vector<DiagnosticConsumer*> NewConsumers) {
Consumers.append(NewConsumers.begin(), NewConsumers.end());
}
bool hasUnfixedError() const { return HasUnfixedError; }
private:
void handleDiagnostic(SourceManager &SM, SourceLoc Loc,
DiagnosticKind Kind, StringRef Text,
const DiagnosticInfo &Info) override {
if (Kind == DiagnosticKind::Note && PrevErrorFilteredOut)
return;
PrevErrorFilteredOut = false;
if (tryFixing(SM, Kind, Info)) {
PrevErrorFilteredOut = true;
return; // filter out.
}
if (Kind == DiagnosticKind::Error)
HasUnfixedError = true;
// Transfer the diagnostic to other consumers.
for (auto *Consumer : Consumers)
Consumer->handleDiagnostic(SM, Loc, Kind, Text, Info);
}
bool tryFixing(SourceManager &SM,
DiagnosticKind Kind,
const DiagnosticInfo &Info) {
if (Kind != DiagnosticKind::Error)
return false;
if (Info.FixIts.empty())
return false;
for (const auto &Fix : Info.FixIts) {
writeEdit(SM, Fix.getRange(), Fix.getText(), OS);
}
return true;
}
void writeEdit(SourceManager &SM, CharSourceRange Range, StringRef Text,
llvm::raw_ostream &OS) {
SourceLoc Loc = Range.getStart();
unsigned BufID = SM.findBufferContainingLoc(Loc);
unsigned Offset = SM.getLocOffsetInBuffer(Loc, BufID);
unsigned Length = Range.getByteLength();
SmallString<200> Path =
StringRef(SM.getIdentifierForBuffer(BufID));
OS << " {\n";
OS << " \"file\": \"";
OS.write_escaped(Path.str()) << "\",\n";
OS << " \"offset\": " << Offset << ",\n";
if (Length != 0)
OS << " \"remove\": " << Length << ",\n";
if (!Text.empty()) {
OS << " \"text\": \"";
OS.write_escaped(Text) << "\",\n";
}
OS << " },\n";
}
};
} // anonymous namespace
int fixit_main(ArrayRef<const char *> Args, const char *Argv0, void *MainAddr){
CompilerInstance Instance;
PrintingDiagnosticConsumer PDC;
Instance.addDiagnosticConsumer(&PDC);
CompilerInvocation Invocation;
std::string MainExecutablePath = llvm::sys::fs::getMainExecutable(Argv0,
MainAddr);
Invocation.setMainExecutablePath(MainExecutablePath);
// Parse arguments.
if (Invocation.parseArgs(Args, Instance.getDiags())) {
return 1;
}
Invocation.getDiagnosticOptions().ShowDiagnosticsAfterFatalError = true;
std::error_code EC;
llvm::raw_fd_ostream OutOS(Invocation.getOutputFilename(), EC, llvm::sys::fs::F_None);
if (OutOS.has_error() || EC) {
Instance.getDiags().diagnose(SourceLoc(), diag::error_opening_output,
Invocation.getOutputFilename(), EC.message());
OutOS.clear_error();
return true;
}
JSONFixitWriter FixWriter(OutOS);
FixWriter.addConsumers(Instance.getDiags().takeConsumers());
Instance.addDiagnosticConsumer(&FixWriter);
// TODO: reorder, if possible, so that diagnostics emitted during
// CompilerInvocation::parseArgs are included in the serialized file.
std::unique_ptr<DiagnosticConsumer> SerializedConsumer;
{
const std::string &SerializedDiagnosticsPath =
Invocation.getFrontendOptions().SerializedDiagnosticsPath;
if (!SerializedDiagnosticsPath.empty()) {
std::error_code EC;
std::unique_ptr<llvm::raw_fd_ostream> OS;
OS.reset(new llvm::raw_fd_ostream(SerializedDiagnosticsPath,
EC,
llvm::sys::fs::F_None));
if (EC) {
Instance.getDiags().diagnose(SourceLoc(),
diag::cannot_open_serialized_file,
SerializedDiagnosticsPath, EC.message());
return 1;
}
SerializedConsumer.reset(
serialized_diagnostics::createConsumer(std::move(OS)));
FixWriter.addConsumer(SerializedConsumer.get());
}
}
if (Invocation.getDiagnosticOptions().UseColor)
PDC.forceColors();
if (Instance.setup(Invocation)) {
return 1;
}
Instance.performSema();
return FixWriter.hasUnfixedError();
}

View File

@@ -349,6 +349,60 @@ static IRGenOutputKind getOutputKind(FrontendOptions::ActionType Action) {
}
}
namespace {
/// If there is an error with fixits it writes the fixits as edits in json
/// format.
class JSONFixitWriter : public DiagnosticConsumer {
std::unique_ptr<llvm::raw_ostream> OSPtr;
public:
explicit JSONFixitWriter(std::unique_ptr<llvm::raw_ostream> OS)
: OSPtr(std::move(OS)) {
*OSPtr << "[\n";
}
~JSONFixitWriter() {
*OSPtr << "]\n";
}
private:
void handleDiagnostic(SourceManager &SM, SourceLoc Loc,
DiagnosticKind Kind, StringRef Text,
const DiagnosticInfo &Info) override {
if (Kind != DiagnosticKind::Error)
return;
if (Info.FixIts.empty())
return;
for (const auto &Fix : Info.FixIts) {
writeEdit(SM, Fix.getRange(), Fix.getText(), *OSPtr);
}
}
void writeEdit(SourceManager &SM, CharSourceRange Range, StringRef Text,
llvm::raw_ostream &OS) {
SourceLoc Loc = Range.getStart();
unsigned BufID = SM.findBufferContainingLoc(Loc);
unsigned Offset = SM.getLocOffsetInBuffer(Loc, BufID);
unsigned Length = Range.getByteLength();
SmallString<200> Path =
StringRef(SM.getIdentifierForBuffer(BufID));
OS << " {\n";
OS << " \"file\": \"";
OS.write_escaped(Path.str()) << "\",\n";
OS << " \"offset\": " << Offset << ",\n";
if (Length != 0)
OS << " \"remove\": " << Length << ",\n";
if (!Text.empty()) {
OS << " \"text\": \"";
OS.write_escaped(Text) << "\",\n";
}
OS << " },\n";
}
};
} // anonymous namespace
/// Performs the compile requested by the user.
/// \returns true on error
static bool performCompile(CompilerInstance &Instance,
@@ -728,6 +782,29 @@ int frontend_main(ArrayRef<const char *>Args,
}
}
std::unique_ptr<DiagnosticConsumer> FixitsConsumer;
{
const std::string &FixitsOutputPath =
Invocation.getFrontendOptions().FixitsOutputPath;
if (!FixitsOutputPath.empty()) {
std::error_code EC;
std::unique_ptr<llvm::raw_fd_ostream> OS;
OS.reset(new llvm::raw_fd_ostream(FixitsOutputPath,
EC,
llvm::sys::fs::F_None));
if (EC) {
Instance.getDiags().diagnose(SourceLoc(),
diag::cannot_open_file,
FixitsOutputPath, EC.message());
return 1;
}
FixitsConsumer.reset(new JSONFixitWriter(std::move(OS)));
Instance.addDiagnosticConsumer(FixitsConsumer.get());
}
}
if (Invocation.getDiagnosticOptions().UseColor)
PDC.forceColors();