Skip to content

Commit 4c34e4b

Browse files
committed
Diagnose and recover from bad module selectors
Specifically, from module selectors at incorrect locations. This is done through a couple of mechanisms: • The various `expect(…)` methods consume a module selector as unexpected syntax. • Various identifier-parsing productions now pre-parse an invalid module selector and convert it to unexpected syntax. In some cases this involves adjusting matching `can`/`at` methods to consume otherwise-invalid module selectors. • The previously-introduced `attach(_:to:)` mechanism is now used in more places. This makes all test cases inherited from the Swift tests pass, except for the `import` syntax which I’m a little iffy on.
1 parent 97513d6 commit 4c34e4b

File tree

11 files changed

+132
-75
lines changed

11 files changed

+132
-75
lines changed

Sources/SwiftParser/Attributes.swift

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -741,15 +741,16 @@ extension Parser {
741741
case (.kind, let handle)?:
742742
let label = self.eat(handle)
743743
let (unexpectedBeforeColon, colon) = self.expect(.colon)
744-
let valueLabel = self.parseAnyIdentifier()
744+
let (unexpectedBeforeValue, value) = self.parseAnyIdentifier()
745745
let comma = self.consume(if: .comma)
746746
elements.append(
747747
.labeledSpecializeArgument(
748748
RawLabeledSpecializeArgumentSyntax(
749749
label: label,
750750
unexpectedBeforeColon,
751751
colon: colon,
752-
value: valueLabel,
752+
unexpectedBeforeValue,
753+
value: value,
753754
trailingComma: comma,
754755
arena: self.arena
755756
)
@@ -759,15 +760,16 @@ extension Parser {
759760
(.spi, let handle)?:
760761
let label = self.eat(handle)
761762
let (unexpectedBeforeColon, colon) = self.expect(.colon)
762-
let valueLabel = self.consumeAnyToken()
763+
let (unexpectedBeforeValue, value) = self.expectIdentifier()
763764
let comma = self.consume(if: .comma)
764765
elements.append(
765766
.labeledSpecializeArgument(
766767
RawLabeledSpecializeArgumentSyntax(
767768
label: label,
768769
unexpectedBeforeColon,
769770
colon: colon,
770-
value: valueLabel,
771+
unexpectedBeforeValue,
772+
value: value,
771773
trailingComma: comma,
772774
arena: self.arena
773775
)

Sources/SwiftParser/Declarations.swift

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -355,11 +355,12 @@ extension Parser {
355355
}
356356

357357
if self.currentToken.isEditorPlaceholder {
358-
let placeholder = self.parseAnyIdentifier()
358+
let (unexpectedBeforePlaceholder, placeholder) = self.parseAnyIdentifier()
359359
return RawDeclSyntax(
360360
RawMissingDeclSyntax(
361361
attributes: attrs.attributes,
362362
modifiers: attrs.modifiers,
363+
unexpectedBeforePlaceholder,
363364
placeholder: placeholder,
364365
arena: self.arena
365366
)
@@ -427,10 +428,11 @@ extension Parser {
427428
var keepGoing: RawTokenSyntax? = nil
428429
var loopProgress = LoopProgressCondition()
429430
repeat {
430-
let name = self.parseAnyIdentifier()
431+
let (unexpectedBeforeName, name) = self.parseAnyIdentifier()
431432
keepGoing = self.consume(if: .period)
432433
elements.append(
433434
RawImportPathComponentSyntax(
435+
unexpectedBeforeName,
434436
name: name,
435437
trailingPeriod: keepGoing,
436438
arena: self.arena
@@ -1232,7 +1234,7 @@ extension Parser {
12321234
(unexpectedBeforeIdentifier, identifier) = self.expectIdentifier(keywordRecovery: true)
12331235

12341236
if currentToken.isEditorPlaceholder {
1235-
let editorPlaceholder = self.parseAnyIdentifier()
1237+
let (_, editorPlaceholder) = self.parseAnyIdentifier()
12361238
unexpectedAfterIdentifier = RawUnexpectedNodesSyntax([editorPlaceholder], arena: self.arena)
12371239
} else {
12381240
unexpectedAfterIdentifier = nil

Sources/SwiftParser/Expressions.swift

Lines changed: 5 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1899,14 +1899,7 @@ extension Parser {
18991899
// Parse identifier (',' identifier)*
19001900
var keepGoing: RawTokenSyntax? = nil
19011901
repeat {
1902-
let unexpected: RawUnexpectedNodesSyntax?
1903-
let name: RawTokenSyntax
1904-
if let identifier = self.consume(if: .identifier) {
1905-
unexpected = nil
1906-
name = identifier
1907-
} else {
1908-
(unexpected, name) = self.expect(.wildcard)
1909-
}
1902+
let (unexpected, name) = self.expect(.identifier, .wildcard, default: .identifier)
19101903
keepGoing = consume(if: .comma)
19111904
params.append(
19121905
RawClosureShorthandParameterSyntax(
@@ -2031,7 +2024,7 @@ extension Parser {
20312024
let unexpectedBeforeLabel: RawUnexpectedNodesSyntax?
20322025
let label: RawTokenSyntax?
20332026
let colon: RawTokenSyntax?
2034-
if self.atArgumentLabel(allowDollarIdentifier: true) && self.peek(isAt: .colon) {
2027+
if self.atArgumentLabel(allowDollarIdentifier: true, followedByColon: true) {
20352028
(unexpectedBeforeLabel, label) = parseArgumentLabel()
20362029
colon = consumeAnyToken()
20372030
} else if let _colon = self.consume(if: .colon) {
@@ -2114,9 +2107,8 @@ extension Parser.Lookahead {
21142107
// Fast path: the next two tokens must be a label and a colon.
21152108
// But 'default:' is ambiguous with switch cases and we disallow it
21162109
// (unless escaped) even outside of switches.
2117-
if !self.atArgumentLabel()
2110+
if !self.atArgumentLabel(followedByColon: true)
21182111
|| self.at(.keyword(.default))
2119-
|| self.peek().rawTokenKind != .colon
21202112
{
21212113
return false
21222114
}
@@ -2616,6 +2608,7 @@ extension Parser.Lookahead {
26162608
}
26172609
} else if lookahead.at(.identifier) || lookahead.at(.wildcard) {
26182610
// Parse identifier (',' identifier)*
2611+
_ = lookahead.consumeModuleSelectorTokens()
26192612
lookahead.consumeAnyToken()
26202613

26212614
/// If the next token is a colon, interpret is as a type annotation and consume a type after it.
@@ -2632,6 +2625,7 @@ extension Parser.Lookahead {
26322625
while consumeOptionalTypeAnnotation() && lookahead.consume(if: .comma) != nil
26332626
&& lookahead.hasProgressed(&parametersProgress)
26342627
{
2628+
_ = lookahead.consumeModuleSelectorTokens()
26352629
if lookahead.at(.identifier) || lookahead.at(.wildcard) {
26362630
lookahead.consumeAnyToken()
26372631
continue

Sources/SwiftParser/Names.swift

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -17,30 +17,36 @@
1717
#endif
1818

1919
extension Parser {
20-
mutating func parseAnyIdentifier() -> RawTokenSyntax {
20+
/// Parse an identifier or operator.
21+
///
22+
/// - Note: If you expect a module selector, parse it before calling this method; it will consume module selectors
23+
/// as unexpected syntax.
24+
mutating func parseAnyIdentifier() -> (RawUnexpectedNodesSyntax?, RawTokenSyntax) {
25+
let moduleSelector = unexpected(self.parseModuleSelector())
2126
if let token = self.consume(if: .identifier) {
22-
return token
27+
return (moduleSelector, token)
2328
} else if let (_, handle) = self.at(anyIn: Operator.self) {
24-
return self.eat(handle)
29+
return (moduleSelector, self.eat(handle))
2530
} else {
26-
return RawTokenSyntax(missing: .identifier, arena: arena)
31+
return (moduleSelector, RawTokenSyntax(missing: .identifier, arena: arena))
2732
}
2833
}
2934

3035
mutating func parseArgumentLabel() -> (RawUnexpectedNodesSyntax?, RawTokenSyntax) {
36+
let unexpectedSelector = unexpected(self.parseModuleSelector())
3137
guard self.atArgumentLabel(allowDollarIdentifier: true) else {
32-
return (nil, missingToken(.identifier))
38+
return (unexpectedSelector, missingToken(.identifier))
3339
}
3440
if let dollarIdent = self.consume(if: .dollarIdentifier) {
3541
return (
36-
RawUnexpectedNodesSyntax([dollarIdent], arena: self.arena),
42+
RawUnexpectedNodesSyntax(combining: unexpectedSelector, dollarIdent, arena: self.arena),
3743
self.missingToken(.identifier)
3844
)
3945
} else {
4046
if let wildcardToken = self.consume(if: .wildcard) {
41-
return (nil, wildcardToken)
47+
return (unexpectedSelector, wildcardToken)
4248
}
43-
return (nil, self.consumeAnyToken(remapping: .identifier))
49+
return (unexpectedSelector, self.consumeAnyToken(remapping: .identifier))
4450
}
4551
}
4652
}
@@ -405,7 +411,7 @@ extension Parser {
405411
var loopProgress = LoopProgressCondition()
406412
while !self.at(.endOfFile, .rightParen) && self.hasProgressed(&loopProgress) {
407413
// Check to see if there is an argument label.
408-
precondition(self.atArgumentLabel() && self.peek(isAt: .colon))
414+
precondition(self.atArgumentLabel(followedByColon: true))
409415
let name = self.consumeAnyToken()
410416
let (unexpectedBeforeColon, colon) = self.expect(.colon)
411417
elements.append(
@@ -526,6 +532,7 @@ extension Parser {
526532
extension Parser.Lookahead {
527533
func canParseBaseTypeForQualifiedDeclName() -> Bool {
528534
var lookahead = self.lookahead()
535+
_ = lookahead.consumeModuleSelectorTokens()
529536
guard lookahead.canParseTypeIdentifier() else {
530537
return false
531538
}
@@ -543,7 +550,7 @@ extension Parser.Lookahead {
543550
var loopProgress = LoopProgressCondition()
544551
while !lookahead.at(.endOfFile, .rightParen) && lookahead.hasProgressed(&loopProgress) {
545552
// Check to see if there is an argument label.
546-
guard lookahead.atArgumentLabel() && lookahead.peek().rawTokenKind == .colon else {
553+
guard lookahead.atArgumentLabel(followedByColon: true) else {
547554
return false
548555
}
549556

Sources/SwiftParser/Parameters.swift

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -298,16 +298,31 @@ extension Parser {
298298
// MARK: - Parameter Modifiers
299299

300300
extension Parser {
301+
mutating func canParseParameterModifier() -> Bool {
302+
var lookahead = self.lookahead()
303+
_ = lookahead.consumeModuleSelectorTokens()
304+
return lookahead.at(anyIn: ParameterModifier.self) != nil
305+
}
306+
301307
mutating func parseParameterModifiers(isClosure: Bool) -> RawDeclModifierListSyntax {
302308
var elements = [RawDeclModifierSyntax]()
303309
var loopProgress = LoopProgressCondition()
304310
while self.hasProgressed(&loopProgress) {
305-
guard let match = self.at(anyIn: ParameterModifier.self),
311+
312+
guard self.canParseParameterModifier(),
306313
!withLookahead({ $0.startsParameterName(isClosure: isClosure, allowMisplacedSpecifierRecovery: false) })
307314
else {
308315
break
309316
}
310-
elements.append(RawDeclModifierSyntax(name: self.eat(match.handle), detail: nil, arena: self.arena))
317+
let moduleSelector = self.parseModuleSelector()
318+
elements.append(
319+
RawDeclModifierSyntax(
320+
unexpected(moduleSelector),
321+
name: self.eat(self.at(anyIn: ParameterModifier.self)!.handle),
322+
detail: nil,
323+
arena: self.arena
324+
)
325+
)
311326
}
312327
if elements.isEmpty {
313328
return self.emptyCollection(RawDeclModifierListSyntax.self)

Sources/SwiftParser/Parser.swift

Lines changed: 35 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -560,19 +560,21 @@ extension Parser {
560560
/// Implements the paradigm shared across all `expect` methods.
561561
@inline(__always)
562562
private mutating func expectImpl(
563+
skipUnexpectedModuleSelector: Bool = true,
563564
consume: (inout Parser) -> RawTokenSyntax?,
564565
canRecoverTo: (inout Lookahead) -> RecoveryConsumptionHandle?,
565566
makeMissing: (inout Parser) -> RawTokenSyntax
566567
) -> (unexpected: RawUnexpectedNodesSyntax?, token: RawTokenSyntax) {
568+
let unexpectedSelector = skipUnexpectedModuleSelector ? unexpected(self.parseModuleSelector()) : nil
567569
if let tok = consume(&self) {
568-
return (nil, tok)
570+
return (unexpectedSelector, tok)
569571
}
570572
var lookahead = self.lookahead()
571573
if let handle = canRecoverTo(&lookahead) {
572574
let (unexpectedTokens, token) = self.eat(handle)
573-
return (unexpectedTokens, token)
575+
return (RawUnexpectedNodesSyntax(combining: unexpectedSelector, unexpectedTokens, arena: self.arena), token)
574576
}
575-
return (nil, makeMissing(&self))
577+
return (unexpectedSelector, makeMissing(&self))
576578
}
577579

578580
/// Attempts to consume a token that matches the given `spec`.
@@ -581,6 +583,8 @@ extension Parser {
581583
/// specified by `spec` and see if the token occurs after that unexpected.
582584
/// 2. If the token couldn't be found after skipping unexpected, it synthesizes
583585
/// a missing token of the requested kind.
586+
/// - Note: If you expect a module selector, parse it before calling this method; it will consume module selectors
587+
/// as unexpected syntax.
584588
@inline(__always)
585589
mutating func expect(
586590
_ spec: TokenSpec
@@ -599,6 +603,8 @@ extension Parser {
599603
/// kinds occurs after the unexpected.
600604
/// 2. If the token couldn't be found after skipping unexpected, it synthesizes
601605
/// a missing token of `defaultKind`.
606+
/// - Note: If you expect a module selector, parse it before calling this method; it will consume module selectors
607+
/// as unexpected syntax.
602608
@inline(__always)
603609
mutating func expect(
604610
_ spec1: TokenSpec,
@@ -619,6 +625,8 @@ extension Parser {
619625
/// kinds occurs after the unexpected.
620626
/// 2. If the token couldn't be found after skipping unexpected, it synthesizes
621627
/// a missing token of `defaultKind`.
628+
/// - Note: If you expect a module selector, parse it before calling this method; it will consume module selectors
629+
/// as unexpected syntax.
622630
@inline(__always)
623631
mutating func expect(
624632
_ spec1: TokenSpec,
@@ -633,6 +641,15 @@ extension Parser {
633641
)
634642
}
635643

644+
/// Attempts to consume a token that matches the given `specSet`.
645+
/// If it cannot be found, the parser tries
646+
/// 1. To eat unexpected tokens that have lower ``TokenPrecedence`` than the
647+
/// lowest precedence of the spec and see if a token of the requested
648+
/// kinds occurs after the unexpected.
649+
/// 2. If the token couldn't be found after skipping unexpected, it synthesizes
650+
/// a missing token of `defaultKind`.
651+
/// - Note: If you expect a module selector, parse it before calling this method; it will consume module selectors
652+
/// as unexpected syntax.
636653
@inline(__always)
637654
mutating func expect<SpecSet: TokenSpecSet>(
638655
anyIn specSet: SpecSet.Type,
@@ -652,12 +669,16 @@ extension Parser {
652669
/// specified by `TokenSpec(tokenKind)` and see if the token occurs after that unexpected.
653670
/// 2. If the token couldn't be found after skipping unexpected, it synthesizes
654671
/// a missing token of the requested kind.
672+
/// - Note: If you expect a module selector, parse it before calling this method; it will consume module selectors
673+
/// as unexpected syntax (except when used to split a colon).
655674
@inline(__always)
656675
mutating func expect(
657676
prefix: SyntaxText, as tokenKind: RawTokenKind
658677
) -> (unexpected: RawUnexpectedNodesSyntax?, token: RawTokenSyntax) {
659678
let spec = TokenSpec(tokenKind)
660679
return expectImpl(
680+
// Don't consume a .colonColon if we're trying to split it
681+
skipUnexpectedModuleSelector: !prefix.hasPrefix(":"),
661682
consume: { $0.consume(ifPrefix: prefix, as: tokenKind) },
662683
canRecoverTo: { $0.canRecoverTo(spec) },
663684
makeMissing: { $0.missingToken(spec) }
@@ -691,34 +712,38 @@ extension Parser {
691712
/// to and identifier.
692713
/// - Returns: The consumed token and any unexpected tokens that were skipped.
693714
/// The token is always guaranteed to be of `TokenKind.identifier`
715+
/// - Note: If you expect a module selector, parse it before calling this method; it will consume module selectors
716+
/// as unexpected syntax.
694717
mutating func expectIdentifier(
695718
keywordRecovery: Bool = false,
696719
allowSelfOrCapitalSelfAsIdentifier: Bool = false,
697720
allowKeywordsAsIdentifier: Bool = false
698721
) -> (RawUnexpectedNodesSyntax?, RawTokenSyntax) {
722+
let unexpectedSelector = unexpected(self.parseModuleSelector())
723+
699724
if let identifier = self.consume(if: .identifier) {
700-
return (nil, identifier)
725+
return (unexpectedSelector, identifier)
701726
}
702727
if allowKeywordsAsIdentifier, self.currentToken.isLexerClassifiedKeyword {
703-
return (nil, self.consumeAnyToken(remapping: .identifier))
728+
return (unexpectedSelector, self.consumeAnyToken(remapping: .identifier))
704729
}
705730
if allowSelfOrCapitalSelfAsIdentifier,
706731
let selfOrCapitalSelf = self.consume(
707732
if: TokenSpec(.self, remapping: .identifier),
708733
TokenSpec(.Self, remapping: .identifier)
709734
)
710735
{
711-
return (nil, selfOrCapitalSelf)
736+
return (unexpectedSelector, selfOrCapitalSelf)
712737
}
713738
if let unknown = self.consume(if: .unknown) {
714739
return (
715-
RawUnexpectedNodesSyntax([unknown], arena: self.arena),
740+
RawUnexpectedNodesSyntax(combining: unexpectedSelector, unknown, arena: self.arena),
716741
self.missingToken(.identifier)
717742
)
718743
}
719744
if let number = self.consume(if: .integerLiteral, .floatLiteral, .dollarIdentifier) {
720745
return (
721-
RawUnexpectedNodesSyntax([number], arena: self.arena),
746+
RawUnexpectedNodesSyntax(combining: unexpectedSelector, number, arena: self.arena),
722747
self.missingToken(.identifier)
723748
)
724749
} else if keywordRecovery,
@@ -727,12 +752,12 @@ extension Parser {
727752
{
728753
let keyword = self.consumeAnyToken()
729754
return (
730-
RawUnexpectedNodesSyntax([keyword], arena: self.arena),
755+
RawUnexpectedNodesSyntax(combining: unexpectedSelector, keyword, arena: self.arena),
731756
self.missingToken(.identifier)
732757
)
733758
}
734759
return (
735-
nil,
760+
unexpectedSelector,
736761
self.missingToken(.identifier)
737762
)
738763
}

0 commit comments

Comments
 (0)