Skip to content

Commit 41286bc

Browse files
authored
Make JSON fix-it outputs be per-primary in batch mode. (swiftlang#14995)
This means moving the output path into SupplementaryOutputPaths, and using the same sort of diagnostic dispatching that serialized diagnostics use. This is part of what's needed to run the migrator in batch mode.
1 parent ab0c74b commit 41286bc

File tree

9 files changed

+135
-49
lines changed

9 files changed

+135
-49
lines changed

include/swift/Basic/SupplementaryOutputPaths.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,9 @@ struct SupplementaryOutputPaths {
4949
/// frontend invocation.
5050
std::string SerializedDiagnosticsPath;
5151

52+
/// The path to which we should output fix-its as source edits.
53+
std::string FixItsOutputPath;
54+
5255
/// The path to which we should output a loaded module trace file.
5356
/// It is currently only used with WMO, but could be generalized.
5457
std::string LoadedModuleTracePath;

include/swift/Frontend/FrontendOptions.h

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -55,9 +55,6 @@ class FrontendOptions {
5555
/// The name of the library to link against when using this module.
5656
std::string ModuleLinkName;
5757

58-
/// The path to which we should output fixits as source edits.
59-
std::string FixitsOutputPath;
60-
6158
/// Arguments which should be passed in immediate mode.
6259
std::vector<std::string> ImmediateArgv;
6360

include/swift/Frontend/InputFile.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,9 @@ class InputFile {
9595
return getPrimarySpecificPaths().SupplementaryOutputs
9696
.SerializedDiagnosticsPath;
9797
}
98+
std::string fixItsOutputPath() const {
99+
return getPrimarySpecificPaths().SupplementaryOutputs.FixItsOutputPath;
100+
}
98101
};
99102
} // namespace swift
100103

lib/Frontend/ArgsToFrontendOptionsConverter.cpp

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -117,9 +117,6 @@ bool ArgsToFrontendOptionsConverter::convert() {
117117
if (checkUnusedSupplementaryOutputPaths())
118118
return true;
119119

120-
if (const Arg *A = Args.getLastArg(OPT_emit_fixits_path))
121-
Opts.FixitsOutputPath = A->getValue();
122-
123120
if (const Arg *A = Args.getLastArg(OPT_module_link_name))
124121
Opts.ModuleLinkName = A->getValue();
125122

lib/Frontend/ArgsToFrontendOutputsConverter.cpp

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -283,13 +283,15 @@ SupplementaryOutputPathsComputer::getSupplementaryOutputPathsFromArguments()
283283
options::OPT_emit_reference_dependencies_path);
284284
auto serializedDiagnostics = getSupplementaryFilenamesFromArguments(
285285
options::OPT_serialize_diagnostics_path);
286+
auto fixItsOutput = getSupplementaryFilenamesFromArguments(
287+
options::OPT_emit_fixits_path);
286288
auto loadedModuleTrace = getSupplementaryFilenamesFromArguments(
287289
options::OPT_emit_loaded_module_trace_path);
288290
auto TBD = getSupplementaryFilenamesFromArguments(options::OPT_emit_tbd_path);
289291

290292
if (!objCHeaderOutput || !moduleOutput || !moduleDocOutput ||
291293
!dependenciesFile || !referenceDependenciesFile ||
292-
!serializedDiagnostics || !loadedModuleTrace || !TBD) {
294+
!serializedDiagnostics || !fixItsOutput || !loadedModuleTrace || !TBD) {
293295
return None;
294296
}
295297
std::vector<SupplementaryOutputPaths> result;
@@ -304,6 +306,7 @@ SupplementaryOutputPathsComputer::getSupplementaryOutputPathsFromArguments()
304306
sop.DependenciesFilePath = (*dependenciesFile)[i];
305307
sop.ReferenceDependenciesFilePath = (*referenceDependenciesFile)[i];
306308
sop.SerializedDiagnosticsPath = (*serializedDiagnostics)[i];
309+
sop.FixItsOutputPath = (*fixItsOutput)[i];
307310
sop.LoadedModuleTracePath = (*loadedModuleTrace)[i];
308311
sop.TBDPath = (*TBD)[i];
309312

@@ -356,6 +359,9 @@ SupplementaryOutputPathsComputer::computeOutputPathsForOneInput(
356359
OPT_serialize_diagnostics, pathsFromArguments.SerializedDiagnosticsPath,
357360
"dia", "", defaultSupplementaryOutputPathExcludingExtension);
358361

362+
// There is no non-path form of -emit-fixits-path
363+
auto fixItsOutputPath = pathsFromArguments.FixItsOutputPath;
364+
359365
auto objcHeaderOutputPath = determineSupplementaryOutputFilename(
360366
OPT_emit_objc_header, pathsFromArguments.ObjCHeaderOutputPath, "h", "",
361367
defaultSupplementaryOutputPathExcludingExtension);
@@ -391,6 +397,7 @@ SupplementaryOutputPathsComputer::computeOutputPathsForOneInput(
391397
sop.DependenciesFilePath = dependenciesFilePath;
392398
sop.ReferenceDependenciesFilePath = referenceDependenciesFilePath;
393399
sop.SerializedDiagnosticsPath = serializedDiagnosticsPath;
400+
sop.FixItsOutputPath = fixItsOutputPath;
394401
sop.LoadedModuleTracePath = loadedModuleTracePath;
395402
sop.TBDPath = tbdPath;
396403
return sop;

lib/Frontend/CompilerInvocation.cpp

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -908,8 +908,10 @@ static bool ParseIRGenArgs(IRGenOptions &Opts, ArgList &Args,
908908
return false;
909909
}
910910

911-
bool ParseMigratorArgs(MigratorOptions &Opts, llvm::Triple &Triple,
912-
StringRef ResourcePath, ArgList &Args,
911+
bool ParseMigratorArgs(MigratorOptions &Opts,
912+
const FrontendOptions &FrontendOpts,
913+
const llvm::Triple &Triple,
914+
StringRef ResourcePath, const ArgList &Args,
913915
DiagnosticEngine &Diags) {
914916
using namespace options;
915917

@@ -958,6 +960,21 @@ bool ParseMigratorArgs(MigratorOptions &Opts, llvm::Triple &Triple,
958960
}
959961
}
960962

963+
if (Opts.shouldRunMigrator()) {
964+
assert(!FrontendOpts.InputsAndOutputs.isWholeModule());
965+
// FIXME: In order to support batch mode properly, the migrator would have
966+
// to support having one remap file path and one migrated file path per
967+
// primary input. The easiest way to do this would be to move processing of
968+
// these paths into FrontendOptions, like other supplementary outputs, and
969+
// to call migrator::updateCodeAndEmitRemapIfNeeded once for each primary
970+
// file.
971+
//
972+
// Supporting WMO would be similar, but WMO is set up to only produce one
973+
// supplementary output for the whole compilation instead of one per input,
974+
// so it's probably not worth it.
975+
FrontendOpts.InputsAndOutputs.assertMustNotBeMoreThanOnePrimaryInput();
976+
}
977+
961978
return false;
962979
}
963980

@@ -1022,7 +1039,7 @@ bool CompilerInvocation::parseArgs(ArrayRef<const char *> Args,
10221039
return true;
10231040
}
10241041

1025-
if (ParseMigratorArgs(MigratorOpts, LangOpts.Target,
1042+
if (ParseMigratorArgs(MigratorOpts, FrontendOpts, LangOpts.Target,
10261043
SearchPathOpts.RuntimeResourcePath, ParsedArgs, Diags)) {
10271044
return true;
10281045
}

lib/FrontendTool/FrontendTool.cpp

Lines changed: 67 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1509,6 +1509,41 @@ computeStatsReporter(const CompilerInvocation &Invocation, CompilerInstance *Ins
15091509
ProfileEvents, ProfileEntities);
15101510
}
15111511

1512+
/// Creates a diagnostic consumer that handles dispatching diagnostics to
1513+
/// multiple output files, based on the supplementary output paths specified by
1514+
/// \p inputsAndOutputs.
1515+
///
1516+
/// If no output files are needed, returns null.
1517+
static std::unique_ptr<DiagnosticConsumer>
1518+
createDispatchingDiagnosticConsumerIfNeeded(
1519+
const FrontendInputsAndOutputs &inputsAndOutputs,
1520+
llvm::function_ref<std::unique_ptr<DiagnosticConsumer>(const InputFile &)>
1521+
maybeCreateSingleConsumer) {
1522+
1523+
// The "4" here is somewhat arbitrary. In practice we're going to have one
1524+
// sub-consumer for each diagnostic file we're trying to output, which (again
1525+
// in practice) is going to be 1 in WMO mode and equal to the number of
1526+
// primary inputs in batch mode. That in turn is going to be "the number of
1527+
// files we need to recompile in this build, divided by the number of jobs".
1528+
// So a value of "4" here means that there would be no heap allocation on a
1529+
// clean build of a module with up to 32 files on an 8-core machine, if the
1530+
// user doesn't customize anything.
1531+
SmallVector<FileSpecificDiagnosticConsumer::ConsumerPair, 4> subConsumers;
1532+
1533+
inputsAndOutputs.forEachInputProducingSupplementaryOutput(
1534+
[&](const InputFile &input) -> bool {
1535+
if (auto subConsumer = maybeCreateSingleConsumer(input))
1536+
subConsumers.emplace_back(input.file(), std::move(subConsumer));
1537+
return false;
1538+
});
1539+
1540+
if (subConsumers.empty())
1541+
return nullptr;
1542+
if (subConsumers.size() == 1)
1543+
return std::move(subConsumers.front()).second;
1544+
return llvm::make_unique<FileSpecificDiagnosticConsumer>(subConsumers);
1545+
}
1546+
15121547
/// Creates a diagnostic consumer that handles serializing diagnostics, based on
15131548
/// the supplementary output paths specified by \p inputsAndOutputs.
15141549
///
@@ -1520,39 +1555,39 @@ computeStatsReporter(const CompilerInvocation &Invocation, CompilerInstance *Ins
15201555
static std::unique_ptr<DiagnosticConsumer>
15211556
createSerializedDiagnosticConsumerIfNeeded(
15221557
const FrontendInputsAndOutputs &inputsAndOutputs) {
1523-
1524-
// The "4" here is somewhat arbitrary. In practice we're going to have one
1525-
// sub-consumer for each .dia file we're trying to output, which (again in
1526-
// practice) is going to be 1 in WMO mode and equal to the number of primary
1527-
// inputs in batch mode. That in turn is going to be "the number of files we
1528-
// need to recompile in this build, divided by the number of jobs". So a
1529-
// value of "4" here means that there would be no heap allocation on a clean
1530-
// build of a module with up to 32 files on an 8-core machine, if the user
1531-
// doesn't customize anything.
1532-
SmallVector<FileSpecificDiagnosticConsumer::ConsumerPair, 4>
1533-
serializedConsumers;
1534-
1535-
inputsAndOutputs.forEachInputProducingSupplementaryOutput(
1536-
[&](const InputFile &Input) -> bool {
1537-
std::string serializedDiagnosticsPath = Input.serializedDiagnosticsPath();
1558+
return createDispatchingDiagnosticConsumerIfNeeded(
1559+
inputsAndOutputs,
1560+
[](const InputFile &input) -> std::unique_ptr<DiagnosticConsumer> {
1561+
std::string serializedDiagnosticsPath = input.serializedDiagnosticsPath();
15381562
if (serializedDiagnosticsPath.empty())
1539-
return false;
1540-
1541-
serializedConsumers.emplace_back(
1542-
Input.file(),
1543-
serialized_diagnostics::createConsumer(serializedDiagnosticsPath));
1544-
1545-
// Continue for other inputs.
1546-
return false;
1563+
return nullptr;
1564+
return serialized_diagnostics::createConsumer(serializedDiagnosticsPath);
15471565
});
1566+
}
15481567

1549-
if (serializedConsumers.empty())
1550-
return nullptr;
1551-
if (serializedConsumers.size() == 1)
1552-
return std::move(serializedConsumers.front()).second;
1553-
return llvm::make_unique<FileSpecificDiagnosticConsumer>(serializedConsumers);
1568+
/// Creates a diagnostic consumer that handles serializing diagnostics, based on
1569+
/// the supplementary output paths specified in \p options.
1570+
///
1571+
/// The returned consumer will handle producing multiple serialized diagnostics
1572+
/// files if necessary, by using sub-consumers for each file and dispatching to
1573+
/// the right one.
1574+
///
1575+
/// If no serialized diagnostics are being produced, returns null.
1576+
static std::unique_ptr<DiagnosticConsumer>
1577+
createJSONFixItDiagnosticConsumerIfNeeded(
1578+
const CompilerInvocation &invocation) {
1579+
return createDispatchingDiagnosticConsumerIfNeeded(
1580+
invocation.getFrontendOptions().InputsAndOutputs,
1581+
[&](const InputFile &input) -> std::unique_ptr<DiagnosticConsumer> {
1582+
std::string fixItsOutputPath = input.fixItsOutputPath();
1583+
if (fixItsOutputPath.empty())
1584+
return nullptr;
1585+
return llvm::make_unique<JSONFixitWriter>(
1586+
fixItsOutputPath, invocation.getDiagnosticOptions());
1587+
});
15541588
}
15551589

1590+
15561591
int swift::performFrontend(ArrayRef<const char *> Args,
15571592
const char *Argv0, void *MainAddr,
15581593
FrontendObserver *observer) {
@@ -1671,17 +1706,10 @@ int swift::performFrontend(ArrayRef<const char *> Args,
16711706
if (SerializedConsumerDispatcher)
16721707
Instance->addDiagnosticConsumer(SerializedConsumerDispatcher.get());
16731708

1674-
// FIXME: The fix-its need to be multiplexed like the serialized diagnostics.
1675-
std::unique_ptr<DiagnosticConsumer> FixitsConsumer;
1676-
{
1677-
const std::string &FixitsOutputPath =
1678-
Invocation.getFrontendOptions().FixitsOutputPath;
1679-
if (!FixitsOutputPath.empty()) {
1680-
FixitsConsumer.reset(new JSONFixitWriter(FixitsOutputPath,
1681-
Invocation.getDiagnosticOptions()));
1682-
Instance->addDiagnosticConsumer(FixitsConsumer.get());
1683-
}
1684-
}
1709+
std::unique_ptr<DiagnosticConsumer> FixItsConsumer =
1710+
createJSONFixItDiagnosticConsumerIfNeeded(Invocation);
1711+
if (FixItsConsumer)
1712+
Instance->addDiagnosticConsumer(FixItsConsumer.get());
16851713

16861714
if (Invocation.getDiagnosticOptions().UseColor)
16871715
PDC.forceColors();
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
enum E2 {
2+
case x
3+
case y
4+
case z
5+
}
6+
7+
func fooHelper(_ e: E2) {
8+
switch e {
9+
}
10+
}

test/FixCode/batch-mode.swift

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
// RUN: not %swift -typecheck -target %target-triple -primary-file %s -emit-fixits-path %t.main.remap -primary-file %S/Inputs/batch-mode-helper.swift -emit-fixits-path %t.helper.remap -diagnostics-editor-mode
2+
// RUN: %FileCheck -check-prefix=CHECK-MAIN %s < %t.main.remap
3+
// RUN: %FileCheck -check-prefix=NEGATIVE-MAIN %s < %t.main.remap
4+
// RUN: %FileCheck -check-prefix=CHECK-HELPER %s < %t.helper.remap
5+
// RUN: %FileCheck -check-prefix=NEGATIVE-HELPER %s < %t.helper.remap
6+
7+
// CHECK-MAIN: "file": "{{.+}}batch-mode.swift"
8+
// CHECK-MAIN: "text": "case .a:\n<#code#>\ncase .b:\n<#code#>\ncase .c:\n<#code#>\n"
9+
// NEGATIVE-MAIN-NOT: batch-mode-helper.swift
10+
11+
// CHECK-HELPER: "file": "{{.+}}batch-mode-helper.swift"
12+
// CHECK-HELPER: "text": "case .x:\n<#code#>\ncase .y:\n<#code#>\ncase .z:\n<#code#>\n"
13+
// NEGATIVE-HELPER-NOT: batch-mode.swift
14+
15+
enum E1 {
16+
case a
17+
case b
18+
case c
19+
}
20+
21+
func fooMain(_ e: E1) {
22+
switch e {
23+
}
24+
}

0 commit comments

Comments
 (0)