Skip to content

Commit 764a55b

Browse files
committed
Allow multiple type specifiers on a type
Having multiple type specifiers on a type is valid in the C++ parser but not in the Swift parser. rdar://118125715
1 parent bdc3df2 commit 764a55b

25 files changed

+678
-200
lines changed

CodeGeneration/Sources/SyntaxSupport/SyntaxNodeKind.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -285,6 +285,8 @@ public enum SyntaxNodeKind: String, CaseIterable {
285285
case typeEffectSpecifiers
286286
case typeExpr
287287
case typeInitializerClause
288+
case typeSpecifier
289+
case typeSpecifierList
288290
case unavailableFromAsyncAttributeArguments
289291
case underscorePrivateAttributeArguments
290292
case unexpectedNodes

CodeGeneration/Sources/SyntaxSupport/TypeNodes.swift

Lines changed: 27 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -35,12 +35,10 @@ public let TYPE_NODES: [Node] = [
3535
),
3636

3737
Node(
38-
kind: .attributedType,
39-
base: .type,
40-
nameForDiagnostics: "type",
41-
traits: [
42-
"WithAttributes"
43-
],
38+
kind: .typeSpecifier,
39+
base: .syntax,
40+
nameForDiagnostics: nil,
41+
documentation: "A specifier that can be attached to a type to eg. mark a parameter as `inout` or `consuming`",
4442
children: [
4543
Child(
4644
name: "specifier",
@@ -54,8 +52,29 @@ public let TYPE_NODES: [Node] = [
5452
.keyword(.consuming),
5553
.keyword(.transferring),
5654
.keyword(._resultDependsOn),
57-
]),
58-
isOptional: true
55+
])
56+
)
57+
]
58+
),
59+
60+
Node(
61+
kind: .typeSpecifierList,
62+
base: .syntaxCollection,
63+
nameForDiagnostics: nil,
64+
elementChoices: [.typeSpecifier]
65+
),
66+
67+
Node(
68+
kind: .attributedType,
69+
base: .type,
70+
nameForDiagnostics: "type",
71+
traits: [
72+
"WithAttributes"
73+
],
74+
children: [
75+
Child(
76+
name: "specifiers",
77+
kind: .collection(kind: .typeSpecifierList, collectionElementName: "Specifier", defaultsToEmpty: true)
5978
),
6079
Child(
6180
name: "attributes",

Release Notes/600.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,10 @@
6262
- Description: `IncrementalEdit` used to store the range that was replaced and the length of the replacement but not the replacement bytes by itself. `IncrementalEdit` now has a `replacement` property that contains the replacement bytes.
6363
- Pull Request: https://github.com/apple/swift-syntax/pull/2527
6464

65+
- `TypeSpecifierListSyntax` and `TypeSpecifierSyntax`
66+
- Description: `AttributedTypeSyntax` can now contain multiple specifiers and these types are used to model the list of specifiers.
67+
- Pull request: https://github.com/apple/swift-syntax/pull/2433
68+
6569
## API Behavior Changes
6670

6771
## Deprecations
@@ -87,6 +91,10 @@
8791
- Description: `EditorPlaceholderDeclSyntax` and `EditorPlaceholderExprSyntax` are now deprecated and placeholders are instead parsed as identifiers within a `MissingDeclSyntax` or `DeclReferenceExprSyntax`.
8892
- Pull request: https://github.com/apple/swift-syntax/pull/2237
8993

94+
- `AttributedTypeSyntax.specifier` has renamed and changed to be a collection
95+
- Description: Types can have multiple specifiers now and the syntax tree has been modified to reflect that.
96+
- Pull request: https://github.com/apple/swift-syntax/pull/2433
97+
9098
## API-Incompatible Changes
9199

92100
- `MacroDefinition` used for expanding macros:

Sources/SwiftParser/Parser.swift

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,19 @@ public struct Parser {
174174
return _emptyRawAttributeListSyntax!
175175
}
176176

177+
var _emptyRawTypeSpecifierListSyntax: RawTypeSpecifierListSyntax?
178+
179+
/// Create an empty collection of the given type.
180+
///
181+
/// These empty collections are only created once and the same node is returned
182+
/// on subsequent calls, reducing memory usage.
183+
mutating func emptyCollection(_: RawTypeSpecifierListSyntax.Type) -> RawTypeSpecifierListSyntax {
184+
if _emptyRawTypeSpecifierListSyntax == nil {
185+
_emptyRawTypeSpecifierListSyntax = RawTypeSpecifierListSyntax(elements: [], arena: self.arena)
186+
}
187+
return _emptyRawTypeSpecifierListSyntax!
188+
}
189+
177190
/// The delegated initializer for the parser.
178191
///
179192
/// - Parameters

Sources/SwiftParser/Types.swift

Lines changed: 35 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ extension Parser {
3131
}
3232

3333
mutating func parseTypeScalar(misplacedSpecifiers: [RawTokenSyntax] = []) -> RawTypeSyntax {
34-
let (specifier, unexpectedBeforeAttrList, attrList) = self.parseTypeAttributeList(misplacedSpecifiers: misplacedSpecifiers)
34+
let specifiersAndAttributes = self.parseTypeAttributeList(misplacedSpecifiers: misplacedSpecifiers)
3535
var base = RawTypeSyntax(self.parseSimpleOrCompositionType())
3636
if self.withLookahead({ $0.atFunctionTypeArrow() }) {
3737
var effectSpecifiers = self.parseTypeEffectSpecifiers()
@@ -88,12 +88,11 @@ extension Parser {
8888
)
8989
}
9090

91-
if unexpectedBeforeAttrList != nil || specifier != nil || !attrList.isEmpty {
91+
if let specifiersAndAttributes {
9292
return RawTypeSyntax(
9393
RawAttributedTypeSyntax(
94-
specifier: specifier,
95-
unexpectedBeforeAttrList,
96-
attributes: attrList,
94+
specifiers: specifiersAndAttributes.specifiers,
95+
attributes: specifiersAndAttributes.attributes,
9796
baseType: base,
9897
arena: self.arena
9998
)
@@ -891,35 +890,44 @@ extension Parser.Lookahead {
891890
}
892891

893892
extension Parser {
894-
mutating func parseTypeAttributeList(misplacedSpecifiers: [RawTokenSyntax] = []) -> (
895-
specifier: RawTokenSyntax?, unexpectedBeforeAttributes: RawUnexpectedNodesSyntax?, attributes: RawAttributeListSyntax
896-
) {
897-
var specifier: RawTokenSyntax? = nil
898-
if canHaveParameterSpecifier {
899-
specifier = self.consume(ifAnyIn: TypeSpecifier.self)
893+
mutating func parseTypeAttributeList(
894+
misplacedSpecifiers: [RawTokenSyntax] = []
895+
) -> (
896+
specifiers: RawTypeSpecifierListSyntax,
897+
attributes: RawAttributeListSyntax
898+
)? {
899+
var specifiers: [RawTypeSpecifierSyntax] = []
900+
while canHaveParameterSpecifier, let specifier = self.consume(ifAnyIn: TypeSpecifierSyntax.SpecifierOptions.self) {
901+
specifiers.append(RawTypeSpecifierSyntax(specifier: specifier, arena: arena))
900902
}
901-
// We can only stick one specifier on this type. Let's pick the first one
902-
if specifier == nil, let misplacedSpecifier = misplacedSpecifiers.first {
903-
specifier = missingToken(misplacedSpecifier.tokenKind, text: misplacedSpecifier.tokenText)
904-
}
905-
var extraneousSpecifiers: [RawTokenSyntax] = []
906-
907-
while canHaveParameterSpecifier,
908-
let extraSpecifier = self.consume(ifAnyIn: AttributedTypeSyntax.SpecifierOptions.self)
909-
{
910-
if specifier == nil {
911-
specifier = extraSpecifier
912-
} else {
913-
extraneousSpecifiers.append(extraSpecifier)
903+
if !misplacedSpecifiers.isEmpty {
904+
specifiers += misplacedSpecifiers.map {
905+
RawTypeSpecifierSyntax(specifier: missingToken($0.tokenKind, text: $0.tokenText), arena: arena)
914906
}
915907
}
916-
let unexpectedBeforeAttributeList = RawUnexpectedNodesSyntax(extraneousSpecifiers, arena: self.arena)
917908

909+
let attributes: RawAttributeListSyntax?
918910
if self.at(.atSign) {
919-
return (specifier, unexpectedBeforeAttributeList, self.parseTypeAttributeListPresent())
911+
attributes = self.parseTypeAttributeListPresent()
912+
} else {
913+
attributes = nil
920914
}
921915

922-
return (specifier, unexpectedBeforeAttributeList, self.emptyCollection(RawAttributeListSyntax.self))
916+
guard !specifiers.isEmpty || attributes != nil else {
917+
// No specifiers or attributes on this type
918+
return nil
919+
}
920+
let specifierList: RawTypeSpecifierListSyntax
921+
if specifiers.isEmpty {
922+
specifierList = self.emptyCollection(RawTypeSpecifierListSyntax.self)
923+
} else {
924+
specifierList = RawTypeSpecifierListSyntax(elements: specifiers, arena: arena)
925+
}
926+
927+
return (
928+
specifierList,
929+
attributes ?? self.emptyCollection(RawAttributeListSyntax.self)
930+
)
923931
}
924932

925933
mutating func parseTypeAttributeListPresent() -> RawAttributeListSyntax {

Sources/SwiftParser/generated/Parser+TokenSpecSet.swift

Lines changed: 92 additions & 92 deletions
Original file line numberDiff line numberDiff line change
@@ -173,98 +173,6 @@ extension AsExprSyntax {
173173
}
174174
}
175175

176-
extension AttributedTypeSyntax {
177-
@_spi(Diagnostics)
178-
public enum SpecifierOptions: TokenSpecSet {
179-
case `inout`
180-
case __shared
181-
case __owned
182-
case isolated
183-
case _const
184-
case borrowing
185-
case consuming
186-
@_spi(ExperimentalLanguageFeatures)
187-
case transferring
188-
@_spi(ExperimentalLanguageFeatures)
189-
case _resultDependsOn
190-
191-
init?(lexeme: Lexer.Lexeme, experimentalFeatures: Parser.ExperimentalFeatures) {
192-
switch PrepareForKeywordMatch(lexeme) {
193-
case TokenSpec(.inout):
194-
self = .inout
195-
case TokenSpec(.__shared):
196-
self = .__shared
197-
case TokenSpec(.__owned):
198-
self = .__owned
199-
case TokenSpec(.isolated):
200-
self = .isolated
201-
case TokenSpec(._const):
202-
self = ._const
203-
case TokenSpec(.borrowing):
204-
self = .borrowing
205-
case TokenSpec(.consuming):
206-
self = .consuming
207-
case TokenSpec(.transferring) where experimentalFeatures.contains(.transferringArgsAndResults):
208-
self = .transferring
209-
case TokenSpec(._resultDependsOn) where experimentalFeatures.contains(.nonescapableTypes):
210-
self = ._resultDependsOn
211-
default:
212-
return nil
213-
}
214-
}
215-
216-
var spec: TokenSpec {
217-
switch self {
218-
case .inout:
219-
return .keyword(.inout)
220-
case .__shared:
221-
return .keyword(.__shared)
222-
case .__owned:
223-
return .keyword(.__owned)
224-
case .isolated:
225-
return .keyword(.isolated)
226-
case ._const:
227-
return .keyword(._const)
228-
case .borrowing:
229-
return .keyword(.borrowing)
230-
case .consuming:
231-
return .keyword(.consuming)
232-
case .transferring:
233-
return .keyword(.transferring)
234-
case ._resultDependsOn:
235-
return .keyword(._resultDependsOn)
236-
}
237-
}
238-
239-
/// Returns a token that satisfies the `TokenSpec` of this case.
240-
///
241-
/// If the token kind of this spec has variable text, e.g. for an identifier, this returns a token with empty text.
242-
@_spi(Diagnostics)
243-
public var tokenSyntax: TokenSyntax {
244-
switch self {
245-
case .inout:
246-
return .keyword(.inout)
247-
case .__shared:
248-
return .keyword(.__shared)
249-
case .__owned:
250-
return .keyword(.__owned)
251-
case .isolated:
252-
return .keyword(.isolated)
253-
case ._const:
254-
return .keyword(._const)
255-
case .borrowing:
256-
return .keyword(.borrowing)
257-
case .consuming:
258-
return .keyword(.consuming)
259-
case .transferring:
260-
return .keyword(.transferring)
261-
case ._resultDependsOn:
262-
return .keyword(._resultDependsOn)
263-
}
264-
}
265-
}
266-
}
267-
268176
extension AvailabilityConditionSyntax {
269177
@_spi(Diagnostics)
270178
public enum AvailabilityKeywordOptions: TokenSpecSet {
@@ -2956,6 +2864,98 @@ extension TupleTypeElementSyntax {
29562864
}
29572865
}
29582866

2867+
extension TypeSpecifierSyntax {
2868+
@_spi(Diagnostics)
2869+
public enum SpecifierOptions: TokenSpecSet {
2870+
case `inout`
2871+
case __shared
2872+
case __owned
2873+
case isolated
2874+
case _const
2875+
case borrowing
2876+
case consuming
2877+
@_spi(ExperimentalLanguageFeatures)
2878+
case transferring
2879+
@_spi(ExperimentalLanguageFeatures)
2880+
case _resultDependsOn
2881+
2882+
init?(lexeme: Lexer.Lexeme, experimentalFeatures: Parser.ExperimentalFeatures) {
2883+
switch PrepareForKeywordMatch(lexeme) {
2884+
case TokenSpec(.inout):
2885+
self = .inout
2886+
case TokenSpec(.__shared):
2887+
self = .__shared
2888+
case TokenSpec(.__owned):
2889+
self = .__owned
2890+
case TokenSpec(.isolated):
2891+
self = .isolated
2892+
case TokenSpec(._const):
2893+
self = ._const
2894+
case TokenSpec(.borrowing):
2895+
self = .borrowing
2896+
case TokenSpec(.consuming):
2897+
self = .consuming
2898+
case TokenSpec(.transferring) where experimentalFeatures.contains(.transferringArgsAndResults):
2899+
self = .transferring
2900+
case TokenSpec(._resultDependsOn) where experimentalFeatures.contains(.nonescapableTypes):
2901+
self = ._resultDependsOn
2902+
default:
2903+
return nil
2904+
}
2905+
}
2906+
2907+
var spec: TokenSpec {
2908+
switch self {
2909+
case .inout:
2910+
return .keyword(.inout)
2911+
case .__shared:
2912+
return .keyword(.__shared)
2913+
case .__owned:
2914+
return .keyword(.__owned)
2915+
case .isolated:
2916+
return .keyword(.isolated)
2917+
case ._const:
2918+
return .keyword(._const)
2919+
case .borrowing:
2920+
return .keyword(.borrowing)
2921+
case .consuming:
2922+
return .keyword(.consuming)
2923+
case .transferring:
2924+
return .keyword(.transferring)
2925+
case ._resultDependsOn:
2926+
return .keyword(._resultDependsOn)
2927+
}
2928+
}
2929+
2930+
/// Returns a token that satisfies the `TokenSpec` of this case.
2931+
///
2932+
/// If the token kind of this spec has variable text, e.g. for an identifier, this returns a token with empty text.
2933+
@_spi(Diagnostics)
2934+
public var tokenSyntax: TokenSyntax {
2935+
switch self {
2936+
case .inout:
2937+
return .keyword(.inout)
2938+
case .__shared:
2939+
return .keyword(.__shared)
2940+
case .__owned:
2941+
return .keyword(.__owned)
2942+
case .isolated:
2943+
return .keyword(.isolated)
2944+
case ._const:
2945+
return .keyword(._const)
2946+
case .borrowing:
2947+
return .keyword(.borrowing)
2948+
case .consuming:
2949+
return .keyword(.consuming)
2950+
case .transferring:
2951+
return .keyword(.transferring)
2952+
case ._resultDependsOn:
2953+
return .keyword(._resultDependsOn)
2954+
}
2955+
}
2956+
}
2957+
}
2958+
29592959
extension UnresolvedAsExprSyntax {
29602960
@_spi(Diagnostics)
29612961
public enum QuestionOrExclamationMarkOptions: TokenSpecSet {

0 commit comments

Comments
 (0)