Skip to content

Another Bundle of Parser Fixes #801

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 12 commits into from
Sep 16, 2022
39 changes: 28 additions & 11 deletions Sources/SwiftParser/Declarations.swift
Original file line number Diff line number Diff line change
Expand Up @@ -30,14 +30,16 @@ extension TokenConsumer {
_ = subparser.consumeAttributeList()
}

var modifierProgress = LoopProgressCondition()
while let (modifierKind, handle) = subparser.at(anyIn: DeclarationModifier.self),
modifierKind != .classKeyword,
modifierProgress.evaluate(subparser.currentToken) {
subparser.eat(handle)
if subparser.at(.leftParen) {
subparser.consumeAnyToken()
subparser.consume(to: .rightParen)
if subparser.currentToken.isKeyword {
var modifierProgress = LoopProgressCondition()
while let (modifierKind, handle) = subparser.at(anyIn: DeclarationModifier.self),
modifierKind != .classKeyword,
modifierProgress.evaluate(subparser.currentToken) {
subparser.eat(handle)
if subparser.at(.leftParen) {
subparser.consumeAnyToken()
subparser.consume(to: .rightParen)
}
}
}

Expand Down Expand Up @@ -75,10 +77,17 @@ extension TokenConsumer {
lookahead.consumeAnyToken()
} while lookahead.atStartOfDeclaration()
return lookahead.at(.identifier)
case .caseKeyword, nil:
case .caseKeyword:
// When 'case' appears inside a function, it's probably a switch
// case, not an enum case declaration.
return false
case nil:
if subparser.at(anyIn: ContextualDeclKeyword.self)?.0 != nil {
subparser.consumeAnyToken()
return subparser.atStartOfDeclaration(
isAtTopLevel: isAtTopLevel, allowRecovery: allowRecovery)
}
return false
default: return true
}
}
Expand Down Expand Up @@ -321,6 +330,13 @@ extension Parser {
} while keepGoing != nil && loopProgress.evaluate(currentToken)
}

let whereClause: RawGenericWhereClauseSyntax?
if self.at(.whereKeyword) {
whereClause = self.parseGenericWhereClause()
} else {
whereClause = nil
}

