Skip to content

Commit 9d33d58

Browse files
committed
[clang] Make serialized diagnostics more reliable
`SDiagsWriter` currently requires clients to call `DiagnosticConsumer::finish()` before it is destroyed if `CompilerInstance::ExecuteAction()` has not been called. The problem with this is that it makes it much harder for clients to properly call `finish()` for cases such as errors from running the driver, or clients that want to emit diagnostics after executing an action, as they need to be careful to not call it twice. This patch removes the call in `CompilerInstance::ExecuteAction()`, and instead adds a call to finish in `SDiagsWriter`'s destructor if it was not already called. It also adds a call where DisableFree would otherwise prevent the destructor from getting called. No test because the issues this caused were in not yet upstreamed code.
1 parent 25f9415 commit 9d33d58

File tree

4 files changed

+31
-16
lines changed

4 files changed

+31
-16
lines changed

clang/include/clang/Basic/Diagnostic.h

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1776,8 +1776,12 @@ class DiagnosticConsumer {
17761776
/// BeginSourceFile() are inaccessible.
17771777
virtual void EndSourceFile() {}
17781778

1779-
/// Callback to inform the diagnostic client that processing of all
1780-
/// source files has ended.
1779+
/// Callback to inform the diagnostic client that processing of all source
1780+
/// files has ended, and that no more diagnostics will be emitted.
1781+
///
1782+
/// This is only called when the DiagnosticConsumer's destructor would not
1783+
/// otherwise be called. Subclasses that require this to be called must ensure
1784+
/// that in their destructor.
17811785
virtual void finish() {}
17821786

17831787
/// Indicates whether the diagnostics handled by this

clang/lib/Frontend/CompilerInstance.cpp

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1016,12 +1016,6 @@ bool CompilerInstance::ExecuteAction(FrontendAction &Act) {
10161016
// better. We generally expect frontend actions to be invoked with (nearly)
10171017
// DesiredStackSpace available.
10181018
noteBottomOfStack();
1019-
1020-
auto FinishDiagnosticClient = llvm::make_scope_exit([&]() {
1021-
// Notify the diagnostic client that all files were processed.
1022-
getDiagnosticClient().finish();
1023-
});
1024-
10251019
raw_ostream &OS = getVerboseOutputStream();
10261020

10271021
if (!Act.PrepareToExecute(*this))

clang/lib/Frontend/SerializedDiagnosticPrinter.cpp

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,7 @@ class SDiagsMerger : SerializedDiagnosticReader {
129129
void writeRecordWithBlob(unsigned ID, RecordData &Record, StringRef Blob);
130130
};
131131

132-
class SDiagsWriter : public DiagnosticConsumer {
132+
class SDiagsWriter final : public DiagnosticConsumer {
133133
friend class SDiagsRenderer;
134134
friend class SDiagsMerger;
135135

@@ -149,7 +149,13 @@ class SDiagsWriter : public DiagnosticConsumer {
149149
EmitPreamble();
150150
}
151151

152-
~SDiagsWriter() override {}
152+
~SDiagsWriter() override {
153+
// Not all uses of DiagnosticConsumer call finish, and not all uses invoke
154+
// the destructor. This makes calling finish optional for cases where it
155+
// is properly destructed.
156+
if (!IsFinishing)
157+
finish();
158+
}
153159

154160
void HandleDiagnostic(DiagnosticsEngine::Level DiagLevel,
155161
const Diagnostic &Info) override;

clang/tools/driver/cc1_main.cpp

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
#include "clang/Frontend/Utils.h"
2727
#include "clang/FrontendTool/Utils.h"
2828
#include "clang/Serialization/ObjectFilePCHContainerReader.h"
29+
#include "llvm/ADT/ScopeExit.h"
2930
#include "llvm/ADT/Statistic.h"
3031
#include "llvm/ADT/StringExtras.h"
3132
#include "llvm/Config/llvm-config.h"
@@ -274,10 +275,21 @@ int cc1_main(ArrayRef<const char *> Argv, const char *Argv0, void *MainAddr) {
274275
static_cast<void*>(&Clang->getDiagnostics()));
275276

276277
DiagsBuffer->FlushDiagnostics(Clang->getDiagnostics());
277-
if (!Success) {
278+
279+
auto FinishDiagnosticClient = [&]() {
280+
// Notify the diagnostic client that all files were processed.
278281
Clang->getDiagnosticClient().finish();
282+
283+
// Our error handler depends on the Diagnostics object, which we're
284+
// potentially about to delete. Uninstall the handler now so that any
285+
// later errors use the default handling behavior instead.
286+
llvm::remove_fatal_error_handler();
287+
};
288+
auto FinishDiagnosticClientScope =
289+
llvm::make_scope_exit([&]() { FinishDiagnosticClient(); });
290+
291+
if (!Success)
279292
return 1;
280-
}
281293

282294
// Execute the frontend actions.
283295
{
@@ -314,10 +326,9 @@ int cc1_main(ArrayRef<const char *> Argv, const char *Argv0, void *MainAddr) {
314326
}
315327
}
316328

317-
// Our error handler depends on the Diagnostics object, which we're
318-
// potentially about to delete. Uninstall the handler now so that any
319-
// later errors use the default handling behavior instead.
320-
llvm::remove_fatal_error_handler();
329+
// Call this before the Clang pointer is moved below.
330+
FinishDiagnosticClient();
331+
FinishDiagnosticClientScope.release();
321332

322333
// When running with -disable-free, don't do any destruction or shutdown.
323334
if (Clang->getFrontendOpts().DisableFree) {

0 commit comments

Comments
 (0)