Skip to content

Commit 4057450

Browse files
committed
Add a convenience function to create a FunctionParameter from source
1 parent 56bbacf commit 4057450

File tree

3 files changed

+138
-117
lines changed

3 files changed

+138
-117
lines changed

Sources/SwiftParser/Declarations.swift

Lines changed: 100 additions & 95 deletions
Original file line numberDiff line numberDiff line change
@@ -909,7 +909,6 @@ extension Parser {
909909
}
910910

911911
extension Parser {
912-
@_spi(RawSyntax)
913912
public enum ParameterSubject {
914913
case closure
915914
case enumCase
@@ -946,6 +945,103 @@ extension Parser {
946945
}
947946
}
948947

948+
@_spi(RawSyntax)
949+
public mutating func parseFunctionParameter(for subject: ParameterSubject) -> RawFunctionParameterSyntax {
950+
// Parse any declaration attributes. The exception here is enum cases
951+
// which only allow types, so we do not consume attributes to allow the
952+
// type attribute grammar a chance to examine them.
953+
let attrs: RawAttributeListSyntax?
954+
if case .enumCase = subject {
955+
attrs = nil
956+
} else {
957+
attrs = self.parseAttributeList()
958+
}
959+
960+
let modifiers = parseParameterModifiers(for: subject)
961+
962+
var misplacedSpecifiers: [RawTokenSyntax] = []
963+
while let specifier = self.consume(ifAnyIn: TypeSpecifier.self) {
964+
misplacedSpecifiers.append(specifier)
965+
}
966+
967+
let unexpectedBeforeFirstName: RawUnexpectedNodesSyntax?
968+
let firstName: RawTokenSyntax?
969+
let unexpectedBeforeSecondName: RawUnexpectedNodesSyntax?
970+
let secondName: RawTokenSyntax?
971+
let unexpectedBeforeColon: RawUnexpectedNodesSyntax?
972+
let colon: RawTokenSyntax?
973+
let shouldParseType: Bool
974+
975+
if self.withLookahead({ $0.startsParameterName(isClosure: subject.isClosure, allowMisplacedSpecifierRecovery: false) }) {
976+
if self.currentToken.canBeArgumentLabel(allowDollarIdentifier: true) {
977+
(unexpectedBeforeFirstName, firstName) = self.parseArgumentLabel()
978+
} else {
979+
unexpectedBeforeFirstName = nil
980+
firstName = nil
981+
}
982+
983+
if self.currentToken.canBeArgumentLabel(allowDollarIdentifier: true) {
984+
(unexpectedBeforeSecondName, secondName) = self.parseArgumentLabel()
985+
} else {
986+
unexpectedBeforeSecondName = nil
987+
secondName = nil
988+
}
989+
if subject.isClosure {
990+
unexpectedBeforeColon = nil
991+
colon = self.consume(if: .colon)
992+
shouldParseType = (colon != nil)
993+
} else {
994+
(unexpectedBeforeColon, colon) = self.expect(.colon)
995+
shouldParseType = true
996+
}
997+
} else {
998+
unexpectedBeforeFirstName = nil
999+
firstName = nil
1000+
unexpectedBeforeSecondName = nil
1001+
secondName = nil
1002+
unexpectedBeforeColon = nil
1003+
colon = nil
1004+
shouldParseType = true
1005+
}
1006+
1007+
let type: RawTypeSyntax?
1008+
if shouldParseType {
1009+
type = self.parseType(misplacedSpecifiers: misplacedSpecifiers)
1010+
} else {
1011+
type = nil
1012+
}
1013+
1014+
let ellipsis: RawTokenSyntax?
1015+
if self.atContextualPunctuator("...") {
1016+
ellipsis = self.consumeAnyToken(remapping: .ellipsis)
1017+
} else {
1018+
ellipsis = nil
1019+
}
1020+
1021+
let defaultArgument: RawInitializerClauseSyntax?
1022+
if self.at(.equal) {
1023+
defaultArgument = self.parseDefaultArgument()
1024+
} else {
1025+
defaultArgument = nil
1026+
}
1027+
1028+
let trailingComma = self.consume(if: .comma)
1029+
return RawFunctionParameterSyntax(
1030+
attributes: attrs,
1031+
modifiers: modifiers,
1032+
RawUnexpectedNodesSyntax(misplacedSpecifiers.map(RawSyntax.init) + (unexpectedBeforeFirstName?.elements ?? []), arena: self.arena),
1033+
firstName: firstName,
1034+
unexpectedBeforeSecondName,
1035+
secondName: secondName,
1036+
unexpectedBeforeColon,
1037+
colon: colon,
1038+
type: type,
1039+
ellipsis: ellipsis,
1040+
defaultArgument: defaultArgument,
1041+
trailingComma: trailingComma,
1042+
arena: self.arena)
1043+
}
1044+
9491045
@_spi(RawSyntax)
9501046
public mutating func parseParameterClause(for subject: ParameterSubject) -> RawParameterClauseSyntax {
9511047
let (unexpectedBeforeLParen, lparen) = self.expect(.leftParen)
@@ -959,100 +1055,9 @@ extension Parser {
9591055
while !self.at(any: [.eof, .rightParen])
9601056
&& keepGoing
9611057
&& loopProgress.evaluate(currentToken) {
962-
// Parse any declaration attributes. The exception here is enum cases
963-
// which only allow types, so we do not consume attributes to allow the
964-
// type attribute grammar a chance to examine them.
965-
let attrs: RawAttributeListSyntax?
966-
if case .enumCase = subject {
967-
attrs = nil
968-
} else {
969-
attrs = self.parseAttributeList()
970-
}
971-
972-
let modifiers = parseParameterModifiers(for: subject)
973-
974-
var misplacedSpecifiers: [RawTokenSyntax] = []
975-
while let specifier = self.consume(ifAnyIn: TypeSpecifier.self) {
976-
misplacedSpecifiers.append(specifier)
977-
}
978-
979-
let unexpectedBeforeFirstName: RawUnexpectedNodesSyntax?
980-
let firstName: RawTokenSyntax?
981-
let unexpectedBeforeSecondName: RawUnexpectedNodesSyntax?
982-
let secondName: RawTokenSyntax?
983-
let unexpectedBeforeColon: RawUnexpectedNodesSyntax?
984-
let colon: RawTokenSyntax?
985-
let shouldParseType: Bool
986-
987-
if self.withLookahead({ $0.startsParameterName(isClosure: subject.isClosure, allowMisplacedSpecifierRecovery: false) }) {
988-
if self.currentToken.canBeArgumentLabel(allowDollarIdentifier: true) {
989-
(unexpectedBeforeFirstName, firstName) = self.parseArgumentLabel()
990-
} else {
991-
unexpectedBeforeFirstName = nil
992-
firstName = nil
993-
}
994-
995-
if self.currentToken.canBeArgumentLabel(allowDollarIdentifier: true) {
996-
(unexpectedBeforeSecondName, secondName) = self.parseArgumentLabel()
997-
} else {
998-
unexpectedBeforeSecondName = nil
999-
secondName = nil
1000-
}
1001-
if subject.isClosure {
1002-
unexpectedBeforeColon = nil
1003-
colon = self.consume(if: .colon)
1004-
shouldParseType = (colon != nil)
1005-
} else {
1006-
(unexpectedBeforeColon, colon) = self.expect(.colon)
1007-
shouldParseType = true
1008-
}
1009-
} else {
1010-
unexpectedBeforeFirstName = nil
1011-
firstName = nil
1012-
unexpectedBeforeSecondName = nil
1013-
secondName = nil
1014-
unexpectedBeforeColon = nil
1015-
colon = nil
1016-
shouldParseType = true
1017-
}
1018-
1019-
let type: RawTypeSyntax?
1020-
if shouldParseType {
1021-
type = self.parseType(misplacedSpecifiers: misplacedSpecifiers)
1022-
} else {
1023-
type = nil
1024-
}
1025-
1026-
let ellipsis: RawTokenSyntax?
1027-
if self.atContextualPunctuator("...") {
1028-
ellipsis = self.consumeAnyToken(remapping: .ellipsis)
1029-
} else {
1030-
ellipsis = nil
1031-
}
1032-
1033-
let defaultArgument: RawInitializerClauseSyntax?
1034-
if self.at(.equal) {
1035-
defaultArgument = self.parseDefaultArgument()
1036-
} else {
1037-
defaultArgument = nil
1038-
}
1039-
1040-
let trailingComma = self.consume(if: .comma)
1041-
keepGoing = trailingComma != nil
1042-
elements.append(RawFunctionParameterSyntax(
1043-
attributes: attrs,
1044-
modifiers: modifiers,
1045-
RawUnexpectedNodesSyntax(misplacedSpecifiers.map(RawSyntax.init) + (unexpectedBeforeFirstName?.elements ?? []), arena: self.arena),
1046-
firstName: firstName,
1047-
unexpectedBeforeSecondName,
1048-
secondName: secondName,
1049-
unexpectedBeforeColon,
1050-
colon: colon,
1051-
type: type,
1052-
ellipsis: ellipsis,
1053-
defaultArgument: defaultArgument,
1054-
trailingComma: trailingComma,
1055-
arena: self.arena))
1058+
let parameter = parseFunctionParameter(for: subject)
1059+
keepGoing = parameter.trailingComma != nil
1060+
elements.append(parameter)
10561061
}
10571062
}
10581063
let (unexpectedBeforeRParen, rparen) = self.expect(.rightParen)

Sources/SwiftSyntaxBuilder/ConvenienceInitializers.swift

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@
1111
//===----------------------------------------------------------------------===//
1212

1313
import Foundation
14-
import SwiftSyntax
14+
@_spi(RawSyntax) import SwiftParser
15+
@_spi(RawSyntax) import SwiftSyntax
1516

1617
extension BinaryOperatorExpr {
1718
public init(text: String) {
@@ -113,6 +114,17 @@ extension FunctionCallExpr {
113114
}
114115
}
115116

117+
extension FunctionParameter {
118+
public init(
119+
_ source: String,
120+
for subject: Parser.ParameterSubject
121+
) {
122+
self = try! performParse(source: Array(source.utf8), parse: {
123+
$0.parseFunctionParameter(for: subject).syntax
124+
})
125+
}
126+
}
127+
116128
extension IfStmt {
117129
/// A convenience initializer that uses builder closures to express an
118130
/// if body, potentially with a second trailing builder closure for an else

Sources/SwiftSyntaxBuilder/Syntax+StringInterpolation.swift

Lines changed: 25 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,30 @@ import SwiftParserDiagnostics
1616
import SwiftDiagnostics
1717
import SwiftBasicFormat
1818

19+
func performParse<SyntaxType: SyntaxProtocol>(source: [UInt8], parse: (inout Parser) throws -> SyntaxType) throws -> SyntaxType {
20+
return try source.withUnsafeBufferPointer { buffer in
21+
var parser = Parser(buffer)
22+
// FIXME: When the parser supports incremental parsing, put the
23+
// interpolatedSyntaxNodes in so we don't have to parse them again.
24+
return try parser.arena.assumingSingleThread {
25+
let result = try parse(&parser)
26+
if !parser.at(.eof) {
27+
var remainingTokens: [TokenSyntax] = []
28+
while !parser.at(.eof) {
29+
remainingTokens.append(parser.consumeAnyToken().syntax)
30+
}
31+
throw SyntaxStringInterpolationError.didNotConsumeAllTokens(remainingTokens: remainingTokens)
32+
}
33+
if result.hasError {
34+
let diagnostics = ParseDiagnosticsGenerator.diagnostics(for: result)
35+
assert(!diagnostics.isEmpty)
36+
throw SyntaxStringInterpolationError.diagnostics(diagnostics, tree: Syntax(result))
37+
}
38+
return result
39+
}
40+
}
41+
}
42+
1943
/// An individual interpolated syntax node.
2044
struct InterpolatedSyntaxNode {
2145
let node: Syntax
@@ -144,27 +168,7 @@ extension SyntaxExpressibleByStringInterpolation {
144168
}
145169

146170
public init(stringInterpolationOrThrow stringInterpolation: SyntaxStringInterpolation) throws {
147-
self = try stringInterpolation.sourceText.withUnsafeBufferPointer { buffer in
148-
var parser = Parser(buffer)
149-
// FIXME: When the parser supports incremental parsing, put the
150-
// interpolatedSyntaxNodes in so we don't have to parse them again.
151-
return try parser.arena.assumingSingleThread {
152-
let result = try Self.parse(from: &parser)
153-
if !parser.at(.eof) {
154-
var remainingTokens: [TokenSyntax] = []
155-
while !parser.at(.eof) {
156-
remainingTokens.append(parser.consumeAnyToken().syntax)
157-
}
158-
throw SyntaxStringInterpolationError.didNotConsumeAllTokens(remainingTokens: remainingTokens)
159-
}
160-
if result.hasError {
161-
let diagnostics = ParseDiagnosticsGenerator.diagnostics(for: result)
162-
assert(!diagnostics.isEmpty)
163-
throw SyntaxStringInterpolationError.diagnostics(diagnostics, tree: Syntax(result))
164-
}
165-
return result
166-
}
167-
}
171+
self = try performParse(source: stringInterpolation.sourceText, parse: Self.parse)
168172
}
169173

170174
@_transparent

0 commit comments

Comments
 (0)