Skip to content

Commit 7e362b4

Browse files
committed
Separate Swift and Clang errors in StoringDiagnosticConsumer
SwiftASTContext resets the state of the DiagnosticConsumer after each import to make failed module imports non-fatal, however, the internal state of the DiagnosticEngine in Swift's embedded Clang compiler does not reset when encountering a fatal error. By storing Clang diagnostics separately, these errors can be surfaces when an expression fails to IRGen (which is usually when these fatal Clang diagnostics turn into an unrecoverable problem). rdar://108557254
1 parent 684b12c commit 7e362b4

File tree

4 files changed

+159
-114
lines changed

4 files changed

+159
-114
lines changed

lldb/source/Plugins/ExpressionParser/Swift/SwiftExpressionParser.cpp

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1663,7 +1663,9 @@ SwiftExpressionParser::Parse(DiagnosticManager &diagnostic_manager,
16631663
// Helper function to diagnose errors in m_swift_scratch_context.
16641664
unsigned buffer_id = UINT32_MAX;
16651665
auto DiagnoseSwiftASTContextError = [&]() {
1666-
assert(m_swift_ast_ctx.HasErrors() && "error expected");
1666+
assert((m_swift_ast_ctx.HasErrors() ||
1667+
m_swift_ast_ctx.HasClangImporterErrors()) &&
1668+
"error expected");
16671669
m_swift_ast_ctx.PrintDiagnostics(diagnostic_manager, buffer_id, first_line,
16681670
last_line);
16691671
};
@@ -1969,7 +1971,11 @@ SwiftExpressionParser::Parse(DiagnosticManager &diagnostic_manager,
19691971
m_module.reset(ContextAndModule.second);
19701972
}
19711973

1972-
if (m_swift_ast_ctx.HasErrors()) {
1974+
// If IRGen failed without errors, the root cause may be a fatal
1975+
// Clang diagnostic.
1976+
if (m_swift_ast_ctx.HasErrors() || m_swift_ast_ctx.HasClangImporterErrors()) {
1977+
diagnostic_manager.Printf(eDiagnosticSeverityRemark,
1978+
"couldn't IRGen expression.");
19731979
DiagnoseSwiftASTContextError();
19741980
return ParseResult::unrecoverable_error;
19751981
}
@@ -1987,7 +1993,7 @@ SwiftExpressionParser::Parse(DiagnosticManager &diagnostic_manager,
19871993
}
19881994
std::string error = "couldn't IRGen expression";
19891995
diagnostic_manager.Printf(
1990-
eDiagnosticSeverityError, "couldn't IRGen expression. %s",
1996+
eDiagnosticSeverityError, "couldn't IRGen expression: %s",
19911997
warnings.empty()
19921998
? "Please enable the expression log by running \"log enable lldb "
19931999
"expr\", then run the failing expression again, and file a "

lldb/source/Plugins/TypeSystem/Swift/SwiftASTContext.cpp

Lines changed: 132 additions & 98 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#include "swift/AST/DebuggerClient.h"
2020
#include "swift/AST/Decl.h"
2121
#include "swift/AST/DiagnosticEngine.h"
22+
#include "swift/AST/DiagnosticsClangImporter.h"
2223
#include "swift/AST/DiagnosticsSema.h"
2324
#include "swift/AST/ExistentialLayout.h"
2425
#include "swift/AST/GenericParamList.h"
@@ -2307,7 +2308,7 @@ Status SwiftASTContext::GetAllErrors() const {
23072308
if (error.Success()) {
23082309
// Retrieve the error message from the DiagnosticConsumer.
23092310
DiagnosticManager diagnostic_manager;
2310-
PrintDiagnostics(diagnostic_manager);
2311+
PrintDiagnostics(diagnostic_manager, true);
23112312
error.SetErrorString(diagnostic_manager.GetString());
23122313
}
23132314
return error;
@@ -2731,6 +2732,7 @@ class StoringDiagnosticConsumer : public swift::DiagnosticConsumer {
27312732
m_ast_context.GetDiagnosticEngine().takeConsumers();
27322733
}
27332734

2735+
/// Consume a Diagnostic from the Swift compiler.
27342736
void handleDiagnostic(swift::SourceManager &source_mgr,
27352737
const swift::DiagnosticInfo &info) override {
27362738
llvm::StringRef bufferName = "<anonymous>";
@@ -2786,9 +2788,9 @@ class StoringDiagnosticConsumer : public swift::DiagnosticConsumer {
27862788
// related to the same error, but we only copy the first one as the fixits
27872789
// are mutually exclusive (for example, one may suggest inserting a '?'
27882790
// and the next may suggest inserting '!')
2789-
if (!m_raw_diagnostics.empty() &&
2790-
info.Kind == swift::DiagnosticKind::Note) {
2791-
auto &last_diagnostic = m_raw_diagnostics.back();
2791+
if (info.Kind == swift::DiagnosticKind::Note &&
2792+
!m_raw_swift_diagnostics.empty()) {
2793+
auto &last_diagnostic = m_raw_swift_diagnostics.back();
27922794
if (last_diagnostic.kind == swift::DiagnosticKind::Error &&
27932795
last_diagnostic.fixits.empty() &&
27942796
last_diagnostic.bufferID == bufferID &&
@@ -2817,32 +2819,40 @@ class StoringDiagnosticConsumer : public swift::DiagnosticConsumer {
28172819
std::string &s = os.str();
28182820
formatted_text = !s.empty() ? std::move(s) : std::string(text);
28192821
}
2820-
m_raw_diagnostics.push_back(
2821-
{formatted_text, info.Kind, bufferName, bufferID, line_col.first,
2822-
line_col.second,
2823-
use_fixits ? info.FixIts
2824-
: llvm::ArrayRef<swift::Diagnostic::FixIt>()});
2825-
2826-
if (info.Kind == swift::DiagnosticKind::Error)
2827-
m_num_errors++;
2822+
RawDiagnostic diagnostic(
2823+
formatted_text, info.Kind, bufferName, bufferID, line_col.first,
2824+
line_col.second,
2825+
use_fixits ? info.FixIts : llvm::ArrayRef<swift::Diagnostic::FixIt>());
2826+
if (info.ID == swift::diag::error_from_clang.ID) {
2827+
if (m_raw_clang_diagnostics.empty() ||
2828+
m_raw_clang_diagnostics.back() != diagnostic) {
2829+
m_raw_clang_diagnostics.push_back(std::move(diagnostic));
2830+
if (info.Kind == swift::DiagnosticKind::Error)
2831+
m_num_clang_errors++;
2832+
}
2833+
} else {
2834+
m_raw_swift_diagnostics.push_back(std::move(diagnostic));
2835+
if (info.Kind == swift::DiagnosticKind::Error)
2836+
m_num_swift_errors++;
2837+
}
28282838
}
28292839

28302840
void Clear() {
28312841
m_ast_context.GetDiagnosticEngine().resetHadAnyError();
2832-
m_raw_diagnostics.clear();
2842+
m_raw_swift_diagnostics.clear();
28332843
m_diagnostics.clear();
2834-
m_num_errors = 0;
2844+
m_num_swift_errors = 0;
2845+
// Don't reset Clang errors. ClangImporter's DiagnosticEngine doesn't either.
28352846
}
28362847

28372848
unsigned NumErrors() {
2838-
if (m_num_errors)
2839-
return m_num_errors;
2840-
else if (m_ast_context.GetASTContext()->hadError())
2841-
return 1;
2842-
else
2843-
return 0;
2849+
if (m_num_swift_errors)
2850+
return m_num_swift_errors;
2851+
return m_ast_context.GetASTContext()->hadError();
28442852
}
28452853

2854+
unsigned NumClangErrors() { return m_num_clang_errors; }
2855+
28462856
static DiagnosticSeverity SeverityForKind(swift::DiagnosticKind kind) {
28472857
switch (kind) {
28482858
case swift::DiagnosticKind::Error:
@@ -2857,93 +2867,101 @@ class StoringDiagnosticConsumer : public swift::DiagnosticConsumer {
28572867
llvm_unreachable("Unhandled DiagnosticKind in switch.");
28582868
}
28592869

2870+
// Forward the stored diagnostics to \c diagnostic_manager.
28602871
void PrintDiagnostics(DiagnosticManager &diagnostic_manager,
28612872
uint32_t bufferID = UINT32_MAX, uint32_t first_line = 0,
28622873
uint32_t last_line = UINT32_MAX) {
28632874
bool added_one_diagnostic = !m_diagnostics.empty();
28642875

2865-
for (std::unique_ptr<Diagnostic> &diagnostic : m_diagnostics) {
2876+
for (std::unique_ptr<Diagnostic> &diagnostic : m_diagnostics)
28662877
diagnostic_manager.AddDiagnostic(std::move(diagnostic));
2867-
}
28682878

2869-
for (const RawDiagnostic &diagnostic : m_raw_diagnostics) {
2870-
// We often make expressions and wrap them in some code. When
2871-
// we see errors we want the line numbers to be correct so we
2872-
// correct them below. LLVM stores in SourceLoc objects as
2873-
// character offsets so there is no way to get LLVM to move its
2874-
// error line numbers around by adjusting the source location,
2875-
// we must do it manually. We also want to use the same error
2876-
// formatting as LLVM and Clang, so we must muck with the
2877-
// string.
2879+
// We often make expressions and wrap them in some code. When we
2880+
// see errors we want the line numbers to be correct so we correct
2881+
// them below. LLVM stores in SourceLoc objects as character
2882+
// offsets so there is no way to get LLVM to move its error line
2883+
// numbers around by adjusting the source location, we must do it
2884+
// manually. We also want to use the same error formatting as LLVM
2885+
// and Clang, so we must muck with the string.
28782886

2887+
auto format_diagnostic = [&](const RawDiagnostic &diagnostic) {
28792888
const DiagnosticSeverity severity = SeverityForKind(diagnostic.kind);
28802889
const DiagnosticOrigin origin = eDiagnosticOriginSwift;
28812890

2882-
if (first_line > 0 && bufferID != UINT32_MAX) {
2883-
// Make sure the error line is in range or in another file.
2884-
if (diagnostic.bufferID == bufferID && !diagnostic.bufferName.empty() &&
2885-
(diagnostic.line < first_line || diagnostic.line > last_line))
2886-
continue;
2887-
// Need to remap the error/warning to a different line.
2888-
StreamString match;
2889-
match.Printf("%s:%u:", diagnostic.bufferName.str().c_str(),
2890-
diagnostic.line);
2891-
const size_t match_len = match.GetString().size();
2892-
size_t match_pos = diagnostic.description.find(match.GetString().str());
2893-
if (match_pos != std::string::npos) {
2894-
// We have some <file>:<line>:" instances that need to be updated.
2895-
StreamString fixed_description;
2896-
size_t start_pos = 0;
2897-
do {
2898-
if (match_pos > start_pos)
2899-
fixed_description.Printf(
2900-
"%s",
2901-
diagnostic.description.substr(start_pos, match_pos).c_str());
2902-
fixed_description.Printf(
2903-
"%s:%u:", diagnostic.bufferName.str().c_str(),
2904-
diagnostic.line - first_line + 1);
2905-
start_pos = match_pos + match_len;
2906-
match_pos =
2907-
diagnostic.description.find(match.GetString().str(), start_pos);
2908-
} while (match_pos != std::string::npos);
2909-
2910-
// Append any last remaining text.
2911-
if (start_pos < diagnostic.description.size())
2912-
fixed_description.Printf(
2913-
"%s", diagnostic.description
2914-
.substr(start_pos,
2915-
diagnostic.description.size() - start_pos)
2916-
.c_str());
2917-
2918-
auto new_diagnostic = std::make_unique<SwiftDiagnostic>(
2919-
fixed_description.GetData(), severity, origin, bufferID);
2920-
for (auto fixit : diagnostic.fixits)
2921-
new_diagnostic->AddFixIt(fixit);
2922-
2923-
diagnostic_manager.AddDiagnostic(std::move(new_diagnostic));
2924-
if (diagnostic.kind == swift::DiagnosticKind::Error)
2925-
added_one_diagnostic = true;
2891+
if ((first_line <= 0) || (bufferID == UINT32_MAX))
2892+
return;
2893+
2894+
// Make sure the error line is in range or in another file.
2895+
if (diagnostic.bufferID == bufferID && !diagnostic.bufferName.empty() &&
2896+
(diagnostic.line < first_line || diagnostic.line > last_line))
2897+
return;
29262898

2927-
}
2928-
}
2929-
}
2899+
// Need to remap the error/warning to a different line.
2900+
StreamString match;
2901+
match.Printf("%s:%u:", diagnostic.bufferName.str().c_str(),
2902+
diagnostic.line);
2903+
const size_t match_len = match.GetString().size();
2904+
size_t match_pos = diagnostic.description.find(match.GetString().str());
2905+
if (match_pos == std::string::npos)
2906+
return;
2907+
2908+
// We have some <file>:<line>:" instances that need to be updated.
2909+
StreamString fixed_description;
2910+
size_t start_pos = 0;
2911+
do {
2912+
if (match_pos > start_pos)
2913+
fixed_description.Printf(
2914+
"%s",
2915+
diagnostic.description.substr(start_pos, match_pos).c_str());
2916+
fixed_description.Printf("%s:%u:", diagnostic.bufferName.str().c_str(),
2917+
diagnostic.line - first_line + 1);
2918+
start_pos = match_pos + match_len;
2919+
match_pos =
2920+
diagnostic.description.find(match.GetString().str(), start_pos);
2921+
} while (match_pos != std::string::npos);
2922+
2923+
// Append any last remaining text.
2924+
if (start_pos < diagnostic.description.size())
2925+
fixed_description.Printf(
2926+
"%s",
2927+
diagnostic.description
2928+
.substr(start_pos, diagnostic.description.size() - start_pos)
2929+
.c_str());
2930+
2931+
auto new_diagnostic = std::make_unique<SwiftDiagnostic>(
2932+
fixed_description.GetData(), severity, origin, bufferID);
2933+
for (auto fixit : diagnostic.fixits)
2934+
new_diagnostic->AddFixIt(fixit);
2935+
2936+
diagnostic_manager.AddDiagnostic(std::move(new_diagnostic));
2937+
if (diagnostic.kind == swift::DiagnosticKind::Error)
2938+
added_one_diagnostic = true;
2939+
};
2940+
2941+
for (auto &diag : m_raw_clang_diagnostics)
2942+
format_diagnostic(diag);
2943+
for (auto &diag : m_raw_swift_diagnostics)
2944+
format_diagnostic(diag);
29302945

29312946
// In general, we don't want to see diagnostics from outside of
2932-
// the source text range of the actual user expression. But if we
2933-
// didn't find any diagnostics in the text range, it's probably
2934-
// because the source range was not specified correctly, and we
2935-
// don't want to lose legit errors because of that. So in that
2936-
// case we'll add them all here:
2947+
// the source text range of the actual user expression. This
2948+
// either indicates a bug in LLDB or that there is only a
2949+
// ClangImporter diagnostic.
29372950
if (!added_one_diagnostic) {
29382951
// This will report diagnostic errors from outside the
29392952
// expression's source range. Those are not interesting to
29402953
// users, so we only emit them in debug builds.
2941-
for (const RawDiagnostic &diagnostic : m_raw_diagnostics) {
2942-
const DiagnosticSeverity severity = SeverityForKind(diagnostic.kind);
2943-
const DiagnosticOrigin origin = eDiagnosticOriginSwift;
2954+
for (const RawDiagnostic &diagnostic : m_raw_clang_diagnostics)
29442955
diagnostic_manager.AddDiagnostic(diagnostic.description.c_str(),
2945-
severity, origin);
2946-
}
2956+
SeverityForKind(diagnostic.kind),
2957+
eDiagnosticOriginClang);
2958+
2959+
// If we printed a clang error, ignore Swift warnings, which are expected.
2960+
if (!m_num_clang_errors || m_num_swift_errors)
2961+
for (const RawDiagnostic &diagnostic : m_raw_swift_diagnostics)
2962+
diagnostic_manager.AddDiagnostic(diagnostic.description.c_str(),
2963+
SeverityForKind(diagnostic.kind),
2964+
eDiagnosticOriginSwift);
29472965
}
29482966
}
29492967

@@ -2955,6 +2973,7 @@ class StoringDiagnosticConsumer : public swift::DiagnosticConsumer {
29552973
return old;
29562974
}
29572975

2976+
/// This is only used by ReconstructTypes.
29582977
void AddDiagnostic(std::unique_ptr<Diagnostic> diagnostic) {
29592978
if (diagnostic)
29602979
m_diagnostics.push_back(std::move(diagnostic));
@@ -2973,6 +2992,13 @@ class StoringDiagnosticConsumer : public swift::DiagnosticConsumer {
29732992
: description(in_desc), kind(in_kind), bufferName(in_bufferName),
29742993
bufferID(in_bufferID), line(in_line), column(in_column),
29752994
fixits(in_fixits) {}
2995+
bool operator==(const RawDiagnostic& other) {
2996+
return kind == other.kind && bufferID == other.bufferID &&
2997+
line == other.line && column == other.column &&
2998+
description == other.description;
2999+
}
3000+
bool operator!=(const RawDiagnostic &other) { return !(*this == other); }
3001+
29763002
std::string description;
29773003
swift::DiagnosticKind kind;
29783004
const llvm::StringRef bufferName;
@@ -2981,14 +3007,16 @@ class StoringDiagnosticConsumer : public swift::DiagnosticConsumer {
29813007
uint32_t column;
29823008
std::vector<swift::DiagnosticInfo::FixIt> fixits;
29833009
};
2984-
typedef std::vector<RawDiagnostic> RawDiagnosticBuffer;
2985-
typedef std::vector<std::unique_ptr<Diagnostic>> DiagnosticList;
29863010

29873011
SwiftASTContext &m_ast_context;
2988-
RawDiagnosticBuffer m_raw_diagnostics;
2989-
DiagnosticList m_diagnostics;
2990-
2991-
unsigned m_num_errors = 0;
3012+
/// Stores all diagnostics coming from the Swift compiler.
3013+
std::vector<RawDiagnostic> m_raw_swift_diagnostics;
3014+
std::vector<RawDiagnostic> m_raw_clang_diagnostics;
3015+
/// Stores diagnostics coming from LLDB.
3016+
std::vector<std::unique_ptr<Diagnostic>> m_diagnostics;
3017+
3018+
unsigned m_num_swift_errors = 0;
3019+
unsigned m_num_clang_errors = 0;
29923020
bool m_colorize = false;
29933021
};
29943022

@@ -3040,7 +3068,7 @@ swift::ASTContext *SwiftASTContext::GetASTContext() {
30403068
m_module_import_warnings.push_back(message);
30413069
} else {
30423070
DiagnosticManager diagnostic_manager;
3043-
PrintDiagnostics(diagnostic_manager);
3071+
PrintDiagnostics(diagnostic_manager, true);
30443072
std::string underlying_error = diagnostic_manager.GetString();
30453073
message = "failed to initialize ClangImporter: ";
30463074
message += underlying_error;
@@ -3353,7 +3381,7 @@ swift::ModuleDecl *SwiftASTContext::GetModule(const SourceModule &module,
33533381
swift::ModuleDecl *module_decl = ast->getModuleByName(module_basename_sref);
33543382
if (HasErrors()) {
33553383
DiagnosticManager diagnostic_manager;
3356-
PrintDiagnostics(diagnostic_manager);
3384+
PrintDiagnostics(diagnostic_manager, true);
33573385
std::string diagnostic = diagnostic_manager.GetString();
33583386
error.SetErrorStringWithFormat(
33593387
"failed to get module \"%s\" from AST context:\n%s",
@@ -4738,8 +4766,14 @@ bool SwiftASTContext::HasErrors() {
47384766
return (
47394767
static_cast<StoringDiagnosticConsumer *>(m_diagnostic_consumer_ap.get())
47404768
->NumErrors() != 0);
4741-
else
4742-
return false;
4769+
return false;
4770+
}
4771+
bool SwiftASTContext::HasClangImporterErrors() {
4772+
if (m_diagnostic_consumer_ap.get())
4773+
return (
4774+
static_cast<StoringDiagnosticConsumer *>(m_diagnostic_consumer_ap.get())
4775+
->NumClangErrors() != 0);
4776+
return false;
47434777
}
47444778

47454779
bool SwiftASTContext::HasFatalErrors(swift::ASTContext *ast_context) {

0 commit comments

Comments
 (0)