Skip to content

Commit 2ac18f1

Browse files
committed
Add diagnostic for misplaced specifiers
1 parent a09903d commit 2ac18f1

File tree

9 files changed

+183
-71
lines changed

9 files changed

+183
-71
lines changed

Sources/SwiftParser/Declarations.swift

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1351,6 +1351,11 @@ extension Parser {
13511351

13521352
let modifiers = parseParameterModifiers(for: subject)
13531353

1354+
var misplacedSpecifiers: [RawTokenSyntax] = []
1355+
while let specifier = self.consume(ifAnyIn: TypeSpecifier.self) {
1356+
misplacedSpecifiers.append(specifier)
1357+
}
1358+
13541359
let firstName: RawTokenSyntax?
13551360
let secondName: RawTokenSyntax?
13561361
let unexpectedBeforeColon: RawUnexpectedNodesSyntax?
@@ -1387,7 +1392,7 @@ extension Parser {
13871392

13881393
let type: RawTypeSyntax?
13891394
if shouldParseType {
1390-
type = self.parseType()
1395+
type = self.parseType(misplacedSpecifiers: misplacedSpecifiers)
13911396
} else {
13921397
type = nil
13931398
}
@@ -1411,6 +1416,7 @@ extension Parser {
14111416
elements.append(RawFunctionParameterSyntax(
14121417
attributes: attrs,
14131418
modifiers: modifiers,
1419+
RawUnexpectedNodesSyntax(misplacedSpecifiers, arena: self.arena),
14141420
firstName: firstName,
14151421
secondName: secondName,
14161422
unexpectedBeforeColon,

Sources/SwiftParser/Diagnostics/ParseDiagnosticsGenerator.swift

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -179,9 +179,9 @@ public class ParseDiagnosticsGenerator: SyntaxAnyVisitor {
179179
if node.presence == .missing {
180180
// If there is an unexpected token in front of the identifier, we assume
181181
// that this unexpected token was intended to be the identifier we are missing.
182-
if case .identifier = node.tokenKind,
183-
let invalidIdentifier = node.previousToken(viewMode: .all),
184-
let previousParent = invalidIdentifier.parent?.as(UnexpectedNodesSyntax.self) {
182+
if node.tokenKind.isIdentifier,
183+
let invalidIdentifier = node.previousToken(viewMode: .all),
184+
let previousParent = invalidIdentifier.parent?.as(UnexpectedNodesSyntax.self) {
185185
let fixIts: [FixIt]
186186
if invalidIdentifier.tokenKind.isKeyword {
187187
fixIts = [FixIt(message: .wrapKeywordInBackticks, changes: [
@@ -273,9 +273,7 @@ public class ParseDiagnosticsGenerator: SyntaxAnyVisitor {
273273
return
274274
}
275275
let message: DiagnosticMessage?
276-
if let identifier = unexpected.onlyToken(where: {
277-
if case .identifier = $0.tokenKind { return true } else { return false }
278-
}) {
276+
if let identifier = unexpected.onlyToken(where: { $0.tokenKind.isIdentifier }) {
279277
message = IdentifierNotAllowedInOperatorName(identifier: identifier)
280278
} else if let tokens = unexpected.onlyTokens(satisfying: { _ in true }) {
281279
message = TokensNotAllowedInOperatorName(tokens: tokens)
@@ -459,6 +457,21 @@ public class ParseDiagnosticsGenerator: SyntaxAnyVisitor {
459457
return .visitChildren
460458
}
461459

460+
public override func visit(_ node: FunctionParameterSyntax) -> SyntaxVisitorContinueKind {
461+
if shouldSkip(node) {
462+
return .skipChildren
463+
}
464+
exchangeTokens(
465+
unexpected: node.unexpectedBetweenModifiersAndFirstName,
466+
unexpectedTokenCondition: { TypeSpecifier(token: $0) != nil },
467+
correctTokens: [node.type?.as(AttributedTypeSyntax.self)?.specifier],
468+
message: { SpecifierOnParameterName(misplacedSpecifiers: $0) },
469+
moveFixIt: { MoveTokensAfterTypeFixIt(movedTokens: $0) },
470+
removeRedundantFixIt: { RemoveRedundantFixIt(removeTokens: $0) }
471+
)
472+
return .visitChildren
473+
}
474+
462475
public override func visit(_ node: PrecedenceGroupAssignmentSyntax) -> SyntaxVisitorContinueKind {
463476
if shouldSkip(node) {
464477
return .skipChildren

Sources/SwiftParser/Diagnostics/ParserDiagnosticMessages.swift

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,14 @@ public struct MissingAttributeArgument: ParserError {
151151
}
152152
}
153153

154+
public struct SpecifierOnParameterName: ParserError {
155+
public let misplacedSpecifiers: [TokenSyntax]
156+
157+
public var message: String {
158+
return "\(nodesDescription(misplacedSpecifiers, format: false)) before a parameter name is not allowed"
159+
}
160+
}
161+
154162
public struct TokensNotAllowedInOperatorName: ParserError {
155163
public let tokens: [TokenSyntax]
156164

@@ -218,6 +226,15 @@ public struct MoveTokensAfterFixIt: ParserFixIt {
218226
}
219227
}
220228

229+
public struct MoveTokensAfterTypeFixIt: ParserFixIt {
230+
/// The token that should be moved
231+
public let movedTokens: [TokenSyntax]
232+
233+
public var message: String {
234+
"move \(nodesDescription(movedTokens, format: false)) after type"
235+
}
236+
}
237+
221238
public struct MoveTokensInFrontOfFixIt: ParserFixIt {
222239
/// The token that should be moved
223240
public let movedTokens: [TokenSyntax]

Sources/SwiftParser/Diagnostics/SyntaxExtensions.swift

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,3 +102,14 @@ extension SyntaxProtocol {
102102
return nil
103103
}
104104
}
105+
106+
extension TokenKind {
107+
var isIdentifier: Bool {
108+
switch self {
109+
case .identifier:
110+
return true
111+
default:
112+
return false
113+
}
114+
}
115+
}

Sources/SwiftParser/Names.swift

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -237,15 +237,13 @@ extension Parser.Lookahead {
237237

238238
extension Lexer.Lexeme {
239239
var canBeArgumentLabel: Bool {
240-
switch self.tokenKind {
241-
case .identifier where self.tokenText == "__shared" || self.tokenText == "__owned":
240+
if TypeSpecifier(lexeme: self) != nil {
242241
return false
242+
}
243+
switch self.tokenKind {
243244
case .identifier, .wildcardKeyword:
244245
// Identifiers, escaped identifiers, and '_' can be argument labels.
245246
return true
246-
case .inoutKeyword:
247-
// inout cannot be used as an argument label.
248-
return false
249247
default:
250248
// All other keywords can be argument labels.
251249
return self.isKeyword

Sources/SwiftParser/RawTokenKindSubset.swift

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -851,6 +851,46 @@ enum PrimaryExpressionStart: RawTokenKindSubset {
851851
}
852852
}
853853

854+
enum TypeSpecifier: RawTokenKindSubset {
855+
case inoutKeyword
856+
case owned
857+
case shared
858+
859+
init?(lexeme: Lexer.Lexeme) {
860+
switch (lexeme.tokenKind, lexeme.tokenText) {
861+
case (.inoutKeyword, _): self = .inoutKeyword
862+
case (.identifier, "__owned"): self = .owned
863+
case (.identifier, "__shared"): self = .shared
864+
default: return nil
865+
}
866+
}
867+
868+
init?(token: TokenSyntax) {
869+
switch (token.tokenKind, token.text) {
870+
case (.inoutKeyword, _): self = .inoutKeyword
871+
case (.contextualKeyword, "__owned"): self = .owned
872+
case (.contextualKeyword, "__shared"): self = .shared
873+
default: return nil
874+
}
875+
}
876+
877+
var rawTokenKind: RawTokenKind {
878+
switch self {
879+
case .inoutKeyword: return .inoutKeyword
880+
case .owned: return .identifier
881+
case .shared: return .identifier
882+
}
883+
}
884+
885+
var contextualKeyword: SyntaxText? {
886+
switch self {
887+
case .inoutKeyword: return nil
888+
case .owned: return "__owned"
889+
case .shared: return "__shared"
890+
}
891+
}
892+
}
893+
854894
/// Union of the following token kind subsets:
855895
/// - `AwaitTry`
856896
/// - `ExpressionPrefixOperator`

Sources/SwiftParser/TokenConsumer.swift

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -216,6 +216,14 @@ extension TokenConsumer {
216216
return nil
217217
}
218218
}
219+
220+
mutating func consume<Subset: RawTokenKindSubset>(ifAnyIn subset: Subset.Type) -> Self.Token? {
221+
if let (_, handle) = self.at(anyIn: subset) {
222+
return self.eat(handle)
223+
} else {
224+
return nil
225+
}
226+
}
219227
}
220228

221229
// MARK: Expecting Tokens (`expect`)

Sources/SwiftParser/Types.swift

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,8 @@ extension Parser {
3232
/// type → self-type
3333
/// type → '(' type ')'
3434
@_spi(RawSyntax)
35-
public mutating func parseType() -> RawTypeSyntax {
36-
let type = self.parseTypeScalar()
35+
public mutating func parseType(misplacedSpecifiers: [RawTokenSyntax] = []) -> RawTypeSyntax {
36+
let type = self.parseTypeScalar(misplacedSpecifiers: misplacedSpecifiers)
3737

3838
// Parse pack expansion 'T...'.
3939
if self.currentToken.isEllipsis {
@@ -47,8 +47,8 @@ extension Parser {
4747
return type
4848
}
4949

50-
mutating func parseTypeScalar() -> RawTypeSyntax {
51-
let (specifier, attrList) = self.parseTypeAttributeList()
50+
mutating func parseTypeScalar(misplacedSpecifiers: [RawTokenSyntax] = []) -> RawTypeSyntax {
51+
let (specifier, attrList) = self.parseTypeAttributeList(misplacedSpecifiers: misplacedSpecifiers)
5252
var base = RawTypeSyntax(self.parseSimpleOrCompositionType())
5353
if self.lookahead().isAtFunctionTypeArrow() {
5454
let firstEffect = self.parseEffectsSpecifier()
@@ -783,8 +783,12 @@ extension Parser.Lookahead {
783783

784784
extension Parser {
785785
@_spi(RawSyntax)
786-
public mutating func parseTypeAttributeList() -> (RawTokenSyntax?, RawAttributeListSyntax?) {
787-
let specifier = self.consume(ifAny: [.inoutKeyword], contextualKeywords: ["__shared", "__owned"])
786+
public mutating func parseTypeAttributeList(misplacedSpecifiers: [RawTokenSyntax] = []) -> (RawTokenSyntax?, RawAttributeListSyntax?) {
787+
var specifier: RawTokenSyntax? = self.consume(ifAnyIn: TypeSpecifier.self)
788+
// We can only stick one specifier on this type. Let's pick the first one
789+
if specifier == nil, let misplacedSpecifier = misplacedSpecifiers.first {
790+
specifier = missingToken(misplacedSpecifier.tokenKind, text: misplacedSpecifier.tokenText)
791+
}
788792

789793
if self.at(any: [.atSign, .inoutKeyword]) {
790794
return (specifier, self.parseTypeAttributeListPresent())

0 commit comments

Comments
 (0)