-
Notifications
You must be signed in to change notification settings - Fork 440
Emit diagnostics for missing nodes #739
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
Emit diagnostics for missing nodes #739
Conversation
cfa069b
to
6422f11
Compare
6422f11
to
e9cd304
Compare
e9cd304
to
bf5e9e8
Compare
@swift-ci Please test |
@@ -13,7 +13,7 @@ | |||
@_spi(RawSyntax) import SwiftSyntax | |||
|
|||
extension TokenConsumer { | |||
func atStartOfDeclaration(isAtTopLevel: Bool = false, allowRecovery: Bool = false) -> Bool { | |||
func atStartOfDeclaration(isAtTopLevel: Bool = false, allowRecovery: Bool = false, allowCase: Bool = false) -> Bool { |
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.
Not used?
if let parent = missingNode.parent, | ||
let childName = parent.childNameForDiagnostics(missingNode.index) { | ||
message = "Expected \(childName)" | ||
if let parent = missingNode.parent, |
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.
Already have parent
let element = RawArrayElementSyntax( | ||
expression: el, trailingComma: comma, arena: self.arena | ||
) | ||
if element.raw.byteLength == 0 { |
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.
Could add a .isEmpty
?
@@ -11,6 +11,7 @@ final class AttributeTests: XCTestCase { | |||
} | |||
""", | |||
diagnostics: [ | |||
DiagnosticSpec(locationMarker: "DIAG_2", message: "Expected declaration"), | |||
DiagnosticSpec(locationMarker: "DIAG_1", message: "Expected 'for' in attribute argument"), | |||
DiagnosticSpec(locationMarker: "DIAG_1", message: "Expected ':' in attribute argument"), | |||
DiagnosticSpec(locationMarker: "DIAG_2", message: "Expected ')' to end attribute"), |
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.
Is there an issue already for not eating func
here?
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.
Now we do: #821
DiagnosticSpec(message: "Expected declaration"), | ||
DiagnosticSpec(message: "Expected ':' in attribute argument"), | ||
DiagnosticSpec(message: "Expected ')' to end attribute"), |
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 do you think about special-casing trailing missing nodes? Ie. If a parent has multiple sequential missing nodes, instead use Expected complete <parent>
. Open to ideas for "complete" there. Ideally pretty printing (which we don't have yet) could then output an example/grammar/something else as well 🤔.
I'd also expect an after attribute
for Expected declaration
, ie. Expected declaration after attribute
+ for it to be after the attribute diagnostics.
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 do you think about special-casing trailing missing nodes? Ie. If a parent has multiple sequential missing nodes, instead use
Expected complete <parent>
. Open to ideas for "complete" there. Ideally pretty printing (which we don't have yet) could then output an example/grammar/something else as well 🤔.
Let’s do this in a follow-up PR because it might be non-trivial. #822
I'd also expect an
after attribute
forExpected declaration
, ie.Expected declaration after attribute
+
Done
for it to be after the attribute diagnostics.
This is non-trivial because we can’t just order the diagnostics by their indexInTree
anymore. Let’s consider do it in a follow-up PR. #823
if let lastChild = missingNode.lastToken(viewMode: .fixedUp), lastChild.presence == .present { | ||
message += " after '\(lastChild.text)'" | ||
} else if let previousToken = missingNode.previousToken(viewMode: .fixedUp), previousToken.presence == .present { | ||
message += " after '\(previousToken.text)'" |
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.
If previousToken.parent != missingNode.parent
, could we use the parent of the previous token rather than the token itself? That would give both the attribute tests Expected declaration after attribute
rather than empty/')', but still keep after <token>
for cases where it makes sense (eg. a ? b :
).
Actually speaking of the ternary case, shouldn't it have in ternary expression
at the end of it?
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.
If
previousToken.parent != missingNode.parent
, could we use the parent of the previous token rather than the token itself? That would give both the attribute testsExpected declaration after attribute
rather than empty/')', but still keepafter <token>
for cases where it makes sense (eg.a ? b :
).
after attribute
should now be added based.
Actually speaking of the ternary case, shouldn't it have
in ternary expression
at the end of it?
It doesn’t have in ternary expression
at the end of it because the expression is not missing inside the ternary expression but in the sequence expression. For a ? b :
the syntax tree looks like
- IdentifierExpr:
a
- UnresolvedTernaryExpr:
? b :
- MissingExpr
Filed #824 so we can take care of it later.
@@ -396,27 +415,35 @@ final class ExpressionTests: XCTestCase { | |||
AssertParse( | |||
"foo ? 1#^DIAG^#", | |||
diagnostics: [ | |||
DiagnosticSpec(message: "Expected ':' after '? ...' in ternary expression") | |||
DiagnosticSpec(message: "Expected ':' after '? ...' in ternary expression"), | |||
DiagnosticSpec(message: "Expected expression"), |
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.
Expanding on merging the missing trailing nodes, maybe we could list the missing nodes as well? Expected complete ternary expression, missing ':' and expression
. I admit that sounds a little weird though 🤔
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.
Let’s take care of this as part of merging diagnostics: #822
DiagnosticSpec(locationMarker: "KEY_PATH_2", message: "Expected expression of key path"), | ||
DiagnosticSpec(locationMarker: "END", message: #"Expected '"' in string literal"#), | ||
DiagnosticSpec(locationMarker: "END", message: "Expected '}' to end 'if' statement"), | ||
DiagnosticSpec(locationMarker: "END", message: "Expected '}' to end function"), |
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.
Unrelated to the PR - we need to add notes and test those as well at some point (eg. I'd expect a function starts here
note).
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.
Filed #825 so we can do it later.
@@ -486,7 +486,7 @@ public enum SyntaxEnum { | |||
case .parameterClause: | |||
return "parameter clause" | |||
case .returnClause: | |||
return "return clause" |
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 diagnostic did you not want to have return clause
? IMO diagnostics with "after return" do make sense.
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.
struct Foo {
subscript(x: String) {}
}
If we keep return clause
as a node name we emit
Expected return type in return clause
instead of
Expected return type in subscript
I liked the second better than the first.
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.
Oh sorry, it's the return clause within a declaration. Sure, definitely agree with you there 👍
@@ -618,13 +618,13 @@ | |||
Node('KeyPathExpr', name_for_diagnostics='key path', kind='Expr', | |||
children=[ | |||
Child('Backslash', kind='BackslashToken'), | |||
Child('RootExpr', kind='Expr', is_optional=True, | |||
Child('RootExpr', kind='Expr', name_for_diagnostics='root', is_optional=True, |
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.
I haven't gone through all of these (do you want me to?) but should we be careful to use the swift.org language here? Ie. type name
and key path components
?
Also, are child names for optional nodes useful? Would they ever actually be used?
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.
I haven't gone through all of these (do you want me to?) but should we be careful to use the swift.org language here? Ie.
type name
andkey path components
?
Honestly, I didn’t put too much effort into this (still took me 30-60 minutes to create these annotations). If you’ve got an idea how to create better names in a principled way, I’d be open to suggestions. Also, we can always update them later, so I don’t think it has to be blocking this PR.
My idea was that we update these as we discover diagnostics that we don’t like…
Also, are child names for optional nodes useful? Would they ever actually be used?
They aren’t being used right now but they don’t hurt either. And maybe we’ll come up with a use case later.
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.
Fair enough.
bf5e9e8
to
cecf603
Compare
@swift-ci Please test |
Re-opening #657 without dependencies
Previously, we weren’t emitting any errors for the missing layout nodes (RawMissingExprSyntax etc.)