-
Notifications
You must be signed in to change notification settings - Fork 440
[Parser] Attributes on MacroExpansionDeclSyntax #1650
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
[Parser] Attributes on MacroExpansionDeclSyntax #1650
Conversation
let (unexpectedBeforePound, poundKeyword) = self.eat(handle) | ||
// Don't allow space between '#' and the macro name. | ||
if poundKeyword.trailingTriviaByteLength != 0 || self.currentToken.leadingTriviaByteLength != 0 { | ||
return RawMacroExpansionDeclSyntax( |
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.
The intent here is that parse #
followed by whitespaces as #<missing identifier>
, and let the caller do the recovery.
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 think we should still continue macro attribute parsing if the identifier is at the same line as the #
. I would expect that to yield better recovery.
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 would you expect as the parsed tree. How do we embed "hasError"?
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.
For example if you have # elif
, we should have unexpectedBeforeMacroName = ' elif'
and macroName
should be a missing identifier with text elif
. That’s also what we do when parsing a. b
(where whitespace around the .
is not balanced).
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.
Thanks. Will do.
let poundKeyword = self.consumeAnyToken() | ||
let (unexpectedBeforeMacro, macro) = self.expectIdentifier() | ||
let (unexpectedBeforeMacro, macro) = self.expectIdentifier(keywordRecovery: 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.
e.g. #class
is invalid but should be recoverable.
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 you add a test case for #<keyword>
?
@swift-ci Please test |
0b2562a
to
72bc06a
Compare
@swift-ci Please test |
], | ||
fixedSource: """ | ||
#if MY_FLAG | ||
#<#identifier#>elif |
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.
Lost the space 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.
Yeah.. no idea what happened.. I guess the diagnostic is after the trailing trivia, but the diagnostic system removed the trailing trivia of #
because that's preferable. @ahoppen any idea?
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.
To start, this gets parsed as follows
SourceFileSyntax
IfConfigDeclSyntax
├─clauses: IfConfigClauseListSyntax
│ ╰─[0]: IfConfigClauseSyntax
│ ├─poundKeyword: poundIfKeyword
│ ├─condition: IdentifierExprSyntax
│ │ ╰─identifier: identifier("MY_FLAG")
│ ╰─elements: CodeBlockItemListSyntax
│ ├─[0]: CodeBlockItemSyntax
│ │ ├─item: MacroExpansionExprSyntax
│ │ │ ├─poundToken: pound
│ │ │ ├─macro: identifier("") MISSING
│ │ │ ╰─argumentList: TupleExprElementListSyntax
│ │ ╰─semicolon: semicolon MISSING
│ ╰─[1]: CodeBlockItemSyntax
│ ╰─item: IdentifierExprSyntax
│ ╰─identifier: identifier("elif")
╰─poundEndif: poundEndifKeyword
When inserting the missing identifier, we see that #
and identifiers don’t need to be separated by a space, so we remove the space. This is really useful for example if you have someCall(1 2)
where a comma is missing. In this case you want to insert it after immediate after the 1
and not the space.
So now to the next question: Why don’t we add a space after <#identifier#>
. For this, we look at the fixed up view of the tree and discover that the missing identifier is followed by a semicolon in that view. And identifier and semicolons don’t need to be separated by spaces. So no space that needs to be inserted here. We just suppress the missing semicolon diagnostic because the code item already has an error. Because most of the time the missing semicolon diagnostic is actually really misleading.
I think fix here would be to improve how we recover here in general so that elif
doesn’t end up in a separate CodeBlockItem
anymore.
@@ -1427,6 +1427,125 @@ final class DeclarationTests: XCTestCase { | |||
) | |||
} | |||
|
|||
func testAttributedMacroExpansionDeclaration() { |
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 test when split by a newline rather than space? And also one for with both attributes and modifiers?
let poundKeyword = self.consumeAnyToken() | ||
let (unexpectedBeforeMacro, macro) = self.expectIdentifier() | ||
let (unexpectedBeforeMacro, macro) = self.expectIdentifier(keywordRecovery: 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.
Could you add a test case for #<keyword>
?
@TiagoMaiaL note that this fixes part of #1395 (ie. whitespace between |
72bc06a
to
424d334
Compare
@swift-ci Please test |
@swift-ci Please test Windows |
* Add `attributes` and `modifiers` to `MacroExpansionDeclSyntax` * Diagnose whitespaces between # and the macro name * Attach attributes to MacroExpansionDeclSyntax rdar://107386648
424d334
to
fd89876
Compare
@swift-ci Please test |
1 similar comment
@swift-ci Please test |
let (unexpectedBeforePound, poundKeyword) = self.eat(handle) | ||
// Don't allow space between '#' and the macro name. | ||
if poundKeyword.trailingTriviaByteLength != 0 || self.currentToken.leadingTriviaByteLength != 0 { | ||
return RawMacroExpansionDeclSyntax( |
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 think we should still continue macro attribute parsing if the identifier is at the same line as the #
. I would expect that to yield better recovery.
], | ||
fixedSource: """ | ||
#if MY_FLAG | ||
#<#identifier#>elif |
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.
To start, this gets parsed as follows
SourceFileSyntax
IfConfigDeclSyntax
├─clauses: IfConfigClauseListSyntax
│ ╰─[0]: IfConfigClauseSyntax
│ ├─poundKeyword: poundIfKeyword
│ ├─condition: IdentifierExprSyntax
│ │ ╰─identifier: identifier("MY_FLAG")
│ ╰─elements: CodeBlockItemListSyntax
│ ├─[0]: CodeBlockItemSyntax
│ │ ├─item: MacroExpansionExprSyntax
│ │ │ ├─poundToken: pound
│ │ │ ├─macro: identifier("") MISSING
│ │ │ ╰─argumentList: TupleExprElementListSyntax
│ │ ╰─semicolon: semicolon MISSING
│ ╰─[1]: CodeBlockItemSyntax
│ ╰─item: IdentifierExprSyntax
│ ╰─identifier: identifier("elif")
╰─poundEndif: poundEndifKeyword
When inserting the missing identifier, we see that #
and identifiers don’t need to be separated by a space, so we remove the space. This is really useful for example if you have someCall(1 2)
where a comma is missing. In this case you want to insert it after immediate after the 1
and not the space.
So now to the next question: Why don’t we add a space after <#identifier#>
. For this, we look at the fixed up view of the tree and discover that the missing identifier is followed by a semicolon in that view. And identifier and semicolons don’t need to be separated by spaces. So no space that needs to be inserted here. We just suppress the missing semicolon diagnostic because the code item already has an error. Because most of the time the missing semicolon diagnostic is actually really misleading.
I think fix here would be to improve how we recover here in general so that elif
doesn’t end up in a separate CodeBlockItem
anymore.
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.
@rintaro and I chatted offline that he will address the comments in a follow-up PR
This addresses the review comments I made on swiftlang#1650. Co-Authored-By: Rintaro Ishizaki <[email protected]>
Addressing the review comments in #1665. |
This addresses the review comments I made on swiftlang#1650. Co-Authored-By: Rintaro Ishizaki <[email protected]>
This addresses the review comments I made on swiftlang#1650. Co-Authored-By: Rintaro Ishizaki <[email protected]>
This addresses the review comments I made on swiftlang#1650. Co-Authored-By: Rintaro Ishizaki <[email protected]>
This addresses the review comments I made on swiftlang#1650. Co-Authored-By: Rintaro Ishizaki <[email protected]>
This addresses the review comments I made on swiftlang#1650. Co-Authored-By: Rintaro Ishizaki <[email protected]>
Address review comments to #1650
attributes
andmodifiers
toMacroExpansionDeclSyntax
#
and the macro nameMacroExpansionDeclSyntax
rdar://107386648