mirror of
https://github.com/apple/swift.git
synced 2025-12-21 12:14:44 +01:00
[Macros] Expand extension macros at the top-level
Ensure we always expand extension macros after the top-level decl for the given attached decl. This ensures correct unqualified lookup behavior, and bans macro implementations from extending the unqualified name (they're expected to use `providingExtensionsOf` instead, which uses the qualified name). rdar://148119538
This commit is contained in:
@@ -957,8 +957,12 @@ static CharSourceRange getExpansionInsertionRange(MacroRole role,
|
|||||||
}
|
}
|
||||||
|
|
||||||
case MacroRole::Extension: {
|
case MacroRole::Extension: {
|
||||||
|
// Extensions are expanded at the top-level.
|
||||||
|
auto *NTD = cast<NominalTypeDecl>(target.get<Decl *>());
|
||||||
|
auto *topLevelDecl = NTD->getTopmostDeclarationDeclContext();
|
||||||
|
|
||||||
SourceLoc afterDeclLoc =
|
SourceLoc afterDeclLoc =
|
||||||
Lexer::getLocForEndOfToken(sourceMgr, target.getEndLoc());
|
Lexer::getLocForEndOfToken(sourceMgr, topLevelDecl->getEndLoc());
|
||||||
return CharSourceRange(afterDeclLoc, 0);
|
return CharSourceRange(afterDeclLoc, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1628,6 +1628,20 @@ public struct FooExtensionMacro: ExtensionMacro {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public struct BadExtensionMacro: ExtensionMacro {
|
||||||
|
public static func expansion(
|
||||||
|
of node: AttributeSyntax,
|
||||||
|
attachedTo declaration: some DeclGroupSyntax,
|
||||||
|
providingExtensionsOf type: some TypeSyntaxProtocol,
|
||||||
|
conformingTo protocols: [TypeSyntax],
|
||||||
|
in context: some MacroExpansionContext
|
||||||
|
) throws -> [ExtensionDeclSyntax] {
|
||||||
|
// Note this is purposefully not using `providingExtensionsOf`.
|
||||||
|
let unqualifiedName = declaration.as(StructDeclSyntax.self)!.name.trimmed
|
||||||
|
return [try ExtensionDeclSyntax("extension \(unqualifiedName) {}")]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public struct ConformanceViaExtensionMacro: ExtensionMacro {
|
public struct ConformanceViaExtensionMacro: ExtensionMacro {
|
||||||
public static func expansion(
|
public static func expansion(
|
||||||
of node: AttributeSyntax,
|
of node: AttributeSyntax,
|
||||||
|
|||||||
@@ -161,6 +161,17 @@ struct TestUndocumentedEncodable {}
|
|||||||
|
|
||||||
// CHECK-DIAGS: error: conformance to 'Codable' (aka 'Decodable & Encodable') is not covered by macro 'UndocumentedEncodable'
|
// CHECK-DIAGS: error: conformance to 'Codable' (aka 'Decodable & Encodable') is not covered by macro 'UndocumentedEncodable'
|
||||||
|
|
||||||
|
@attached(extension)
|
||||||
|
macro BadExtension() = #externalMacro(module: "MacroDefinition", type: "BadExtensionMacro")
|
||||||
|
|
||||||
|
// Make sure 'extension Foo' is rejected here as it needs to
|
||||||
|
// be a qualified reference.
|
||||||
|
struct HasSomeNestedType {
|
||||||
|
@BadExtension // expected-note {{in expansion of macro 'BadExtension' on struct 'SomeNestedType' here}}
|
||||||
|
struct SomeNestedType {}
|
||||||
|
}
|
||||||
|
// CHECK-DIAGS: error: cannot find type 'SomeNestedType' in scope
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@attached(extension, conformances: Equatable)
|
@attached(extension, conformances: Equatable)
|
||||||
|
|||||||
@@ -121,6 +121,14 @@ func remoteCall<Result: ConjureRemoteValue>(function: String, arguments: [String
|
|||||||
return Result.conjureValue()
|
return Result.conjureValue()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@attached(extension, conformances: Equatable)
|
||||||
|
macro AddEquatable() = #externalMacro(module: "MacroDefinition", type: "EquatableMacro")
|
||||||
|
|
||||||
|
struct HasNestedType {
|
||||||
|
@AddEquatable
|
||||||
|
struct Inner {}
|
||||||
|
}
|
||||||
|
|
||||||
// REQUIRES: swift_swift_parser, executable_test, shell, asserts
|
// REQUIRES: swift_swift_parser, executable_test, shell, asserts
|
||||||
// REQUIRES: swift_feature_PreambleMacros
|
// REQUIRES: swift_feature_PreambleMacros
|
||||||
|
|
||||||
@@ -379,3 +387,10 @@ func remoteCall<Result: ConjureRemoteValue>(function: String, arguments: [String
|
|||||||
// BODY_EXPAND-NEXT: return try await remoteCall(function: "f", arguments: ["a": a, "b": b])
|
// BODY_EXPAND-NEXT: return try await remoteCall(function: "f", arguments: ["a": a, "b": b])
|
||||||
// BODY_EXPAND-NEXT: }"
|
// BODY_EXPAND-NEXT: }"
|
||||||
// BODY_EXPAND-NEXT: source.edit.kind.active:
|
// BODY_EXPAND-NEXT: source.edit.kind.active:
|
||||||
|
|
||||||
|
// Make sure the extension is added at the top level.
|
||||||
|
// RUN: %sourcekitd-test -req=refactoring.expand.macro -pos=128:4 %s -- ${COMPILER_ARGS[@]} | %FileCheck -check-prefix=ADD_EQUATABLE_EXPAND %s
|
||||||
|
// ADD_EQUATABLE_EXPAND: source.edit.kind.active:
|
||||||
|
// ADD_EQUATABLE_EXPAND-NEXT: 130:2-130:2 (@__swiftmacro_9MacroUser13HasNestedTypeV5Inner12AddEquatablefMe_.swift) "extension HasNestedType.Inner: Equatable {
|
||||||
|
// ADD_EQUATABLE_EXPAND-NEXT: }"
|
||||||
|
// ADD_EQUATABLE_EXPAND-NEXT: source.edit.kind.active:
|
||||||
|
|||||||
Reference in New Issue
Block a user