Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions swift/internal/compiling.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -761,9 +761,10 @@ def compile(
# passed; the compiler does not attempt to load them when deserializing
# modules.
used_plugins = list(plugins)
for module_context in transitive_modules:
if module_context.swift and module_context.swift.plugins:
used_plugins.extend(module_context.swift.plugins)
for swift_info in swift_infos + private_swift_infos:
for module_context in swift_info.direct_modules:
if module_context.swift and module_context.swift.plugins:
used_plugins.extend(module_context.swift.plugins)

if include_dev_srch_paths != None and is_test != None:
fail("""\
Expand Down
3 changes: 3 additions & 0 deletions test/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ load(":imported_framework_tests.bzl", "imported_framework_test_suite")
load(":interop_hints_tests.bzl", "interop_hints_test_suite")
load(":layering_check_tests.bzl", "layering_check_test_suite")
load(":localized_strings_tests.bzl", "localized_strings_test_suite")
load(":macro_plugin_tests.bzl", "macro_plugin_test_suite")
load(":mainattr_tests.bzl", "mainattr_test_suite")
load(":mixed_language_tests.bzl", "mixed_language_test_suite")
load(":module_cache_settings_tests.bzl", "module_cache_settings_test_suite")
Expand Down Expand Up @@ -70,6 +71,8 @@ layering_check_test_suite(name = "layering_check")

localized_strings_test_suite(name = "localized_strings")

macro_plugin_test_suite(name = "macro_plugin")

mainattr_test_suite(name = "mainattr")

mixed_language_test_suite(name = "mixed_language")
Expand Down
53 changes: 53 additions & 0 deletions test/fixtures/macros/BUILD
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
load("//swift:swift_compiler_plugin.bzl", "swift_compiler_plugin")
load("//swift:swift_library.bzl", "swift_library")
load("//test/fixtures:common.bzl", "FIXTURE_TAGS")

package(
default_visibility = ["//test:__subpackages__"],
)

licenses(["notice"])

###############################################################################
# Fixtures for testing compiler plugin (macro) propagation

swift_compiler_plugin(
name = "stringify_macro",
srcs = [
"StringifyMacro.swift",
"StringifyMacroPlugin.swift",
],
module_name = "StringifyMacroPlugin",
tags = FIXTURE_TAGS,
deps = [
"@swift-syntax//:SwiftCompilerPlugin",
"@swift-syntax//:SwiftSyntax",
"@swift-syntax//:SwiftSyntaxBuilder",
"@swift-syntax//:SwiftSyntaxMacroExpansion",
"@swift-syntax//:SwiftSyntaxMacros",
],
)

swift_library(
name = "stringify",
srcs = ["Stringify.swift"],
module_name = "Stringify",
plugins = [":stringify_macro"],
tags = FIXTURE_TAGS,
)

swift_library(
name = "stringify_user",
srcs = ["StringifyUser.swift"],
module_name = "StringifyUser",
tags = FIXTURE_TAGS,
deps = [":stringify"],
)

swift_library(
name = "transitive_user",
srcs = ["TransitiveUser.swift"],
module_name = "TransitiveUser",
tags = FIXTURE_TAGS,
deps = [":stringify_user"],
)
17 changes: 17 additions & 0 deletions test/fixtures/macros/Stringify.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// Copyright 2023 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.

@freestanding(expression)
public macro stringify<T>(_ value: T) -> (T, String) =
#externalMacro(module: "StringifyMacroPlugin", type: "StringifyMacro")
29 changes: 29 additions & 0 deletions test/fixtures/macros/StringifyMacro.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// Copyright 2023 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.

import SwiftSyntax
import SwiftSyntaxBuilder
import SwiftSyntaxMacros

public struct StringifyMacro: ExpressionMacro {
public static func expansion(
of node: some FreestandingMacroExpansionSyntax,
in context: some MacroExpansionContext
) -> ExprSyntax {
guard let argument = node.argumentList.first?.expression else {
fatalError("compiler bug: the macro does not have any arguments")
}
return "(\(argument), \(literal: argument.description))"
}
}
25 changes: 25 additions & 0 deletions test/fixtures/macros/StringifyMacroPlugin.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// Copyright 2023 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.

#if canImport(SwiftCompilerPlugin)
import SwiftCompilerPlugin
import SwiftSyntaxMacros

@main
struct StringifyMacroPlugin: CompilerPlugin {
let providingMacros: [Macro.Type] = [
StringifyMacro.self
]
}
#endif
21 changes: 21 additions & 0 deletions test/fixtures/macros/StringifyUser.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// Copyright 2026 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.

import Stringify

// Directly depends on the macro-declaring module and contains an expansion
// site, so its compilation must load the plugin.
public func stringifyUser() -> (Int, String) {
#stringify(1 + 1)
}
22 changes: 22 additions & 0 deletions test/fixtures/macros/TransitiveUser.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// Copyright 2026 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.

import StringifyUser

// Only sees the macro-declaring module transitively (through StringifyUser)
// and contains no expansion sites, so its compilation must not load the
// plugin.
public func transitiveUser() -> String {
String(describing: stringifyUser())
}
66 changes: 66 additions & 0 deletions test/macro_plugin_tests.bzl
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
# Copyright 2026 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 compiler plugin (macro) propagation."""

load(
"//test/rules:action_command_line_test.bzl",
"action_command_line_test",
)

def macro_plugin_test_suite(name, tags = []):
"""Test suite for compiler plugin propagation to dependents.

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

# The module that declares the macro loads its own plugin.
action_command_line_test(
name = "{}_declaring_module_loads_plugin".format(name),
expected_argv = ["-load-plugin-executable"],
mnemonic = "SwiftCompile",
tags = all_tags,
target_under_test = "//test/fixtures/macros:stringify",
)

# A module that directly depends on the library declaring the macro must
# load the plugin, since its sources may contain expansion sites.
action_command_line_test(
name = "{}_direct_dependent_loads_plugin".format(name),
expected_argv = ["-load-plugin-executable"],
mnemonic = "SwiftCompile",
tags = all_tags,
target_under_test = "//test/fixtures/macros:stringify_user",
)

# A module that only sees the macro-declaring module transitively must
# not: macros are source-level transformations, so the compiler never
# loads plugins when deserializing dependency modules. Passing the plugin
# anyway makes its executable an input to every transitive consumer's
# compilation, so any change to the plugin recompiles all of them.
action_command_line_test(
name = "{}_transitive_dependent_does_not_load_plugin".format(name),
mnemonic = "SwiftCompile",
not_expected_argv = ["-load-plugin-executable"],
tags = all_tags,
target_under_test = "//test/fixtures/macros:transitive_user",
)

native.test_suite(
name = name,
tags = all_tags,
)
Loading