Skip to content

Disallow the self/Self keyword where it’s not allowed in types #1986

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Aug 23, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions CodeGeneration/Sources/SyntaxSupport/TypeNodes.swift
Original file line number Diff line number Diff line change
Expand Up @@ -300,7 +300,7 @@ public let TYPE_NODES: [Node] = [
),
Child(
name: "name",
kind: .token(choices: [.token(.identifier), .keyword(.self), .keyword(.Self)]),
kind: .token(choices: [.token(.identifier), .keyword(.self)]),
nameForDiagnostics: "name"
),
Child(
Expand Down Expand Up @@ -435,7 +435,6 @@ public let TYPE_NODES: [Node] = [
name: "name",
kind: .token(choices: [
.token(.identifier),
.keyword(.self),
.keyword(.Self),
.keyword(.Any),
.token(.wildcard),
Expand Down
76 changes: 47 additions & 29 deletions Sources/SwiftParser/Names.swift
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,7 @@ extension Parser {

if self.lookahead().canParseBaseTypeForQualifiedDeclName() {
type = RawExprSyntax(RawTypeExprSyntax(type: self.parseQualifiedTypeIdentifier(), arena: self.arena))
period = self.consumePrefix(".", as: .period)
period = self.expectWithoutRecovery(prefix: ".", as: .period)
} else {
type = nil
period = nil
Expand Down Expand Up @@ -181,52 +181,70 @@ extension Parser {
return RawTypeSyntax(self.parseAnyType())
}

var result: RawTypeSyntax?
var keepGoing: RawTokenSyntax? = nil
let (unexpectedBeforeName, name) = self.expect(anyIn: IdentifierTypeSyntax.NameOptions.self, default: .identifier)
let generics: RawGenericArgumentClauseSyntax?
if self.atContextualPunctuator("<") {
generics = self.parseGenericArguments()
} else {
generics = nil
}

var result = RawTypeSyntax(
RawIdentifierTypeSyntax(
unexpectedBeforeName,
name: name,
genericArgumentClause: generics,
arena: self.arena
)
)

// If qualified name base type cannot be parsed from the current
// point (i.e. the next type identifier is not followed by a '.'),
// then the next identifier is the final declaration name component.
var backtrack = self.lookahead()
guard
backtrack.consume(ifPrefix: ".", as: .period) != nil,
backtrack.canParseBaseTypeForQualifiedDeclName()
else {
return result
}

var keepGoing = self.consume(if: .period)
var loopProgress = LoopProgressCondition()
repeat {
let (unexpectedBeforeName, name) = self.expect(.identifier, .keyword(.self), .keyword(.Self), default: .identifier)
while keepGoing != nil && self.hasProgressed(&loopProgress) {
let (unexpectedBeforeName, name) = self.expect(.identifier, .keyword(.self), TokenSpec(.Self, remapping: .identifier), default: .identifier)
let generics: RawGenericArgumentClauseSyntax?
if self.atContextualPunctuator("<") {
generics = self.parseGenericArguments()
} else {
generics = nil
}
if let keepGoing {
result = RawTypeSyntax(
RawMemberTypeSyntax(
baseType: result!,
period: keepGoing,
unexpectedBeforeName,
name: name,
genericArgumentClause: generics,
arena: self.arena
)
)
} else {
result = RawTypeSyntax(
RawIdentifierTypeSyntax(
unexpectedBeforeName,
name: name,
genericArgumentClause: generics,
arena: self.arena
)
result = RawTypeSyntax(
RawMemberTypeSyntax(
baseType: result,
period: keepGoing!,
unexpectedBeforeName,
name: name,
genericArgumentClause: generics,
arena: self.arena
)
}
)

// If qualified name base type cannot be parsed from the current
// point (i.e. the next type identifier is not followed by a '.'),
// then the next identifier is the final declaration name component.
var backtrack = self.lookahead()
backtrack.consumePrefix(".", as: .period)
guard backtrack.canParseBaseTypeForQualifiedDeclName() else {
guard
backtrack.consume(ifPrefix: ".", as: .period) != nil,
backtrack.canParseBaseTypeForQualifiedDeclName()
else {
break
}

keepGoing = self.consume(if: .period)
} while keepGoing != nil && self.hasProgressed(&loopProgress)
}

return result!
return result
}
}

Expand Down
6 changes: 5 additions & 1 deletion Sources/SwiftParser/Parameters.swift
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,11 @@ extension Parser {

let type: RawTypeSyntax

if colon.presence == .missing, let secondName = names.secondName, secondName.tokenText.isStartingWithUppercase {
if colon.presence == .missing,
let secondName = names.secondName,
secondName.tokenKind == .identifier,
secondName.tokenText.isStartingWithUppercase
{
// Synthesize the secondName parameter as a type node.
type = RawTypeSyntax(
RawIdentifierTypeSyntax(
Expand Down
1 change: 1 addition & 0 deletions Sources/SwiftParser/Parser.swift
Original file line number Diff line number Diff line change
Expand Up @@ -635,6 +635,7 @@ extension Parser {
/// currently positioned at a keyword, consume that keyword and remap it
/// to and identifier.
/// - Returns: The consumed token and any unexpected tokens that were skipped.
/// The token is always guaranteed to be of `TokenKind.identifier`
mutating func expectIdentifier(
keywordRecovery: Bool = false,
allowSelfOrCapitalSelfAsIdentifier: Bool = false,
Expand Down
41 changes: 24 additions & 17 deletions Sources/SwiftParser/Types.swift
Original file line number Diff line number Diff line change
Expand Up @@ -270,7 +270,20 @@ extension Parser {
)
)
} else {
let (name, generics) = self.parseTypeNameWithGenerics(allowKeywordAsName: true)
let name: RawTokenSyntax
if let handle = self.at(anyIn: MemberTypeSyntax.NameOptions.self)?.handle {
name = self.eat(handle)
} else if self.currentToken.isLexerClassifiedKeyword {
name = self.consumeAnyToken(remapping: .identifier)
} else {
name = missingToken(.identifier)
}
let generics: RawGenericArgumentClauseSyntax?
if self.atContextualPunctuator("<") {
generics = self.parseGenericArguments()
} else {
generics = nil
}
base = RawTypeSyntax(
RawMemberTypeSyntax(
baseType: base,
Expand Down Expand Up @@ -322,30 +335,23 @@ extension Parser {
)
}

mutating func parseTypeNameWithGenerics(allowKeywordAsName: Bool) -> (RawTokenSyntax, RawGenericArgumentClauseSyntax?) {
let name: RawTokenSyntax
if let identOrSelf = self.consume(if: .identifier, .keyword(.self), .keyword(.Self)) {
name = identOrSelf
} else if allowKeywordAsName && self.currentToken.isLexerClassifiedKeyword {
name = self.consumeAnyToken(remapping: .identifier)
} else {
name = missingToken(.identifier)
}
if self.atContextualPunctuator("<") {
return (name, self.parseGenericArguments())
}
return (name, nil)
}

/// Parse a type identifier.
mutating func parseTypeIdentifier() -> RawTypeSyntax {
if self.at(.keyword(.Any)) {
return RawTypeSyntax(self.parseAnyType())
}

let (name, generics) = parseTypeNameWithGenerics(allowKeywordAsName: false)
let (unexpectedBeforeName, name) = self.expect(anyIn: IdentifierTypeSyntax.NameOptions.self, default: .identifier)
let generics: RawGenericArgumentClauseSyntax?
if self.atContextualPunctuator("<") {
generics = self.parseGenericArguments()
} else {
generics = nil
}

return RawTypeSyntax(
RawIdentifierTypeSyntax(
unexpectedBeforeName,
name: name,
genericArgumentClause: generics,
arena: self.arena
Expand Down Expand Up @@ -479,6 +485,7 @@ extension Parser {
if let first,
second == nil,
colon?.isMissing == true,
first.tokenKind == .identifier,
first.tokenText.isStartingWithUppercase
{
elements.append(
Expand Down
14 changes: 0 additions & 14 deletions Sources/SwiftParser/generated/Parser+TokenSpecSet.swift
Original file line number Diff line number Diff line change
Expand Up @@ -1559,7 +1559,6 @@ extension IdentifierTypeSyntax {
@_spi(Diagnostics)
public enum NameOptions: TokenSpecSet {
case identifier
case `self`
case `Self`
case `Any`
case wildcard
Expand All @@ -1568,8 +1567,6 @@ extension IdentifierTypeSyntax {
switch PrepareForKeywordMatch(lexeme) {
case TokenSpec(.identifier):
self = .identifier
case TokenSpec(.self):
self = .self
case TokenSpec(.Self):
self = .Self
case TokenSpec(.Any):
Expand All @@ -1585,8 +1582,6 @@ extension IdentifierTypeSyntax {
switch self {
case .identifier:
return .identifier
case .self:
return .keyword(.self)
case .Self:
return .keyword(.Self)
case .Any:
Expand All @@ -1604,8 +1599,6 @@ extension IdentifierTypeSyntax {
switch self {
case .identifier:
return .identifier("")
case .self:
return .keyword(.self)
case .Self:
return .keyword(.Self)
case .Any:
Expand Down Expand Up @@ -2097,16 +2090,13 @@ extension MemberTypeSyntax {
public enum NameOptions: TokenSpecSet {
case identifier
case `self`
case `Self`

init?(lexeme: Lexer.Lexeme) {
switch PrepareForKeywordMatch(lexeme) {
case TokenSpec(.identifier):
self = .identifier
case TokenSpec(.self):
self = .self
case TokenSpec(.Self):
self = .Self
default:
return nil
}
Expand All @@ -2118,8 +2108,6 @@ extension MemberTypeSyntax {
return .identifier
case .self:
return .keyword(.self)
case .Self:
return .keyword(.Self)
}
}

Expand All @@ -2133,8 +2121,6 @@ extension MemberTypeSyntax {
return .identifier("")
case .self:
return .keyword(.self)
case .Self:
return .keyword(.Self)
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1406,7 +1406,6 @@ func validateLayout(layout: RawSyntaxBuffer, as kind: SyntaxKind) {
assertNoError(kind, 0, verify(layout[0], as: RawUnexpectedNodesSyntax?.self))
assertNoError(kind, 1, verify(layout[1], as: RawTokenSyntax.self, tokenChoices: [
.tokenKind(.identifier),
.keyword("self"),
.keyword("Self"),
.keyword("Any"),
.tokenKind(.wildcard)
Expand Down Expand Up @@ -1805,7 +1804,7 @@ func validateLayout(layout: RawSyntaxBuffer, as kind: SyntaxKind) {
assertNoError(kind, 2, verify(layout[2], as: RawUnexpectedNodesSyntax?.self))
assertNoError(kind, 3, verify(layout[3], as: RawTokenSyntax.self, tokenChoices: [.tokenKind(.period)]))
assertNoError(kind, 4, verify(layout[4], as: RawUnexpectedNodesSyntax?.self))
assertNoError(kind, 5, verify(layout[5], as: RawTokenSyntax.self, tokenChoices: [.tokenKind(.identifier), .keyword("self"), .keyword("Self")]))
assertNoError(kind, 5, verify(layout[5], as: RawTokenSyntax.self, tokenChoices: [.tokenKind(.identifier), .keyword("self")]))
assertNoError(kind, 6, verify(layout[6], as: RawUnexpectedNodesSyntax?.self))
assertNoError(kind, 7, verify(layout[7], as: RawGenericArgumentClauseSyntax?.self))
assertNoError(kind, 8, verify(layout[8], as: RawUnexpectedNodesSyntax?.self))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -970,7 +970,7 @@ public struct FunctionTypeSyntax: TypeSyntaxProtocol, SyntaxHashable {

/// ### Children
///
/// - `name`: (`<identifier>` | `'self'` | `'Self'` | `'Any'` | `'_'`)
/// - `name`: (`<identifier>` | `'Self'` | `'Any'` | `'_'`)
/// - `genericArgumentClause`: ``GenericArgumentClauseSyntax``?
public struct IdentifierTypeSyntax: TypeSyntaxProtocol, SyntaxHashable {
public let _syntaxNode: Syntax
Expand Down Expand Up @@ -1218,7 +1218,7 @@ public struct ImplicitlyUnwrappedOptionalTypeSyntax: TypeSyntaxProtocol, SyntaxH
///
/// - `baseType`: ``TypeSyntax``
/// - `period`: `'.'`
/// - `name`: (`<identifier>` | `'self'` | `'Self'`)
/// - `name`: (`<identifier>` | `'self'`)
/// - `genericArgumentClause`: ``GenericArgumentClauseSyntax``?
public struct MemberTypeSyntax: TypeSyntaxProtocol, SyntaxHashable {
public let _syntaxNode: Syntax
Expand Down
31 changes: 1 addition & 30 deletions Tests/SwiftParserTest/Assertions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -529,35 +529,6 @@ public struct AssertParseOptions: OptionSet {
}

extension ParserTestCase {
/// Same as `assertParse` overload with a `(String) -> S` `parse`,
/// parsing the resulting `String` as a ``SourceFileSyntax``.
func assertParse(
_ markedSource: String,
substructure expectedSubstructure: (some SyntaxProtocol)? = Optional<Syntax>.none,
substructureAfterMarker: String = "START",
diagnostics expectedDiagnostics: [DiagnosticSpec] = [],
applyFixIts: [String]? = nil,
fixedSource expectedFixedSource: String? = nil,
options: AssertParseOptions = [],
experimentalFeatures: Parser.ExperimentalFeatures? = nil,
file: StaticString = #file,
line: UInt = #line
) {
assertParse(
markedSource,
{ SourceFileSyntax.parse(from: &$0) },
substructure: expectedSubstructure,
substructureAfterMarker: substructureAfterMarker,
diagnostics: expectedDiagnostics,
applyFixIts: applyFixIts,
fixedSource: expectedFixedSource,
options: options,
experimentalFeatures: experimentalFeatures,
file: file,
line: line
)
}

/// After a test case has been mutated, assert that the mutated source
/// round-trips and doesn’t hit any assertion failures in the parser.
fileprivate func assertRoundTrip<S: SyntaxProtocol>(
Expand Down Expand Up @@ -615,7 +586,7 @@ extension ParserTestCase {
/// `nil` to enable the default set of features provided by the test case.
func assertParse<S: SyntaxProtocol>(
_ markedSource: String,
_ parse: (inout Parser) -> S,
_ parse: (inout Parser) -> S = { SourceFileSyntax.parse(from: &$0) },
substructure expectedSubstructure: (some SyntaxProtocol)? = Optional<Syntax>.none,
substructureAfterMarker: String = "START",
diagnostics expectedDiagnostics: [DiagnosticSpec] = [],
Expand Down
18 changes: 18 additions & 0 deletions Tests/SwiftParserTest/AttributeTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,24 @@ final class AttributeTests: ParserTestCase {
) {}
"""
)

assertParse(
"""
@derivative(of: 1️⃣Self.other)
func foo() {}
""",
substructure: Syntax(TokenSyntax.keyword(.Self)),
substructureAfterMarker: "1️⃣"
)

assertParse(
"""
@derivative(of: Foo.1️⃣Self.other)
func foo() {}
""",
substructure: Syntax(TokenSyntax.identifier("Self")),
substructureAfterMarker: "1️⃣"
)
}

func testTransposeAttribute() {
Expand Down
Loading