Skip to content

Commit e51d851

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 9c34b1f commit e51d851

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
@@ -178,6 +178,19 @@ public struct Parser {
178178
return _emptyRawAttributeListSyntax!
179179
}
180180

181+
var _emptyRawTypeSpecifierListSyntax: RawTypeSpecifierListSyntax?
182+
183+
/// Create an empty collection of the given type.
184+
///
185+
/// These empty collections are only created once and the same node is returned
186+
/// on subsequent calls, reducing memory usage.
187+
mutating func emptyCollection(_: RawTypeSpecifierListSyntax.Type) -> RawTypeSpecifierListSyntax {
188+
if _emptyRawTypeSpecifierListSyntax == nil {
189+
_emptyRawTypeSpecifierListSyntax = RawTypeSpecifierListSyntax(elements: [], arena: self.arena)
190+
}
191+
return _emptyRawTypeSpecifierListSyntax!
192+
}
193+
181194
/// The delegated initializer for the parser.
182195
///
183196
/// - 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
@@ -177,98 +177,6 @@ extension AsExprSyntax {
177177
}
178178
}
179179

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

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

0 commit comments

Comments
 (0)