- Notifications
You must be signed in to change notification settings - Fork 457
[SwiftParser] Diagnose missing 'in' after closure signature #3162
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
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
| @@ -744,6 +744,49 @@ public class ParseDiagnosticsGenerator: SyntaxAnyVisitor { | |
if shouldSkip(node) { | ||
return .skipChildren | ||
} | ||
if let unexpected = node.unexpectedBetweenReturnClauseAndInKeyword, | ||
let tokens = unexpected.onlyPresentTokens(satisfying: { _ in true }), | ||
!tokens.isEmpty | ||
{ | ||
addDiagnostic( | ||
unexpected, | ||
.unexpectedTokensPriorToIn, | ||
handledNodes: [unexpected.id] | ||
) | ||
let baseInKeyword: TokenSyntax = | ||
node.inKeyword.isMissing | ||
? node.inKeyword | ||
: node.inKeyword.with(\.presence, .missing) | ||
let adjustedInKeyword = | ||
baseInKeyword | ||
.with(\.leadingTrivia, tokens.first?.leadingTrivia ?? baseInKeyword.leadingTrivia) | ||
.with(\.trailingTrivia, []) | ||
addDiagnostic( | ||
unexpected, | ||
.expectedInAfterClosureSignature, | ||
fixIts: [ | ||
FixIt( | ||
message: InsertTokenFixIt(missingNodes: [Syntax(adjustedInKeyword)]), | ||
changes: [.makePresent(adjustedInKeyword)] | ||
) | ||
], | ||
handledNodes: [adjustedInKeyword.id] | ||
) | ||
} | ||
| ||
if node.inKeyword.isMissing { | ||
addDiagnostic( | ||
node.inKeyword, | ||
.expectedInAfterClosureSignature, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I thought missing tokens are also automatically handled ( There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is correct, this one is actually redundant and still gives the diagnostic, just with a different wording. I'll remove it | ||
fixIts: [ | ||
FixIt( | ||
message: InsertTokenFixIt(missingNodes: [Syntax(node.inKeyword)]), | ||
changes: [.makePresent(node.inKeyword)] | ||
) | ||
], | ||
handledNodes: [node.inKeyword.id] | ||
) | ||
} | ||
handleMisplacedEffectSpecifiers(effectSpecifiers: node.effectSpecifiers, output: node.returnClause) | ||
return .visitChildren | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,117 @@ | ||
@_spi(ExperimentalLanguageFeatures) @_spi(RawSyntax) import SwiftParser | ||
@_spi(ExperimentalLanguageFeatures) @_spi(RawSyntax) import SwiftSyntax | ||
import XCTest | ||
| ||
final class ClosureMissingInTests: ParserTestCase { | ||
| ||
func testMissingInAfterSignature() { | ||
assertParse( | ||
""" | ||
_ = { (x: Int) -> Int 1️⃣0 } | ||
""", | ||
diagnostics: [ | ||
DiagnosticSpec( | ||
locationMarker: "1️⃣", | ||
message: "expected 'in' after the closure signature", | ||
fixIts: ["insert 'in'"] | ||
) | ||
], | ||
fixedSource: """ | ||
_ = { (x: Int) -> Int in 0 } | ||
""" | ||
) | ||
} | ||
| ||
func testArrayLiteralNotMisparsedAsSignature() { | ||
assertParse( | ||
""" | ||
_ = { [x, y] } | ||
""" | ||
) | ||
} | ||
| ||
func testAsyncIsNotASignatureGate() { | ||
assertParse( | ||
""" | ||
_ = { async } | ||
""" | ||
) | ||
} | ||
| ||
func testShorthandParamsWithReturnType() { | ||
assertParse( | ||
""" | ||
_ = { x, _ -> Int 1️⃣x } | ||
""", | ||
diagnostics: [ | ||
DiagnosticSpec( | ||
locationMarker: "1️⃣", | ||
message: "expected 'in' after the closure signature", | ||
fixIts: ["insert 'in'"] | ||
) | ||
], | ||
fixedSource: """ | ||
_ = { x, _ -> Int in x } | ||
""" | ||
) | ||
} | ||
| ||
func testResyncTokensBeforeIn() { | ||
assertParse( | ||
""" | ||
_ = { () -> Int | ||
1️⃣2️⃣0 | ||
in | ||
1 | ||
} | ||
""", | ||
diagnostics: [ | ||
DiagnosticSpec( | ||
locationMarker: "1️⃣", | ||
message: "unexpected tokens prior to 'in'" | ||
), | ||
DiagnosticSpec( | ||
locationMarker: "2️⃣", | ||
message: "expected 'in' after the closure signature", | ||
fixIts: ["insert 'in'"] | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This doesn't look right. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, it's because of the resync fix it that adds an 'in', but doesn't remove the previous one. I'll update it depending on whether we should keep the fix it block in our new block or rely on the UnexpectedNodes diagnostic. | ||
), | ||
], | ||
fixedSource: | ||
""" | ||
_ = { () -> Int | ||
0 | ||
in | ||
in | ||
1 | ||
} | ||
""" | ||
) | ||
} | ||
| ||
func testMissingInInFunctionArgument() { | ||
assertParse( | ||
""" | ||
test(make: { () -> [Int] 1️⃣ | ||
return [3] | ||
}, consume: { _ in | ||
print("Test") | ||
}) | ||
""", | ||
diagnostics: [ | ||
DiagnosticSpec( | ||
locationMarker: "1️⃣", | ||
message: "expected 'in' after the closure signature", | ||
fixIts: ["insert 'in'"] | ||
) | ||
], | ||
fixedSource: | ||
""" | ||
test(make: { () -> [Int] in | ||
return [3] | ||
}, consume: { _ in | ||
print("Test") | ||
}) | ||
""" | ||
) | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What's the intent of this block?
Also, is
unexpectedTokensPriorToIn
really needed?UnexpectedNodesSyntax
should be automatically handled. I think it should diagnose it likeunexpected code in closure signature
or something.Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's the block synthesizing the custom diagnostic for the resync case and reporting the fix-it. This is indeed redundant, only difference being phrasing from the custom diagnostic message in ParserDiagnosticMessages ("unexpected ... in closure signature" instead of after closure signature).
However, if we remove it, we'll still have the generic diagnostic from the UnexpectedNodes visitor, but no fix it.
Should we keep it ?