diff --git a/include/swift/AST/DiagnosticsSema.def b/include/swift/AST/DiagnosticsSema.def index f6d6e26c7ba03..7bf952557fbcb 100644 --- a/include/swift/AST/DiagnosticsSema.def +++ b/include/swift/AST/DiagnosticsSema.def @@ -7101,6 +7101,16 @@ ERROR(macro_in_nested,none, ERROR(macro_without_role,none, "macro %0 must declare its applicable roles via '@freestanding' or @attached'", (DeclName)) +ERROR(macro_result_type_cannot_be_used,none, + "only a freestanding expression macro can produce a result of type %0", + (Type)) +NOTE(macro_remove_result_type,none, + "remove the result type if the macro does not produce a value", + ()) +NOTE(macro_make_freestanding_expression,none, + "make this macro a freestanding expression macro", ()) +ERROR(macro_multiple_freestanding_roles,none, + "macro can only have a single freestanding role", ()) ERROR(macro_expansion_missing_pound,none, "expansion of macro %0 requires leading '#'", (DeclName)) ERROR(macro_expansion_missing_arguments,none, diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp index f13fd1591b0c3..c1954fd25efc1 100644 --- a/lib/AST/Decl.cpp +++ b/lib/AST/Decl.cpp @@ -3357,12 +3357,18 @@ TypeRepr *ValueDecl::getResultTypeRepr() const { returnRepr = FD->getResultTypeRepr(); } else if (auto *SD = dyn_cast(this)) { returnRepr = SD->getElementTypeRepr(); + } else if (auto *MD = dyn_cast(this)) { + returnRepr = MD->resultType.getTypeRepr(); } return returnRepr; } TypeRepr *ValueDecl::getOpaqueResultTypeRepr() const { + // FIXME: Macros don't allow opaque result types yet. + if (isa(this)) + return nullptr; + auto *returnRepr = this->getResultTypeRepr(); auto *dc = getDeclContext(); diff --git a/lib/ASTGen/Sources/ASTGen/Macros.swift b/lib/ASTGen/Sources/ASTGen/Macros.swift index f4eeb3bb30d11..9c8d5bbfe0a97 100644 --- a/lib/ASTGen/Sources/ASTGen/Macros.swift +++ b/lib/ASTGen/Sources/ASTGen/Macros.swift @@ -67,6 +67,8 @@ extension MacroRole { case 0x10: self = .member case 0x20: self = .peer case 0x40: self = .conformance + case 0x80: self = .codeItem + default: fatalError("unknown macro role") } } @@ -414,6 +416,7 @@ func expandFreestandingMacro( macroKind: UInt8, discriminatorText: UnsafePointer, discriminatorTextLength: Int, + rawMacroRole: UInt8, sourceFilePtr: UnsafeRawPointer, sourceLocationPtr: UnsafePointer?, expandedSourcePointer: UnsafeMutablePointer?>, @@ -446,11 +449,13 @@ func expandFreestandingMacro( ) let discriminator = String(decoding: discriminatorBuffer, as: UTF8.self) + let macroRole = MacroRole(rawMacroRole: rawMacroRole) let expandedSource: String? switch MacroPluginKind(rawValue: macroKind)! { case .InProcess: expandedSource = expandFreestandingMacroInProcess( macroPtr: macroPtr, + macroRole: macroRole, diagEnginePtr: diagEnginePtr, expansionSyntax: expansion, sourceFilePtr: sourceFilePtr, @@ -458,6 +463,7 @@ func expandFreestandingMacro( case .Executable: expandedSource = expandFreestandingMacroIPC( macroPtr: macroPtr, + macroRole: macroRole, diagEnginePtr: diagEnginePtr, expansionSyntax: expansion, sourceFilePtr: sourceFilePtr, @@ -485,6 +491,7 @@ func expandFreestandingMacro( func expandFreestandingMacroIPC( macroPtr: UnsafeRawPointer, + macroRole: MacroRole, diagEnginePtr: UnsafeMutablePointer, expansionSyntax: FreestandingMacroExpansionSyntax, sourceFilePtr: UnsafePointer, @@ -502,9 +509,21 @@ func expandFreestandingMacroIPC( let macro = macroPtr.assumingMemoryBound(to: ExportedExecutableMacro.self).pointee + // Map the macro role. + let pluginMacroRole: PluginMessage.MacroRole + switch macroRole { + case .accessor, .member, .memberAttribute, .peer, .conformance: + preconditionFailure("unhandled macro role for freestanding macro") + + case .expression: pluginMacroRole = .expression + case .declaration: pluginMacroRole = .freeStandingDeclaration + case .codeItem: pluginMacroRole = .codeItem + } + // Send the message. let message = HostToPluginMessage.expandFreestandingMacro( macro: .init(moduleName: macro.moduleName, typeName: macro.typeName, name: macroName), + macroRole: pluginMacroRole, discriminator: discriminator, syntax: PluginMessage.Syntax(syntax: Syntax(expansionSyntax), in: sourceFilePtr)!) do { @@ -541,6 +560,7 @@ func expandFreestandingMacroIPC( func expandFreestandingMacroInProcess( macroPtr: UnsafeRawPointer, + macroRole: MacroRole, diagEnginePtr: UnsafeMutablePointer, expansionSyntax: FreestandingMacroExpansionSyntax, sourceFilePtr: UnsafePointer, @@ -580,6 +600,7 @@ func expandFreestandingMacroInProcess( return SwiftSyntaxMacroExpansion.expandFreestandingMacro( definition: macro, + macroRole: macroRole, node: node, in: context ) diff --git a/lib/ASTGen/Sources/ASTGen/PluginMessages.swift b/lib/ASTGen/Sources/ASTGen/PluginMessages.swift index df67a178f43c3..cd058797f90f0 100644 --- a/lib/ASTGen/Sources/ASTGen/PluginMessages.swift +++ b/lib/ASTGen/Sources/ASTGen/PluginMessages.swift @@ -20,6 +20,7 @@ internal enum HostToPluginMessage: Codable { /// Expand a '@freestanding' macro. case expandFreestandingMacro( macro: PluginMessage.MacroReference, + macroRole: PluginMessage.MacroRole? = nil, discriminator: String, syntax: PluginMessage.Syntax ) @@ -91,6 +92,7 @@ internal enum PluginToHostMessage: Codable { case member case peer case conformance + case codeItem } struct SourceLocation: Codable { diff --git a/lib/IDETool/SyntacticMacroExpansion.cpp b/lib/IDETool/SyntacticMacroExpansion.cpp index afcffa672721b..e7a16e7bb62db 100644 --- a/lib/IDETool/SyntacticMacroExpansion.cpp +++ b/lib/IDETool/SyntacticMacroExpansion.cpp @@ -412,7 +412,7 @@ void SyntacticMacroExpansionInstance::expand( SourceFile *SF, const MacroExpansionSpecifier &expansion, SourceEditConsumer &consumer) { - // Find the expansion at 'expantion.offset'. + // Find the expansion at 'expansion.offset'. MacroExpansionFinder expansionFinder( SourceMgr, SourceMgr.getLocForOffset(*SF->getBufferID(), expansion.offset)); diff --git a/lib/Sema/TypeCheckDeclPrimary.cpp b/lib/Sema/TypeCheckDeclPrimary.cpp index 905f77175f42a..91ed49d6f040f 100644 --- a/lib/Sema/TypeCheckDeclPrimary.cpp +++ b/lib/Sema/TypeCheckDeclPrimary.cpp @@ -2017,6 +2017,17 @@ class DeclChecker : public DeclVisitor { llvm_unreachable("should always be type-checked already"); } + /// Determine the number of bits set. + static unsigned numBitsSet(uint64_t value) { + unsigned count = 0; + for (uint64_t i : range(0, 63)) { + if (value & (uint64_t(1) << i)) + ++count; + } + + return count; + } + void visitMacroDecl(MacroDecl *MD) { TypeChecker::checkDeclAttributes(MD); checkAccessControl(MD); @@ -2060,6 +2071,30 @@ class DeclChecker : public DeclVisitor { break; } } + + // If the macro has a (non-Void) result type, it must have the freestanding + // expression role. Other roles cannot have result types. + if (auto resultTypeRepr = MD->getResultTypeRepr()) { + if (!MD->getMacroRoles().contains(MacroRole::Expression) && + !MD->getResultInterfaceType()->isEqual(Ctx.getVoidType())) { + auto resultType = MD->getResultInterfaceType(); + Ctx.Diags.diagnose( + MD->arrowLoc, diag::macro_result_type_cannot_be_used, resultType) + .highlight(resultTypeRepr->getSourceRange()); + Ctx.Diags.diagnose(MD->arrowLoc, diag::macro_make_freestanding_expression) + .fixItInsert(MD->getAttributeInsertionLoc(false), + "@freestanding(expression)\n"); + Ctx.Diags.diagnose(MD->arrowLoc, diag::macro_remove_result_type) + .fixItRemove(SourceRange(MD->arrowLoc, resultTypeRepr->getEndLoc())); + } + } + + // A macro can only have a single freestanding macro role. + MacroRoles freestandingRolesInhabited = + MD->getMacroRoles() & getFreestandingMacroRoles(); + if (numBitsSet(freestandingRolesInhabited.toRaw()) > 1) { + MD->diagnose(diag::macro_multiple_freestanding_roles); + } } void visitMacroExpansionDecl(MacroExpansionDecl *MED) { diff --git a/lib/Sema/TypeCheckMacros.cpp b/lib/Sema/TypeCheckMacros.cpp index 6fb677cbefcc7..b6a6dacaa5d97 100644 --- a/lib/Sema/TypeCheckMacros.cpp +++ b/lib/Sema/TypeCheckMacros.cpp @@ -66,7 +66,8 @@ extern "C" ptrdiff_t swift_ASTGen_checkMacroDefinition( extern "C" ptrdiff_t swift_ASTGen_expandFreestandingMacro( void *diagEngine, void *macro, uint8_t externalKind, - const char *discriminator, ptrdiff_t discriminatorLength, void *sourceFile, + const char *discriminator, ptrdiff_t discriminatorLength, + uint8_t rawMacroRole, void *sourceFile, const void *sourceLocation, const char **evaluatedSource, ptrdiff_t *evaluatedSourceLength); @@ -901,6 +902,13 @@ evaluateFreestandingMacro(FreestandingMacroExpansion *expansion, #endif }); + // Only one freestanding macro role is permitted, so look at the roles to + // figure out which one to use. + MacroRole macroRole = + macroRoles.contains(MacroRole::Expression) ? MacroRole::Expression + : macroRoles.contains(MacroRole::Declaration) ? MacroRole::Declaration + : MacroRole::CodeItem; + auto macroDef = macro->getDefinition(); switch (macroDef.kind) { case MacroDefinition::Kind::Undefined: @@ -961,7 +969,8 @@ evaluateFreestandingMacro(FreestandingMacroExpansion *expansion, swift_ASTGen_expandFreestandingMacro( &ctx.Diags, externalDef->opaqueHandle, static_cast(externalDef->kind), discriminator->data(), - discriminator->size(), astGenSourceFile, + discriminator->size(), + static_cast(macroRole), astGenSourceFile, expansion->getSourceRange().Start.getOpaquePointerValue(), &evaluatedSourceAddress, &evaluatedSourceLength); if (!evaluatedSourceAddress) diff --git a/test/Macros/Inputs/syntax_macro_definitions.swift b/test/Macros/Inputs/syntax_macro_definitions.swift index 7eaa3505105c4..b01cb48da66dc 100644 --- a/test/Macros/Inputs/syntax_macro_definitions.swift +++ b/test/Macros/Inputs/syntax_macro_definitions.swift @@ -73,6 +73,26 @@ public struct StringifyMacro: ExpressionMacro { } } +public struct ExprAndDeclMacro: ExpressionMacro, DeclarationMacro { + public static func expansion( + of macro: some FreestandingMacroExpansionSyntax, + in context: some MacroExpansionContext + ) -> ExprSyntax { + guard let argument = macro.argumentList.first?.expression else { + fatalError("boom") + } + + return "(\(argument), \(StringLiteralExprSyntax(content: argument.description)))" + } + + public static func expansion( + of macro: some FreestandingMacroExpansionSyntax, + in context: some MacroExpansionContext + ) -> [DeclSyntax] { + return [] + } +} + public struct StringifyAndTryMacro: ExpressionMacro { public static func expansion( of macro: some FreestandingMacroExpansionSyntax, diff --git a/test/Macros/macro_expand.swift b/test/Macros/macro_expand.swift index 9067be4b3f1dd..9aca53fce374d 100644 --- a/test/Macros/macro_expand.swift +++ b/test/Macros/macro_expand.swift @@ -510,3 +510,27 @@ func testHasEqualsSelf( _ = (zP == true) _ = (wP == true) } + +// Macro whose implementation is both an expression and declaration macro. +@freestanding(declaration) +macro AsDeclMacro(_ value: T) = #externalMacro(module: "MacroDefinition", type: "ExprAndDeclMacro") + +@freestanding(expression) +macro AsExprMacro(_ value: T) -> (T, String) = #externalMacro(module: "MacroDefinition", type: "ExprAndDeclMacro") + +func testExpressionAndDeclarationMacro() { + #AsExprMacro(1 + 1) // expected-warning{{expression of type '(Int, String)' is unused}} + struct Inner { + #AsDeclMacro(1 + 1) + } + #AsDeclMacro(1 + 1) +} + +// Expression macro implementation with declaration macro role +@freestanding(declaration) macro stringifyAsDeclMacro(_ value: T) = #externalMacro(module: "MacroDefinition", type: "StringifyMacro") +func testExpressionAsDeclarationMacro() { +#if TEST_DIAGNOSTICS + #stringifyAsDeclMacro(1+1) + // expected-error@-1{{macro implementation type 'StringifyMacro' doesn't conform to required protocol 'DeclarationMacro' (from macro 'stringifyAsDeclMacro')}} +#endif +} diff --git a/test/Macros/macro_plugin_basic.swift b/test/Macros/macro_plugin_basic.swift index fbd9633f4095a..d62321f20dfab 100644 --- a/test/Macros/macro_plugin_basic.swift +++ b/test/Macros/macro_plugin_basic.swift @@ -23,9 +23,9 @@ // CHECK: ->(plugin:[[#PID:]]) {"getCapability":{}} // CHECK: <-(plugin:[[#PID]]) {"getCapabilityResult":{"capability":{"protocolVersion":1}}} -// CHECK: ->(plugin:[[#PID]]) {"expandFreestandingMacro":{"discriminator":"$s{{.+}}","macro":{"moduleName":"TestPlugin","name":"testString","typeName":"TestStringMacro"},"syntax":{"kind":"expression","location":{"column":19,"fileID":"MyApp/test.swift","fileName":"BUILD_DIR{{.+}}test.swift","line":5,"offset":301},"source":"#testString(123)"}}} +// CHECK: ->(plugin:[[#PID]]) {"expandFreestandingMacro":{"discriminator":"$s{{.+}}","macro":{"moduleName":"TestPlugin","name":"testString","typeName":"TestStringMacro"},"macroRole":"expression","syntax":{"kind":"expression","location":{"column":19,"fileID":"MyApp/test.swift","fileName":"BUILD_DIR{{.+}}test.swift","line":5,"offset":301},"source":"#testString(123)"}}} // CHECK: <-(plugin:[[#PID]]) {"expandFreestandingMacroResult":{"diagnostics":[],"expandedSource":"\"123\"\n + \"foo \""}} -// CHECK: ->(plugin:[[#PID]]) {"expandFreestandingMacro":{"discriminator":"$s{{.+}}","macro":{"moduleName":"TestPlugin","name":"testStringWithError","typeName":"TestStringWithErrorMacro"},"syntax":{"kind":"expression","location":{"column":19,"fileID":"MyApp/test.swift","fileName":"BUILD_DIR{{.+}}test.swift","line":6,"offset":336},"source":"#testStringWithError(321)"}}} +// CHECK: ->(plugin:[[#PID]]) {"expandFreestandingMacro":{"discriminator":"$s{{.+}}","macro":{"moduleName":"TestPlugin","name":"testStringWithError","typeName":"TestStringWithErrorMacro"},"macroRole":"expression","syntax":{"kind":"expression","location":{"column":19,"fileID":"MyApp/test.swift","fileName":"BUILD_DIR{{.+}}test.swift","line":6,"offset":336},"source":"#testStringWithError(321)"}}} // CHECK: <-(plugin:[[#PID]]) {"expandFreestandingMacroResult":{"diagnostics":[{"fixIts":[],"highlights":[],"message":"message from plugin","notes":[],"position":{"fileName":"BUILD_DIR{{.*}}test.swift","offset":336},"severity":"error"}],"expandedSource":"\"bar\""}} //--- test.swift diff --git a/test/Macros/macro_plugin_error.swift b/test/Macros/macro_plugin_error.swift index 458ec20891e0f..3f599142f7397 100644 --- a/test/Macros/macro_plugin_error.swift +++ b/test/Macros/macro_plugin_error.swift @@ -23,12 +23,12 @@ // CHECK: ->(plugin:[[#PID1:]]) {"getCapability":{}} // CHECK-NEXT: <-(plugin:[[#PID1]]) {"getCapabilityResult":{"capability":{"protocolVersion":1}}} -// CHECK-NEXT: ->(plugin:[[#PID1]]) {"expandFreestandingMacro":{"discriminator":"$s{{.+}}","macro":{"moduleName":"TestPlugin","name":"fooMacro","typeName":"FooMacro"},"syntax":{"kind":"expression","location":{"column":19,"fileID":"MyApp/test.swift","fileName":"BUILD_DIR{{.+}}test.swift","line":6,"offset":[[#]]},"source":"#fooMacro(1)"}}} +// CHECK-NEXT: ->(plugin:[[#PID1]]) {"expandFreestandingMacro":{"discriminator":"$s{{.+}}","macro":{"moduleName":"TestPlugin","name":"fooMacro","typeName":"FooMacro"},"macroRole":"expression","syntax":{"kind":"expression","location":{"column":19,"fileID":"MyApp/test.swift","fileName":"BUILD_DIR{{.+}}test.swift","line":6,"offset":[[#]]},"source":"#fooMacro(1)"}}} // CHECK-NEXT: <-(plugin:[[#PID1]]) {"invalidResponse":{}} -// CHECK-NEXT: ->(plugin:[[#PID1]]) {"expandFreestandingMacro":{"discriminator":"$s{{.+}}","macro":{"moduleName":"TestPlugin","name":"fooMacro","typeName":"FooMacro"},"syntax":{"kind":"expression","location":{"column":19,"fileID":"MyApp/test.swift","fileName":"BUILD_DIR{{.+}}test.swift","line":8,"offset":[[#]]},"source":"#fooMacro(2)"}}} +// CHECK-NEXT: ->(plugin:[[#PID1]]) {"expandFreestandingMacro":{"discriminator":"$s{{.+}}","macro":{"moduleName":"TestPlugin","name":"fooMacro","typeName":"FooMacro"},"macroRole":"expression","syntax":{"kind":"expression","location":{"column":19,"fileID":"MyApp/test.swift","fileName":"BUILD_DIR{{.+}}test.swift","line":8,"offset":[[#]]},"source":"#fooMacro(2)"}}} // ^ This messages causes the mock plugin exit because there's no matching expected message. -// CHECK: ->(plugin:[[#PID2:]]) {"expandFreestandingMacro":{"discriminator":"$s{{.+}}","macro":{"moduleName":"TestPlugin","name":"fooMacro","typeName":"FooMacro"},"syntax":{"kind":"expression","location":{"column":19,"fileID":"MyApp/test.swift","fileName":"BUILD_DIR{{.+}}test.swift","line":10,"offset":[[#]]},"source":"#fooMacro(3)"}}} +// CHECK: ->(plugin:[[#PID2:]]) {"expandFreestandingMacro":{"discriminator":"$s{{.+}}","macro":{"moduleName":"TestPlugin","name":"fooMacro","typeName":"FooMacro"},"macroRole":"expression","syntax":{"kind":"expression","location":{"column":19,"fileID":"MyApp/test.swift","fileName":"BUILD_DIR{{.+}}test.swift","line":10,"offset":[[#]]},"source":"#fooMacro(3)"}}} // CHECK-NEXT: <-(plugin:[[#PID2:]]) {"expandFreestandingMacroResult":{"diagnostics":[],"expandedSource":"3.description"}} //--- test.swift diff --git a/test/Macros/macro_plugin_server.swift b/test/Macros/macro_plugin_server.swift index b4bc369b6b39d..66116d2dfdecc 100644 --- a/test/Macros/macro_plugin_server.swift +++ b/test/Macros/macro_plugin_server.swift @@ -37,16 +37,16 @@ // CHECK-NEXT: <-(plugin:[[#PID1]]) {"loadPluginLibraryResult":{"diagnostics":[],"loaded":true}} // CHECK-NEXT: ->(plugin:[[#PID1]]) {"loadPluginLibrary":{"libraryPath":"BUILD_DIR{{.*}}plugins/libEvilMacros.dylib","moduleName":"EvilMacros"}} // CHECK-NEXT: <-(plugin:[[#PID1]]) {"loadPluginLibraryResult":{"diagnostics":[],"loaded":true}} -// CHECK-NEXT: ->(plugin:[[#PID1]]) {"expandFreestandingMacro":{"discriminator":"${{.*}}","macro":{"moduleName":"MacroDefinition","name":"stringify","typeName":"StringifyMacro"},"syntax":{"kind":"expression","location":{{{.+}}},"source":"#stringify(a + b)"}}} +// CHECK-NEXT: ->(plugin:[[#PID1]]) {"expandFreestandingMacro":{"discriminator":"${{.*}}","macro":{"moduleName":"MacroDefinition","name":"stringify","typeName":"StringifyMacro"},"macroRole":"expression","syntax":{"kind":"expression","location":{{{.+}}},"source":"#stringify(a + b)"}}} // CHECK-NEXT: <-(plugin:[[#PID1]]) {"expandFreestandingMacroResult":{"diagnostics":[],"expandedSource":"(a + b, \"a + b\")"}} -// CHECK-NEXT: ->(plugin:[[#PID1]]) {"expandFreestandingMacro":{"discriminator":"${{.*}}","macro":{"moduleName":"EvilMacros","name":"evil","typeName":"CrashingMacro"},"syntax":{"kind":"expression","location":{{{.+}}},"source":"#evil(42)"}}} +// CHECK-NEXT: ->(plugin:[[#PID1]]) {"expandFreestandingMacro":{"discriminator":"${{.*}}","macro":{"moduleName":"EvilMacros","name":"evil","typeName":"CrashingMacro"},"macroRole":"expression","syntax":{"kind":"expression","location":{{{.+}}},"source":"#evil(42)"}}} // ^ This crashes the plugin server. // CHECK-NEXT: ->(plugin:[[#PID2:]]) {"loadPluginLibrary":{"libraryPath":"BUILD_DIR{{.*}}plugins/libMacroDefinition.dylib","moduleName":"MacroDefinition"}} // CHECK-NEXT: <-(plugin:[[#PID2]]) {"loadPluginLibraryResult":{"diagnostics":[],"loaded":true}} // CHECK-NEXT: ->(plugin:[[#PID2]]) {"loadPluginLibrary":{"libraryPath":"BUILD_DIR{{.*}}plugins/libEvilMacros.dylib","moduleName":"EvilMacros"}} // CHECK-NEXT: <-(plugin:[[#PID2]]) {"loadPluginLibraryResult":{"diagnostics":[],"loaded":true}} -// CHECK-NEXT: ->(plugin:[[#PID2]]) {"expandFreestandingMacro":{"discriminator":"${{.*}}","macro":{"moduleName":"MacroDefinition","name":"stringify","typeName":"StringifyMacro"},"syntax":{"kind":"expression","location":{{{.+}}},"source":"#stringify(b + a)"}}} +// CHECK-NEXT: ->(plugin:[[#PID2]]) {"expandFreestandingMacro":{"discriminator":"${{.*}}","macro":{"moduleName":"MacroDefinition","name":"stringify","typeName":"StringifyMacro"},"macroRole":"expression","syntax":{"kind":"expression","location":{{{.+}}},"source":"#stringify(b + a)"}}} // CHECK-NEXT: <-(plugin:[[#PID2]]) {"expandFreestandingMacroResult":{"diagnostics":[],"expandedSource":"(b + a, \"b + a\")"}} @freestanding(expression) macro stringify(_ value: T) -> (T, String) = #externalMacro(module: "MacroDefinition", type: "StringifyMacro") diff --git a/test/Macros/macros_diagnostics.swift b/test/Macros/macros_diagnostics.swift index 429ac87f77fc5..2f9b00a44716e 100644 --- a/test/Macros/macros_diagnostics.swift +++ b/test/Macros/macros_diagnostics.swift @@ -46,7 +46,7 @@ internal struct X { } // expected-note{{type declared here}} // expected-warning@-1{{external macro implementation type}} struct ZZZ { - macro m5() -> Int = #externalMacro(module: "BuiltinMacros", type: "Blah") + macro m5() = #externalMacro(module: "BuiltinMacros", type: "Blah") // expected-error@-1{{macro 'm5()' can only be declared at file scope}} // expected-error@-2{{macro 'm5()' must declare its applicable roles}} // expected-warning@-3{{external macro implementation type}} @@ -200,3 +200,21 @@ struct SomeType { // expected-error@-2{{use of protocol 'Hashable' as a type must be written 'any Hashable'}} // expected-error@-3{{external macro implementation type}} } + + + +@freestanding(declaration) macro nonExpressionReturnsInt(_: T) -> Int = #externalMacro(module: "A", type: "B") +// expected-warning@-1{{external macro implementation type}} +// expected-error@-2{{only a freestanding expression macro can produce a result of type 'Int'}} +// expected-note@-3{{make this macro a freestanding expression macro}}{{1-1=@freestanding(expression)\n}} +// expected-note@-4{{remove the result type if the macro does not produce a value}}{{67-74=}} + +@freestanding(declaration) macro nonExpressionReturnsVoid(_: T) -> Void = #externalMacro(module: "A", type: "B") +// expected-warning@-1{{external macro implementation type}} + + +@freestanding(expression) +@freestanding(declaration) +macro multipleFreestandingRoles(_: T) -> Void = #externalMacro(module: "A", type: "B") +// expected-warning@-1{{external macro implementation type}} +// expected-error@-2{{macro can only have a single freestanding role}} diff --git a/test/Macros/parsing.swift b/test/Macros/parsing.swift index 1de66078abec3..f4380d1ecfd24 100644 --- a/test/Macros/parsing.swift +++ b/test/Macros/parsing.swift @@ -35,6 +35,7 @@ protocol Q { associatedtype Assoc } @freestanding(expression) @freestanding(declaration, names: named(Foo)) @attached(accessor) macro m10(_: String) = #externalMacro(module: "A", type: "M4") // expected-warning@-1{{external macro implementation type 'A.M4' could not be found for macro 'm10'}} +// expected-error@-2{{macro can only have a single freestanding role}} @attached( accessor,