Skip to content

Commit 0bb299a

Browse files
committed
[frontend] Add 'finishProcessing()' method for DiagnosticConsumers to do their finalization (e.g. writing to a file)
Use this to avoid creating an empty serialized diagnostics file when the compiler crashes.
1 parent 20a429b commit 0bb299a

File tree

7 files changed

+118
-59
lines changed

7 files changed

+118
-59
lines changed

include/swift/AST/DiagnosticConsumer.h

+3
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,9 @@ class DiagnosticConsumer {
9696
StringRef FormatString,
9797
ArrayRef<DiagnosticArgument> FormatArgs,
9898
const DiagnosticInfo &Info) = 0;
99+
100+
/// \returns true if an error occurred while finishing-up.
101+
virtual bool finishProcessing() { return false; }
99102
};
100103

101104
/// \brief DiagnosticConsumer that discards all diagnostics.

include/swift/AST/DiagnosticEngine.h

+4
Original file line numberDiff line numberDiff line change
@@ -741,6 +741,10 @@ namespace swift {
741741
/// \returns true if diagnostic is marked with PointsToFirstBadToken
742742
/// option.
743743
bool isDiagnosticPointsToFirstBadToken(DiagID id) const;
744+
745+
/// \returns true if any diagnostic consumer gave an error while invoking
746+
//// \c finishProcessing.
747+
bool finishProcessing();
744748

745749
/// \brief Format the given diagnostic text and place the result in the given
746750
/// buffer.

include/swift/Frontend/SerializedDiagnosticConsumer.h

+4-5
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
#include <memory>
2222

2323
namespace llvm {
24-
class raw_ostream;
24+
class StringRef;
2525
}
2626

2727
namespace swift {
@@ -30,13 +30,12 @@ namespace swift {
3030

3131
namespace serialized_diagnostics {
3232
/// \brief Create a DiagnosticConsumer that serializes diagnostics to a
33-
/// stream.
33+
/// file.
3434
///
35-
/// \param OS the stream to emit the diagnostics. The consumer takes
36-
/// ownership of the stream.
35+
/// \param serializedDiagnosticsPath the file path to write the diagnostics.
3736
///
3837
/// \returns A new diagnostic consumer that serializes diagnostics.
39-
DiagnosticConsumer *createConsumer(std::unique_ptr<llvm::raw_ostream> OS);
38+
DiagnosticConsumer *createConsumer(llvm::StringRef serializedDiagnosticsPath);
4039
}
4140
}
4241

lib/AST/DiagnosticEngine.cpp

+8
Original file line numberDiff line numberDiff line change
@@ -247,6 +247,14 @@ bool DiagnosticEngine::isDiagnosticPointsToFirstBadToken(DiagID ID) const {
247247
return storedDiagnosticInfos[(unsigned) ID].pointsToFirstBadToken;
248248
}
249249

250+
bool DiagnosticEngine::finishProcessing() {
251+
bool hadError = false;
252+
for (auto &Consumer : Consumers) {
253+
hadError = Consumer->finishProcessing() || hadError;
254+
}
255+
return hadError;
256+
}
257+
250258
/// \brief Skip forward to one of the given delimiters.
251259
///
252260
/// \param Text The text to search through, which will be updated to point

lib/Frontend/SerializedDiagnosticConsumer.cpp

+41-15
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,15 @@
1616

1717
#include "swift/Frontend/SerializedDiagnosticConsumer.h"
1818
#include "swift/AST/DiagnosticConsumer.h"
19+
#include "swift/AST/DiagnosticsFrontend.h"
1920
#include "swift/Basic/LLVM.h"
2021
#include "swift/Basic/SourceManager.h"
22+
#include "swift/Frontend/PrintingDiagnosticConsumer.h"
2123
#include "swift/Parse/Lexer.h"
2224
#include "llvm/ADT/StringRef.h"
2325
#include "llvm/ADT/Twine.h"
2426
#include "llvm/ADT/DenseMap.h"
27+
#include "llvm/Support/FileSystem.h"
2528
#include "llvm/Support/raw_ostream.h"
2629
#include "llvm/ADT/IntrusiveRefCntPtr.h"
2730
#include "llvm/ADT/SmallString.h"
@@ -84,17 +87,19 @@ typedef SmallVector<uint64_t, 64> RecordData;
8487
typedef SmallVectorImpl<uint64_t> RecordDataImpl;
8588

8689
struct SharedState : llvm::RefCountedBase<SharedState> {
87-
SharedState(std::unique_ptr<raw_ostream> OS)
88-
: Stream(Buffer), OS(std::move(OS)), EmittedAnyDiagBlocks(false) { }
90+
SharedState(StringRef serializedDiagnosticsPath)
91+
: Stream(Buffer),
92+
SerializedDiagnosticsPath(serializedDiagnosticsPath),
93+
EmittedAnyDiagBlocks(false) {}
8994

9095
/// \brief The byte buffer for the serialized content.
9196
llvm::SmallString<1024> Buffer;
9297

9398
/// \brief The BitStreamWriter for the serialized diagnostics.
9499
llvm::BitstreamWriter Stream;
95100

96-
/// \brief The name of the diagnostics file.
97-
std::unique_ptr<raw_ostream> OS;
101+
/// \brief The path of the diagnostics file.
102+
std::string SerializedDiagnosticsPath;
98103

99104
/// \brief The set of constructed record abbreviations.
100105
AbbreviationMap Abbrevs;
@@ -124,15 +129,21 @@ struct SharedState : llvm::RefCountedBase<SharedState> {
124129
class SerializedDiagnosticConsumer : public DiagnosticConsumer {
125130
/// \brief State shared among the various clones of this diagnostic consumer.
126131
llvm::IntrusiveRefCntPtr<SharedState> State;
132+
bool CalledFinishProcessing = false;
127133
public:
128-
SerializedDiagnosticConsumer(std::unique_ptr<raw_ostream> OS)
129-
: State(new SharedState(std::move(OS))) {
134+
SerializedDiagnosticConsumer(StringRef serializedDiagnosticsPath)
135+
: State(new SharedState(serializedDiagnosticsPath)) {
130136
emitPreamble();
131137
}
132138

133-
~SerializedDiagnosticConsumer() override {
134-
// FIXME: we may not wish to put this in a destructor.
135-
// That's not what clang does.
139+
~SerializedDiagnosticConsumer() {
140+
assert(CalledFinishProcessing && "did not call finishProcessing()");
141+
}
142+
143+
bool finishProcessing() override {
144+
assert(!CalledFinishProcessing &&
145+
"called finishProcessing() multiple times");
146+
CalledFinishProcessing = true;
136147

137148
// NOTE: clang also does check for shared instances. We don't
138149
// have these yet in Swift, but if we do we need to add an extra
@@ -142,10 +153,25 @@ class SerializedDiagnosticConsumer : public DiagnosticConsumer {
142153
if (State->EmittedAnyDiagBlocks)
143154
exitDiagBlock();
144155

145-
// Write the generated bitstream to "Out".
146-
State->OS->write((char *)&State->Buffer.front(), State->Buffer.size());
147-
State->OS->flush();
148-
State->OS.reset(nullptr);
156+
// Write the generated bitstream to the file.
157+
std::error_code EC;
158+
std::unique_ptr<llvm::raw_fd_ostream> OS;
159+
OS.reset(new llvm::raw_fd_ostream(State->SerializedDiagnosticsPath, EC,
160+
llvm::sys::fs::F_None));
161+
if (EC) {
162+
// Create a temporary diagnostics engine to print the error to stderr.
163+
SourceManager dummyMgr;
164+
DiagnosticEngine DE(dummyMgr);
165+
PrintingDiagnosticConsumer PDC;
166+
DE.addConsumer(PDC);
167+
DE.diagnose(SourceLoc(), diag::cannot_open_serialized_file,
168+
State->SerializedDiagnosticsPath, EC.message());
169+
return true;
170+
}
171+
172+
OS->write((char *)&State->Buffer.front(), State->Buffer.size());
173+
OS->flush();
174+
return false;
149175
}
150176

151177
void handleDiagnostic(SourceManager &SM, SourceLoc Loc,
@@ -197,8 +223,8 @@ class SerializedDiagnosticConsumer : public DiagnosticConsumer {
197223
} // end anonymous namespace
198224

199225
namespace swift { namespace serialized_diagnostics {
200-
DiagnosticConsumer *createConsumer(std::unique_ptr<llvm::raw_ostream> OS) {
201-
return new SerializedDiagnosticConsumer(std::move(OS));
226+
DiagnosticConsumer *createConsumer(StringRef serializedDiagnosticsPath) {
227+
return new SerializedDiagnosticConsumer(serializedDiagnosticsPath);
202228
}
203229
} // namespace serialized_diagnostics
204230
} // namespace swift

lib/FrontendTool/FrontendTool.cpp

+46-39
Original file line numberDiff line numberDiff line change
@@ -305,19 +305,17 @@ namespace {
305305
/// format.
306306
class JSONFixitWriter
307307
: public DiagnosticConsumer, public migrator::FixitFilter {
308+
std::string FixitsOutputPath;
308309
std::unique_ptr<llvm::raw_ostream> OSPtr;
309310
bool FixitAll;
310311
std::vector<SingleEdit> AllEdits;
311312

312313
public:
313-
JSONFixitWriter(std::unique_ptr<llvm::raw_ostream> OS,
314+
JSONFixitWriter(std::string fixitsOutputPath,
314315
const DiagnosticOptions &DiagOpts)
315-
: OSPtr(std::move(OS)),
316+
: FixitsOutputPath(fixitsOutputPath),
316317
FixitAll(DiagOpts.FixitCodeForAllDiagnostics) {}
317318

318-
~JSONFixitWriter() override {
319-
swift::writeEditsInJson(llvm::makeArrayRef(AllEdits), *OSPtr);
320-
}
321319
private:
322320
void handleDiagnostic(SourceManager &SM, SourceLoc Loc,
323321
DiagnosticKind Kind,
@@ -330,6 +328,27 @@ class JSONFixitWriter
330328
AllEdits.push_back({SM, Fix.getRange(), Fix.getText()});
331329
}
332330
}
331+
332+
bool finishProcessing() override {
333+
std::error_code EC;
334+
std::unique_ptr<llvm::raw_fd_ostream> OS;
335+
OS.reset(new llvm::raw_fd_ostream(FixitsOutputPath,
336+
EC,
337+
llvm::sys::fs::F_None));
338+
if (EC) {
339+
// Create a temporary diagnostics engine to print the error to stderr.
340+
SourceManager dummyMgr;
341+
DiagnosticEngine DE(dummyMgr);
342+
PrintingDiagnosticConsumer PDC;
343+
DE.addConsumer(PDC);
344+
DE.diagnose(SourceLoc(), diag::cannot_open_serialized_file,
345+
FixitsOutputPath, EC.message());
346+
return true;
347+
}
348+
349+
swift::writeEditsInJson(llvm::makeArrayRef(AllEdits), *OS);
350+
return false;
351+
}
333352
};
334353

335354
} // anonymous namespace
@@ -1026,9 +1045,23 @@ int swift::performFrontend(ArrayRef<const char *> Args,
10261045
llvm::make_unique<CompilerInstance>();
10271046
Instance->addDiagnosticConsumer(&PDC);
10281047

1048+
struct FinishDiagProcessingCheckRAII {
1049+
bool CalledFinishDiagProcessing = false;
1050+
~FinishDiagProcessingCheckRAII() {
1051+
assert(CalledFinishDiagProcessing && "returned from the function "
1052+
"without calling finishDiagProcessing");
1053+
}
1054+
} FinishDiagProcessingCheckRAII;
1055+
1056+
auto finishDiagProcessing = [&](int retValue) -> int {
1057+
FinishDiagProcessingCheckRAII.CalledFinishDiagProcessing = true;
1058+
bool err = Instance->getDiags().finishProcessing();
1059+
return retValue ? retValue : err;
1060+
};
1061+
10291062
if (Args.empty()) {
10301063
Instance->getDiags().diagnose(SourceLoc(), diag::error_no_frontend_args);
1031-
return 1;
1064+
return finishDiagProcessing(1);
10321065
}
10331066

10341067
CompilerInvocation Invocation;
@@ -1041,7 +1074,7 @@ int swift::performFrontend(ArrayRef<const char *> Args,
10411074

10421075
// Parse arguments.
10431076
if (Invocation.parseArgs(Args, Instance->getDiags(), workingDirectory)) {
1044-
return 1;
1077+
return finishDiagProcessing(1);
10451078
}
10461079

10471080
// Setting DWARF Version depend on platform
@@ -1063,14 +1096,14 @@ int swift::performFrontend(ArrayRef<const char *> Args,
10631096
Options->PrintHelp(llvm::outs(), displayName(MainExecutablePath).c_str(),
10641097
"Swift frontend", IncludedFlagsBitmask,
10651098
ExcludedFlagsBitmask);
1066-
return 0;
1099+
return finishDiagProcessing(0);
10671100
}
10681101

10691102
if (Invocation.getFrontendOptions().RequestedAction ==
10701103
FrontendOptions::NoneAction) {
10711104
Instance->getDiags().diagnose(SourceLoc(),
10721105
diag::error_missing_frontend_action);
1073-
return 1;
1106+
return finishDiagProcessing(1);
10741107
}
10751108

10761109
// Because the serialized diagnostics consumer is initialized here,
@@ -1084,21 +1117,8 @@ int swift::performFrontend(ArrayRef<const char *> Args,
10841117
const std::string &SerializedDiagnosticsPath =
10851118
Invocation.getFrontendOptions().SerializedDiagnosticsPath;
10861119
if (!SerializedDiagnosticsPath.empty()) {
1087-
std::error_code EC;
1088-
std::unique_ptr<llvm::raw_fd_ostream> OS;
1089-
OS.reset(new llvm::raw_fd_ostream(SerializedDiagnosticsPath,
1090-
EC,
1091-
llvm::sys::fs::F_None));
1092-
1093-
if (EC) {
1094-
Instance->getDiags().diagnose(SourceLoc(),
1095-
diag::cannot_open_serialized_file,
1096-
SerializedDiagnosticsPath, EC.message());
1097-
return 1;
1098-
}
1099-
11001120
SerializedConsumer.reset(
1101-
serialized_diagnostics::createConsumer(std::move(OS)));
1121+
serialized_diagnostics::createConsumer(SerializedDiagnosticsPath));
11021122
Instance->addDiagnosticConsumer(SerializedConsumer.get());
11031123
}
11041124
}
@@ -1108,20 +1128,7 @@ int swift::performFrontend(ArrayRef<const char *> Args,
11081128
const std::string &FixitsOutputPath =
11091129
Invocation.getFrontendOptions().FixitsOutputPath;
11101130
if (!FixitsOutputPath.empty()) {
1111-
std::error_code EC;
1112-
std::unique_ptr<llvm::raw_fd_ostream> OS;
1113-
OS.reset(new llvm::raw_fd_ostream(FixitsOutputPath,
1114-
EC,
1115-
llvm::sys::fs::F_None));
1116-
1117-
if (EC) {
1118-
Instance->getDiags().diagnose(SourceLoc(),
1119-
diag::cannot_open_file,
1120-
FixitsOutputPath, EC.message());
1121-
return 1;
1122-
}
1123-
1124-
FixitsConsumer.reset(new JSONFixitWriter(std::move(OS),
1131+
FixitsConsumer.reset(new JSONFixitWriter(FixitsOutputPath,
11251132
Invocation.getDiagnosticOptions()));
11261133
Instance->addDiagnosticConsumer(FixitsConsumer.get());
11271134
}
@@ -1167,7 +1174,7 @@ int swift::performFrontend(ArrayRef<const char *> Args,
11671174
}
11681175

11691176
if (Instance->setup(Invocation)) {
1170-
return 1;
1177+
return finishDiagProcessing(1);
11711178
}
11721179

11731180
if (StatsReporter) {
@@ -1212,7 +1219,7 @@ int swift::performFrontend(ArrayRef<const char *> Args,
12121219
}
12131220
}
12141221

1215-
return (HadError ? 1 : ReturnValue);
1222+
return finishDiagProcessing(HadError ? 1 : ReturnValue);
12161223
}
12171224

12181225
void FrontendObserver::parsedArgs(CompilerInvocation &invocation) {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
// RUN: rm -rf %t
2+
// RUN: mkdir -p %t
3+
4+
// RUN: not %target-swift-frontend -typecheck -serialize-diagnostics-path %t/nonexistent/some.dia %s 2>%t.err.txt
5+
// RUN: %FileCheck --input-file=%t.err.txt %s -check-prefix=OPEN-FAIL
6+
// OPEN-FAIL: cannot open file '{{.*}}/nonexistent/some.dia' for diagnostics emission
7+
8+
var CRASH = 0
9+
10+
// Make sure no diagnostic file is created if the compiler crashes.
11+
// RUN: not --crash %target-swift-frontend -typecheck -serialize-diagnostics-path %t/some.dia %s -debug-forbid-typecheck-prefix CRASH
12+
// RUN: not find %t/some.dia

0 commit comments

Comments
 (0)