Skip to content

Commit bc4b3d7

Browse files
committed
Parse module selectors in most valid locations
This commit ports over tests from the compiler’s (future) `test/NameLookup/module_selector.swift` file and makes sure the correct uses parse as expected. It also tests that ill-formed module selectors (ones with a missing or non-identifier module name) are diagnosed correctly. This commit doesn’t handle module selectors in scoped `import` statements; the related test has been XFAILed.
1 parent 012411e commit bc4b3d7

File tree

8 files changed

+2400
-33
lines changed

8 files changed

+2400
-33
lines changed

Sources/SwiftParser/Attributes.swift

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -230,7 +230,11 @@ extension Parser {
230230
)
231231
}
232232

233-
switch peek(isAtAnyIn: DeclarationAttributeWithSpecialSyntax.self) {
233+
// An attribute qualified by a module selector is *always* a custom attribute, even if it has the same name (or
234+
// module name) as a builtin attribute.
235+
let builtinAttr = self.unlessPeekModuleSelector { $0.peek(isAtAnyIn: DeclarationAttributeWithSpecialSyntax.self) }
236+
237+
switch builtinAttr {
234238
case .abi:
235239
return parseAttribute(argumentMode: .required) { parser in
236240
return (nil, .abiArguments(parser.parseABIAttributeArguments()))

Sources/SwiftParser/Declarations.swift

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2189,11 +2189,15 @@ extension Parser {
21892189
)
21902190
pound = pound.tokenView.withTokenDiagnostic(tokenDiagnostic: diagnostic, arena: self.arena)
21912191
}
2192+
2193+
let moduleSelector: RawModuleSelectorSyntax?
21922194
let unexpectedBeforeMacro: RawUnexpectedNodesSyntax?
21932195
let macro: RawTokenSyntax
21942196
if !self.atStartOfLine {
2197+
moduleSelector = self.parseModuleSelectorIfPresent()
21952198
(unexpectedBeforeMacro, macro) = self.expectIdentifier(allowKeywordsAsIdentifier: true)
21962199
} else {
2200+
moduleSelector = nil
21972201
unexpectedBeforeMacro = nil
21982202
macro = self.missingToken(.identifier)
21992203
}
@@ -2241,7 +2245,7 @@ extension Parser {
22412245
modifiers: attrs.modifiers,
22422246
unexpectedBeforePound,
22432247
pound: pound,
2244-
moduleSelector: nil,
2248+
moduleSelector: moduleSelector,
22452249
unexpectedBeforeMacro,
22462250
macroName: macro,
22472251
genericArgumentClause: generics,

Sources/SwiftParser/Expressions.swift

Lines changed: 28 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,12 @@
1818

1919
extension TokenConsumer {
2020
mutating func atStartOfExpression() -> Bool {
21+
if self.isAtModuleSelector() {
22+
var lookahead = self.lookahead()
23+
_ = lookahead.consumeModuleSelectorTokensIfPresent()
24+
return lookahead.atStartOfExpression()
25+
}
26+
2127
switch self.at(anyIn: ExpressionStart.self) {
2228
case (.awaitTryMove, let handle)?:
2329
var lookahead = self.lookahead()
@@ -662,7 +668,7 @@ extension Parser {
662668

663669
// Parse the name portion.
664670
let declName: RawDeclReferenceExprSyntax
665-
if let indexOrSelf = self.consume(if: .integerLiteral, .keyword(.self)) {
671+
if !self.isAtModuleSelector(), let indexOrSelf = self.consume(if: .integerLiteral, .keyword(.self)) {
666672
// Handle "x.42" - a tuple index.
667673
declName = RawDeclReferenceExprSyntax(
668674
moduleSelector: nil,
@@ -1241,18 +1247,25 @@ extension Parser {
12411247
arena: self.arena
12421248
)
12431249
)
1250+
case (.colonColon, _)?, // Module selector with no module name
1251+
(.wildcard, _)? where self.isAtModuleSelector(), // Module selectors with invalid module names
1252+
(.Any, _)? where self.isAtModuleSelector(),
1253+
(.`self`, _)? where self.isAtModuleSelector(),
1254+
(.`Self`, _)? where self.isAtModuleSelector(),
1255+
(.super, _)? where self.isAtModuleSelector():
1256+
return self.parseIdentifierExpression(flavor: flavor)
12441257
case (.identifier, let handle)?, (.self, let handle)?, (.`init`, let handle)?, (.`deinit`, let handle)?,
12451258
(.`subscript`, let handle)?:
12461259
// If we have "case let x" followed by ".", "(", "[", or a generic
12471260
// argument list, we parse x as a normal name, not a binding, because it
12481261
// is the start of an enum or expr pattern.
12491262
if pattern.admitsBinding && self.lookahead().isInBindingPatternPosition() {
12501263
let identifier = self.eat(handle)
1251-
let pattern = RawIdentifierPatternSyntax(
1264+
let patternNode = RawIdentifierPatternSyntax(
12521265
identifier: identifier,
12531266
arena: self.arena
12541267
)
1255-
return RawExprSyntax(RawPatternExprSyntax(pattern: pattern, arena: self.arena))
1268+
return RawExprSyntax(RawPatternExprSyntax(pattern: patternNode, arena: self.arena))
12561269
}
12571270

12581271
return self.parseIdentifierExpression(flavor: flavor)
@@ -1385,6 +1398,7 @@ extension Parser {
13851398
)
13861399
pound = pound.tokenView.withTokenDiagnostic(tokenDiagnostic: diagnostic, arena: self.arena)
13871400
}
1401+
let moduleSelector = parseModuleSelectorIfPresent()
13881402
let unexpectedBeforeMacroName: RawUnexpectedNodesSyntax?
13891403
let macroName: RawTokenSyntax
13901404
if !self.atStartOfLine {
@@ -1432,7 +1446,7 @@ extension Parser {
14321446
return RawMacroExpansionExprSyntax(
14331447
unexpectedBeforePound,
14341448
pound: pound,
1435-
moduleSelector: nil,
1449+
moduleSelector: moduleSelector,
14361450
unexpectedBeforeMacroName,
14371451
macroName: macroName,
14381452
genericArgumentClause: generics,
@@ -1969,6 +1983,14 @@ extension Parser {
19691983
}
19701984
}
19711985

1986+
extension TokenConsumer {
1987+
mutating func atBinaryOperatorArgument() -> Bool {
1988+
var lookahead = self.lookahead()
1989+
_ = lookahead.consumeModuleSelectorTokensIfPresent()
1990+
return lookahead.at(.binaryOperator) && lookahead.peek(isAt: .comma, .rightParen, .rightSquare)
1991+
}
1992+
}
1993+
19721994
extension Parser {
19731995
/// Parse the elements of an argument list.
19741996
///
@@ -2020,7 +2042,7 @@ extension Parser {
20202042
// this case lexes as a binary operator because it neither leads nor
20212043
// follows a proper subexpression.
20222044
let expr: RawExprSyntax
2023-
if self.at(.binaryOperator) && self.peek(isAt: .comma, .rightParen, .rightSquare) {
2045+
if self.atBinaryOperatorArgument() {
20242046
expr = RawExprSyntax(self.parseDeclReferenceExpr(.operators))
20252047
} else {
20262048
expr = self.parseExpression(flavor: flavor, pattern: pattern)
@@ -2400,9 +2422,9 @@ extension Parser {
24002422

24012423
unknownAttr = RawAttributeSyntax(
24022424
atSign: at,
2403-
unexpectedBeforeIdent,
24042425
attributeName: RawIdentifierTypeSyntax(
24052426
moduleSelector: nil,
2427+
unexpectedBeforeIdent,
24062428
name: ident,
24072429
genericArgumentClause: nil,
24082430
arena: self.arena

Sources/SwiftParser/Names.swift

Lines changed: 112 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,107 @@ extension Parser {
4545
}
4646
}
4747

48+
extension TokenConsumer {
49+
/// Do the subsequent tokens have the form of a module selector? Encompasses some invalid syntax which nonetheless
50+
/// can be handled by `consumeModuleSelectorTokensIfPresent()`.
51+
///
52+
/// - Postcondition: If `true`, either the current token or the next token is `.colonColon`.
53+
mutating func isAtModuleSelector() -> Bool {
54+
// If this is a module selector, the next token should be `::`.
55+
guard self.peek(isAt: .colonColon) else {
56+
// ...however, we will also allow the *current* token to be `::`. `consumeModuleSelectorTokensIfPresent()` will
57+
// create a missing identifier.
58+
return self.at(.colonColon)
59+
}
60+
61+
// Technically the current token *should* be an identifier, but we also want to diagnose other tokens that might be
62+
// used by accident or given special meanings later ('_', certain keywords).
63+
return self.at(.identifier, .wildcard, .keyword(.Any))
64+
|| self.at(.keyword(.self), .keyword(.Self), .keyword(.super))
65+
}
66+
67+
mutating func unlessPeekModuleSelector<T>(_ operation: (inout Self) -> T?) -> T? {
68+
var lookahead = self.lookahead()
69+
lookahead.skipSingle()
70+
if lookahead.isAtModuleSelector() {
71+
return nil
72+
}
73+
return operation(&self)
74+
}
75+
76+
/// If the subsequent tokens have the form of a module selector, valid or otherwise, consume and return them;
77+
/// otherwise consume nothing and return `nil`. Additionally consumes invalid chained module selectors.
78+
///
79+
/// Returns a tuple comprised of:
80+
///
81+
/// - `moduleNameOrUnexpected`: The module name if present; in a valid module selector, this will be a present
82+
/// identifier, but either of those can be untrue in invalid code.
83+
/// - `colonColonToken`: The `::` indicating this module selector. Always `.colonColon`, always present.
84+
/// - `extra`: Tokens for additional trailing module selectors. There is no situation in which two module selectors
85+
/// can be validly chained.
86+
mutating func consumeModuleSelectorTokensIfPresent() -> (
87+
moduleNameOrUnexpected: Token, colonColonToken: Token, extra: [Token]
88+
)? {
89+
guard self.isAtModuleSelector() else {
90+
return nil
91+
}
92+
93+
let moduleName: Token
94+
let colonColonToken: Token
95+
96+
// Did we forget the module name?
97+
if let earlyColonColon = self.consume(if: .colonColon) {
98+
moduleName = self.missingToken(.identifier)
99+
colonColonToken = earlyColonColon
100+
} else {
101+
// Consume whatever comes before the `::`, plus the `::` itself. (Whether or not the "name" is an identifier is
102+
// checked elsewhere.)
103+
moduleName = self.consumeAnyToken()
104+
colonColonToken = self.eat(.colonColon)
105+
}
106+
107+
var extra: [Token] = []
108+
while self.isAtModuleSelector() {
109+
if !self.at(.colonColon) {
110+
extra.append(self.consumeAnyToken())
111+
}
112+
extra.append(self.eat(.colonColon))
113+
}
114+
return (moduleName, colonColonToken, extra)
115+
}
116+
}
117+
118+
extension Parser {
119+
/// Parses one or more module selectors, if present.
120+
mutating func parseModuleSelectorIfPresent() -> RawModuleSelectorSyntax? {
121+
guard let (moduleNameOrUnexpected, colonColon, extra) = consumeModuleSelectorTokensIfPresent() else {
122+
return nil
123+
}
124+
125+
let leadingUnexpected: [RawSyntax]
126+
let moduleName: RawTokenSyntax
127+
let trailingUnexpected: [RawSyntax]
128+
129+
if moduleNameOrUnexpected.tokenKind == .identifier {
130+
leadingUnexpected = []
131+
moduleName = moduleNameOrUnexpected
132+
} else {
133+
leadingUnexpected = [RawSyntax(moduleNameOrUnexpected)]
134+
moduleName = RawTokenSyntax(missing: .identifier, arena: arena)
135+
}
136+
137+
trailingUnexpected = extra.map { RawSyntax($0) }
138+
139+
return RawModuleSelectorSyntax(
140+
RawUnexpectedNodesSyntax(leadingUnexpected, arena: arena),
141+
moduleName: moduleName,
142+
colonColon: colonColon,
143+
RawUnexpectedNodesSyntax(trailingUnexpected, arena: arena),
144+
arena: arena
145+
)
146+
}
147+
}
148+
48149
extension Parser {
49150
struct DeclNameOptions: OptionSet {
50151
var rawValue: UInt8
@@ -68,6 +169,12 @@ extension Parser {
68169
}
69170

70171
mutating func parseDeclReferenceExpr(_ flags: DeclNameOptions = []) -> RawDeclReferenceExprSyntax {
172+
// Consume a module selector if present.
173+
let moduleSelector = self.parseModuleSelectorIfPresent()
174+
175+
// If a module selector is found, we parse the name after it according to SE-0071 rules.
176+
let allowKeywords = flags.contains(.keywords) || moduleSelector != nil
177+
71178
// Consume the base name.
72179
let base: RawTokenSyntax
73180
if let identOrSelf = self.consume(if: .identifier, .keyword(.self), .keyword(.Self))
@@ -80,7 +187,7 @@ extension Parser {
80187
let special = self.consume(if: .keyword(.`deinit`), .keyword(.`subscript`))
81188
{
82189
base = special
83-
} else if flags.contains(.keywords) && self.currentToken.isLexerClassifiedKeyword {
190+
} else if allowKeywords && self.currentToken.isLexerClassifiedKeyword {
84191
base = self.consumeAnyToken(remapping: .identifier)
85192
} else {
86193
base = missingToken(.identifier)
@@ -89,7 +196,7 @@ extension Parser {
89196
// Parse an argument list, if the flags allow it and it's present.
90197
let args = self.parseArgLabelList(flags)
91198
return RawDeclReferenceExprSyntax(
92-
moduleSelector: nil,
199+
moduleSelector: moduleSelector,
93200
baseName: base,
94201
argumentNames: args,
95202
arena: self.arena
@@ -212,12 +319,8 @@ extension Parser {
212319
var keepGoing = self.consume(if: .period)
213320
var loopProgress = LoopProgressCondition()
214321
while keepGoing != nil && self.hasProgressed(&loopProgress) {
215-
let (unexpectedBeforeName, name) = self.expect(
216-
.identifier,
217-
.keyword(.self),
218-
TokenSpec(.Self, remapping: .identifier),
219-
default: .identifier
220-
)
322+
let memberModuleSelector = self.parseModuleSelectorIfPresent()
323+
let name = self.parseMemberTypeName()
221324
let generics: RawGenericArgumentClauseSyntax?
222325
if self.at(prefix: "<") {
223326
generics = self.parseGenericArguments()
@@ -228,8 +331,7 @@ extension Parser {
228331
RawMemberTypeSyntax(
229332
baseType: result,
230333
period: keepGoing!,
231-
moduleSelector: nil,
232-
unexpectedBeforeName,
334+
moduleSelector: memberModuleSelector,
233335
name: name,
234336
genericArgumentClause: generics,
235337
arena: self.arena

Sources/SwiftParser/TokenSpecSet.swift

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -835,6 +835,7 @@ enum PrimaryExpressionStart: TokenSpecSet {
835835
case `Any`
836836
case atSign // For recovery
837837
case `Self`
838+
case colonColon
838839
case `deinit`
839840
case dollarIdentifier
840841
case `false`
@@ -867,6 +868,7 @@ enum PrimaryExpressionStart: TokenSpecSet {
867868
case TokenSpec(.Any): self = .Any
868869
case TokenSpec(.atSign): self = .atSign
869870
case TokenSpec(.Self): self = .Self
871+
case TokenSpec(.colonColon): self = .colonColon
870872
case TokenSpec(.deinit): self = .`deinit`
871873
case TokenSpec(.dollarIdentifier): self = .dollarIdentifier
872874
case TokenSpec(.false): self = .false
@@ -902,6 +904,7 @@ enum PrimaryExpressionStart: TokenSpecSet {
902904
case .Any: return .keyword(.Any)
903905
case .atSign: return .atSign
904906
case .Self: return .keyword(.Self)
907+
case .colonColon: return .colonColon
905908
case .`deinit`: return .keyword(.`deinit`)
906909
case .dollarIdentifier: return .dollarIdentifier
907910
case .false: return .keyword(.false)

0 commit comments

Comments
 (0)