Skip to content

Commit ee1bd18

Browse files
committed
Fix to produce a single diagnostic that says it's invalid when an unexpected node exists between the argument and the right paren in AttributeSyntax
1 parent 6941aa6 commit ee1bd18

File tree

4 files changed

+81
-30
lines changed

4 files changed

+81
-30
lines changed

Sources/SwiftParserDiagnostics/ParseDiagnosticsGenerator.swift

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -459,6 +459,29 @@ public class ParseDiagnosticsGenerator: SyntaxAnyVisitor {
459459
)
460460
return .visitChildren
461461
}
462+
if let unexpectedBetweenArgumentAndRightParen = node.unexpectedBetweenArgumentAndRightParen,
463+
var missingArgumentNode = node.argument?.missingNodes.only
464+
{
465+
if missingArgumentNode.kind == .token,
466+
let expectedNode = missingArgumentNode.parent?.ancestorOrSelf(mapping: { node in
467+
if node.nodeTypeNameForDiagnostics(allowBlockNames: false) != nil {
468+
return node
469+
} else {
470+
return nil
471+
}
472+
})
473+
{
474+
missingArgumentNode = expectedNode
475+
}
476+
addDiagnostic(
477+
missingArgumentNode,
478+
ConnectedExpectedNodeAndUnexpectedNodesError(expectedNode: missingArgumentNode, unexpectedNodes: unexpectedBetweenArgumentAndRightParen),
479+
handledNodes: [
480+
missingArgumentNode.id,
481+
unexpectedBetweenArgumentAndRightParen.id,
482+
]
483+
)
484+
}
462485
return .visitChildren
463486
}
464487

Sources/SwiftParserDiagnostics/ParserDiagnosticMessages.swift

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -287,6 +287,34 @@ public struct CannotParseVersionTuple: ParserError {
287287
}
288288
}
289289

290+
public struct ConnectedExpectedNodeAndUnexpectedNodesError: ParserError {
291+
public let expectedNode: Syntax
292+
public let unexpectedNodes: UnexpectedNodesSyntax
293+
294+
public var message: String {
295+
if let expectedNodeTypeName = expectedNode.nodeTypeNameForDiagnostics(allowBlockNames: false) {
296+
if expectedNode.isMissingAllTokens {
297+
return "expected \(expectedNodeTypeName), \(unexpectedNodes.shortSingleLineContentDescription) is not the valid \(expectedNodeTypeName)"
298+
} else {
299+
if expectedNode.position > unexpectedNodes.position {
300+
if expectedNode.leadingTrivia.isEmpty {
301+
return "expected \(expectedNodeTypeName), code '\(unexpectedNodes.description + expectedNode.description)' is not the valid \(expectedNodeTypeName)"
302+
} else {
303+
return "expected \(expectedNodeTypeName), \(unexpectedNodes.shortSingleLineContentDescription) and \(expectedNode.shortSingleLineContentDescription) are not the valid \(expectedNodeTypeName)"
304+
}
305+
} else if expectedNode.position < unexpectedNodes.position {
306+
if expectedNode.trailingTrivia.isEmpty {
307+
return "expected \(expectedNodeTypeName), code '\(expectedNode.description + unexpectedNodes.description)' is not the valid \(expectedNodeTypeName)"
308+
} else {
309+
return "expected \(expectedNodeTypeName), \(unexpectedNodes.shortSingleLineContentDescription) and \(expectedNode.shortSingleLineContentDescription) are not the valid \(expectedNodeTypeName)"
310+
}
311+
}
312+
}
313+
}
314+
return "unexpected \(unexpectedNodes.shortSingleLineContentDescription)"
315+
}
316+
}
317+
290318
public struct DuplicateEffectSpecifiers: ParserError {
291319
public let correctSpecifier: TokenSyntax
292320
public let unexpectedSpecifier: TokenSyntax

Sources/SwiftParserDiagnostics/PresenceUtils.swift

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,13 +34,33 @@ class PresentNodeChecker: SyntaxAnyVisitor {
3434
}
3535
}
3636

