-
Notifications
You must be signed in to change notification settings - Fork 448
Convert lit
-based syntax classification test to XCTest
#1953
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
Conversation
XCTExpectFailure( | ||
""" | ||
'@available' is classified as @ and a typeIdentifier | ||
'iOS' is classified as a typeIdentifier | ||
'8.0' and '10.10' are classified as integerLiteral | ||
""" | ||
) |
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.
Hmm, this is expected by the way the syntax tree gets represented because by now we always model the name of an attribute as a type. Also the lines between builtin attributes (like @availablable
) with completely custom syntax, user-defined attributes that are types (like result builders and property wrappers) and user-defined types that aren’t types (attached macros) are blurring. So, what I would expect is available
to be classified as an attribute
.
You should be able to achieve that by changing \AttributeSyntax.attributeName
to return (.attribute, false)
in SyntaxClassification.classify(_ keyPath:)
Same for testAttribute2
where objc
and IBOutlet
should be classified as 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.
You should be able to achieve that by changing \AttributeSyntax.attributeName to return (.attribute, false)
Do you mean we changing this to (.attribute, true)
? Since \AttributeSyntax.attributeName
already returns (.attribute, false)
now.
If so, we may need to make some changes on handleLayout
(as my new commits) since \AttributeSyntax.attributeName
is not a TokenSyntax
and the second return value of SyntaxClassification.classify(_ keyPath:)
only has effects in handleToken
.
But I can't find a proper workaround for classifying the trivia pieces in a layout node for now though.
On a second thought, what do you think about refactor ClassificationVisitor
as a subclass of SyntaxAnyVisitor
like ParseDiagnosticsGenerator
. That should make this case handled easier. And the 8.0
and 10.10
can be specified as floatingLiteral
rather that some integerLiteral
s in a version tuple.
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.
Do you mean we changing this to
(.attribute, true)
? Since\AttributeSyntax.attributeName
already returns(.attribute, false)
now.If so, we may need to make some changes on
handleLayout
(as my new commits) since\AttributeSyntax.attributeName
is not aTokenSyntax
and the second return value ofSyntaxClassification.classify(_ keyPath:)
only has effects inhandleToken
.
Yes, that’s what I meant. But I didn’t realize it only had an effect on tokens 😢
On a second thought, what do you think about refactor
ClassificationVisitor
as a subclass ofSyntaxAnyVisitor
likeParseDiagnosticsGenerator
. That should make this case handled easier. And the8.0
and10.10
can be specified asfloatingLiteral
rather that someintegerLiteral
s in a version tuple.
I think that would be a good idea and should make the classifier easier to maintain. If the SyntaxAnyVisitor
-based implementation is significantly slower than the current one, we should look into why that is and see if we can make SyntaxAnyVisitor
faster.
In any case, I would prefer to do this in a follow-up PR. We can just keep the test case disabled for now. One note though: XCTExpectFailure
is not supported on Linux IIRC, so you would need to do try XCTSkipIf(true, <message>)
|
||
if let classification, classification.force == true { | ||
let range = SyntaxClassifiedRange( | ||
kind: classification.classification, | ||
range: ByteSourceRange(offset: byteOffset, length: child.byteLength - child.leadingTriviaByteLength - child.trailingTriviaByteLength) | ||
) | ||
report(range: range) | ||
byteOffset += child.byteLength | ||
continue | ||
} | ||
|
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, did you find a solution to #1953 (comment) after all? Or doesn’t this work?
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.
That could be a solution if we don't refactorClassificationVisitor
, and that works.😉
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 that’s a fine solution. Good idea 👍🏽
6f9c15b
to
9d7c597
Compare
9d7c597
to
54d3de5
Compare
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.
Thank you. Your idea with forcing the classification of an entire subtree sounds like a good solution for now. We would still be classifying the comment in the following as an attribute but that’s something we can fix in a rewrite of SyntaxClassifier
.
@MyType /* abc */ . NestedResultBuilder
func foo() {}
// Leading trivia. | ||
report(range: SyntaxClassifiedRange(kind: .none, range: ByteSourceRange(offset: byteOffset, length: child.leadingTriviaByteLength))) |
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.
We should also classify the trivia for doc comments (both leading and trailing). A test case to test that would be.
@MyAttribute // some comment
func foo() {}
To do that, I think you should factor out the following and call it to classify the trivia.
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.
Ah, yeah. But we also have to add two public properties leadingTriviaPieces
and trailingTriviaPieces
in RawSyntax
to get the trivia pieces as in my new commit.
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, I thought we could have a function to classify the trivia pieces so we don’t need to repeat this loop everywhere.
Something like
/// Classifies `trivia` and returns the number of bytes the trivia took up in the source
func classify(trivia: [RawTriviaPiece]) -> Int {
var classifiedBytes = 0
for triviaPiece in triviaPieces {
let range = triviaPiece.classify(offset: byteOffset)
report(range: range)
classifiedBytes += triviaPiece.byteLength
}
return classifiedBytes
}
And then here you can just do
if let triviaPieces = child.leadingTriviaPieces {
byteOffset += classify(trivia: triviaPieces)
}
And you can also use the function in handleToken
.
My thinking is that repeating an implementation twice is still OK but as soon as we reach 3 or more copies, we should really factor it out.
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.
Ah, I think that looks nice. 👍
|
||
if let classification, classification.force == true { | ||
let range = SyntaxClassifiedRange( | ||
kind: classification.classification, | ||
range: ByteSourceRange(offset: byteOffset, length: child.byteLength - child.leadingTriviaByteLength - child.trailingTriviaByteLength) | ||
) | ||
report(range: range) | ||
byteOffset += child.byteLength | ||
continue | ||
} | ||
|
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 that’s a fine solution. Good idea 👍🏽
54d3de5
to
ad6358a
Compare
ad6358a
to
462b69b
Compare
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.
Looks good 👍🏽 Thanks
@swift-ci Please test |
This breaks |
Sure, here is the link swiftlang/sourcekit-lsp#788 |
@swift-ci Please test |
We should use the
assertClassification
to make these test cases look nicer and get rid of the dependency onFileCheck
eventually.I converted a subset of the original
lit
-tests since I think most of them are mutually covered. There are some tests not producing the expected results and I wrote down the reasons inXCTExpectFailure
. Maybe we should determine whether changing the test cases or marking these as bugs to fix.