let rangle: RawTokenSyntax
if self.currentToken.starts(with: ">") {
rangle = self.consumeAnyToken(remapping: .rightAngle)
Expand All @@ -337,6 +353,7 @@ extension Parser {
return RawGenericParameterClauseSyntax(
leftAngleBracket: langle,
genericParameterList: parameters,
genericWhereClause: whereClause,
rightAngleBracket: rangle,
arena: self.arena)
}
Expand Down Expand Up @@ -1154,7 +1171,7 @@ extension Parser {

// Parse the '!' or '?' for a failable initializer.
let failable: RawTokenSyntax?
if let parsedFailable = self.consume(ifAny: [.exclamationMark, .postfixQuestionMark]) {
if let parsedFailable = self.consume(ifAny: [.exclamationMark, .postfixQuestionMark, .infixQuestionMark]) {
failable = parsedFailable
} else if let parsedFailable = self.consumeIfContextualPunctuator("!") {
failable = parsedFailable
Expand Down Expand Up @@ -1373,7 +1390,7 @@ extension Parser {
} else {
unexpectedBeforeReturnType = nil
}
let result = self.parseType()
let result = self.parseResultType()
let returnClause = RawReturnClauseSyntax(
unexpectedBeforeArrow,
arrow: arrow,
Expand Down
19 changes: 13 additions & 6 deletions Sources/SwiftParser/Expressions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -962,14 +962,21 @@ extension Parser {
@_spi(RawSyntax)
public mutating func parseIdentifierExpression() -> RawExprSyntax {
let (name, args) = self.parseDeclNameRef(.compoundNames)
let identifier = RawIdentifierExprSyntax(
identifier: name, declNameArguments: args,
arena: self.arena)

guard self.lookahead().canParseAsGenericArgumentList() else {
return RawExprSyntax(identifier)
if name.tokenText.hasPrefix("<#") && name.tokenText.hasSuffix("#>") && args == nil {
return RawExprSyntax(
RawEditorPlaceholderExprSyntax(
identifier: name,
arena: self.arena))
}
return RawExprSyntax(RawIdentifierExprSyntax(
identifier: name, declNameArguments: args,
arena: self.arena))
}

let identifier = RawIdentifierExprSyntax(
identifier: name, declNameArguments: args,
arena: self.arena)
let generics = self.parseGenericArguments()
return RawExprSyntax(RawSpecializeExprSyntax(
expression: RawExprSyntax(identifier), genericArgumentClause: generics,
Expand Down Expand Up @@ -1794,7 +1801,7 @@ extension Parser {
// At this point, we know we have a closure signature. Parse the capture list
// and parameters.
var elements = [RawClosureCaptureItemSyntax]()
do {
if !self.at(.rightSquareBracket) {
var keepGoing: RawTokenSyntax? = nil
var loopProgress = LoopProgressCondition()
repeat {
Expand Down
36 changes: 31 additions & 5 deletions Sources/SwiftParser/Lexer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -904,7 +904,6 @@ extension Lexer.Cursor {
_ = self.advanceToEndOfLine()
continue
case UInt8(ascii: "*"):
self = start
_ = self.advanceToEndOfSlashStarComment()
continue
default:
Expand Down Expand Up @@ -1605,10 +1604,10 @@ extension Lexer.Cursor {
if !self.isAtEndOfFile, self.peek() == UInt8(ascii: ".") && TokStart.peek() != UInt8(ascii: ".") {
break
}
// if (Identifier::isEditorPlaceholder(StringRef(CurPtr, BufferEnd-CurPtr)) &&
// rangeContainsPlaceholderEnd(CurPtr + 2, BufferEnd)) {
// break
// }
let text = SyntaxText(baseAddress: self.input.baseAddress, count: self.input.count)
if text.hasPrefix("<#") && text.containsPlaceholderEnd() {
break
}

// // If we are lexing a `/.../` regex literal, we don't consider `/` to be an
// // operator character.
Expand Down Expand Up @@ -2055,6 +2054,13 @@ extension Lexer.Cursor {
case nil:
return nil
case UInt8(ascii: "/"):
// If we're at the end of the literal, peek ahead to see if the closing
// slash is actually the start of a comment.
if !Tmp.isAtEndOfFile &&
(Tmp.peek() == UInt8(ascii: "/") || Tmp.peek() == UInt8(ascii: "*")) {
return nil
}

var EndLex = Tmp
for _ in 0..<poundCount {
guard EndLex.advance(matching: UInt8(ascii: "#")) != nil else {
Expand Down Expand Up @@ -2257,3 +2263,23 @@ extension Unicode.Scalar {
return self.value >= 0x20 && self.value < 0x7F
}
}

extension SyntaxText {
fileprivate func containsPlaceholderEnd() -> Bool {
guard self.count >= 2 else {
return false
}

for idx in 0..<(self.count - 1) {
let c = self[idx]
guard c != UInt8(ascii: "\n") else {
return false
}

if self[idx] == UInt8(ascii: "#"), self[idx+1] == UInt8(ascii: ">") {
return true
}
}
return false
}
}
46 changes: 45 additions & 1 deletion Sources/SwiftParser/Names.swift
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ extension Parser {
let type: RawTypeSyntax?
let dot: RawTokenSyntax?
if self.lookahead().canParseBaseTypeForQualifiedDeclName() {
type = self.parseTypeIdentifier()
type = self.parseQualifiedTypeIdentifier()
dot = self.consumePrefix(".", as: .period)
} else {
type = nil
Expand All @@ -151,6 +151,50 @@ extension Parser {
arguments: args,
arena: self.arena)
}

@_spi(RawSyntax)
public mutating func parseQualifiedTypeIdentifier() -> RawTypeSyntax {
if self.at(.anyKeyword) {
return RawTypeSyntax(self.parseAnyType())
}

var result: RawTypeSyntax?
var keepGoing: RawTokenSyntax? = nil
var loopProgress = LoopProgressCondition()
repeat {
let (name, _) = self.parseDeclNameRef()
let generics: RawGenericArgumentClauseSyntax?
if self.atContextualPunctuator("<") {
generics = self.parseGenericArguments()
} else {
generics = nil
}
if let keepGoing = keepGoing {
result = RawTypeSyntax(RawMemberTypeIdentifierSyntax(
baseType: result!,
period: keepGoing,
name: name,
genericArgumentClause: generics,
arena: self.arena))
} else {
result = RawTypeSyntax(RawSimpleTypeIdentifierSyntax(
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 {
break
}

keepGoing = self.consume(if: .period) ?? self.consume(if: .prefixPeriod)
} while keepGoing != nil && loopProgress.evaluate(currentToken)

return result!
}
}

extension Parser.Lookahead {
Expand Down
2 changes: 1 addition & 1 deletion Sources/SwiftParser/Patterns.swift
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ extension Parser {
return (pattern, nil)
}

let result = self.parseType()
let result = self.parseResultType()
let type = RawTypeAnnotationSyntax(
colon: colon,
type: result,
Expand Down
19 changes: 18 additions & 1 deletion Sources/SwiftParser/Types.swift
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,8 @@ extension Parser {
) {
base = RawTypeSyntax(RawMetatypeTypeSyntax(
baseType: base, period: period, typeOrProtocol: type, arena: self.arena))
}
continue
}

if !self.currentToken.isAtStartOfLine {
if self.currentToken.isOptionalToken {
Expand Down Expand Up @@ -860,6 +861,22 @@ extension Parser {
}
}

extension Parser {
mutating func parseResultType() -> RawTypeSyntax {
if self.currentToken.starts(with: "<") {
let generics = self.parseGenericParameters()
let baseType = self.parseType()
return RawTypeSyntax(
RawNamedOpaqueReturnTypeSyntax(
genericParameters: generics,
baseType: baseType,
arena: self.arena))
} else {
return self.parseType()
}
}
}

extension Lexer.Lexeme {
var isBinaryOperator: Bool {
return self.tokenKind == .spacedBinaryOperator
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,7 @@ allows Swift tools to parse, inspect, generate, and transform Swift source code.
- <doc:SwiftSyntax/TupleTypeSyntax>
- <doc:SwiftSyntax/FunctionTypeSyntax>
- <doc:SwiftSyntax/AttributedTypeSyntax>
- <doc:SwiftSyntax/NamedOpaqueReturnTypeSyntax>

### Patterns

Expand Down
Loading