Skip to content

Commit c8c7465

Browse files
committed
Add diagnostic for wrong LabeledSpecializeEntry label
1 parent 1d6e574 commit c8c7465

File tree

4 files changed

+74
-75
lines changed

4 files changed

+74
-75
lines changed

Sources/SwiftParser/Attributes.swift

Lines changed: 15 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -638,55 +638,21 @@ extension Parser {
638638
}
639639

640640
extension Parser {
641-
enum SpecializeParameter: TokenSpecSet {
642-
case target
643-
case availability
644-
case exported
645-
case kind
646-
case spi
647-
case spiModule
648-
case available
649-
650-
init?(lexeme: Lexer.Lexeme) {
651-
switch PrepareForKeywordMatch(lexeme) {
652-
case TokenSpec(.target): self = .target
653-
case TokenSpec(.availability): self = .availability
654-
case TokenSpec(.exported): self = .exported
655-
case TokenSpec(.kind): self = .kind
656-
case TokenSpec(.spi): self = .spi
657-
case TokenSpec(.spiModule): self = .spiModule
658-
case TokenSpec(.available): self = .available
659-
default: return nil
660-
}
661-
}
662-
663-
var spec: TokenSpec {
664-
switch self {
665-
case .target: return .keyword(.target)
666-
case .availability: return .keyword(.availability)
667-
case .exported: return .keyword(.exported)
668-
case .kind: return .keyword(.kind)
669-
case .spi: return .keyword(.spi)
670-
case .spiModule: return .keyword(.spiModule)
671-
case .available: return .keyword(.available)
672-
}
673-
}
674-
}
675641
mutating func parseSpecializeAttributeArgumentList() -> RawSpecializeAttributeArgumentListSyntax {
676642
var elements = [RawSpecializeAttributeArgumentListSyntax.Element]()
677643
// Parse optional "exported" and "kind" labeled parameters.
678644
var loopProgress = LoopProgressCondition()
679-
while !self.at(.endOfFile, .rightParen, .keyword(.where)) && self.hasProgressed(&loopProgress) {
680-
switch self.at(anyIn: SpecializeParameter.self) {
645+
LOOP: while !self.at(.endOfFile, .rightParen, .keyword(.where)) && self.hasProgressed(&loopProgress) {
646+
switch self.at(anyIn: LabeledSpecializeArgumentSyntax.LabelOptions.self) {
681647
case (.target, let handle)?:
682-
let ident = self.eat(handle)
648+
let label = self.eat(handle)
683649
let (unexpectedBeforeColon, colon) = self.expect(.colon)
684650
let declName = self.parseDeclReferenceExpr([.zeroArgCompoundNames, .keywordsUsingSpecialNames, .operators])
685651
let comma = self.consume(if: .comma)
686652
elements.append(
687653
.specializeTargetFunctionArgument(
688654
RawSpecializeTargetFunctionArgumentSyntax(
689-
targetLabel: ident,
655+
targetLabel: label,
690656
unexpectedBeforeColon,
691657
colon: colon,
692658
declName: declName,
@@ -696,14 +662,14 @@ extension Parser {
696662
)
697663
)
698664
case (.availability, let handle)?:
699-
let ident = self.eat(handle)
665+
let label = self.eat(handle)
700666
let (unexpectedBeforeColon, colon) = self.expect(.colon)
701667
let availability = self.parseAvailabilitySpecList()
702668
let (unexpectedBeforeSemi, semi) = self.expect(.semicolon)
703669
elements.append(
704670
.specializeAvailabilityArgument(
705671
RawSpecializeAvailabilityArgumentSyntax(
706-
availabilityLabel: ident,
672+
availabilityLabel: label,
707673
unexpectedBeforeColon,
708674
colon: colon,
709675
availabilityArguments: availability,
@@ -714,7 +680,7 @@ extension Parser {
714680
)
715681
)
716682
case (.available, let handle)?:
717-
let ident = self.eat(handle)
683+
let label = self.eat(handle)
718684
let (unexpectedBeforeColon, colon) = self.expect(.colon)
719685
// FIXME: I have no idea what this is supposed to be, but the Syntax
720686
// tree only allows us to insert a token so we'll take anything.
@@ -723,7 +689,7 @@ extension Parser {
723689
elements.append(
724690
.labeledSpecializeArgument(
725691
RawLabeledSpecializeArgumentSyntax(
726-
label: ident,
692+
label: label,
727693
unexpectedBeforeColon,
728694
colon: colon,
729695
value: available,
@@ -733,14 +699,14 @@ extension Parser {
733699
)
734700
)
735701
case (.exported, let handle)?:
736-
let ident = self.eat(handle)
702+
let label = self.eat(handle)
737703
let (unexpectedBeforeColon, colon) = self.expect(.colon)
738704
let (unexpectedBeforeValue, value) = self.expect(.keyword(.true), .keyword(.false), default: .keyword(.false))
739705
let comma = self.consume(if: .comma)
740706
elements.append(
741707
.labeledSpecializeArgument(
742708
RawLabeledSpecializeArgumentSyntax(
743-
label: ident,
709+
label: label,
744710
unexpectedBeforeColon,
745711
colon: colon,
746712
unexpectedBeforeValue,
@@ -751,14 +717,14 @@ extension Parser {
751717
)
752718
)
753719
case (.kind, let handle)?:
754-
let ident = self.eat(handle)
720+
let label = self.eat(handle)
755721
let (unexpectedBeforeColon, colon) = self.expect(.colon)
756722
let valueLabel = self.parseAnyIdentifier()
757723
let comma = self.consume(if: .comma)
758724
elements.append(
759725
.labeledSpecializeArgument(
760726
RawLabeledSpecializeArgumentSyntax(
761-
label: ident,
727+
label: label,
762728
unexpectedBeforeColon,
763729
colon: colon,
764730
value: valueLabel,
@@ -769,14 +735,14 @@ extension Parser {
769735
)
770736
case (.spiModule, let handle)?,
771737
(.spi, let handle)?:
772-
let ident = self.eat(handle)
738+
let label = self.eat(handle)
773739
let (unexpectedBeforeColon, colon) = self.expect(.colon)
774740
let valueLabel = self.consumeAnyToken()
775741
let comma = self.consume(if: .comma)
776742
elements.append(
777743
.labeledSpecializeArgument(
778744
RawLabeledSpecializeArgumentSyntax(
779-
label: ident,
745+
label: label,
780746
unexpectedBeforeColon,
781747
colon: colon,
782748
value: valueLabel,
@@ -786,22 +752,7 @@ extension Parser {
786752
)
787753
)
788754
case nil:
789-
let ident = self.consumeAnyToken()
790-
let (unexpectedBeforeColon, colon) = self.expect(.colon)
791-
let valueLabel = self.consumeAnyToken()
792-
let comma = self.consume(if: .comma)
793-
elements.append(
794-
.labeledSpecializeArgument(
795-
RawLabeledSpecializeArgumentSyntax(
796-
label: ident,
797-
unexpectedBeforeColon,
798-
colon: colon,
799-
value: valueLabel,
800-
trailingComma: comma,
801-
arena: self.arena
802-
)
803-
)
804-
)
755+
break LOOP
805756
}
806757
}
807758

Sources/SwiftParserDiagnostics/ParseDiagnosticsGenerator.swift

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1226,6 +1226,25 @@ public class ParseDiagnosticsGenerator: SyntaxAnyVisitor {
12261226
return .visitChildren
12271227
}
12281228

1229+
public override func visit(_ node: LabeledSpecializeArgumentSyntax) -> SyntaxVisitorContinueKind {
1230+
if shouldSkip(node) {
1231+
return .skipChildren
1232+
}
1233+
1234+
if let unexpectedIdentifier = node.unexpectedBeforeLabel?.onlyPresentToken(where: { $0.tokenKind.isIdentifier }) {
1235+
addDiagnostic(
1236+
unexpectedIdentifier,
1237+
UnknownParameterError(
1238+
parameter: unexpectedIdentifier,
1239+
validParameters: LabeledSpecializeArgumentSyntax.LabelOptions.allCases.map { $0.tokenSyntax }
1240+
),
1241+
handledNodes: [unexpectedIdentifier.id, node.label.id]
1242+
)
1243+
}
1244+
1245+
return .visitChildren
1246+
}
1247+
12291248
public override func visit(_ node: MacroExpansionDeclSyntax) -> SyntaxVisitorContinueKind {
12301249
if shouldSkip(node) {
12311250
return .skipChildren

Sources/SwiftParserDiagnostics/ParserDiagnosticMessages.swift

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -524,6 +524,23 @@ public struct UnknownDirectiveError: ParserError {
524524
}
525525
}
526526

527+
public struct UnknownParameterError: ParserError {
528+
public let parameter: TokenSyntax
529+
public let validParameters: [TokenSyntax]
530+
531+
public var message: String {
532+
var message = "unknown parameter '\(parameter.text)'"
533+
534+
if let parentTypeName = parameter.parent?.ancestorOrSelf(mapping: { $0.nodeTypeNameForDiagnostics(allowBlockNames: false) }) {
535+
message += " in \(parentTypeName)"
536+
}
537+
538+
message += "; valid parameters are \(nodesDescription(validParameters, format: true))"
539+
540+
return message
541+
}
542+
}
543+
527544
// MARK: - Notes (please sort alphabetically)
528545

529546
public struct EffectSpecifierDeclaredHere: ParserNote {

Tests/SwiftParserTest/AttributeTests.swift

Lines changed: 23 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -73,41 +73,53 @@ final class AttributeTests: XCTestCase {
7373
func testMissingClosingParenToAttribute() {
7474
assertParse(
7575
"""
76-
@_specializeℹ️(e1️⃣
76+
@_specializeℹ️(1️⃣e
77+
func foo() {}
7778
""",
7879
diagnostics: [
7980
DiagnosticSpec(
80-
message: "expected ':' in attribute argument",
81-
fixIts: ["insert ':'"]
81+
locationMarker: "1️⃣",
82+
message: "expected argument for '@_specialize' attribute",
83+
fixIts: ["insert attribute argument"]
8284
),
8385
DiagnosticSpec(
86+
locationMarker: "1️⃣",
8487
message: "expected ')' to end attribute",
8588
notes: [NoteSpec(message: "to match this opening '('")],
8689
fixIts: ["insert ')'"]
8790
),
8891
DiagnosticSpec(
89-
message: "expected declaration after attribute",
90-
fixIts: ["insert declaration"]
92+
locationMarker: "1️⃣",
93+
message: "unexpected code 'e' in function"
9194
),
9295
],
9396
fixedSource: """
94-
@_specialize(e:) <#declaration#>
97+
@_specialize()e
98+
func foo() {}
9599
"""
96100
)
97101
}
98102

99103
func testMultipleInvalidSpecializeParams() {
100104
assertParse(
101105
"""
102-
@_specialize(e1️⃣, exported2️⃣)3️⃣
106+
@_specialize(1️⃣e, exported)2️⃣
107+
func foo() {}
103108
""",
104109
diagnostics: [
105-
DiagnosticSpec(locationMarker: "1️⃣", message: "expected ':' in attribute argument", fixIts: ["insert ':'"]),
106-
DiagnosticSpec(locationMarker: "2️⃣", message: "expected ': false' in attribute argument", fixIts: ["insert ': false'"]),
107-
DiagnosticSpec(locationMarker: "3️⃣", message: "expected declaration after attribute", fixIts: ["insert declaration"]),
110+
DiagnosticSpec(
111+
locationMarker: "1️⃣",
112+
message: "expected argument for '@_specialize' attribute",
113+
fixIts: ["insert attribute argument"]
114+
),
115+
DiagnosticSpec(
116+
locationMarker: "1️⃣",
117+
message: "unexpected code 'e, exported' in attribute"
118+
),
108119
],
109120
fixedSource: """
110-
@_specialize(e:, exported: false) <#declaration#>
121+
@_specialize(e, exported)
122+
func foo() {}
111123
"""
112124
)
113125
}

0 commit comments

Comments
 (0)