Skip to content

Commit 47b505e

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 4265ac0 commit 47b505e

25 files changed

+750
-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/511.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,10 @@
3232
- Description: The `throwsSpecifier` for the effects nodes (`AccessorEffectSpecifiers`, `FunctionEffectSpecifiers`, `TypeEffectSpecifiers`, `EffectSpecifiers`) has been replaced with `throwsClause`, which captures both the throws specifier and the (optional) thrown error type, as introduced by SE-0413.
3333
- Pull Request: https://github.com/apple/swift-syntax/pull/2379
3434

35+
- `TypeSpecifierListSyntax` and `TypeSpecifierSyntax`
36+
- Description: `AttributedTypeSyntax` can now contain multiple specifiers and these types are used to model the list of specifiers.
37+
- Pull request: https://github.com/apple/swift-syntax/pull/2433
38+
3539
## API Behavior Changes
3640

3741
## Deprecations
@@ -57,6 +61,10 @@
5761
- Description: `EditorPlaceholderDeclSyntax` and `EditorPlaceholderExprSyntax` are now deprecated and placeholders are instead parsed as identifiers within a `MissingDeclSyntax` or `DeclReferenceExprSyntax`.
5862
- Pull request: https://github.com/apple/swift-syntax/pull/2237
5963

64+
- `AttributedTypeSyntax.specifier` has renamed and changed to be a collection
65+
- Description: Types can have multiple specifiers now and the syntax tree has been modified to reflect that.
66+
- Pull request: https://github.com/apple/swift-syntax/pull/2433
67+
6068
## API-Incompatible Changes
6169

6270
- Effect specifiers:

Sources/SwiftParser/Parser.swift

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,19 @@ public struct Parser {
168168
return _emptyRawAttributeListSyntax!
169169
}
170170

171+
var _emptyRawTypeSpecifierListSyntax: RawTypeSpecifierListSyntax?
172+
173+
/// Create an empty collection of the given type.
174+
///
175+
/// These empty collections are only created once and the same node is returned
176+
/// on subsequent calls, reducing memory usage.
177+
mutating func emptyCollection(_: RawTypeSpecifierListSyntax.Type) -> RawTypeSpecifierListSyntax {
178+
if _emptyRawTypeSpecifierListSyntax == nil {
179+
_emptyRawTypeSpecifierListSyntax = RawTypeSpecifierListSyntax(elements: [], arena: self.arena)
180+
}
181+
return _emptyRawTypeSpecifierListSyntax!
182+
}
183+
171184
/// The delegated initializer for the parser.
172185
///
173186
/// - 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
)
@@ -880,35 +879,44 @@ extension Parser.Lookahead {
880879
}
881880

