Skip to content

Commit 1a9bfe7

Browse files
committed
Allow calls to init without leading self. or super.
ASTGen will need to diagnose the missing `self.` or `super.`
1 parent 0663a53 commit 1a9bfe7

File tree

8 files changed

+43
-51
lines changed

8 files changed

+43
-51
lines changed

Sources/SwiftParser/Declarations.swift

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ extension DeclarationModifier {
3131
extension TokenConsumer {
3232
func atStartOfDeclaration(
3333
isAtTopLevel: Bool = false,
34+
allowInitDecl: Bool = true,
3435
allowRecovery: Bool = false
3536
) -> Bool {
3637
if self.at(anyIn: PoundDeclarationStart.self) != nil {
@@ -93,20 +94,22 @@ extension TokenConsumer {
9394
var lookahead = subparser.lookahead()
9495
repeat {
9596
lookahead.consumeAnyToken()
96-
} while lookahead.atStartOfDeclaration()
97+
} while lookahead.atStartOfDeclaration(allowInitDecl: allowInitDecl)
9798
return lookahead.at(.identifier)
9899
case .caseKeyword:
99100
// When 'case' appears inside a function, it's probably a switch
100101
// case, not an enum case declaration.
101102
return false
103+
case .initKeyword:
104+
return allowInitDecl
102105
case .some(_):
103106
// All other decl start keywords unconditonally start a decl.
104107
return true
105108
case nil:
106109
if subparser.at(anyIn: ContextualDeclKeyword.self)?.0 != nil {
107110
subparser.consumeAnyToken()
108111
return subparser.atStartOfDeclaration(
109-
isAtTopLevel: isAtTopLevel, allowRecovery: allowRecovery)
112+
isAtTopLevel: isAtTopLevel, allowInitDecl: allowInitDecl, allowRecovery: allowRecovery)
110113
}
111114
return false
112115
}
@@ -1310,7 +1313,7 @@ extension Parser {
13101313
whereClause = nil
13111314
}
13121315

1313-
let items = self.parseOptionalCodeBlock()
1316+
let items = self.parseOptionalCodeBlock(allowInitDecl: false)
13141317

13151318
return RawInitializerDeclSyntax(
13161319
attributes: attrs.attributes,

Sources/SwiftParser/Diagnostics/MissingNodesError.swift

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -226,12 +226,15 @@ public struct MissingNodesError: ParserError {
226226
/// If applicable, returns a string that describes the node in which the missing nodes are expected.
227227
private func parentContextClause(anchor: Syntax?) -> String? {
228228
// anchorParent is the first parent that has a type name for diagnostics.
229-
guard let anchorParent = anchor?.ancestorOrSelf(where: {
230-
$0.nodeTypeNameForDiagnostics(allowBlockNames: false) != nil
229+
guard let (anchorParent, anchorTypeName) = anchor?.ancestorOrSelf(mapping: { (node: Syntax) -> (Syntax, String)? in
230+
if let name = node.nodeTypeNameForDiagnostics(allowBlockNames: false) {
231+
return (node, name)
232+
} else {
233+
return nil
234+
}
231235
}) else {
232236
return nil
233237
}
234-
let anchorTypeName = anchorParent.nodeTypeNameForDiagnostics(allowBlockNames: false)!
235238
if anchorParent.is(SourceFileSyntax.self) {
236239
return nil
237240
}

Sources/SwiftParser/Diagnostics/ParserDiagnosticMessages.swift

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -224,10 +224,10 @@ public struct SpaceSeparatedIdentifiersError: ParserError {
224224
public let additionalTokens: [TokenSyntax]
225225

226226
public var message: String {
227-
if let anchorParent = firstToken.parent?.ancestorOrSelf(where: {
228-
$0.nodeTypeNameForDiagnostics(allowBlockNames: false) != nil
227+
if let name = firstToken.parent?.ancestorOrSelf(mapping: {
228+
$0.nodeTypeNameForDiagnostics(allowBlockNames: false)
229229
}) {
230-
return "found an unexpected second identifier in \(anchorParent.nodeTypeNameForDiagnostics(allowBlockNames: false)!)"
230+
return "found an unexpected second identifier in \(name)"
231231
} else {
232232
return "found an unexpected second identifier"
233233
}
@@ -266,7 +266,7 @@ public struct UnexpectedNodesError: ParserError {
266266
if let parent = unexpectedNodes.parent {
267267
if let parentTypeName = parent.nodeTypeNameForDiagnostics(allowBlockNames: false), parent.children(viewMode: .sourceAccurate).first?.id == unexpectedNodes.id {
268268
message += " before \(parentTypeName)"
269-
} else if let parentTypeName = parent.ancestorOrSelf(where: { $0.nodeTypeNameForDiagnostics(allowBlockNames: false) != nil })?.nodeTypeNameForDiagnostics(allowBlockNames: false) {
269+
} else if let parentTypeName = parent.ancestorOrSelf(mapping: { $0.nodeTypeNameForDiagnostics(allowBlockNames: false) }) {
270270
message += " in \(parentTypeName)"
271271
}
272272
}

Sources/SwiftParser/Expressions.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1041,7 +1041,7 @@ extension Parser {
10411041
poundDsohandle: tok,
10421042
arena: self.arena
10431043
))
1044-
case (.identifier, let handle)?, (.selfKeyword, let handle)?:
1044+
case (.identifier, let handle)?, (.selfKeyword, let handle)?, (.initKeyword, let handle)?:
10451045
// If we have "case let x." or "case let x(", we parse x as a normal
10461046
// name, not a binding, because it is the start of an enum pattern or
10471047
// call pattern.

Sources/SwiftParser/Names.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ extension Parser {
5454
mutating func parseDeclNameRef(_ flags: DeclNameOptions = []) -> (RawTokenSyntax, RawDeclNameArgumentsSyntax?) {
5555
// Consume the base name.
5656
let ident: RawTokenSyntax
57-
if self.at(.identifier) || self.at(any: [.selfKeyword, .capitalSelfKeyword]) {
57+
if self.at(.identifier) || self.at(any: [.selfKeyword, .capitalSelfKeyword, .initKeyword]) {
5858
ident = self.expectIdentifierWithoutRecovery()
5959
} else if flags.contains(.operators), let (_, _) = self.at(anyIn: Operator.self) {
6060
ident = self.consumeAnyToken(remapping: .unspacedBinaryOperator)

Sources/SwiftParser/RawTokenKindSubset.swift

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -443,13 +443,15 @@ enum IdentifierTokens: RawTokenKindSubset {
443443
case anyKeyword
444444
case capitalSelfKeyword
445445
case identifier
446+
case initKeyword
446447
case selfKeyword
447448

448449
init?(lexeme: Lexer.Lexeme) {
449450
switch lexeme.tokenKind {
450451
case .anyKeyword: self = .anyKeyword
451452
case .capitalSelfKeyword: self = .capitalSelfKeyword
452453
case .identifier: self = .identifier
454+
case .initKeyword: self = .initKeyword
453455
case .selfKeyword: self = .selfKeyword
454456
default: return nil
455457
}
@@ -460,6 +462,7 @@ enum IdentifierTokens: RawTokenKindSubset {
460462
case .anyKeyword: return .anyKeyword
461463
case .capitalSelfKeyword: return .capitalSelfKeyword
462464
case .identifier: return .identifier
465+
case .initKeyword: return .initKeyword
463466
case .selfKeyword: return .selfKeyword
464467
}
465468
}
@@ -743,6 +746,7 @@ enum PrimaryExpressionStart: RawTokenKindSubset {
743746
case falseKeyword
744747
case floatingLiteral
745748
case identifier
749+
case initKeyword
746750
case integerLiteral
747751
case leftBrace
748752
case leftParen
@@ -773,6 +777,7 @@ enum PrimaryExpressionStart: RawTokenKindSubset {
773777
case .falseKeyword: self = .falseKeyword
774778
case .floatingLiteral: self = .floatingLiteral
775779
case .identifier: self = .identifier
780+
case .initKeyword: self = .initKeyword
776781
case .integerLiteral: self = .integerLiteral
777782
case .leftBrace: self = .leftBrace
778783
case .leftParen: self = .leftParen
@@ -806,6 +811,7 @@ enum PrimaryExpressionStart: RawTokenKindSubset {
806811
case .falseKeyword: return .falseKeyword
807812
case .floatingLiteral: return .floatingLiteral
808813
case .identifier: return .identifier
814+
case .initKeyword: return .initKeyword
809815
case .integerLiteral: return .integerLiteral
810816
case .leftBrace: return .leftBrace
811817
case .leftParen: return .leftParen

Sources/SwiftParser/TopLevel.swift

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -37,12 +37,12 @@ extension Parser {
3737
}
3838

3939
extension Parser {
40-
mutating func parseCodeBlockItemList(isAtTopLevel: Bool, stopCondition: (inout Parser) -> Bool) -> RawCodeBlockItemListSyntax {
40+
mutating func parseCodeBlockItemList(isAtTopLevel: Bool, allowInitDecl: Bool = true, stopCondition: (inout Parser) -> Bool) -> RawCodeBlockItemListSyntax {
4141
var elements = [RawCodeBlockItemSyntax]()
4242
var loopProgress = LoopProgressCondition()
4343
while !stopCondition(&self), loopProgress.evaluate(currentToken) {
4444
let newItemAtStartOfLine = self.currentToken.isAtStartOfLine
45-
guard let newElement = self.parseCodeBlockItem(isAtTopLevel: isAtTopLevel) else {
45+
guard let newElement = self.parseCodeBlockItem(isAtTopLevel: isAtTopLevel, allowInitDecl: allowInitDecl) else {
4646
break
4747
}
4848
if let lastItem = elements.last, lastItem.semicolon == nil && !newItemAtStartOfLine {
@@ -68,11 +68,11 @@ extension Parser {
6868
///
6969
/// This function is used when parsing places where function bodies are
7070
/// optional - like the function requirements in protocol declarations.
71-
mutating func parseOptionalCodeBlock() -> RawCodeBlockSyntax? {
71+
mutating func parseOptionalCodeBlock(allowInitDecl: Bool = true) -> RawCodeBlockSyntax? {
7272
guard self.at(.leftBrace) else {
7373
return nil
7474
}
75-
return self.parseCodeBlock()
75+
return self.parseCodeBlock(allowInitDecl: allowInitDecl)
7676
}
7777

7878
/// Parse a code block.
@@ -85,9 +85,9 @@ extension Parser {
8585
/// `introducer` is the `while`, `if`, ... keyword that is the cause that the code block is being parsed.
8686
/// If the left brace is missing, its indentation will be used to judge whether a following `}` was
8787
/// indented to close this code block or a surrounding context. See `expectRightBrace`.
88-
mutating func parseCodeBlock(introducer: RawTokenSyntax? = nil) -> RawCodeBlockSyntax {
88+
mutating func parseCodeBlock(introducer: RawTokenSyntax? = nil, allowInitDecl: Bool = true) -> RawCodeBlockSyntax {
8989
let (unexpectedBeforeLBrace, lbrace) = self.expect(.leftBrace)
90-
let itemList = parseCodeBlockItemList(isAtTopLevel: false, stopCondition: { $0.at(.rightBrace) })
90+
let itemList = parseCodeBlockItemList(isAtTopLevel: false, allowInitDecl: allowInitDecl, stopCondition: { $0.at(.rightBrace) })
9191
let (unexpectedBeforeRBrace, rbrace) = self.expectRightBrace(leftBrace: lbrace, introducer: introducer)
9292

9393
return .init(
@@ -118,7 +118,7 @@ extension Parser {
118118
/// statement → compiler-control-statement
119119
/// statements → statement statements?
120120
@_spi(RawSyntax)
121-
public mutating func parseCodeBlockItem(isAtTopLevel: Bool = false) -> RawCodeBlockItemSyntax? {
121+
public mutating func parseCodeBlockItem(isAtTopLevel: Bool = false, allowInitDecl: Bool = true) -> RawCodeBlockItemSyntax? {
122122
if self.at(any: [.caseKeyword, .defaultKeyword]) {
123123
// 'case' and 'default' are invalid in code block items.
124124
// Parse them and put them in their own CodeBlockItem but as an unexpected node.
@@ -134,7 +134,7 @@ extension Parser {
134134

135135
// FIXME: It is unfortunate that the Swift book refers to these as
136136
// "statements" and not "items".
137-
let item = self.parseItem(isAtTopLevel: isAtTopLevel)
137+
let item = self.parseItem(isAtTopLevel: isAtTopLevel, allowInitDecl: allowInitDecl)
138138
let semi = self.consume(if: .semicolon)
139139
var trailingSemis: [RawTokenSyntax] = []
140140
while let trailingSemi = self.consume(if: .semicolon) {
@@ -158,7 +158,7 @@ extension Parser {
158158
/// closing braces while trying to recover to the next item.
159159
/// If we are not at the top level, such a closing brace should close the
160160
/// wrapping declaration instead of being consumed by lookeahead.
161-
private mutating func parseItem(isAtTopLevel: Bool = false) -> RawSyntax {
161+
private mutating func parseItem(isAtTopLevel: Bool = false, allowInitDecl: Bool = true) -> RawSyntax {
162162
if self.at(.poundIfKeyword) {
163163
let directive = self.parsePoundIfDirective {
164164
$0.parseCodeBlockItem()
@@ -174,13 +174,13 @@ extension Parser {
174174
return RawSyntax(directive)
175175
} else if self.at(.poundSourceLocationKeyword) {
176176
return RawSyntax(self.parsePoundSourceLocationDirective())
177-
} else if self.atStartOfDeclaration() {
177+
} else if self.atStartOfDeclaration(allowInitDecl: allowInitDecl) {
178178
return RawSyntax(self.parseDeclaration())
179179
} else if self.atStartOfStatement() {
180180
return RawSyntax(self.parseStatement())
181181
} else if self.atStartOfExpression() {
182182
return RawSyntax(self.parseExpression())
183-
} else if self.atStartOfDeclaration(isAtTopLevel: isAtTopLevel, allowRecovery: true) {
183+
} else if self.atStartOfDeclaration(isAtTopLevel: isAtTopLevel, allowInitDecl: allowInitDecl, allowRecovery: true) {
184184
return RawSyntax(self.parseDeclaration())
185185
} else if self.atStartOfStatement(allowRecovery: true) {
186186
return RawSyntax(self.parseStatement())

Tests/SwiftParserTest/translated/InitDeinitTests.swift

Lines changed: 8 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -333,15 +333,9 @@ final class InitDeinitTests: XCTestCase {
333333
AssertParse(
334334
"""
335335
class Aaron {
336-
init(x: Int) {}
337-
convenience init() { init(x: 1️⃣1) }
336+
convenience init() { init(x: 1) }
338337
}
339-
""",
340-
diagnostics: [
341-
// TODO: Old parser expected error on line 3: missing 'self.' at initializer invocation, Fix-It replacements: 24 - 24 = 'self.'
342-
DiagnosticSpec(message: "expected type in parameter"),
343-
DiagnosticSpec(message: "unexpected code '1' in parameter clause"),
344-
]
338+
"""
345339
)
346340
}
347341

@@ -350,15 +344,10 @@ final class InitDeinitTests: XCTestCase {
350344
"""
351345
class Theodosia: Aaron {
352346
init() {
353-
init(x: 1️⃣2)
347+
init(x: 2)
354348
}
355349
}
356-
""",
357-
diagnostics: [
358-
// TODO: Old parser expected error on line 3: missing 'super.' at initializer invocation, Fix-It replacements: 5 - 5 = 'super.'
359-
DiagnosticSpec(message: "expected type in parameter"),
360-
DiagnosticSpec(message: "unexpected code '2' in parameter clause"),
361-
]
350+
"""
362351
)
363352
}
364353

@@ -367,14 +356,9 @@ final class InitDeinitTests: XCTestCase {
367356
"""
368357
struct AaronStruct {
369358
init(x: Int) {}
370-
init() { init(x: 1️⃣1) }
359+
init() { init(x: 1) }
371360
}
372-
""",
373-
diagnostics: [
374-
// TODO: Old parser expected error on line 3: missing 'self.' at initializer invocation, Fix-It replacements: 12 - 12 = 'self.'
375-
DiagnosticSpec(message: "expected type in parameter"),
376-
DiagnosticSpec(message: "unexpected code '1' in parameter clause"),
377-
]
361+
"""
378362
)
379363
}
380364

@@ -383,13 +367,9 @@ final class InitDeinitTests: XCTestCase {
383367
"""
384368
enum AaronEnum: Int {
385369
case A = 1
386-
init(x: Int) { init(rawValue: x)1️⃣! }
370+
init(x: Int) { init(rawValue: x)! }
387371
}
388-
""",
389-
diagnostics: [
390-
// TODO: Old parser expected error on line 3: missing 'self.' at initializer invocation, Fix-It replacements: 18 - 18 = 'self.'
391-
DiagnosticSpec(message: "unexpected code '!' in initializer"),
392-
]
372+
"""
393373
)
394374
}
395375

0 commit comments

Comments
 (0)