diff --git a/include/swift/AST/DiagnosticsParse.def b/include/swift/AST/DiagnosticsParse.def index 0b9f14a6c84c6..13804024890d7 100644 --- a/include/swift/AST/DiagnosticsParse.def +++ b/include/swift/AST/DiagnosticsParse.def @@ -2056,6 +2056,8 @@ ERROR(macro_expansion_expr_expected_macro_identifier,PointsToFirstBadToken, "expected a macro identifier for a pound literal expression", ()) ERROR(macro_expansion_decl_expected_macro_identifier,PointsToFirstBadToken, "expected a macro identifier for a pound literal declaration", ()) +ERROR(extra_whitespace_macro_expansion_identifier,PointsToFirstBadToken, + "extraneous whitespace between '#' and macro name is not permitted", ()) ERROR(macro_role_attr_expected_kind,PointsToFirstBadToken, "expected %select{a freestanding|an attached}0 macro role such as " diff --git a/include/swift/Parse/Parser.h b/include/swift/Parse/Parser.h index ed88d1180ddeb..fbe291cd18f5f 100644 --- a/include/swift/Parse/Parser.h +++ b/include/swift/Parse/Parser.h @@ -900,11 +900,15 @@ class Parser { // Decl Parsing /// Returns true if parser is at the start of a Swift decl or decl-import. - bool isStartOfSwiftDecl(bool allowPoundIfAttributes = true); + bool isStartOfSwiftDecl(bool allowPoundIfAttributes = true, + bool hadAttrsOrModifiers = false); /// Returns true if the parser is at the start of a SIL decl. bool isStartOfSILDecl(); + /// Returns true if the parser is at a freestanding macro expansion. + bool isStartOfFreestandingMacroExpansion(); + /// Parse the top-level Swift items into the provided vector. /// /// Each item will be a declaration, statement, or expression. @@ -1747,6 +1751,16 @@ class Parser { DeclNameRef parseDeclNameRef(DeclNameLoc &loc, const Diagnostic &diag, DeclNameOptions flags); + /// Parse macro expansion. + /// + /// macro-expansion: + /// '#' identifier generic-arguments? expr-paren? trailing-closure? + ParserStatus parseFreestandingMacroExpansion( + SourceLoc £Loc, DeclNameLoc ¯oNameLoc, DeclNameRef ¯oNameRef, + SourceLoc &leftAngleLoc, SmallVectorImpl &genericArgs, + SourceLoc &rightAngleLoc, ArgumentList *&argList, bool isExprBasic, + const Diagnostic &diag); + ParserResult parseExprIdentifier(); Expr *parseExprEditorPlaceholder(Token PlaceholderTok, Identifier PlaceholderId); diff --git a/lib/AST/ASTWalker.cpp b/lib/AST/ASTWalker.cpp index b6e78f6a26665..597b4a57b3f30 100644 --- a/lib/AST/ASTWalker.cpp +++ b/lib/AST/ASTWalker.cpp @@ -1482,7 +1482,7 @@ class Traversal : public ASTVisitorisInMacroExpansionInContext()) + D->isInMacroExpansionInContext() && !Walker.Parent.isNull()) return true; if (auto *VD = dyn_cast(D)) { diff --git a/lib/AST/Attr.cpp b/lib/AST/Attr.cpp index 0ff68dc29bce1..b0333b3a39344 100644 --- a/lib/AST/Attr.cpp +++ b/lib/AST/Attr.cpp @@ -856,7 +856,7 @@ OrigDeclAttrFilter::operator()(const DeclAttribute *Attr) const { auto declLoc = decl->getStartLoc(); auto *mod = decl->getModuleContext(); auto *declFile = mod->getSourceFileContainingLocation(declLoc); - auto *attrFile = mod->getSourceFileContainingLocation(Attr->AtLoc); + auto *attrFile = mod->getSourceFileContainingLocation(Attr->getLocation()); if (!declFile || !attrFile) return Attr; diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp index 10598265bc54f..0734d56f870d9 100644 --- a/lib/AST/Decl.cpp +++ b/lib/AST/Decl.cpp @@ -17,6 +17,7 @@ #include "swift/AST/Decl.h" #include "swift/AST/ASTContext.h" #include "swift/AST/ASTMangler.h" +#include "swift/AST/ASTPrinter.h" #include "swift/AST/ASTWalker.h" #include "swift/AST/AccessRequests.h" #include "swift/AST/AccessScope.h" @@ -10065,6 +10066,13 @@ void swift::simple_display(llvm::raw_ostream &out, const Decl *decl) { typeRepr->print(out); else ext->getSelfNominalTypeDecl()->dumpRef(out); + } else if (auto med = dyn_cast(decl)) { + out << '#' << med->getMacroName() << " in "; + printContext(out, med->getDeclContext()); + if (med->getLoc().isValid()) { + out << '@'; + med->getLoc().print(out, med->getASTContext().SourceMgr); + } } else { out << "(unknown decl)"; } diff --git a/lib/ASTGen/CMakeLists.txt b/lib/ASTGen/CMakeLists.txt index 63654fc3b102b..b42be7a88d7e1 100644 --- a/lib/ASTGen/CMakeLists.txt +++ b/lib/ASTGen/CMakeLists.txt @@ -32,5 +32,6 @@ add_pure_swift_host_library(swiftASTGen STATIC SwiftSyntax::SwiftParserDiagnostics SwiftSyntax::SwiftSyntax SwiftSyntax::SwiftSyntaxMacros + SwiftSyntax::SwiftSyntaxMacroExpansion swiftLLVMJSON ) diff --git a/lib/ASTGen/Package.swift b/lib/ASTGen/Package.swift index 683feef602934..f982dd84d26f6 100644 --- a/lib/ASTGen/Package.swift +++ b/lib/ASTGen/Package.swift @@ -37,10 +37,10 @@ let package = Package( .product(name: "SwiftOperators", package: "swift-syntax"), .product(name: "SwiftParser", package: "swift-syntax"), .product(name: "SwiftSyntaxMacros", package: "swift-syntax"), + .product(name: "SwiftSyntaxMacroExpansion", package: "swift-syntax"), "swiftLLVMJSON" ], path: "Sources/ASTGen", - exclude: ["CMakeLists.txt"], swiftSettings: swiftSetttings ), .target( diff --git a/lib/ASTGen/Sources/ASTGen/Macros.swift b/lib/ASTGen/Sources/ASTGen/Macros.swift index 125821804e35b..12fbaaf20c420 100644 --- a/lib/ASTGen/Sources/ASTGen/Macros.swift +++ b/lib/ASTGen/Sources/ASTGen/Macros.swift @@ -16,6 +16,7 @@ import SwiftOperators import SwiftParser import SwiftSyntax import SwiftSyntaxMacros +import SwiftSyntaxMacroExpansion extension SyntaxProtocol { func token(at position: AbsolutePosition) -> TokenSyntax? { @@ -56,14 +57,19 @@ enum MacroPluginKind: UInt8 { case Executable = 1 } -enum MacroRole: UInt8 { - case Expression = 0x01 - case FreestandingDeclaration = 0x02 - case Accessor = 0x04 - case MemberAttribute = 0x08 - case Member = 0x10 - case Peer = 0x20 - case Conformance = 0x40 +extension MacroRole { + init(rawMacroRole: UInt8) { + switch rawMacroRole { + case 0x01: self = .expression + case 0x02: self = .declaration + case 0x04: self = .accessor + case 0x08: self = .memberAttribute + case 0x10: self = .member + case 0x20: self = .peer + case 0x40: self = .conformance + default: fatalError("unknown macro role") + } + } } extension String { @@ -428,6 +434,12 @@ func expandFreestandingMacro( sourceFilePtr: sourceFilePtr, sourceLocationPtr: sourceLocationPtr, type: Syntax.self) else { return 1 } + guard let expansion = macroSyntax.asProtocol( + FreestandingMacroExpansionSyntax.self + ) else { + print("not on a macro expansion node: \(macroSyntax.debugDescription)") + return 1 + } let discriminatorBuffer = UnsafeBufferPointer( start: discriminatorText, count: discriminatorTextLength @@ -440,14 +452,14 @@ func expandFreestandingMacro( expandedSource = expandFreestandingMacroInProcess( macroPtr: macroPtr, diagEnginePtr: diagEnginePtr, - macroSyntax: macroSyntax, + expansionSyntax: expansion, sourceFilePtr: sourceFilePtr, discriminator: discriminator) case .Executable: expandedSource = expandFreestandingMacroIPC( macroPtr: macroPtr, diagEnginePtr: diagEnginePtr, - macroSyntax: macroSyntax, + expansionSyntax: expansion, sourceFilePtr: sourceFilePtr, discriminator: discriminator) } @@ -474,15 +486,15 @@ func expandFreestandingMacro( func expandFreestandingMacroIPC( macroPtr: UnsafeRawPointer, diagEnginePtr: UnsafeMutablePointer, - macroSyntax: Syntax, + expansionSyntax: FreestandingMacroExpansionSyntax, sourceFilePtr: UnsafePointer, discriminator: String ) -> String? { let macroName: String - if let exprSyntax = macroSyntax.as(MacroExpansionExprSyntax.self) { + if let exprSyntax = expansionSyntax.as(MacroExpansionExprSyntax.self) { macroName = exprSyntax.macro.text - } else if let declSyntax = macroSyntax.as(MacroExpansionDeclSyntax.self) { + } else if let declSyntax = expansionSyntax.as(MacroExpansionDeclSyntax.self) { macroName = declSyntax.macro.text } else { fatalError("unknown syntax") @@ -494,7 +506,7 @@ func expandFreestandingMacroIPC( let message = HostToPluginMessage.expandFreestandingMacro( macro: .init(moduleName: macro.moduleName, typeName: macro.typeName, name: macroName), discriminator: discriminator, - syntax: PluginMessage.Syntax(syntax: macroSyntax, in: sourceFilePtr)!) + syntax: PluginMessage.Syntax(syntax: Syntax(expansionSyntax), in: sourceFilePtr)!) do { let result = try macro.plugin.sendMessageAndWait(message) guard @@ -516,7 +528,7 @@ func expandFreestandingMacroIPC( srcMgr.insert(sourceFilePtr) srcMgr.diagnose( diagnostic: .init( - node: macroSyntax, + node: Syntax(expansionSyntax), // FIXME: This is probably a plugin communication error. // The error might not be relevant as the diagnostic message. message: ASTGenMacroDiagnostic.thrownError(error) @@ -530,10 +542,13 @@ func expandFreestandingMacroIPC( func expandFreestandingMacroInProcess( macroPtr: UnsafeRawPointer, diagEnginePtr: UnsafeMutablePointer, - macroSyntax: Syntax, + expansionSyntax: FreestandingMacroExpansionSyntax, sourceFilePtr: UnsafePointer, discriminator: String ) -> String? { + // Get the macro. + let macroPtr = macroPtr.bindMemory(to: ExportedMacro.self, capacity: 1) + let macro = macroPtr.pointee.macro // Create a source manager. This should probably persist and be given to us. let sourceManager = SourceManager(cxxDiagnosticEngine: diagEnginePtr) @@ -543,14 +558,7 @@ func expandFreestandingMacroInProcess( discriminator: discriminator ) - guard let parentExpansion = macroSyntax.asProtocol( - FreestandingMacroExpansionSyntax.self - ) else { - print("not on a macro expansion node: \(macroSyntax.debugDescription)") - return nil - } - - let macroName = parentExpansion.macro.text + let macroName = expansionSyntax.macro.text // Make sure we emit all of the diagnostics from the context. defer { @@ -565,73 +573,16 @@ func expandFreestandingMacroInProcess( context.diagnostics = [] } - let macroPtr = macroPtr.bindMemory(to: ExportedMacro.self, capacity: 1) - let macro = macroPtr.pointee.macro - - let evaluatedSyntax: Syntax - do { - switch macro { - // Handle expression macro. - case let exprMacro as ExpressionMacro.Type: - func expandExpressionMacro( - _ node: Node - ) throws -> ExprSyntax { - return try exprMacro.expansion( - of: sourceManager.detach( - node, - foldingWith: OperatorTable.standardOperators - ), - in: context - ) - } - - evaluatedSyntax = Syntax( - try _openExistential(parentExpansion, do: expandExpressionMacro) - ) - - // Handle declaration macro. The resulting decls are wrapped in a - // `CodeBlockItemListSyntax`. - case let declMacro as DeclarationMacro.Type: - func expandDeclarationMacro( - _ node: Node - ) throws -> [DeclSyntax] { - return try declMacro.expansion( - of: sourceManager.detach( - node, - foldingWith: OperatorTable.standardOperators - ), - in: context - ) - } - let decls = try _openExistential(parentExpansion, do: expandDeclarationMacro) - evaluatedSyntax = Syntax(CodeBlockItemListSyntax( - decls.map { CodeBlockItemSyntax(item: .decl($0)) })) - - case let codeItemMacro as CodeItemMacro.Type: - func expandCodeItemMacro( - _ node: Node - ) throws -> [CodeBlockItemSyntax] { - return try codeItemMacro.expansion( - of: sourceManager.detach( - node, - foldingWith: OperatorTable.standardOperators - ), - in: context - ) - } - let items = try _openExistential(parentExpansion, do: expandCodeItemMacro) - evaluatedSyntax = Syntax(CodeBlockItemListSyntax(items)) - - default: - print("not an expression macro or a declaration macro") - return nil - } - } catch { - context.addDiagnostics(from: error, node: macroSyntax) - return nil - } + let node = sourceManager.detach( + expansionSyntax, + foldingWith: OperatorTable.standardOperators + ) - return evaluatedSyntax.formattedExpansion(macro.formatMode) + return SwiftSyntaxMacroExpansion.expandFreestandingMacro( + definition: macro, + node: node, + in: context + ) } /// Retrieve a syntax node in the given source file, with the given type. @@ -768,7 +719,7 @@ func expandAttachedMacro( } // Fixup the source. - var expandedSource: String = collapse(expansions: expandedSources, for: MacroRole(rawValue: rawMacroRole)!, attachedTo: declarationNode) + var expandedSource: String = collapse(expansions: expandedSources, for: MacroRole(rawMacroRole: rawMacroRole), attachedTo: declarationNode) // Form the result buffer for our caller. expandedSource.withUTF8 { utf8 in @@ -802,15 +753,16 @@ func expandAttachedMacroIPC( // Map the macro role. let macroRole: PluginMessage.MacroRole - switch MacroRole(rawValue: rawMacroRole)! { - case .Accessor: macroRole = .accessor - case .Member: macroRole = .member - case .MemberAttribute: macroRole = .memberAttribute - case .Peer: macroRole = .peer - case .Conformance: macroRole = .conformance + switch MacroRole(rawMacroRole: rawMacroRole) { + case .accessor: macroRole = .accessor + case .member: macroRole = .member + case .memberAttribute: macroRole = .memberAttribute + case .peer: macroRole = .peer + case .conformance: macroRole = .conformance case - .Expression, - .FreestandingDeclaration: + .expression, + .declaration, + .codeItem: preconditionFailure("unhandled macro role for attached macro") } @@ -892,7 +844,6 @@ func expandAttachedMacroInProcess( // Get the macro. let macroPtr = macroPtr.bindMemory(to: ExportedMacro.self, capacity: 1) let macro = macroPtr.pointee.macro - let macroRole = MacroRole(rawValue: rawMacroRole) // Create a source manager covering the files we know about. let sourceManager = SourceManager(cxxDiagnosticEngine: diagEnginePtr) @@ -922,147 +873,21 @@ func expandAttachedMacroInProcess( context.diagnostics = [] } - var expandedSources: [String] - do { - switch (macro, macroRole) { - case (let attachedMacro as AccessorMacro.Type, .Accessor): - let accessors = try attachedMacro.expansion( - of: sourceManager.detach( - customAttrNode, - foldingWith: OperatorTable.standardOperators - ), - providingAccessorsOf: sourceManager.detach(declarationNode), - in: context - ) - - // Form a buffer of accessor declarations to return to the caller. - expandedSources = accessors.map { - $0.formattedExpansion(macro.formatMode) - } - - case (let attachedMacro as MemberAttributeMacro.Type, .MemberAttribute): - // Dig out the node for the parent declaration of the to-expand - // declaration. Only member attribute macros need this. - guard let parentDeclNode = parentDeclNode, - let parentDeclGroup = parentDeclNode.asProtocol(DeclGroupSyntax.self) - else { - return nil - } - - // Local function to expand a member atribute macro once we've opened up - // the existential. - func expandMemberAttributeMacro( - _ node: Node - ) throws -> [AttributeSyntax] { - return try attachedMacro.expansion( - of: sourceManager.detach( - customAttrNode, - foldingWith: OperatorTable.standardOperators - ), - attachedTo: sourceManager.detach(node), - providingAttributesFor: sourceManager.detach(declarationNode), - in: context - ) - } - - let attributes = try _openExistential( - parentDeclGroup, do: expandMemberAttributeMacro - ) - - // Form a buffer containing an attribute list to return to the caller. - expandedSources = attributes.map { - $0.formattedExpansion(macro.formatMode) - } - - case (let attachedMacro as MemberMacro.Type, .Member): - guard let declGroup = declarationNode.asProtocol(DeclGroupSyntax.self) - else { - return nil - } - - // Local function to expand a member macro once we've opened up - // the existential. - func expandMemberMacro( - _ node: Node - ) throws -> [DeclSyntax] { - return try attachedMacro.expansion( - of: sourceManager.detach( - customAttrNode, - foldingWith: OperatorTable.standardOperators - ), - providingMembersOf: sourceManager.detach(node), - in: context - ) - } - - let members = try _openExistential(declGroup, do: expandMemberMacro) - - // Form a buffer of member declarations to return to the caller. - expandedSources = members.map { - $0.formattedExpansion(macro.formatMode) - } - - case (let attachedMacro as PeerMacro.Type, .Peer): - let peers = try attachedMacro.expansion( - of: sourceManager.detach( - customAttrNode, - foldingWith: OperatorTable.standardOperators - ), - providingPeersOf: sourceManager.detach( - declarationNode, - foldingWith: OperatorTable.standardOperators - ), - in: context - ) - - // Form a buffer of peer declarations to return to the caller. - expandedSources = peers.map { - $0.formattedExpansion(macro.formatMode) - } - - case (let attachedMacro as ConformanceMacro.Type, .Conformance): - guard let declGroup = declarationNode.asProtocol(DeclGroupSyntax.self), - let identified = declarationNode.asProtocol(IdentifiedDeclSyntax.self) else { - return nil - } - - // Local function to expand a conformance macro once we've opened up - // the existential. - func expandConformanceMacro( - _ node: Node - ) throws -> [(TypeSyntax, GenericWhereClauseSyntax?)] { - return try attachedMacro.expansion( - of: sourceManager.detach( - customAttrNode, - foldingWith: OperatorTable.standardOperators - ), - providingConformancesOf: sourceManager.detach(node), - in: context - ) - } - - let conformances = try _openExistential( - declGroup, do: expandConformanceMacro - ) - - // Form a buffer of extension declarations to return to the caller. - expandedSources = conformances.map { typeSyntax, whereClause in - let typeName = identified.identifier.trimmedDescription - let protocolName = typeSyntax.trimmedDescription - let whereClause = whereClause?.trimmedDescription ?? "" - return "extension \(typeName) : \(protocolName) \(whereClause) {}" - } - - default: - print("\(macroPtr) does not conform to any known attached macro protocol") - return nil - } - } catch { - context.addDiagnostics(from: error, node: declarationNode) - return nil - } - - return expandedSources + let attributeNode = sourceManager.detach( + customAttrNode, + foldingWith: OperatorTable.standardOperators + ) + let declarationNode = sourceManager.detach(declarationNode) + let parentDeclNode = parentDeclNode.map { sourceManager.detach($0) } + + return SwiftSyntaxMacroExpansion.expandAttachedMacro( + definition: macro, + macroRole: MacroRole(rawMacroRole: rawMacroRole), + attributeNode: attributeNode, + declarationNode: declarationNode, + parentDeclNode: parentDeclNode, + in: context + ) } fileprivate extension SyntaxProtocol { @@ -1092,7 +917,7 @@ fileprivate func collapse( var expansions = expansions var separator: String = "\n\n" - if role == .Accessor, + if role == .accessor, let varDecl = declarationNode.as(VariableDeclSyntax.self), let binding = varDecl.bindings.first, binding.accessor == nil { @@ -1101,7 +926,7 @@ fileprivate func collapse( expansions = expansions.map({ indent($0, with: indentation) }) expansions[0] = "{\n" + expansions[0] expansions[expansions.count - 1] += "\n}" - } else if role == .MemberAttribute { + } else if role == .memberAttribute { separator = " " } diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index 612c03aed535d..55e8d7dc73ae2 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -28,6 +28,7 @@ if (SWIFT_SWIFT_PARSER) SwiftOperators SwiftSyntaxBuilder SwiftSyntaxMacros + SwiftSyntaxMacroExpansion SwiftCompilerPluginMessageHandling ) diff --git a/lib/Parse/ParseDecl.cpp b/lib/Parse/ParseDecl.cpp index be28b4dd1ca98..3ac83ac40f86e 100644 --- a/lib/Parse/ParseDecl.cpp +++ b/lib/Parse/ParseDecl.cpp @@ -4768,7 +4768,8 @@ static void skipAttribute(Parser &P) { } } -bool Parser::isStartOfSwiftDecl(bool allowPoundIfAttributes) { +bool Parser::isStartOfSwiftDecl(bool allowPoundIfAttributes, + bool hadAttrsOrModifiers) { if (Tok.is(tok::at_sign) && peekToken().is(tok::kw_rethrows)) { // @rethrows does not follow the general rule of @ so // it is needed to short circuit this else there will be an infinite @@ -4817,13 +4818,25 @@ bool Parser::isStartOfSwiftDecl(bool allowPoundIfAttributes) { (Tok.is(tok::pound_endif) && !allowPoundIfAttributes)) return true; - return isStartOfSwiftDecl(allowPoundIfAttributes); + return isStartOfSwiftDecl(allowPoundIfAttributes, + /*hadAttrsOrModifiers=*/true); } - if (Tok.is(tok::pound) && peekToken().is(tok::identifier)) { - // Macro expansions at the top level are declarations. - return !isInSILMode() && SF.Kind != SourceFileKind::Interface && - CurDeclContext->isModuleScopeContext() && !allowTopLevelCode(); + if (Tok.is(tok::pound)) { + if (isStartOfFreestandingMacroExpansion()) { + if (isInSILMode() || SF.Kind == SourceFileKind::Interface) + return false; + + // Parse '#' after attrs/modifiers as a macro expansion decl. + if (hadAttrsOrModifiers) + return true; + + // Macro expansions at the top level of non-script file are declarations. + return CurDeclContext->isModuleScopeContext() && !allowTopLevelCode(); + } + + // Otherwise, prefer parsing it as an expression. + return false; } // Skip a #if that contains only attributes in all branches. These will be @@ -4831,8 +4844,14 @@ bool Parser::isStartOfSwiftDecl(bool allowPoundIfAttributes) { if (Tok.is(tok::pound_if) && allowPoundIfAttributes) { BacktrackingScope backtrack(*this); bool sawAnyAttributes = false; - return skipIfConfigOfAttributes(sawAnyAttributes) && - (Tok.is(tok::eof) || (sawAnyAttributes && isStartOfSwiftDecl())); + if (!skipIfConfigOfAttributes(sawAnyAttributes)) + return false; + if (Tok.is(tok::eof)) + return true; + if (!sawAnyAttributes) + return false; + return isStartOfSwiftDecl(/*allowPoundIfAttributes=*/true, + /*hadAttrsOrModifiers=*/true); } // If we have a decl modifying keyword, check if the next token is a valid @@ -4854,13 +4873,15 @@ bool Parser::isStartOfSwiftDecl(bool allowPoundIfAttributes) { // If we found the start of a decl while trying to skip over the // paren, then we have something incomplete like 'private('. Return // true for better recovery. - if (isStartOfSwiftDecl(/*allowPoundIfAttributes=*/false)) + if (isStartOfSwiftDecl(/*allowPoundIfAttributes=*/false, + /*hadAttrsOrModifiers=*/true)) return true; skipSingle(); } } - return isStartOfSwiftDecl(/*allowPoundIfAttributes=*/false); + return isStartOfSwiftDecl(/*allowPoundIfAttributes=*/false, + /*hadAttrsOrModifiers=*/true); } } @@ -4887,7 +4908,8 @@ bool Parser::isStartOfSwiftDecl(bool allowPoundIfAttributes) { consumeToken(tok::l_paren); consumeToken(tok::identifier); consumeToken(tok::r_paren); - return isStartOfSwiftDecl(/*allowPoundIfAttributes=*/false); + return isStartOfSwiftDecl(/*allowPoundIfAttributes=*/false, + /*hadAttrsOrModifiers=*/true); } if (Tok.isContextualKeyword("actor")) { @@ -4899,13 +4921,15 @@ bool Parser::isStartOfSwiftDecl(bool allowPoundIfAttributes) { // it's an actor declaration, otherwise, it isn't. do { consumeToken(); - } while (isStartOfSwiftDecl(/*allowPoundIfAttributes=*/false)); + } while (isStartOfSwiftDecl(/*allowPoundIfAttributes=*/false, + /*hadAttrsOrModifiers=*/true)); return Tok.is(tok::identifier); } // 'macro' name - if (Tok.isContextualKeyword("macro") && Tok2.is(tok::identifier)) - return true; + if (Tok.isContextualKeyword("macro")) { + return Tok2.is(tok::identifier); + } if (Tok.isContextualKeyword("package")) { // If `case` is the next token after `return package` statement, @@ -4942,12 +4966,14 @@ bool Parser::isStartOfSwiftDecl(bool allowPoundIfAttributes) { // If we found the start of a decl while trying to skip over the // paren, then we have something incomplete like 'package('. Return // true for better recovery. - if (isStartOfSwiftDecl(/*allowPoundIfAttributes=*/false)) + if (isStartOfSwiftDecl(/*allowPoundIfAttributes=*/false, + /*hadAttrsOrModifiers=*/true)) return true; skipSingle(); } } - return isStartOfSwiftDecl(/*allowPoundIfAttributes=*/false); + return isStartOfSwiftDecl(/*allowPoundIfAttributes=*/false, + /*hadAttrsOrModifiers=*/true); } } @@ -4958,7 +4984,8 @@ bool Parser::isStartOfSwiftDecl(bool allowPoundIfAttributes) { // Otherwise, do a recursive parse. Parser::BacktrackingScope Backtrack(*this); consumeToken(tok::identifier); - return isStartOfSwiftDecl(/*allowPoundIfAttributes=*/false); + return isStartOfSwiftDecl(/*allowPoundIfAttributes=*/false, + /*hadAttrsOrModifiers=*/true); } bool Parser::isStartOfSILDecl() { @@ -4986,6 +5013,23 @@ bool Parser::isStartOfSILDecl() { llvm_unreachable("Unhandled case in switch"); } +bool Parser::isStartOfFreestandingMacroExpansion() { + // Check if "'#' " where the identifier is on the sameline. + if (!Tok.is(tok::pound)) + return false; + const Token &Tok2 = peekToken(); + if (Tok2.isAtStartOfLine()) + return false; + + if (Tok2.isAny(tok::identifier, tok::code_complete)) + return true; + if (Tok2.isKeyword()) { + // allow keywords right after '#' so we can diagnose it when parsing. + return Tok.getRange().getEnd() == Tok2.getLoc(); + } + return false; +} + void Parser::consumeDecl(ParserPosition BeginParserPosition, ParseDeclOptions Flags, bool IsTopLevel) { @@ -5238,9 +5282,15 @@ Parser::parseDecl(ParseDeclOptions Flags, // Handled below. break; case tok::pound: - if (Tok.isAtStartOfLine() && - peekToken().is(tok::code_complete) && - Tok.getLoc().getAdvancedLoc(1) == peekToken().getLoc()) { + if (!isStartOfFreestandingMacroExpansion()) { + consumeToken(tok::pound); + diagnose(Tok.getLoc(), + diag::macro_expansion_decl_expected_macro_identifier); + DeclResult = makeParserError(); + break; + } + + if (peekToken().is(tok::code_complete)) { consumeToken(); if (CodeCompletionCallbacks) { CodeCompletionCallbacks->completeAfterPoundDirective(); @@ -5252,6 +5302,7 @@ Parser::parseDecl(ParseDeclOptions Flags, // Parse as a macro expansion. DeclResult = parseDeclMacroExpansion(Flags, Attributes); + StaticLoc = SourceLoc(); // Ignore 'static' on macro expansion break; case tok::pound_if: @@ -9743,52 +9794,23 @@ ParserResult Parser::parseDeclMacro(DeclAttributes &attributes) { ParserResult Parser::parseDeclMacroExpansion(ParseDeclOptions flags, DeclAttributes &attributes) { - SourceLoc poundLoc = consumeToken(tok::pound); + SourceLoc poundLoc; DeclNameLoc macroNameLoc; - DeclNameRef macroNameRef = parseDeclNameRef( - macroNameLoc, diag::macro_expansion_decl_expected_macro_identifier, - DeclNameOptions()); + DeclNameRef macroNameRef; + SourceLoc leftAngleLoc, rightAngleLoc; + SmallVector genericArgs; + ArgumentList *argList = nullptr; + ParserStatus status = parseFreestandingMacroExpansion( + poundLoc, macroNameLoc, macroNameRef, leftAngleLoc, genericArgs, + rightAngleLoc, argList, /*isExprBasic=*/false, + diag::macro_expansion_decl_expected_macro_identifier); if (!macroNameRef) - return makeParserError(); + return status; - ParserStatus status; - SourceLoc leftAngleLoc, rightAngleLoc; - SmallVector genericArgs; - if (canParseAsGenericArgumentList()) { - auto genericArgsStatus = parseGenericArguments( - genericArgs, leftAngleLoc, rightAngleLoc); - status |= genericArgsStatus; - if (genericArgsStatus.isErrorOrHasCompletion()) - diagnose(leftAngleLoc, diag::while_parsing_as_left_angle_bracket); - } + auto *med = new (Context) MacroExpansionDecl( + CurDeclContext, poundLoc, macroNameRef, macroNameLoc, leftAngleLoc, + Context.AllocateCopy(genericArgs), rightAngleLoc, argList); + med->getAttrs() = attributes; - ArgumentList *argList = nullptr; - if (Tok.isFollowingLParen()) { - auto result = parseArgumentList(tok::l_paren, tok::r_paren, - /*isExprBasic*/ false, - /*allowTrailingClosure*/ true); - status |= result; - if (result.hasCodeCompletion()) - return makeParserCodeCompletionResult(); - argList = result.getPtrOrNull(); - } else if (Tok.is(tok::l_brace)) { - SmallVector trailingClosures; - auto closuresStatus = parseTrailingClosures(/*isExprBasic*/ false, - macroNameLoc.getSourceRange(), - trailingClosures); - status |= closuresStatus; - - if (!trailingClosures.empty()) { - argList = ArgumentList::createParsed(Context, SourceLoc(), - trailingClosures, SourceLoc(), - /*trailingClosureIdx*/ 0); - } - } - - return makeParserResult( - status, - new (Context) MacroExpansionDecl( - CurDeclContext, poundLoc, macroNameRef, macroNameLoc, - leftAngleLoc, Context.AllocateCopy(genericArgs), rightAngleLoc, - argList)); + return makeParserResult(status, med); } diff --git a/lib/Parse/ParseExpr.cpp b/lib/Parse/ParseExpr.cpp index 8cd668acbf1e3..1eee5bcf87512 100644 --- a/lib/Parse/ParseExpr.cpp +++ b/lib/Parse/ParseExpr.cpp @@ -1841,6 +1841,12 @@ ParserResult Parser::parseExprPrimary(Diag<> ID, bool isExprBasic) { } case tok::pound: + if (!isStartOfFreestandingMacroExpansion()) { + consumeToken(tok::pound); + diagnose(Tok.getLoc(), + diag::macro_expansion_expr_expected_macro_identifier); + return makeParserError(); + } if (peekToken().is(tok::code_complete) && Tok.getLoc().getAdvancedLoc(1) == peekToken().getLoc()) { return parseExprPoundCodeCompletion(/*ParentKind*/None); @@ -2263,6 +2269,71 @@ DeclNameRef Parser::parseDeclNameRef(DeclNameLoc &loc, return DeclNameRef({ Context, baseName, argumentLabels }); } +ParserStatus Parser::parseFreestandingMacroExpansion( + SourceLoc £Loc, DeclNameLoc ¯oNameLoc, DeclNameRef ¯oNameRef, + SourceLoc &leftAngleLoc, SmallVectorImpl &genericArgs, + SourceLoc &rightAngleLoc, ArgumentList *&argList, bool isExprBasic, + const Diagnostic &diag) { + SourceLoc poundEndLoc = Tok.getRange().getEnd(); + poundLoc = consumeToken(tok::pound); + + if (Tok.isAtStartOfLine()) { + // Diagnose and bail out if there's no macro name on the same line. + diagnose(poundEndLoc, diag); + return makeParserError(); + } + + bool hasWhitespaceBeforeName = poundEndLoc != Tok.getLoc(); + + // Diagnose and parse keyword right after `#`. + if (Tok.isKeyword() && !hasWhitespaceBeforeName) { + diagnose(Tok, diag::keyword_cant_be_identifier, Tok.getText()); + diagnose(Tok, diag::backticks_to_escape) + .fixItReplace(Tok.getLoc(), "`" + Tok.getText().str() + "`"); + + // Let 'parseDeclNameRef' to parse this as an identifier. + Tok.setKind(tok::identifier); + } + macroNameRef = parseDeclNameRef(macroNameLoc, diag, DeclNameOptions()); + if (!macroNameRef) + return makeParserError(); + + // Diagnose whitespace between '#' and the macro name, and continue parsing. + if (hasWhitespaceBeforeName) { + diagnose(poundEndLoc, diag::extra_whitespace_macro_expansion_identifier) + .fixItRemoveChars(poundEndLoc, macroNameLoc.getStartLoc()); + } + + ParserStatus status; + if (canParseAsGenericArgumentList()) { + auto genericArgsStatus = + parseGenericArguments(genericArgs, leftAngleLoc, rightAngleLoc); + status |= genericArgsStatus; + if (genericArgsStatus.isErrorOrHasCompletion()) + diagnose(leftAngleLoc, diag::while_parsing_as_left_angle_bracket); + } + + if (Tok.isFollowingLParen()) { + auto result = parseArgumentList(tok::l_paren, tok::r_paren, isExprBasic, + /*allowTrailingClosure*/ true); + status |= result; + argList = result.getPtrOrNull(); + } else if (Tok.is(tok::l_brace)) { + SmallVector trailingClosures; + auto closuresStatus = parseTrailingClosures( + isExprBasic, macroNameLoc.getSourceRange(), trailingClosures); + status |= closuresStatus; + + if (!trailingClosures.empty()) { + argList = ArgumentList::createParsed(Context, SourceLoc(), + trailingClosures, SourceLoc(), + /*trailingClosureIdx*/ 0); + } + } + + return status; +} + /// expr-identifier: /// unqualified-decl-name generic-args? ParserResult Parser::parseExprIdentifier() { @@ -3370,45 +3441,18 @@ Parser::parseExprCallSuffix(ParserResult fn, bool isExprBasic) { } ParserResult Parser::parseExprMacroExpansion(bool isExprBasic) { - SourceLoc poundLoc = consumeToken(tok::pound); + SourceLoc poundLoc; DeclNameLoc macroNameLoc; - DeclNameRef macroNameRef = parseDeclNameRef( - macroNameLoc, diag::macro_expansion_expr_expected_macro_identifier, - DeclNameOptions()); - if (!macroNameRef) - return makeParserError(); - - ParserStatus status; + DeclNameRef macroNameRef; SourceLoc leftAngleLoc, rightAngleLoc; - SmallVector genericArgs; - if (canParseAsGenericArgumentList()) { - auto genericArgsStatus = parseGenericArguments( - genericArgs, leftAngleLoc, rightAngleLoc); - status |= genericArgsStatus; - if (genericArgsStatus.isErrorOrHasCompletion()) - diagnose(leftAngleLoc, diag::while_parsing_as_left_angle_bracket); - } - + SmallVector genericArgs; ArgumentList *argList = nullptr; - if (Tok.isFollowingLParen()) { - auto result = parseArgumentList(tok::l_paren, tok::r_paren, isExprBasic, - /*allowTrailingClosure*/ true); - argList = result.getPtrOrNull(); - status |= result; - } else if (Tok.is(tok::l_brace) && - isValidTrailingClosure(isExprBasic, *this)) { - SmallVector trailingClosures; - auto closureStatus = parseTrailingClosures(isExprBasic, - macroNameLoc.getSourceRange(), - trailingClosures); - status |= closureStatus; - - if (!trailingClosures.empty()) { - argList = ArgumentList::createParsed(Context, SourceLoc(), - trailingClosures, SourceLoc(), - /*trailingClosureIdx*/ 0); - } - } + ParserStatus status = parseFreestandingMacroExpansion( + poundLoc, macroNameLoc, macroNameRef, leftAngleLoc, genericArgs, + rightAngleLoc, argList, isExprBasic, + diag::macro_expansion_expr_expected_macro_identifier); + if (!macroNameRef) + return status; return makeParserResult( status, diff --git a/lib/Sema/TypeCheckAvailability.cpp b/lib/Sema/TypeCheckAvailability.cpp index 83cea139c6305..515815f6c674b 100644 --- a/lib/Sema/TypeCheckAvailability.cpp +++ b/lib/Sema/TypeCheckAvailability.cpp @@ -473,6 +473,7 @@ class TypeRefinementContextBuilder : private ASTWalker { private: MacroWalking getMacroWalkingBehavior() const override { + // Expansion buffers will have their type refinement contexts built lazily. return MacroWalking::Arguments; } @@ -558,7 +559,7 @@ class TypeRefinementContextBuilder : private ASTWalker { /// Returns a new context to be introduced for the declaration, or nullptr /// if no new context should be introduced. TypeRefinementContext *getNewContextForSignatureOfDecl(Decl *D) { - if (!isa(D) && !isa(D)) + if (!isa(D) && !isa(D) && !isa(D)) return nullptr; // Only introduce for an AbstractStorageDecl if it is not local. We @@ -1430,7 +1431,9 @@ class InnermostAncestorFinder : private ASTWalker { Optional getInnermostMatchingNode() { return InnermostMatchingNode; } MacroWalking getMacroWalkingBehavior() const override { - return MacroWalking::ArgumentsAndExpansion; + // This is SourceRange based finder. 'SM.rangeContains()' fails anyway when + // crossing source buffers. + return MacroWalking::Arguments; } PreWalkResult walkToExprPre(Expr *E) override { @@ -3244,7 +3247,8 @@ class ExprAvailabilityWalker : public ASTWalker { bool shouldWalkIntoTapExpression() override { return false; } MacroWalking getMacroWalkingBehavior() const override { - return MacroWalking::ArgumentsAndExpansion; + // Expanded source should be type checked and diagnosed separately. + return MacroWalking::Arguments; } PreWalkResult walkToExprPre(Expr *E) override { diff --git a/test/Macros/Inputs/top_level_freestanding_other.swift b/test/Macros/Inputs/top_level_freestanding_other.swift index 82897c727f113..c4a296a34fee5 100644 --- a/test/Macros/Inputs/top_level_freestanding_other.swift +++ b/test/Macros/Inputs/top_level_freestanding_other.swift @@ -11,9 +11,9 @@ var globalVar2 = { #stringify(1 + 1) }() func deprecated() -> Int { 0 } var globalVar3 = #stringify({ deprecated() }) -// expected-note@-1 2{{in expansion of macro 'stringify' here}} +// expected-note@-1 {{in expansion of macro 'stringify' here}} // expected-warning@-2{{'deprecated()' is deprecated}} var globalVar4 = #stringify({ deprecated() }) -// expected-note@-1 2{{in expansion of macro 'stringify' here}} +// expected-note@-1 {{in expansion of macro 'stringify' here}} // expected-warning@-2{{'deprecated()' is deprecated}} diff --git a/test/Macros/macro_attribute_expansiondecl.swift b/test/Macros/macro_attribute_expansiondecl.swift new file mode 100644 index 0000000000000..9b1a3ad2e5844 --- /dev/null +++ b/test/Macros/macro_attribute_expansiondecl.swift @@ -0,0 +1,160 @@ +// REQUIRES: swift_swift_parser, OS=macosx + +// RUN: %empty-directory(%t) +// RUN: mkdir -p %t/src +// RUN: mkdir -p %t/plugins + +// RUN: split-file %s %t/src + +//#-- Prepare the macro dylib plugin. +// RUN: %host-build-swift \ +// RUN: -swift-version 5 \ +// RUN: -emit-library -o %t/plugins/%target-library-name(MacroDefinition) \ +// RUN: -module-name MacroDefinition \ +// RUN: %t/src/MacroDefinition.swift \ +// RUN: -g -no-toolchain-stdlib-rpath + +// RUN: %target-swift-frontend \ +// RUN: -typecheck -verify \ +// RUN: -enable-experimental-feature FreestandingMacros \ +// RUN: -parse-as-library \ +// RUN: -dump-macro-expansions \ +// RUN: -plugin-path %t/plugins \ +// RUN: %t/src/test.swift + + +//--- MacroDefinition.swift +import SwiftDiagnostics +import SwiftOperators +import SwiftSyntax +import SwiftSyntaxBuilder +import SwiftSyntaxMacros + +public struct GlobalFuncAndVarMacro: DeclarationMacro { + public static func expansion( + of node: some FreestandingMacroExpansionSyntax, + in context: some MacroExpansionContext + ) throws -> [DeclSyntax] { + return ["func globalFunc() {}", "var globalVar: Int { 1 }"] + } +} + +public struct MemberFuncAndVarMacro: DeclarationMacro { + public static func expansion( + of node: some FreestandingMacroExpansionSyntax, + in context: some MacroExpansionContext + ) throws -> [DeclSyntax] { + return ["func memberFunc() {}", "var memberVar: Int { 1 }"] + } +} + +public struct LocalFuncAndVarMacro: DeclarationMacro { + public static func expansion( + of node: some FreestandingMacroExpansionSyntax, + in context: some MacroExpansionContext + ) throws -> [DeclSyntax] { + return ["func LocalFunc() {}", "var LocalVar: Int { 1 }"] + } +} + +public struct FuncFromClosureMacro: DeclarationMacro { + public static func expansion( + of node: some FreestandingMacroExpansionSyntax, + in context: some MacroExpansionContext + ) throws -> [DeclSyntax] { + guard + let closure = node.trailingClosure, + let arg1 = node.argumentList.first?.expression else { + return [] + } + + return [""" + func fromClosure() { + print(\(arg1)) + \(closure.statements) + } + """] + } +} + +//--- test.swift + +@freestanding(declaration, names: named(globalFunc), named(globalVar)) macro globalDecls() = #externalMacro(module: "MacroDefinition", type: "GlobalFuncAndVarMacro") +@freestanding(declaration, names: named(memberFunc), named(memberVar)) macro memberDecls() = #externalMacro(module: "MacroDefinition", type: "MemberFuncAndVarMacro") +@freestanding(declaration, names: named(localFunc), named(localVar)) macro localDecls() = #externalMacro(module: "MacroDefinition", type: "LocalFuncAndVarMacro") + +@available(SwiftStdlib 9999, *) +#globalDecls + +func testGlobal() { // expected-note {{add @available attribute to enclosing global function}} + globalFunc() // expected-error {{'globalFunc()' is only available in macOS 9999 or newer}} expected-note {{add 'if #available' version check}} + // FIXME(109376568): Global variable introduced by macro expansion not found + _ = globalVar // expected-error {{cannot find 'globalVar' in scope}} +} + +struct S { + @available(SwiftStdlib 9999, *) + #memberDecls +} +func testMember(value: S) { // expected-note 2 {{add @available attribute to enclosing global function}} + value.memberFunc() // expected-error {{'memberFunc()' is only available in macOS 9999 or newer}} expected-note {{add 'if #available' version check}} + _ = value.memberVar // expected-error {{'memberVar' is only available in macOS 9999 or newer}} expected-note {{add 'if #available' version check}} +} + +struct T { + static #memberDecls +} +func testStatic() { + T.memberFunc() // OK + _ = T.memberVar // OK +} + +func testLocal() { +// FIXME(109376102): Local vars with freestanding macro is not supported yet. +#if false + #localDecls + do { + } +#endif +} + +@freestanding(declaration, names: named(fromClosure)) macro funcFromClosureMacro(_: String, _: () -> Void) = #externalMacro(module: "MacroDefinition", type: "FuncFromClosureMacro") + +@available(macOS 99, *) +func APIFrom99() -> String { "" } +@available(macOS 999, *) +func APIFrom999() -> String { "" } + +@available(macOS 99, *) +#funcFromClosureMacro(APIFrom99()) { + _ = APIFrom99() + if #available(macOS 999, *) { + _ = APIFrom99() + _ = APIFrom999() + } +} + +struct S1 { + @available(macOS 99, *) + #funcFromClosureMacro(APIFrom99()) { + _ = APIFrom99() + if #available(macOS 999, *) { + _ = APIFrom99() + _ = APIFrom999() + } + } +} + +// FIXME: Diagnostics could be better. +struct S2 { // expected-note 4 {{add @available attribute to enclosing struct}} + // expected-note@+3 6 {{in expansion of macro 'funcFromClosureMacro' here}} + // expected-error@+2 {{'APIFrom99()' is only available in macOS 99 or newer}} + // expected-error@+2 {{'APIFrom99()' is only available in macOS 99 or newer}} expected-note@+2 {{add 'if #available' version check}} + #funcFromClosureMacro(APIFrom99()) { + _ = APIFrom99() + if #available(macOS 999, *) { + _ = APIFrom99() + _ = APIFrom999() + } + } +} diff --git a/test/Macros/macro_keywordname.swift b/test/Macros/macro_keywordname.swift new file mode 100644 index 0000000000000..3a302538b312e --- /dev/null +++ b/test/Macros/macro_keywordname.swift @@ -0,0 +1,61 @@ +// REQUIRES: swift_swift_parser + +// RUN: %empty-directory(%t) +// RUN: mkdir -p %t/src +// RUN: mkdir -p %t/plugins +// RUN: mkdir -p %t/lib + +// RUN: split-file %s %t/src + +//#-- Prepare the macro dylib plugin. +// RUN: %host-build-swift \ +// RUN: -swift-version 5 \ +// RUN: -emit-library -o %t/plugins/%target-library-name(MacroDefinition) \ +// RUN: -module-name MacroDefinition \ +// RUN: %t/src/MacroDefinition.swift \ +// RUN: -g -no-toolchain-stdlib-rpath + +//#-- Prepare the macro library. +// RUN: %target-swift-frontend \ +// RUN: -swift-version 5 \ +// RUN: -emit-module -o %t/lib/MacroLib.swiftmodule \ +// RUN: -module-name MacroLib \ +// RUN: -plugin-path %t/plugins \ +// RUN: %t/src/MacroLib.swift + +// RUN: %target-swift-frontend \ +// RUN: -typecheck -verify \ +// RUN: -I %t/lib \ +// RUN: -plugin-path %t/plugins \ +// RUN: %t/src/test.swift + +//--- MacroDefinition.swift +import SwiftSyntax +import SwiftSyntaxMacros + +public struct OneMacro: ExpressionMacro { + public static func expansion( + of node: some FreestandingMacroExpansionSyntax, + in context: some MacroExpansionContext + ) throws -> ExprSyntax { + return "1" + } +} + + +//--- MacroLib.swift +@freestanding(expression) public macro `public`() -> Int = #externalMacro(module: "MacroDefinition", type: "OneMacro") +@freestanding(expression) public macro `escaped`() -> Int = #externalMacro(module: "MacroDefinition", type: "OneMacro") +@freestanding(expression) public macro normal() -> Int = #externalMacro(module: "MacroDefinition", type: "OneMacro") + +//--- test.swift +import MacroLib +@freestanding(expression) public macro `class`() -> Int = #externalMacro(module: "MacroDefinition", type: "OneMacro") + +func test() { + let _: Int = #public() // expected-error {{keyword 'public' cannot be used as an identifier here}} expected-note {{if this name is unavoidable, use backticks to escape it}} + let _: Int = #`public`() + let _: Int = #escaped() + let _: Int = #`class`() + let _: Int = #normal() +} diff --git a/test/Macros/macro_self.swift b/test/Macros/macro_self.swift index dce2806fa938a..4e8b39b0fc173 100644 --- a/test/Macros/macro_self.swift +++ b/test/Macros/macro_self.swift @@ -9,11 +9,11 @@ func sync() {} macro Self() = #externalMacro(module: "MacroDefinition", type: "InvalidMacro") func testSelfAsFreestandingMacro() { - _ = #self // expected-error {{expected a macro identifier for a pound literal expression}} + _ = #self // expected-error {{keyword 'self' cannot be used as an identifier here}} expected-note {{use backticks to escape it}} } func testCapitalSelfAsFreestandingMacro() { - _ = #Self // expected-error {{expected a macro identifier for a pound literal expression}} + _ = #Self // expected-error {{keyword 'Self' cannot be used as an identifier here}} expected-note {{use backticks to escape it}} } func testSelfAsAttachedMacro() { diff --git a/test/Parse/line-directive.swift b/test/Parse/line-directive.swift index fcf6df3f2d45e..fd0675bb75909 100644 --- a/test/Parse/line-directive.swift +++ b/test/Parse/line-directive.swift @@ -30,8 +30,8 @@ public struct S { // expected-error@+5{{operators must have one or two arguments}} // expected-error@+4{{member operator '/()' must have at least one argument of type 'S'}} // expected-error@+3{{expected '{' in body of function declaration}} -// expected-error@+2 2 {{consecutive declarations on a line must be separated by ';}} -// expected-error@+1 2 {{expected a macro identifier}} +// expected-error@+2 {{consecutive declarations on a line must be separated by ';}} +// expected-error@+1 {{expected a macro identifier}} / ###line 25 "line-directive.swift" } // expected-error@+1{{#line directive was renamed to #sourceLocation}} diff --git a/test/Parse/macro_decl.swift b/test/Parse/macro_decl.swift index 26767524bf435..3fc66878c1f8d 100644 --- a/test/Parse/macro_decl.swift +++ b/test/Parse/macro_decl.swift @@ -16,6 +16,11 @@ extension String { #memberwiseInit(flavor: .chocolate, haha: true) { "abc" } + @available(macOS 999, *) + public #memberwiseInit() + + static internal #memberwiseInit + struct Foo { #memberwiseInit @@ -26,3 +31,26 @@ extension String { // expected-error @+1 {{expected a macro identifier for a pound literal declaration}} #() } + +@RandomAttr #someFunc + +public #someFunc + +#someFunc + +func test() { + @discardableResult #someFunc + + dynamic #someFunc + + @CustomAttr + isolated #someFunc +} + +public # someFunc // expected-error {{extraneous whitespace between '#' and macro name is not permitted}} {{9-10=}} + +struct S { + # someFunc // expected-error {{extraneous whitespace between '#' and macro name is not permitted}} {{4-5=}} + #class // expected-error {{keyword 'class' cannot be used as an identifier here}} expected-note {{if this name is unavoidable, use backticks to escape it}} {{4-9=`class`}} + # struct Inner {} // expected-error {{expected a macro identifier for a pound literal declaration}} expected-error {{consecutive declarations on a line}} +} diff --git a/test/Parse/macro_expr.swift b/test/Parse/macro_expr.swift index 235502c091a25..3e9a6d061931a 100644 --- a/test/Parse/macro_expr.swift +++ b/test/Parse/macro_expr.swift @@ -35,3 +35,17 @@ _ = #another { // expected-error @+1 {{expected a macro identifier for a pound literal expression}} _ = #() + +do { + _ = # // expected-error {{expected a macro identifier for a pound literal expression}} + name() +} +do { + _ = # macro() // expected-error {{extraneous whitespace between '#' and macro name is not permitted}} {{8-9=}} +} +do { + _ = #public() // expected-error {{keyword 'public' cannot be used as an identifier here}} expected-note {{if this name is unavoidable, use backticks to escape it}} {{8-14=`public`}} +} +do { + _ = # public() // expected-error {{expected a macro identifier for a pound literal expression}} +} diff --git a/test/Parse/object_literals.swift b/test/Parse/object_literals.swift index 76171ff64e671..da567e499b35a 100644 --- a/test/Parse/object_literals.swift +++ b/test/Parse/object_literals.swift @@ -4,5 +4,4 @@ let _ = #notAPound // expected-error {{no macro named 'notAPound'}} let _ = #notAPound(1, 2) // expected-error {{no macro named 'notAPound'}} let _ = #Color // expected-error {{no macro named 'Color'}} -let _ = [##] // expected-error 2 {{expected a macro identifier}} {{none}} -// expected-error @-1 {{consecutive statements on a line must be separated by ';'}} +let _ = [##] // expected-error {{expected a macro identifier}} {{none}} diff --git a/test/Parse/operator_decl.swift b/test/Parse/operator_decl.swift index cbe3945a64277..1daec13369d74 100644 --- a/test/Parse/operator_decl.swift +++ b/test/Parse/operator_decl.swift @@ -52,7 +52,6 @@ infix operator aa--: A // expected-error {{'aa' is considered an identifier and infix operator <<$$@< // expected-error {{'$$' is considered an identifier and must not appear within an operator name}} infix operator !!@aa // expected-error {{'@' is not allowed in operator names}} infix operator #++= // expected-error {{'#' is not allowed in operator names}} -// expected-error@-1 {{expected a macro identifier}} infix operator ++=# // expected-error {{'#' is not allowed in operator names}} infix operator -># // expected-error {{'#' is not allowed in operator names}} @@ -61,7 +60,6 @@ infix operator -># // expected-error {{'#' is not allowed in operator names}} infix operator =#= // expected-error@-1 {{'#' is not allowed in operator names}} // expected-error@-2 {{'=' must have consistent whitespace on both sides}} -// expected-error@-3 {{expected a macro identifier}} infix operator +++= infix operator *** : A diff --git a/test/Parse/raw_string_errors.swift b/test/Parse/raw_string_errors.swift index b9feab1b377b6..a7ae88b0b2c7f 100644 --- a/test/Parse/raw_string_errors.swift +++ b/test/Parse/raw_string_errors.swift @@ -12,7 +12,7 @@ let _ = #"\##("invalid")"# let _ = ###"""invalid"###### // expected-error@-1{{too many '#' characters in closing delimiter}}{{26-29=}} // expected-error@-2{{consecutive statements on a line must be separated by ';'}} -// expected-error@-3 3 {{expected a macro identifier}} +// expected-error@-3{{expected a macro identifier}} let _ = ####"invalid"### // expected-error@-1{{unterminated string literal}} @@ -20,7 +20,7 @@ let _ = ####"invalid"### let _ = ###"invalid"###### // expected-error@-1{{too many '#' characters in closing delimiter}}{{24-27=}} // expected-error@-2{{consecutive statements on a line must be separated by ';'}} -// expected-error@-3 3 {{expected a macro identifier}} +// expected-error@-3{{expected a macro identifier}} let _ = ##"""aa foobar diff --git a/test/StringProcessing/Frontend/disable-flag.swift b/test/StringProcessing/Frontend/disable-flag.swift index 5c9c237d04357..a9a9d41042177 100644 --- a/test/StringProcessing/Frontend/disable-flag.swift +++ b/test/StringProcessing/Frontend/disable-flag.swift @@ -9,6 +9,6 @@ _ = /x/ // expected-error@-2 {{cannot find 'x' in scope}} // expected-error@-3 {{'/' is not a postfix unary operator}} -_ = #/x/# // expected-error 2 {{expected a macro identifier}} +_ = #/x/# // expected-error {{expected a macro identifier}} func foo(_ x: Regex) {} // expected-error {{cannot find type 'Regex' in scope}} diff --git a/test/decl/import/import.swift b/test/decl/import/import.swift index 23ee6dddb1e1f..6f7c10f537fcf 100644 --- a/test/decl/import/import.swift +++ b/test/decl/import/import.swift @@ -34,7 +34,10 @@ func f1(_ a: Swift.Int) -> Swift.Void { print(a) } import func Swift.print // rdar://14418336 -#import something_nonexistent // expected-error {{expected a macro identifier}} expected-error {{no such module 'something_nonexistent'}} +#import something_nonexistent +// expected-error@-1 {{keyword 'import' cannot be used as an identifier here}} expected-note@-1 {{use backticks to escape it}} +// expected-error@-2 {{no macro named 'import'}} +// expected-error@-3 {{consecutive statements on a line}} expected-error@-3 {{cannot find 'something_nonexistent' in scope}} // Import specific decls import typealias Swift.Int diff --git a/tools/swift-plugin-server/CMakeLists.txt b/tools/swift-plugin-server/CMakeLists.txt index aed4400e50e0c..09332309b10c9 100644 --- a/tools/swift-plugin-server/CMakeLists.txt +++ b/tools/swift-plugin-server/CMakeLists.txt @@ -18,6 +18,7 @@ if (SWIFT_SWIFT_PARSER) $ SWIFT_DEPENDENCIES SwiftSyntax::SwiftSyntaxMacros + SwiftSyntax::SwiftSyntaxMacroExpansion SwiftSyntax::SwiftCompilerPluginMessageHandling swiftLLVMJSON )