882881
extension Parser {
883-
mutating func parseTypeAttributeList(misplacedSpecifiers: [RawTokenSyntax] = []) -> (
884-
specifier: RawTokenSyntax?, unexpectedBeforeAttributes: RawUnexpectedNodesSyntax?, attributes: RawAttributeListSyntax
885-
) {
886-
var specifier: RawTokenSyntax? = nil
887-
if canHaveParameterSpecifier {
888-
specifier = self.consume(ifAnyIn: TypeSpecifier.self)
882+
mutating func parseTypeAttributeList(
883+
misplacedSpecifiers: [RawTokenSyntax] = []
884+
) -> (
885+
specifiers: RawTypeSpecifierListSyntax,
886+
attributes: RawAttributeListSyntax
887+
)? {
888+
var specifiers: [RawTypeSpecifierSyntax] = []
889+
while canHaveParameterSpecifier, let specifier = self.consume(ifAnyIn: TypeSpecifierSyntax.SpecifierOptions.self) {
890+
specifiers.append(RawTypeSpecifierSyntax(specifier: specifier, arena: arena))
889891
}
890-
// We can only stick one specifier on this type. Let's pick the first one
891-
if specifier == nil, let misplacedSpecifier = misplacedSpecifiers.first {
892-
specifier = missingToken(misplacedSpecifier.tokenKind, text: misplacedSpecifier.tokenText)
893-
}
894-
var extraneousSpecifiers: [RawTokenSyntax] = []
895-
896-
while canHaveParameterSpecifier,
897-
let extraSpecifier = self.consume(ifAnyIn: AttributedTypeSyntax.SpecifierOptions.self)
898-
{
899-
if specifier == nil {
900-
specifier = extraSpecifier
901-
} else {
902-
extraneousSpecifiers.append(extraSpecifier)
892+
if !misplacedSpecifiers.isEmpty {
893+
specifiers += misplacedSpecifiers.map {
894+
RawTypeSpecifierSyntax(specifier: missingToken($0.tokenKind, text: $0.tokenText), arena: arena)
903895
}
904896
}
905-
let unexpectedBeforeAttributeList = RawUnexpectedNodesSyntax(extraneousSpecifiers, arena: self.arena)
906897

898+
let attributes: RawAttributeListSyntax?
907899
if self.at(.atSign) {
908-
return (specifier, unexpectedBeforeAttributeList, self.parseTypeAttributeListPresent())
900+
attributes = self.parseTypeAttributeListPresent()
901+
} else {
902+
attributes = nil
909903
}
910904

911-
return (specifier, unexpectedBeforeAttributeList, self.emptyCollection(RawAttributeListSyntax.self))
905+
guard !specifiers.isEmpty || attributes != nil else {
906+
// No specifiers or attributes on this type
907+
return nil
908+
}
909+
let specifierList: RawTypeSpecifierListSyntax
910+
if specifiers.isEmpty {
911+
specifierList = self.emptyCollection(RawTypeSpecifierListSyntax.self)
912+
} else {
913+
specifierList = RawTypeSpecifierListSyntax(elements: specifiers, arena: arena)
914+
}
915+
916+
return (
917+
specifierList,
918+
attributes ?? self.emptyCollection(RawAttributeListSyntax.self)
919+
)
912920
}
913921

914922
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 {
@@ -2928,6 +2836,98 @@ extension TupleTypeElementSyntax {
29282836
}
29292837
}
29302838

2839+
extension TypeSpecifierSyntax {
2840+
@_spi(Diagnostics)
2841+
public enum SpecifierOptions: TokenSpecSet {
2842+
case `inout`
2843+
case __shared
2844+
case __owned
2845+
case isolated
2846+
case _const
2847+
case borrowing
2848+
case consuming
2849+
@_spi(ExperimentalLanguageFeatures)
2850+
case transferring
2851+
@_spi(ExperimentalLanguageFeatures)
2852+
case _resultDependsOn
2853+
2854+
init?(lexeme: Lexer.Lexeme, experimentalFeatures: Parser.ExperimentalFeatures) {
2855+
switch PrepareForKeywordMatch(lexeme) {
2856+
case TokenSpec(.inout):
2857+
self = .inout
2858+
case TokenSpec(.__shared):
2859+
self = .__shared
2860+
case TokenSpec(.__owned):
2861+
self = .__owned
2862+
case TokenSpec(.isolated):
2863+
self = .isolated
2864+
case TokenSpec(._const):
2865+
self = ._const
2866+
case TokenSpec(.borrowing):
2867+
self = .borrowing
2868+
case TokenSpec(.consuming):
2869+
self = .consuming
2870+
case TokenSpec(.transferring) where experimentalFeatures.contains(.transferringArgsAndResults):
2871+
self = .transferring
2872+
case TokenSpec(._resultDependsOn) where experimentalFeatures.contains(.nonescapableTypes):
2873+
self = ._resultDependsOn
2874+
default:
2875+
return nil
2876+
}
2877+
}
2878+
2879+
var spec: TokenSpec {
2880+
switch self {
2881+
case .inout:
2882+
return .keyword(.inout)
2883+
case .__shared:
2884+
return .keyword(.__shared)
2885+
case .__owned:
2886+
return .keyword(.__owned)
2887+
case .isolated:
2888+
return .keyword(.isolated)
2889+
case ._const:
2890+
return .keyword(._const)
2891+
case .borrowing:
2892+
return .keyword(.borrowing)
2893+
case .consuming:
2894+
return .keyword(.consuming)
2895+
case .transferring:
2896+
return .keyword(.transferring)
2897+
case ._resultDependsOn:
2898+
return .keyword(._resultDependsOn)
2899+
}
2900+
}
2901+
2902+
/// Returns a token that satisfies the `TokenSpec` of this case.
2903+
///
2904+
/// If the token kind of this spec has variable text, e.g. for an identifier, this returns a token with empty text.
2905+
@_spi(Diagnostics)
2906+
public var tokenSyntax: TokenSyntax {
2907+
switch self {
2908+
case .inout:
2909+
return .keyword(.inout)
2910+
case .__shared:
2911+
return .keyword(.__shared)
2912+
case .__owned:
2913+
return .keyword(.__owned)
2914+
case .isolated:
2915+
return .keyword(.isolated)
2916+
case ._const:
2917+
return .keyword(._const)
2918+
case .borrowing:
2919+
return .keyword(.borrowing)
2920+
case .consuming:
2921+
return .keyword(.consuming)
2922+
case .transferring:
2923+
return .keyword(.transferring)
2924+
case ._resultDependsOn:
2925+
return .keyword(._resultDependsOn)
2926+
}
2927+
}
2928+
}
2929+
}
2930+
29312931
extension UnresolvedAsExprSyntax {
29322932
@_spi(Diagnostics)
29332933
public enum QuestionOrExclamationMarkOptions: TokenSpecSet {

0 commit comments

Comments
 (0)