From 5a1a5239453edd15f2e4bf9a975ca9f1f1e60d7c Mon Sep 17 00:00:00 2001 From: David Beck <340006+davbeck@users.noreply.github.com> Date: Thu, 11 Dec 2025 09:09:43 -0800 Subject: [PATCH 1/5] Add swift.emit_diagnostics feature --- swift/internal/compiling.bzl | 39 ++++++++++++++++++++-- swift/internal/feature_names.bzl | 5 +++ swift/toolchains/config/compile_config.bzl | 8 +++++ 3 files changed, 50 insertions(+), 2 deletions(-) diff --git a/swift/internal/compiling.bzl b/swift/internal/compiling.bzl index dfbd725cf..085e4cf7b 100644 --- a/swift/internal/compiling.bzl +++ b/swift/internal/compiling.bzl @@ -41,6 +41,7 @@ load( "SWIFT_FEATURE_DECLARE_SWIFTSOURCEINFO", "SWIFT_FEATURE_EMIT_BC", "SWIFT_FEATURE_EMIT_C_MODULE", + "SWIFT_FEATURE_EMIT_DIAGNOSTICS", "SWIFT_FEATURE_EMIT_PRIVATE_SWIFTINTERFACE", "SWIFT_FEATURE_EMIT_SWIFTDOC", "SWIFT_FEATURE_EMIT_SWIFTINTERFACE", @@ -522,7 +523,7 @@ def compile( if split_derived_file_generation: all_compile_outputs = compact([ compile_outputs.indexstore_directory, - ]) + compile_outputs.object_files + compile_outputs.const_values_files + ]) + compile_outputs.object_files + compile_outputs.const_values_files + compile_outputs.diagnostics_files all_derived_outputs = compact([ # The `.swiftmodule` file is explicitly listed as the first output # because it will always exist and because Bazel uses it as a key for @@ -550,7 +551,7 @@ def compile( compile_outputs.generated_header_file, compile_outputs.indexstore_directory, compile_outputs.macro_expansion_directory, - ]) + compile_outputs.object_files + compile_outputs.const_values_files + ]) + compile_outputs.object_files + compile_outputs.const_values_files + compile_outputs.diagnostics_files all_derived_outputs = [] # In `upstream` they call `merge_compilation_contexts` on passed in @@ -1340,6 +1341,11 @@ def _declare_compile_outputs( indexstore_directory = None include_index_unit_paths = False + emit_diagnostics = is_feature_enabled( + feature_configuration = feature_configuration, + feature_name = SWIFT_FEATURE_EMIT_DIAGNOSTICS, + ) + if not output_nature.emits_multiple_objects: # If we're emitting a single object, we don't use an object map; we just # declare the output file that the compiler will generate and there are @@ -1356,6 +1362,17 @@ def _declare_compile_outputs( const_values_files = [ actions.declare_file("{}.swiftconstvalues".format(target_name)), ] + if emit_diagnostics: + diagnostics_files = [ + _declare_per_source_output_file( + actions = actions, + extension = "dia", + target_name = target_name, + src = srcs[0], + ), + ] + else: + diagnostics_files = [] output_file_map = None derived_files_output_file_map = None # TODO(b/147451378): Support indexing even with a single object file. @@ -1389,6 +1406,7 @@ def _declare_compile_outputs( # object files so that we can pass them all to the archive action. output_info = _declare_multiple_outputs_and_write_output_file_map( actions = actions, + emit_diagnostics = emit_diagnostics, extract_const_values = extract_const_values, is_wmo = output_nature.is_wmo, emits_bc = emits_bc, @@ -1400,6 +1418,7 @@ def _declare_compile_outputs( object_files = output_info.object_files ast_files = output_info.ast_files const_values_files = output_info.const_values_files + diagnostics_files = output_info.diagnostics_files output_file_map = output_info.output_file_map derived_files_output_file_map = output_info.derived_files_output_file_map @@ -1416,6 +1435,7 @@ def _declare_compile_outputs( compile_outputs = struct( ast_files = ast_files, const_values_files = const_values_files, + diagnostics_files = diagnostics_files, generated_header_file = generated_header, generated_module_map_file = generated_module_map, indexstore_directory = indexstore_directory, @@ -1459,6 +1479,7 @@ def _declare_per_source_output_file(actions, extension, target_name, src): def _declare_multiple_outputs_and_write_output_file_map( actions, + emit_diagnostics, extract_const_values, is_wmo, emits_bc, @@ -1470,6 +1491,8 @@ def _declare_multiple_outputs_and_write_output_file_map( Args: actions: The object used to register actions. + emit_diagnostics: A Boolean value indicating whether serialized + diagnostics files (.dia) should be emitted during this compilation. extract_const_values: A Boolean value indicating whether constant values should be extracted during this compilation. is_wmo: A Boolean value indicating whether whole-module-optimization was @@ -1516,6 +1539,7 @@ def _declare_multiple_outputs_and_write_output_file_map( # Output files that will be emitted by the compiler. ast_files = [] + diagnostics_files = [] output_objs = [] const_values_files = [] @@ -1572,6 +1596,16 @@ def _declare_multiple_outputs_and_write_output_file_map( const_values_files.append(const_values_file) file_outputs["const-values"] = const_values_file.path + if emit_diagnostics: + diagnostics_file = _declare_per_source_output_file( + actions = actions, + extension = "dia", + target_name = target_name, + src = src, + ) + diagnostics_files.append(diagnostics_file) + file_outputs["diagnostics"] = diagnostics_file.path + output_map[src.path] = file_outputs if whole_module_map: @@ -1592,6 +1626,7 @@ def _declare_multiple_outputs_and_write_output_file_map( ast_files = ast_files, const_values_files = const_values_files, derived_files_output_file_map = derived_files_output_map_file, + diagnostics_files = diagnostics_files, object_files = output_objs, output_file_map = output_map_file, ) diff --git a/swift/internal/feature_names.bzl b/swift/internal/feature_names.bzl index adc818fbd..70a8f189d 100644 --- a/swift/internal/feature_names.bzl +++ b/swift/internal/feature_names.bzl @@ -432,3 +432,8 @@ SWIFT_FEATURE_ENABLE_CPP23_INTEROP = "swift.enable_cpp23_interop" # automatically include it at compile time, and provide it to the linker at link time # * alwayslink on swift_library() target will now default to False, as it is not required in embedded mode SWIFT_FEATURE_ENABLE_EMBEDDED = "swift.enable_embedded" + +# If enabled, requests the compiler to emit serialized diagnostics files (.dia) +# for each source file. These files contain machine-readable compiler +# diagnostics that can be consumed by IDEs and other tools. +SWIFT_FEATURE_EMIT_DIAGNOSTICS = "swift.emit_diagnostics" diff --git a/swift/toolchains/config/compile_config.bzl b/swift/toolchains/config/compile_config.bzl index f91271c74..055cde656 100644 --- a/swift/toolchains/config/compile_config.bzl +++ b/swift/toolchains/config/compile_config.bzl @@ -49,6 +49,7 @@ load( "SWIFT_FEATURE_DISABLE_SYSTEM_INDEX", "SWIFT_FEATURE_EMIT_BC", "SWIFT_FEATURE_EMIT_C_MODULE", + "SWIFT_FEATURE_EMIT_DIAGNOSTICS", "SWIFT_FEATURE_EMIT_PRIVATE_SWIFTINTERFACE", "SWIFT_FEATURE_EMIT_SWIFTDOC", "SWIFT_FEATURE_EMIT_SWIFTINTERFACE", @@ -159,6 +160,13 @@ def compile_action_configs( features = [SWIFT_FEATURE_EMIT_BC], ), + # Emit serialized diagnostics file(s). + ActionConfigInfo( + actions = [SWIFT_ACTION_COMPILE], + configurators = [add_arg("-serialize-diagnostics")], + features = [SWIFT_FEATURE_EMIT_DIAGNOSTICS], + ), + # Add the single object file or object file map, whichever is needed. ActionConfigInfo( actions = [SWIFT_ACTION_COMPILE], From 647e215ff105d08bbb4738ac68b8eca20bf9def6 Mon Sep 17 00:00:00 2001 From: David Beck <340006+davbeck@users.noreply.github.com> Date: Thu, 11 Dec 2025 09:10:16 -0800 Subject: [PATCH 2/5] Add diagnostics_files to supplemental_outputs --- swift/internal/compiling.bzl | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/swift/internal/compiling.bzl b/swift/internal/compiling.bzl index 085e4cf7b..96f7766db 100644 --- a/swift/internal/compiling.bzl +++ b/swift/internal/compiling.bzl @@ -439,6 +439,10 @@ def compile( representations of constant values extracted from the source files, if requested via a direct dependency. + * `diagnostics_files`: A list of `File`s that contains serialized + diagnostics (.dia) emitted by the compiler when the feature + `swift.emit_diagnostics` is enabled. + * `indexstore_directory`: A directory-type `File` that represents the indexstore output files created when the feature `swift.index_while_building` is enabled. @@ -831,6 +835,7 @@ to use swift_common.compile(include_dev_srch_paths = ...) instead.\ supplemental_outputs = struct( ast_files = compile_outputs.ast_files, const_values_files = compile_outputs.const_values_files, + diagnostics_files = compile_outputs.diagnostics_files, indexstore_directory = compile_outputs.indexstore_directory, macro_expansion_directory = compile_outputs.macro_expansion_directory, ), From 99f2d0af606caccacbebef37fbf251fe93e195bb Mon Sep 17 00:00:00 2001 From: David Beck <340006+davbeck@users.noreply.github.com> Date: Thu, 11 Dec 2025 09:11:01 -0800 Subject: [PATCH 3/5] Add diagnostic to output_groups --- swift/internal/output_groups.bzl | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/swift/internal/output_groups.bzl b/swift/internal/output_groups.bzl index 6f23f17e8..a46238687 100644 --- a/swift/internal/output_groups.bzl +++ b/swift/internal/output_groups.bzl @@ -37,6 +37,7 @@ def supplemental_compilation_output_groups( """ ast_files = [] const_values_files = [] + diagnostics_files = [] indexstore_files = list(additional_indexstore_files) macro_expansions_files = [] @@ -45,6 +46,8 @@ def supplemental_compilation_output_groups( ast_files.extend(outputs.ast_files) if outputs.const_values_files: const_values_files.extend(outputs.const_values_files) + if hasattr(outputs, "diagnostics_files") and outputs.diagnostics_files: + diagnostics_files.extend(outputs.diagnostics_files) if outputs.indexstore_directory: indexstore_files.append(outputs.indexstore_directory) if outputs.macro_expansion_directory: @@ -55,6 +58,8 @@ def supplemental_compilation_output_groups( output_groups["swift_ast_file"] = depset(ast_files) if const_values_files: output_groups["const_values"] = depset(const_values_files) + if diagnostics_files: + output_groups["swift_diagnostics"] = depset(diagnostics_files) if indexstore_files: output_groups["swift_index_store"] = depset(indexstore_files) if macro_expansions_files: From cabe73000567245265a9a9610d1e280563ba7091 Mon Sep 17 00:00:00 2001 From: David Beck <340006+davbeck@users.noreply.github.com> Date: Thu, 11 Dec 2025 09:11:25 -0800 Subject: [PATCH 4/5] Fix diagnostics output for whole module optimization --- swift/internal/compiling.bzl | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/swift/internal/compiling.bzl b/swift/internal/compiling.bzl index 96f7766db..e83ffbcd6 100644 --- a/swift/internal/compiling.bzl +++ b/swift/internal/compiling.bzl @@ -1375,6 +1375,7 @@ def _declare_compile_outputs( target_name = target_name, src = srcs[0], ), + actions.declare_file("{}.dia".format(target_name)), ] else: diagnostics_files = [] @@ -1555,6 +1556,13 @@ def _declare_multiple_outputs_and_write_output_file_map( const_values_files.append(const_values_file) whole_module_map["const-values"] = const_values_file.path + if emit_diagnostics and is_wmo: + diagnostics_file = actions.declare_file( + "{}.dia".format(target_name), + ) + diagnostics_files.append(diagnostics_file) + whole_module_map["diagnostics"] = diagnostics_file.path + for src in srcs: file_outputs = {} @@ -1601,7 +1609,7 @@ def _declare_multiple_outputs_and_write_output_file_map( const_values_files.append(const_values_file) file_outputs["const-values"] = const_values_file.path - if emit_diagnostics: + if emit_diagnostics and not is_wmo: diagnostics_file = _declare_per_source_output_file( actions = actions, extension = "dia", From 9d03eecbf1f29f957e5c96b22a7d02b6bf6bda8b Mon Sep 17 00:00:00 2001 From: David Beck <340006+davbeck@users.noreply.github.com> Date: Thu, 11 Dec 2025 09:12:25 -0800 Subject: [PATCH 5/5] Add tests for diagnostics --- test/BUILD | 3 ++ test/diagnostics_tests.bzl | 57 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 60 insertions(+) create mode 100644 test/diagnostics_tests.bzl diff --git a/test/BUILD b/test/BUILD index 5929d0850..7f3d81548 100644 --- a/test/BUILD +++ b/test/BUILD @@ -7,6 +7,7 @@ load(":compiler_arguments_tests.bzl", "compiler_arguments_test_suite") load(":const_values_tests.bzl", "const_values_test_suite") load(":coverage_settings_tests.bzl", "coverage_settings_test_suite") load(":debug_settings_tests.bzl", "debug_settings_test_suite") +load(":diagnostics_tests.bzl", "diagnostics_test_suite") load(":environment_tests.bzl", "environment_test_suite") load(":features_tests.bzl", "features_test_suite") load(":generated_header_tests.bzl", "generated_header_test_suite") @@ -45,6 +46,8 @@ coverage_settings_test_suite(name = "coverage_settings") debug_settings_test_suite(name = "debug_settings") +diagnostics_test_suite(name = "diagnostics") + environment_test_suite(name = "environment") features_test_suite(name = "features") diff --git a/test/diagnostics_tests.bzl b/test/diagnostics_tests.bzl new file mode 100644 index 000000000..2e861e6a3 --- /dev/null +++ b/test/diagnostics_tests.bzl @@ -0,0 +1,57 @@ +# Copyright 2024 The Bazel Authors. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Tests for `swift.emit_diagnostics` feature.""" + +load( + "//test/rules:action_command_line_test.bzl", + "make_action_command_line_test_rule", +) +load( + "//test/rules:provider_test.bzl", + "provider_test", +) + +diagnostics_command_line_test = make_action_command_line_test_rule( + config_settings = { + "//command_line_option:features": [ + "swift.emit_diagnostics", + ], + }, +) + +def diagnostics_test_suite(name, tags = []): + """Test suite for `swift_library` producing .dia files. + + Args: + name: The base name to be used in things created by this macro. + tags: Additional tags to apply to each test. + """ + all_tags = [name] + tags + + diagnostics_command_line_test( + name = "{}_serialize_diagnostics_flag".format(name), + expected_argv = [ + "-serialize-diagnostics", + ], + mnemonic = "SwiftCompile", + tags = all_tags, + target_under_test = "//test/fixtures/basic:first", + ) + + native.test_suite( + name = name, + tags = all_tags, + ) +