Skip to content
Merged
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
16 changes: 14 additions & 2 deletions lib/Sema/TypeCheckMacros.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1788,8 +1788,20 @@ static TinyPtrVector<ProtocolDecl *> getIntroducedConformances(
bool hasExistingConformance = llvm::any_of(
existingConformances,
[&](ProtocolConformance *conformance) {
return conformance->getSourceKind() !=
ConformanceEntryKind::PreMacroExpansion;
// The conformance is coming from a macro expansion, so ignore it.
if (conformance->getSourceKind() ==
ConformanceEntryKind::PreMacroExpansion)
return false;

// Check whether the conformance comes from an extension defined by
// a macro.
if (auto conformingExt =
dyn_cast<ExtensionDecl>(conformance->getDeclContext())) {
if (conformingExt->isInMacroExpansionInContext())
return false;
}

return true;
});

if (!hasExistingConformance) {
Expand Down
40 changes: 40 additions & 0 deletions test/Macros/Inputs/syntax_macro_definitions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -1808,6 +1808,46 @@ public struct AddAllConformancesMacro: ExtensionMacro {
}
}

public struct ListConformancesMacro { }

extension ListConformancesMacro: ExtensionMacro {
public static func expansion(
of node: AttributeSyntax,
attachedTo decl: some DeclGroupSyntax,
providingExtensionsOf type: some TypeSyntaxProtocol,
conformingTo protocols: [TypeSyntax],
in context: some MacroExpansionContext
) throws -> [ExtensionDeclSyntax] {
protocols.map { proto in
let decl: DeclSyntax =
"""
extension \(type): \(proto) {}
"""
return decl.cast(ExtensionDeclSyntax.self)
}
}
}

extension ListConformancesMacro: MemberMacro {
public static func expansion(
of node: AttributeSyntax,
providingMembersOf declaration: some DeclGroupSyntax,
conformingTo protocols: [TypeSyntax],
in context: some MacroExpansionContext
) throws -> [DeclSyntax] {
let typeName = declaration.asProtocol(NamedDeclSyntax.self)!.name.text

let protocolNames: [ExprSyntax] = protocols.map { "\(literal: $0.trimmedDescription)" }
let protocolsArray: ExprSyntax =
"[ \(raw: protocolNames.map { $0.description }.joined(separator: ", ")) ]"
let unknownDecl: DeclSyntax =
"""
@_nonoverride static func conformances() -> [String: [String]] { [ \(literal: typeName): \(protocolsArray) ] }
"""
return [unknownDecl]
}
}

public struct AlwaysAddCodable: ExtensionMacro {
public static func expansion(
of node: AttributeSyntax,
Expand Down
48 changes: 48 additions & 0 deletions test/Macros/macro_expand_conformances_xref.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
// REQUIRES: swift_swift_parser, executable_test

// RUN: %empty-directory(%t)
// RUN: %host-build-swift -swift-version 5 -emit-library -o %t/%target-library-name(MacroDefinition) -module-name=MacroDefinition %S/Inputs/syntax_macro_definitions.swift -g -no-toolchain-stdlib-rpath

// Check for errors first
// RUN: %target-swift-frontend -swift-version 5 -typecheck -load-plugin-library %t/%target-library-name(MacroDefinition) %s -I %t -disable-availability-checking

// RUN: %target-swift-frontend -swift-version 5 -typecheck -load-plugin-library %t/%target-library-name(MacroDefinition) %s -I %t -disable-availability-checking -dump-macro-expansions > %t/expansions-dump.txt 2>&1
// RUN: %FileCheck -check-prefix=CHECK-DUMP %s < %t/expansions-dump.txt


protocol P1 {}
protocol P2 {}

@attached(extension, conformances: P1, P2)
@attached(member, conformances: P1, P2, names: named(conformances))
macro ListConformances() = #externalMacro(module: "MacroDefinition", type: "ListConformancesMacro")


// CHECK-DUMP: [ "Root": [ "P1", "P2" ] ]
// CHECK-DUMP: extension Root: P1
// CHECK-DUMP: extension Root: P2
@ListConformances
class Root {
// CHECK-DUMP: extension OtherRoot: P1
// CHECK-DUMP: extension OtherRoot: P2
var other: OtherRoot?
}

// CHECK-DUMP: [ "P1Root": [ "P2" ] ]
// CHECK-DUMP-NOT: extension P1Root: P1
// CHECK-DUMP: extension P1Root: P2
@ListConformances
class P1Root: P1 { }

// CHECK-DUMP: [ "OtherRoot": [ "P1", "P2" ] ]
@ListConformances
class OtherRoot {
// CHECK-DUMP-NOT: extension OtherP1Root: P1
// CHECK-DUMP: extension OtherP1Root: P2
var other: OtherP1Root?
}

// CHECK-DUMP: [ "OtherP1Root": [ "P2" ] ]
@ListConformances
class OtherP1Root: P1 { }