37+
/// Walks a tree and collects the missing nodes in it.
38+
class MissingNodeChecker: SyntaxAnyVisitor {
39+
var missingNodes: [Syntax] = []
40+
41+
override func visitAny(_ node: Syntax) -> SyntaxVisitorContinueKind {
42+
if node.isMissingAllTokens {
43+
missingNodes.append(node)
44+
return .skipChildren
45+
}
46+
return .visitChildren
47+
}
48+
}
49+
3750
extension SyntaxProtocol {
3851
/// Returns `true` if all tokens nodes in this tree are missing.
3952
var isMissingAllTokens: Bool {
4053
let checker = PresentNodeChecker(viewMode: .all)
4154
checker.walk(Syntax(self))
4255
return !checker.hasPresentToken
4356
}
57+
58+
/// Returns all missing nodes in this tree
59+
var missingNodes: [Syntax] {
60+
let checker = MissingNodeChecker(viewMode: .all)
61+
checker.walk(Syntax(self))
62+
return checker.missingNodes
63+
}
4464
}
4565

4666
/// Transforms a syntax tree by making all missing tokens present.

Tests/SwiftParserTest/AvailabilityTests.swift

Lines changed: 10 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -148,28 +148,18 @@ final class AvailabilityTests: XCTestCase {
148148
func test() {}
149149
""",
150150
diagnostics: [
151-
DiagnosticSpec(message: "expected version tuple in version restriction", fixIts: ["insert version tuple"]),
152-
DiagnosticSpec(message: "unexpected code '10e10' in attribute"),
153-
],
154-
fixedSource: """
155-
@available(OSX <#integer literal#>10e10)
156-
func test() {}
157-
"""
151+
DiagnosticSpec(message: "expected version tuple, code '10e10' is not the valid version tuple")
152+
]
158153
)
159154

160155
assertParse(
161156
"""
162-
@available(OSX 10.1️⃣0e10)
157+
@available(OSX 1️⃣10.0e10)
163158
func test() {}
164159
""",
165160
diagnostics: [
166-
DiagnosticSpec(message: "expected integer literal in version tuple", fixIts: ["insert integer literal"]),
167-
DiagnosticSpec(message: "unexpected code '0e10' in attribute"),
168-
],
169-
fixedSource: """
170-
@available(OSX 10.<#integer literal#>0e10)
171-
func test() {}
172-
"""
161+
DiagnosticSpec(message: "expected version tuple, code '10.0e10' is not the valid version tuple")
162+
]
173163
)
174164

175165
assertParse(
@@ -178,28 +168,18 @@ final class AvailabilityTests: XCTestCase {
178168
func test() {}
179169
""",
180170
diagnostics: [
181-
DiagnosticSpec(message: "expected version tuple in version restriction", fixIts: ["insert version tuple"]),
182-
DiagnosticSpec(message: "unexpected code '0xff' in attribute"),
183-
],
184-
fixedSource: """
185-
@available(OSX <#integer literal#>0xff)
186-
func test() {}
187-
"""
171+
DiagnosticSpec(message: "expected version tuple, code '0xff' is not the valid version tuple")
172+
]
188173
)
189174

190175
assertParse(
191176
"""
192-
@available(OSX 1.0.1️⃣0xff)
177+
@available(OSX 1️⃣1.0.0xff)
193178
func test() {}
194179
""",
195180
diagnostics: [
196-
DiagnosticSpec(message: "expected integer literal in version tuple", fixIts: ["insert integer literal"]),
197-
DiagnosticSpec(message: "unexpected code '0xff' in attribute"),
198-
],
199-
fixedSource: """
200-
@available(OSX 1.0.<#integer literal#>0xff)
201-
func test() {}
202-
"""
181+
DiagnosticSpec(message: "expected version tuple, code '1.0.0xff' is not the valid version tuple")
182+
]
203183
)
204184
}
205185
}

0 commit comments

Comments
 (0)