diff --git a/doc/rules.md b/doc/rules.md
index 8c5b63dce..7540d104f 100644
--- a/doc/rules.md
+++ b/doc/rules.md
@@ -458,7 +458,8 @@ its transitive dependencies be propagated.
swift_library(name, deps, srcs, data, always_include_developer_search_paths, alwayslink, copts,
defines, generated_header_name, generates_header, library_evolution, linkopts,
- linkstatic, module_name, package_name, plugins, private_deps, swiftc_inputs)
+ linkstatic, module_name, package_name, plugins, private_deps, suppress_warning_groups,
+ swiftc_inputs)
Compiles and links Swift code into a static library and Swift module.
@@ -485,6 +486,7 @@ Compiles and links Swift code into a static library and Swift module.
| package_name | The semantic package of the Swift target being built. Targets with the same package_name can access APIs using the 'package' access control modifier in Swift 5.9+. | String | optional | `""` |
| plugins | A list of `swift_compiler_plugin` targets that should be loaded by the compiler when compiling this module and any modules that directly depend on it. | List of labels | optional | `[]` |
| private_deps | A list of targets that are implementation-only dependencies of the target being built. Libraries/linker flags from these dependencies will be propagated to dependent for linking, but artifacts/flags required for compilation (such as .swiftmodule files, C headers, and search paths) will not be propagated.
Allowed kinds of dependencies are:
* `swift_library` (or anything propagating `SwiftInfo`)
* `cc_library` and `objc_library` (or anything propagating `CcInfo`) | List of labels | optional | `[]` |
+| suppress_warning_groups | A list of Swift diagnostic groups to suppress in build output (for example, `DeprecatedDeclaration`). These diagnostics are filtered from the compiler's output by the wrapper and do not change the compiler's behavior. See https://docs.swift.org/compiler/documentation/diagnostics/diagnostic-groups/ for available diagnostic groups. | List of strings | optional | `[]` |
| swiftc_inputs | Additional files that are referenced using `$(location ...)` in attributes that support location expansion. | List of labels | optional | `[]` |
@@ -618,7 +620,7 @@ remaining modules collected are not present in the `aliases` of the
swift_overlay(name, deps, srcs, always_include_developer_search_paths, alwayslink, copts, defines,
library_evolution, linkopts, linkstatic, package_name, plugins, private_deps,
- swiftc_inputs)
+ suppress_warning_groups, swiftc_inputs)
A Swift overlay that sits on top of a C/Objective-C library, allowing an author
@@ -709,6 +711,7 @@ almost always an anti-pattern.
| package_name | The semantic package of the Swift target being built. Targets with the same package_name can access APIs using the 'package' access control modifier in Swift 5.9+. | String | optional | `""` |
| plugins | A list of `swift_compiler_plugin` targets that should be loaded by the compiler when compiling this module and any modules that directly depend on it. | List of labels | optional | `[]` |
| private_deps | A list of targets that are implementation-only dependencies of the target being built. Libraries/linker flags from these dependencies will be propagated to dependent for linking, but artifacts/flags required for compilation (such as .swiftmodule files, C headers, and search paths) will not be propagated.
Allowed kinds of dependencies are:
* `swift_library` (or anything propagating `SwiftInfo`)
* `cc_library` and `objc_library` (or anything propagating `CcInfo`) | List of labels | optional | `[]` |
+| suppress_warning_groups | A list of Swift diagnostic groups to suppress in build output (for example, `DeprecatedDeclaration`). These diagnostics are filtered from the compiler's output by the wrapper and do not change the compiler's behavior. See https://docs.swift.org/compiler/documentation/diagnostics/diagnostic-groups/ for available diagnostic groups. | List of strings | optional | `[]` |
| swiftc_inputs | Additional files that are referenced using `$(location ...)` in attributes that support location expansion. | List of labels | optional | `[]` |
@@ -772,7 +775,8 @@ swift_proto_compiler(name, name, deps, srcs, data, additional_compiler_deps, additional_compiler_info,
always_include_developer_search_paths, alwayslink, compilers, copts, defines,
generated_header_name, generates_header, library_evolution, linkopts, linkstatic,
- module_name, package_name, plugins, protos, swiftc_inputs)
+ module_name, package_name, plugins, protos, suppress_warning_groups,
+ swiftc_inputs)
Generates a Swift static library from one or more targets producing `ProtoInfo`.
@@ -837,6 +841,7 @@ swift_proto_library(
| package_name | The semantic package of the Swift target being built. Targets with the same package_name can access APIs using the 'package' access control modifier in Swift 5.9+. | String | optional | `""` |
| plugins | A list of `swift_compiler_plugin` targets that should be loaded by the compiler when compiling this module and any modules that directly depend on it. | List of labels | optional | `[]` |
| protos | A list of `proto_library` targets (or targets producing `ProtoInfo`), from which the Swift source files should be generated. | List of labels | optional | `[]` |
+| suppress_warning_groups | A list of Swift diagnostic groups to suppress in build output (for example, `DeprecatedDeclaration`). These diagnostics are filtered from the compiler's output by the wrapper and do not change the compiler's behavior. See https://docs.swift.org/compiler/documentation/diagnostics/diagnostic-groups/ for available diagnostic groups. | List of strings | optional | `[]` |
| swiftc_inputs | Additional files that are referenced using `$(location ...)` in attributes that support location expansion. | List of labels | optional | `[]` |
diff --git a/examples/xplatform/suppress_warning_groups/BUILD b/examples/xplatform/suppress_warning_groups/BUILD
new file mode 100644
index 000000000..bad1a4ae3
--- /dev/null
+++ b/examples/xplatform/suppress_warning_groups/BUILD
@@ -0,0 +1,14 @@
+load("//swift:swift_library.bzl", "swift_library")
+
+swift_library(
+ name = "impl_dep",
+ srcs = ["ImplDep.swift"],
+ module_name = "ImplDep",
+)
+
+swift_library(
+ name = "impl_only_suppressed",
+ srcs = ["ImplementationOnlySuppressed.swift"],
+ private_deps = [":impl_dep"],
+ suppress_warning_groups = ["ImplementationOnlyDeprecated"],
+)
diff --git a/examples/xplatform/suppress_warning_groups/ImplDep.swift b/examples/xplatform/suppress_warning_groups/ImplDep.swift
new file mode 100644
index 000000000..f3b4a05cc
--- /dev/null
+++ b/examples/xplatform/suppress_warning_groups/ImplDep.swift
@@ -0,0 +1,3 @@
+public struct Dep {
+ public init() {}
+}
diff --git a/examples/xplatform/suppress_warning_groups/ImplementationOnlySuppressed.swift b/examples/xplatform/suppress_warning_groups/ImplementationOnlySuppressed.swift
new file mode 100644
index 000000000..c2ab6d2ba
--- /dev/null
+++ b/examples/xplatform/suppress_warning_groups/ImplementationOnlySuppressed.swift
@@ -0,0 +1,5 @@
+@_implementationOnly import ImplDep
+
+func useDepSuppressed() {
+ _ = Dep()
+}
diff --git a/proto/swift_proto_utils.bzl b/proto/swift_proto_utils.bzl
index 551420c7a..e505a7054 100644
--- a/proto/swift_proto_utils.bzl
+++ b/proto/swift_proto_utils.bzl
@@ -271,6 +271,14 @@ def compile_swift_protos_for_target(
copts = expand_make_variables(ctx, copts, "copts")
linkopts = expand_locations(ctx, getattr(attr, "linkopts", []), swiftc_inputs)
linkopts = expand_make_variables(ctx, linkopts, "linkopts")
+ suppress_warning_groups = getattr(attr, "suppress_warning_groups", [])
+ if suppress_warning_groups:
+ if "-print-diagnostic-groups" not in copts:
+ copts.append("-print-diagnostic-groups")
+ for group in suppress_warning_groups:
+ copts.append(
+ "-Xwrapped-swift=-suppress-warning-group={}".format(group),
+ )
# Compile the generated Swift source files as a module:
include_dev_srch_paths = include_developer_search_paths(attr)
diff --git a/swift/internal/attrs.bzl b/swift/internal/attrs.bzl
index 070511b4b..56281eb67 100644
--- a/swift/internal/attrs.bzl
+++ b/swift/internal/attrs.bzl
@@ -323,6 +323,15 @@ Note that by default, this value will default to True. But if the
swift.enable_embedded feature is on, this value will be automatically overridden
to False, as the swift features that cause -force_load to be required (such as
reflection) are not available in that mode.
+""",
+ ),
+ "suppress_warning_groups": attr.string_list(
+ doc = """\
+A list of Swift diagnostic groups to suppress in build output (for example,
+`DeprecatedDeclaration`). These diagnostics are filtered from the compiler's
+output by the wrapper and do not change the compiler's behavior. See
+https://docs.swift.org/compiler/documentation/diagnostics/diagnostic-groups/
+for available diagnostic groups.
""",
),
"generated_header_name": attr.string(
diff --git a/swift/swift_library.bzl b/swift/swift_library.bzl
index 24ade7444..c24c96225 100644
--- a/swift/swift_library.bzl
+++ b/swift/swift_library.bzl
@@ -129,6 +129,15 @@ def _swift_library_impl(ctx):
linkopts = expand_locations(ctx, ctx.attr.linkopts, ctx.attr.swiftc_inputs)
linkopts = expand_make_variables(ctx, linkopts, "linkopts")
srcs = ctx.files.srcs
+ if ctx.attr.suppress_warning_groups:
+ if "-print-diagnostic-groups" not in copts:
+ copts.append("-print-diagnostic-groups")
+ for group in ctx.attr.suppress_warning_groups:
+ copts.append(
+ "-Xwrapped-swift=-suppress-warning-group={}".format(
+ group,
+ ),
+ )
module_copts = additional_per_module_swiftcopts(
ctx.label,
diff --git a/swift/swift_overlay.bzl b/swift/swift_overlay.bzl
index a17e4d529..61cf488ff 100644
--- a/swift/swift_overlay.bzl
+++ b/swift/swift_overlay.bzl
@@ -47,6 +47,17 @@ def _swift_overlay_impl(ctx):
deps = ctx.attr.deps
private_deps = ctx.attr.private_deps
+ copts = list(ctx.attr.copts)
+ if ctx.attr.suppress_warning_groups:
+ if "-print-diagnostic-groups" not in copts:
+ copts.append("-print-diagnostic-groups")
+ for group in ctx.attr.suppress_warning_groups:
+ copts.append(
+ "-Xwrapped-swift=-suppress-warning-group={}".format(
+ group,
+ ),
+ )
+
features = list(ctx.features)
if ctx.attr.library_evolution:
features.append(SWIFT_FEATURE_ENABLE_LIBRARY_EVOLUTION)
@@ -59,7 +70,7 @@ def _swift_overlay_impl(ctx):
label = ctx.label,
srcs = ctx.files.srcs,
additional_inputs = ctx.files.swiftc_inputs,
- copts = ctx.attr.copts,
+ copts = copts,
defines = ctx.attr.defines,
disabled_features = ctx.disabled_features,
enabled_features = ctx.features,
diff --git a/test/BUILD b/test/BUILD
index 5929d0850..d6772c29b 100644
--- a/test/BUILD
+++ b/test/BUILD
@@ -22,6 +22,7 @@ load(":private_deps_tests.bzl", "private_deps_test_suite")
load(":private_swiftinterface_tests.bzl", "private_swiftinterface_test_suite")
load(":runtime_deps_tests.bzl", "runtime_deps_test_suite")
load(":split_derived_files_tests.bzl", "split_derived_files_test_suite")
+load(":suppress_warning_groups_tests.bzl", "suppress_warning_groups_test_suite")
load(":swift_binary_linking_tests.bzl", "swift_binary_linking_test_suite")
load(":swift_through_non_swift_tests.bzl", "swift_through_non_swift_test_suite")
load(":symbol_graphs_tests.bzl", "symbol_graphs_test_suite")
@@ -69,6 +70,8 @@ private_deps_test_suite(name = "private_deps")
split_derived_files_test_suite(name = "split_derived_files")
+suppress_warning_groups_test_suite(name = "suppress_warning_groups")
+
swift_binary_linking_test_suite(name = "swift_binary_rules")
swift_through_non_swift_test_suite(name = "swift_through_non_swift")
diff --git a/test/fixtures/suppress_warning_groups/BUILD b/test/fixtures/suppress_warning_groups/BUILD
new file mode 100644
index 000000000..c7196e8a8
--- /dev/null
+++ b/test/fixtures/suppress_warning_groups/BUILD
@@ -0,0 +1,31 @@
+load("//swift:swift_library.bzl", "swift_library")
+load("//swift:swift_overlay.bzl", "swift_overlay")
+load("//test/fixtures:common.bzl", "FIXTURE_TAGS")
+
+package(
+ default_visibility = ["//test:__subpackages__"],
+)
+
+swift_library(
+ name = "single_category",
+ srcs = ["Library.swift"],
+ suppress_warning_groups = ["DeprecatedDeclaration"],
+ tags = FIXTURE_TAGS,
+)
+
+swift_library(
+ name = "multiple_categories",
+ srcs = ["Library.swift"],
+ suppress_warning_groups = [
+ "DeprecatedDeclaration",
+ "ImplementationOnlyDeprecated",
+ ],
+ tags = FIXTURE_TAGS,
+)
+
+swift_overlay(
+ name = "overlay",
+ srcs = ["Overlay.swift"],
+ suppress_warning_groups = ["OverlayDeprecated"],
+ tags = FIXTURE_TAGS,
+)
diff --git a/test/fixtures/suppress_warning_groups/Library.swift b/test/fixtures/suppress_warning_groups/Library.swift
new file mode 100644
index 000000000..2da93851b
--- /dev/null
+++ b/test/fixtures/suppress_warning_groups/Library.swift
@@ -0,0 +1 @@
+public func libraryFunction() {}
diff --git a/test/fixtures/suppress_warning_groups/Overlay.swift b/test/fixtures/suppress_warning_groups/Overlay.swift
new file mode 100644
index 000000000..c39879658
--- /dev/null
+++ b/test/fixtures/suppress_warning_groups/Overlay.swift
@@ -0,0 +1 @@
+public func overlayFunction() {}
diff --git a/test/suppress_warning_groups_tests.bzl b/test/suppress_warning_groups_tests.bzl
new file mode 100644
index 000000000..9de7343bc
--- /dev/null
+++ b/test/suppress_warning_groups_tests.bzl
@@ -0,0 +1,95 @@
+"""Tests for suppress_warning_groups."""
+
+load(
+ "//test/rules:action_command_line_test.bzl",
+ "action_command_line_test",
+)
+load("@bazel_skylib//lib:unittest.bzl", "analysistest", "unittest")
+load("//swift/internal:providers.bzl", "SwiftOverlayCompileInfo")
+
+def suppress_warning_groups_test_suite(name, tags = []):
+ """Test suite for suppress_warning_groups handling.
+
+ Args:
+ name: The base name to be used in targets created by this macro.
+ tags: Additional tags to apply to each test.
+ """
+ all_tags = [name] + tags
+
+ action_command_line_test(
+ name = "{}_swift_library_single".format(name),
+ expected_argv = [
+ "-Xwrapped-swift=-suppress-warning-group=DeprecatedDeclaration",
+ ],
+ mnemonic = "SwiftCompile",
+ tags = all_tags,
+ target_under_test = "//test/fixtures/suppress_warning_groups:single_category",
+ )
+
+ action_command_line_test(
+ name = "{}_swift_library_multiple".format(name),
+ expected_argv = [
+ "-Xwrapped-swift=-suppress-warning-group=DeprecatedDeclaration",
+ "-Xwrapped-swift=-suppress-warning-group=ImplementationOnlyDeprecated",
+ ],
+ mnemonic = "SwiftCompile",
+ tags = all_tags,
+ target_under_test = "//test/fixtures/suppress_warning_groups:multiple_categories",
+ )
+
+ overlay_copts_test(
+ name = "{}_swift_overlay_copts".format(name),
+ expected_copt = "-Xwrapped-swift=-suppress-warning-group=OverlayDeprecated",
+ tags = all_tags,
+ target_under_test = "//test/fixtures/suppress_warning_groups:overlay",
+ )
+
+ native.test_suite(
+ name = name,
+ tags = all_tags,
+ )
+
+def _normalize_copt(copt):
+ if copt.startswith("\"") and copt.endswith("\""):
+ return copt[1:-1]
+ return copt
+
+def _overlay_copts_test_impl(ctx):
+ env = analysistest.begin(ctx)
+ target_under_test = analysistest.target_under_test(env)
+
+ if SwiftOverlayCompileInfo not in target_under_test:
+ unittest.fail(
+ env,
+ "Target '{}' did not provide SwiftOverlayCompileInfo.".format(
+ target_under_test.label,
+ ),
+ )
+ return analysistest.end(env)
+
+ actual = [
+ _normalize_copt(copt)
+ for copt in target_under_test[SwiftOverlayCompileInfo].copts
+ ]
+ expected = ctx.attr.expected_copt
+ if expected not in actual:
+ unittest.fail(
+ env,
+ ("Expected '{}' to contain '{}' in SwiftOverlayCompileInfo.copts, " +
+ "but got {}.").format(
+ target_under_test.label,
+ expected,
+ actual,
+ ),
+ )
+ return analysistest.end(env)
+
+overlay_copts_test = analysistest.make(
+ _overlay_copts_test_impl,
+ attrs = {
+ "expected_copt": attr.string(
+ mandatory = True,
+ doc = "The compiler option expected in SwiftOverlayCompileInfo.copts.",
+ ),
+ },
+)
diff --git a/tools/worker/swift_runner.cc b/tools/worker/swift_runner.cc
index 86e70fc20..a2dc91bf8 100644
--- a/tools/worker/swift_runner.cc
+++ b/tools/worker/swift_runner.cc
@@ -14,8 +14,11 @@
#include "tools/worker/swift_runner.h"
+#include
+#include
#include
#include
+#include
#include "tools/common/bazel_substitutions.h"
#include "tools/common/process.h"
@@ -103,6 +106,391 @@ static bool StripPrefix(const std::string &prefix, std::string &str) {
return true;
}
+static std::string NormalizeDiagnosticGroup(std::string group) {
+ size_t first = group.find_first_not_of(" \t");
+ if (first == std::string::npos) {
+ return "";
+ }
+ size_t last = group.find_last_not_of(" \t");
+ group = group.substr(first, last - first + 1);
+ if (group.rfind("[#", 0) == 0 && group.back() == ']') {
+ group = group.substr(2, group.size() - 3);
+ }
+ if (!group.empty() && group.front() == '#') {
+ group.erase(0, 1);
+ }
+ return group;
+}
+
+static std::string StripAnsiCodes(const std::string &line) {
+ auto skip_csi_sequence = [&](size_t index) {
+ size_t pos = index + 2;
+ while (pos < line.size() && (line[pos] < '@' || line[pos] > '~')) {
+ ++pos;
+ }
+ return pos;
+ };
+
+ auto skip_osc_sequence = [&](size_t index) {
+ size_t pos = index + 2;
+ while (pos < line.size()) {
+ if (line[pos] == '\a') {
+ break;
+ }
+ if (line[pos] == '\x1b' && pos + 1 < line.size() &&
+ line[pos + 1] == '\\') {
+ ++pos;
+ break;
+ }
+ ++pos;
+ }
+ return pos;
+ };
+
+ auto try_skip_escape = [&](size_t *index) {
+ if (line[*index] != '\x1b' || *index + 1 >= line.size()) {
+ return false;
+ }
+ if (line[*index + 1] == '[') {
+ *index = skip_csi_sequence(*index);
+ return true;
+ }
+ if (line[*index + 1] == ']') {
+ *index = skip_osc_sequence(*index);
+ return true;
+ }
+ return false;
+ };
+
+ std::string cleaned;
+ cleaned.reserve(line.size());
+ for (size_t i = 0; i < line.size(); ++i) {
+ if (try_skip_escape(&i)) {
+ continue;
+ }
+ cleaned.push_back(line[i]);
+ }
+ return cleaned;
+}
+
+static bool LineHasSuppressedGroup(
+ const std::string &line,
+ const std::vector &suppressed_tokens) {
+ if (suppressed_tokens.empty()) {
+ return false;
+ }
+ std::string cleaned = StripAnsiCodes(line);
+ for (const auto &token : suppressed_tokens) {
+ if (cleaned.find(token) != std::string::npos) {
+ return true;
+ }
+ }
+ return false;
+}
+
+static bool IsGroupDocLine(
+ const std::string &line,
+ std::string *group) {
+ std::string cleaned = StripAnsiCodes(line);
+ size_t nonspace = cleaned.find_first_not_of(" \t");
+ if (nonspace == std::string::npos) {
+ return false;
+ }
+ if (cleaned.compare(nonspace, 2, "[#") != 0) {
+ return false;
+ }
+ size_t closing = cleaned.find(']', nonspace + 2);
+ if (closing == std::string::npos) {
+ return false;
+ }
+ if (closing + 1 >= cleaned.size() || cleaned[closing + 1] != ':') {
+ return false;
+ }
+ if (group) {
+ *group = cleaned.substr(nonspace + 2, closing - (nonspace + 2));
+ }
+ return true;
+}
+
+enum class DiagnosticSeverity {
+ kUnknown,
+ kWarning,
+ kError,
+};
+
+static bool ConsumeColonNumber(const std::string &line, size_t *pos) {
+ if (*pos >= line.size() || line[*pos] != ':') {
+ return false;
+ }
+ size_t number_start = *pos + 1;
+ size_t number_end = number_start;
+ while (number_end < line.size() &&
+ std::isdigit(static_cast(line[number_end]))) {
+ ++number_end;
+ }
+ if (number_end == number_start || number_end >= line.size() ||
+ line[number_end] != ':') {
+ return false;
+ }
+ *pos = number_end;
+ return true;
+}
+
+static DiagnosticSeverity IsDiagnosticHeader(const std::string &line) {
+ std::string cleaned = StripAnsiCodes(line);
+ size_t nonspace = cleaned.find_first_not_of(" \t");
+ if (nonspace != std::string::npos) {
+ if (cleaned.compare(nonspace, 6, "error:") == 0) {
+ return DiagnosticSeverity::kError;
+ }
+ if (cleaned.compare(nonspace, 8, "warning:") == 0) {
+ return DiagnosticSeverity::kWarning;
+ }
+ }
+ for (size_t i = 0; i < cleaned.size(); ++i) {
+ if (cleaned[i] != ':') {
+ continue;
+ }
+ size_t pos = i;
+ if (!ConsumeColonNumber(cleaned, &pos)) {
+ continue;
+ }
+ if (!ConsumeColonNumber(cleaned, &pos)) {
+ continue;
+ }
+ size_t severity_start = pos + 1;
+ if (severity_start < cleaned.size() && cleaned[severity_start] == ' ') {
+ ++severity_start;
+ }
+ if (cleaned.compare(severity_start, 8, "warning:") == 0) {
+ return DiagnosticSeverity::kWarning;
+ }
+ if (cleaned.compare(severity_start, 6, "error:") == 0) {
+ return DiagnosticSeverity::kError;
+ }
+ }
+ return DiagnosticSeverity::kUnknown;
+}
+
+static bool HasLocatedDiagnosticKind(
+ const std::string &line,
+ const std::string &kind) {
+ for (size_t i = 0; i < line.size(); ++i) {
+ if (line[i] != ':') {
+ continue;
+ }
+ size_t pos = i;
+ if (!ConsumeColonNumber(line, &pos) || !ConsumeColonNumber(line, &pos)) {
+ continue;
+ }
+ size_t kind_start = pos + 1;
+ if (kind_start < line.size() && line[kind_start] == ' ') {
+ ++kind_start;
+ }
+ if (line.compare(kind_start, kind.size(), kind) == 0) {
+ return true;
+ }
+ }
+ return false;
+}
+
+static bool IsDiagnosticContinuationLine(
+ const std::string &line,
+ bool is_group_doc_line) {
+ if (is_group_doc_line) {
+ return true;
+ }
+
+ std::string cleaned = StripAnsiCodes(line);
+ if (cleaned.empty()) {
+ return true;
+ }
+
+ size_t nonspace = cleaned.find_first_not_of(" \t");
+ if (nonspace == std::string::npos) {
+ return true;
+ }
+
+ // Most source snippets, caret/fix-it lines, and wrapped diagnostic text are
+ // indented under a header line.
+ if (nonspace != 0) {
+ return true;
+ }
+
+ if (cleaned.compare(0, 5, "note:") == 0 ||
+ cleaned.compare(0, 7, "remark:") == 0) {
+ return true;
+ }
+ if (HasLocatedDiagnosticKind(cleaned, "note:") ||
+ HasLocatedDiagnosticKind(cleaned, "remark:")) {
+ return true;
+ }
+
+ // Match source-context line-number formatting such as "12 | foo()".
+ if (std::isdigit(static_cast(cleaned[0]))) {
+ size_t pos = 1;
+ while (pos < cleaned.size() &&
+ std::isdigit(static_cast(cleaned[pos]))) {
+ ++pos;
+ }
+ while (pos < cleaned.size() && cleaned[pos] == ' ') {
+ ++pos;
+ }
+ if (pos < cleaned.size() && cleaned[pos] == '|') {
+ return true;
+ }
+ }
+
+ if (cleaned[0] == '|' || cleaned[0] == '^' || cleaned[0] == '~') {
+ return true;
+ }
+
+ return false;
+}
+
+class DiagnosticFilteringStreambuf : public std::streambuf {
+ public:
+ DiagnosticFilteringStreambuf(
+ std::ostream *dest,
+ const std::vector &groups)
+ : dest_(dest),
+ groups_(groups),
+ in_diagnostic_block_(false),
+ suppress_current_block_(false),
+ current_severity_(DiagnosticSeverity::kUnknown) {
+ suppressed_tokens_.reserve(groups_.size());
+ for (const auto &group : groups_) {
+ suppressed_tokens_.push_back("[#" + group + "]");
+ }
+ }
+
+ void FlushRemainder() {
+ if (!buffer_.empty()) {
+ ProcessLine(buffer_, /*has_newline=*/false);
+ buffer_.clear();
+ }
+ FlushDiagnosticBlock();
+ dest_->flush();
+ }
+
+ protected:
+ int overflow(int ch) override {
+ if (ch == traits_type::eof()) {
+ return traits_type::not_eof(ch);
+ }
+ char c = static_cast(ch);
+ return xsputn(&c, 1) == 1 ? ch : traits_type::eof();
+ }
+
+ std::streamsize xsputn(const char *s, std::streamsize n) override {
+ buffer_.append(s, static_cast(n));
+ size_t pos = 0;
+ while ((pos = buffer_.find('\n')) != std::string::npos) {
+ std::string line = buffer_.substr(0, pos);
+ ProcessLine(line, /*has_newline=*/true);
+ buffer_.erase(0, pos + 1);
+ }
+ return n;
+ }
+
+ private:
+ struct Line {
+ std::string text;
+ bool has_newline;
+ };
+
+ void WriteLine(const Line &line) {
+ dest_->write(line.text.data(), line.text.size());
+ if (line.has_newline) {
+ dest_->put('\n');
+ }
+ }
+
+ bool ShouldSuppressCurrentBlock() const {
+ return in_diagnostic_block_ && suppress_current_block_ &&
+ current_severity_ == DiagnosticSeverity::kWarning;
+ }
+
+ void FlushDiagnosticBlock() {
+ if (!in_diagnostic_block_ || diagnostic_block_.empty()) {
+ return;
+ }
+ if (!ShouldSuppressCurrentBlock()) {
+ for (const auto &line : diagnostic_block_) {
+ WriteLine(line);
+ }
+ }
+ diagnostic_block_.clear();
+ suppress_current_block_ = false;
+ in_diagnostic_block_ = false;
+ current_severity_ = DiagnosticSeverity::kUnknown;
+ }
+
+ void ProcessLine(const std::string &line, bool has_newline) {
+ DiagnosticSeverity severity = IsDiagnosticHeader(line);
+ std::string doc_group;
+ bool is_group_doc_line = IsGroupDocLine(line, &doc_group);
+ bool is_doc_for_suppressed_group = false;
+ if (is_group_doc_line) {
+ is_doc_for_suppressed_group =
+ std::find(groups_.begin(), groups_.end(), doc_group) != groups_.end();
+ if (is_doc_for_suppressed_group) {
+ return;
+ }
+ }
+
+ if (severity != DiagnosticSeverity::kUnknown) {
+ FlushDiagnosticBlock();
+ in_diagnostic_block_ = true;
+ current_severity_ = severity;
+ }
+
+ Line current{line, has_newline};
+ if (in_diagnostic_block_) {
+ if (severity == DiagnosticSeverity::kUnknown &&
+ !IsDiagnosticContinuationLine(line, is_group_doc_line)) {
+ FlushDiagnosticBlock();
+ WriteLine(current);
+ return;
+ }
+
+ if (!is_group_doc_line &&
+ LineHasSuppressedGroup(line, suppressed_tokens_)) {
+ suppress_current_block_ = true;
+ }
+
+ diagnostic_block_.push_back(current);
+ } else {
+ WriteLine(current);
+ }
+ }
+
+ std::ostream *dest_;
+ std::string buffer_;
+ const std::vector &groups_;
+ std::vector suppressed_tokens_;
+ std::vector diagnostic_block_;
+ bool in_diagnostic_block_;
+ bool suppress_current_block_;
+ DiagnosticSeverity current_severity_;
+};
+
+class DiagnosticFilteringStream : public std::ostream {
+ public:
+ DiagnosticFilteringStream(
+ std::ostream *dest,
+ const std::vector &groups)
+ : std::ostream(nullptr),
+ buffer_(dest, groups) {
+ rdbuf(&buffer_);
+ }
+
+ void FlushRemainder() { buffer_.FlushRemainder(); }
+
+ private:
+ DiagnosticFilteringStreambuf buffer_;
+};
+
} // namespace
SwiftRunner::SwiftRunner(const std::vector &args,
@@ -125,11 +513,22 @@ int SwiftRunner::Run(std::ostream *stderr_stream, bool stdout_to_stderr) {
std::filesystem::remove(swift_source_info_path_);
}
- int exit_code = RunSubProcess(
- args_, &job_env_, stderr_stream, stdout_to_stderr);
-
- if (exit_code != 0) {
- return exit_code;
+ int exit_code = 0;
+ bool should_filter_output = !suppress_warning_groups_.empty();
+ if (should_filter_output) {
+ DiagnosticFilteringStream filtered(stderr_stream, suppress_warning_groups_);
+ // Preserve stdout unless the caller explicitly requested redirecting it.
+ exit_code = RunSubProcess(
+ args_, &job_env_, &filtered, /*stdout_to_stderr=*/stdout_to_stderr);
+ filtered.FlushRemainder();
+ if (exit_code != 0) {
+ return exit_code;
+ }
+ } else {
+ exit_code = RunSubProcess(args_, &job_env_, stderr_stream, stdout_to_stderr);
+ if (exit_code != 0) {
+ return exit_code;
+ }
}
if (!generated_header_rewriter_path_.empty()) {
@@ -149,6 +548,9 @@ int SwiftRunner::Run(std::ostream *stderr_stream, bool stdout_to_stderr) {
exit_code = RunSubProcess(
rewriter_args, /*env=*/nullptr, stderr_stream, stdout_to_stderr);
+ if (exit_code != 0) {
+ return exit_code;
+ }
}
auto enable_global_index_store = global_index_store_import_path_ != "";
@@ -365,6 +767,16 @@ bool SwiftRunner::ProcessArgument(
changed = true;
} else if (StripPrefix("-global-index-store-import-path=", new_arg)) {
changed = true;
+ } else if (StripPrefix("-suppress-warning-group=", new_arg)) {
+ std::string group = NormalizeDiagnosticGroup(new_arg);
+ if (!group.empty() &&
+ std::find(
+ suppress_warning_groups_.begin(),
+ suppress_warning_groups_.end(),
+ group) == suppress_warning_groups_.end()) {
+ suppress_warning_groups_.push_back(group);
+ }
+ changed = true;
} else {
// TODO(allevato): Report that an unknown wrapper arg was found and give
// the caller a way to exit gracefully.
diff --git a/tools/worker/swift_runner.h b/tools/worker/swift_runner.h
index 74b30b98d..61b294657 100644
--- a/tools/worker/swift_runner.h
+++ b/tools/worker/swift_runner.h
@@ -59,6 +59,10 @@ extern bool ArgumentEnablesWMO(const std::string &arg);
// the directory afterwards. This should resolve issues where the module
// cache state is not refreshed correctly in all situations, which
// sometimes results in hard-to-diagnose crashes in `swiftc`.
+//
+// -Xwrapped-swift=-suppress-warning-group=
+// When specified, diagnostics that include the given group (for example,
+// `DeprecatedDeclaration`) will be filtered from the compiler output.
class SwiftRunner {
public:
// Create a new spawner that launches a Swift tool with the given arguments.
@@ -182,6 +186,9 @@ class SwiftRunner {
// Whether `.swiftsourceinfo` files are being generated.
bool emit_swift_source_info_;
+
+ // Diagnostic groups to suppress from compiler output.
+ std::vector suppress_warning_groups_;
};
#endif // BUILD_BAZEL_RULES_SWIFT_TOOLS_WORKER_SWIFT_RUNNER_H_