Files
swift-mirror/test/Macros/macro_import_access_tracking.swift
Gage 13f4b68380 [Sema] Base import access-level tracking on macro expansions, not macro usage
Fixes a spurious "package import of 'X' was not used in package
declarations" warning when declarations introduced by a macro expansion
are the only package-level use of module 'X'.

Two changes to recordRequiredImportAccessLevelForDecl():

- Skip references to MacroDecls. Macros are consumed at compile time and
  are not part of the importing module's API surface, so using a macro
  from module 'X' should not, by itself, require any access level on
  'import X'. What matters is whether the expansion references
  declarations from 'X'.

- When the referencing decl context is inside a macro expansion buffer,
  walk up to the enclosing user-written source file before registering
  the required access level. The import lives in the user's file, so
  usage inside expansions must be attributed there; otherwise references
  in macro-generated package declarations are recorded against the
  synthesized buffer and the user's import is reported as unused.

Together these make the diagnostic depend solely on what the expansion
produces: a macro whose expansion references types from its own module
keeps the import "used"; a macro whose expansion references nothing from
its module correctly leaves the import flagged; and types from a third
module referenced only in expansions now count toward that module's
import as well.
2026-06-30 00:16:38 -06:00

254 lines
11 KiB
Swift

// Import-access tracking for macro-expanded code: references made by an
// expansion are attributed to the user file holding the import (PR #86376).
// REQUIRES: swift_swift_parser
// REQUIRES: OS=macosx
// REQUIRES: swift_feature_InternalImportsByDefault
// RUN: %empty-directory(%t)
// RUN: split-file --leading-lines %s %t
// Build the shared macro plugin.
// RUN: %host-build-swift -swift-version 5 -emit-library \
// RUN: -o %t/%target-library-name(MacroDefinition) \
// RUN: -module-name=MacroDefinition \
// RUN: %S/Inputs/syntax_macro_definitions.swift \
// RUN: -g -no-toolchain-stdlib-rpath
// Build the two test libraries.
// RUN: %target-swift-frontend -emit-module -module-name MacroLib \
// RUN: -o %t/MacroLib.swiftmodule %t/MacroLib.swift \
// RUN: -enable-library-evolution
// RUN: %target-swift-frontend -emit-module -module-name OtherLib \
// RUN: -o %t/OtherLib.swiftmodule %t/OtherLib.swift \
// RUN: -enable-library-evolution
// RUN: %target-swift-frontend -typecheck -verify -package-name pkg \
// RUN: -enable-upcoming-feature InternalImportsByDefault \
// RUN: -load-plugin-library %t/%target-library-name(MacroDefinition) \
// RUN: -I %t %t/Client_PeerWithHelper.swift
// RUN: %target-swift-frontend -typecheck -verify -package-name pkg \
// RUN: -enable-upcoming-feature InternalImportsByDefault \
// RUN: -load-plugin-library %t/%target-library-name(MacroDefinition) \
// RUN: -I %t %t/Client_PeerNoTypeRef.swift
// RUN: %target-swift-frontend -typecheck -verify -package-name pkg \
// RUN: -enable-upcoming-feature InternalImportsByDefault \
// RUN: -load-plugin-library %t/%target-library-name(MacroDefinition) \
// RUN: -I %t %t/Client_PeerMultiModule.swift
// RUN: %target-swift-frontend -typecheck -verify -package-name pkg \
// RUN: -enable-upcoming-feature InternalImportsByDefault \
// RUN: -load-plugin-library %t/%target-library-name(MacroDefinition) \
// RUN: -I %t %t/Client_Member.swift
// RUN: %target-swift-frontend -typecheck -verify -package-name pkg \
// RUN: -enable-upcoming-feature InternalImportsByDefault \
// RUN: -load-plugin-library %t/%target-library-name(MacroDefinition) \
// RUN: -I %t %t/Client_Extension.swift
// RUN: %target-swift-frontend -typecheck -verify -package-name pkg \
// RUN: -enable-upcoming-feature InternalImportsByDefault \
// RUN: -load-plugin-library %t/%target-library-name(MacroDefinition) \
// RUN: -I %t %t/Client_Declaration.swift
// RUN: %target-swift-frontend -typecheck -verify -package-name pkg \
// RUN: -enable-upcoming-feature InternalImportsByDefault \
// RUN: -load-plugin-library %t/%target-library-name(MacroDefinition) \
// RUN: -I %t %t/Client_PublicSymmetry.swift
// RUN: %target-swift-frontend -typecheck -verify -package-name pkg \
// RUN: -enable-upcoming-feature InternalImportsByDefault \
// RUN: -load-plugin-library %t/%target-library-name(MacroDefinition) \
// RUN: -I %t %t/Client_PublicNoTypeRef.swift
// The access-level error originates inside the expansion buffer, matched by
// an expansion block; -verify-ignore-unrelated drops MacroLib interface noise.
// RUN: %target-swift-frontend -typecheck -verify -verify-ignore-unrelated -package-name pkg \
// RUN: -enable-upcoming-feature InternalImportsByDefault \
// RUN: -load-plugin-library %t/%target-library-name(MacroDefinition) \
// RUN: -I %t %t/Client_InternalIsTooLow.swift
// RUN: %target-swift-frontend -typecheck -verify -package-name pkg \
// RUN: -enable-upcoming-feature InternalImportsByDefault \
// RUN: -load-plugin-library %t/%target-library-name(MacroDefinition) \
// RUN: -I %t %t/Client_PublicImport_PackageOnlyExpansion.swift
// RUN: %target-swift-frontend -typecheck -verify -package-name pkg \
// RUN: -enable-upcoming-feature InternalImportsByDefault \
// RUN: -load-plugin-library %t/%target-library-name(MacroDefinition) \
// RUN: -I %t %t/Client_Accessor.swift
// RUN: %target-swift-frontend -typecheck -verify -package-name pkg \
// RUN: -enable-upcoming-feature InternalImportsByDefault \
// RUN: -load-plugin-library %t/%target-library-name(MacroDefinition) \
// RUN: -I %t %t/Client_Expression.swift
// RUN: %target-swift-frontend -typecheck -verify -package-name pkg \
// RUN: -enable-upcoming-feature InternalImportsByDefault \
// RUN: -load-plugin-library %t/%target-library-name(MacroDefinition) \
// RUN: -I %t %t/Client_MemberAttribute.swift
//--- MacroLib.swift
public struct Helper {
public init() {}
}
// Generic emitters: each re-emits its string-literal argument verbatim as code.
@attached(peer, names: suffixed(Mock))
public macro PeerThatEmitsCode(_ codeString: String) =
#externalMacro(module: "MacroDefinition", type: "PeerThatEmitsCodeMacro")
@attached(member, names: arbitrary)
public macro MemberThatEmitsCode(_ codeString: String) =
#externalMacro(module: "MacroDefinition", type: "MemberThatEmitsCodeMacro")
@attached(extension, names: arbitrary)
public macro ExtensionThatEmitsCode(_ codeString: String) =
#externalMacro(module: "MacroDefinition", type: "ExtensionThatEmitsCodeMacro")
@freestanding(declaration, names: arbitrary)
public macro declarationThatEmitsCode(_ codeString: String) =
#externalMacro(module: "MacroDefinition", type: "DeclarationThatEmitsCodeMacro")
@attached(accessor)
public macro AccessorThatEmitsCode(_ codeString: String) =
#externalMacro(module: "MacroDefinition", type: "AccessorThatEmitsCodeMacro")
@freestanding(expression)
public macro expressionThatEmitsCode(_ codeString: String) -> Helper =
#externalMacro(module: "MacroDefinition", type: "ExpressionThatEmitsCodeMacro")
@attached(memberAttribute)
public macro MemberAttributeThatAddsPeer(_ codeString: String) =
#externalMacro(module: "MacroDefinition", type: "MemberAttributeThatAddsPeerMacro")
//--- OtherLib.swift
public struct OtherHelper {
public init() {}
}
//--- Client_PeerWithHelper.swift
// Peer expansion references Helper at package level, so the import is used.
package import MacroLib // expected-no-diagnostics
@PeerThatEmitsCode("package final class PeerWithHelperServiceMock { package var helper: Helper = Helper() }")
package protocol PeerWithHelperService {
func doThing()
}
//--- Client_PeerNoTypeRef.swift
// Expansion references only Int, so package import is genuinely unused: warn.
package import MacroLib // expected-warning {{package import of 'MacroLib' was not used in package declarations}}
@PeerThatEmitsCode("package final class PeerNoTypeRefServiceMock { package var counter: Int = 0 }")
package protocol PeerNoTypeRefService {
func doThing()
}
//--- Client_PeerMultiModule.swift
// Expansion references OtherHelper (OtherLib) at package level; MacroLib itself is unused.
package import MacroLib // expected-warning {{package import of 'MacroLib' was not used in package declarations}}
package import OtherLib // expected-no-diagnostics
@PeerThatEmitsCode("package final class PeerMultiModuleServiceMock { package var helper: OtherHelper = OtherHelper() }")
package protocol PeerMultiModuleService {
func doThing()
}
//--- Client_Member.swift
// Member-macro expansion adds a package member typed from MacroLib.
package import MacroLib // expected-no-diagnostics
@MemberThatEmitsCode("package var helper: Helper = Helper()")
package class MemberService {
package init() {}
}
//--- Client_Extension.swift
// Extension-macro expansion adds a package method returning a MacroLib type.
package import MacroLib // expected-no-diagnostics
@ExtensionThatEmitsCode("package func makeHelper() -> Helper { Helper() }")
package class ExtensionService {}
//--- Client_Declaration.swift
// Freestanding declaration macro emits a package class with a MacroLib-typed property.
package import MacroLib // expected-no-diagnostics
#declarationThatEmitsCode("package final class FreestandingMock { package var helper: Helper = Helper() }")
//--- Client_PublicSymmetry.swift
// Public analogue of Client_PeerWithHelper; must not regress.
public import MacroLib // expected-no-diagnostics
@PeerThatEmitsCode("public final class PublicSymmetryServiceMock { public var helper: Helper = Helper() }")
public protocol PublicSymmetryService {
func doThing()
}
//--- Client_PublicNoTypeRef.swift
// Public analogue of Client_PeerNoTypeRef; only this case proves the MacroDecl skip on the public path.
public import MacroLib // expected-warning {{public import of 'MacroLib' was not used in public declarations}}
@PeerThatEmitsCode("package final class PublicNoTypeRefServiceMock { package var counter: Int = 0 }")
public protocol PublicNoTypeRefService {
func doThing()
}
//--- Client_InternalIsTooLow.swift
// Negative guard: package expansion needs Helper but import is internal, so the access-level error must still fire.
internal import MacroLib // expected-note {{struct 'Helper' imported as 'internal' from 'MacroLib' here}}
// expected-note@+1 2 {{in expansion of macro 'PeerThatEmitsCode' on protocol 'InternalIsTooLowService' here}}
@PeerThatEmitsCode("""
package final class InternalIsTooLowServiceMock {
package var helper: Helper = Helper()
}
""")
package protocol InternalIsTooLowService {
func doThing()
}
/*
expected-expansion@-2:2 {{
expected-error@2:15{{property cannot be declared package because its type uses an internal type}}
expected-note@2:15{{struct 'Helper' is imported by this file as 'internal' from 'MacroLib'}}
}}
*/
//--- Client_PublicImport_PackageOnlyExpansion.swift
// Expansion references Helper only at package level, so the public import is overkill: warn.
public import MacroLib // expected-warning {{public import of 'MacroLib' was not used in public declarations}}
@PeerThatEmitsCode("package final class PublicImportPackageOnlyServiceMock { package var helper: Helper = Helper() }")
package protocol PublicImportPackageOnlyService {
func doThing()
}
//--- Client_Accessor.swift
// Accessor body references Helper (not API surface), so the package import is oversized: warn.
package import MacroLib // expected-warning {{package import of 'MacroLib' was not used in package declarations}}
package class AccessorService {
@AccessorThatEmitsCode("get { _ = Helper(); return 42 }")
package var value: Int
}
//--- Client_Expression.swift
// Expression macro in a function body; same body-context reasoning as Client_Accessor.
package import MacroLib // expected-warning {{package import of 'MacroLib' was not used in package declarations}}
package func consumeExpression() {
_ = #expressionThatEmitsCode("Helper()")
}
//--- Client_MemberAttribute.swift
// Member-attribute macro adds a peer macro, so the walk-up must reach this file through both buffers.
package import MacroLib // expected-no-diagnostics
@MemberAttributeThatAddsPeer("package var doThingMock: Helper = Helper()")
package class MemberAttributeService {
package func doThing() {}
}