-
Notifications
You must be signed in to change notification settings - Fork 4.1k
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
Parse incomplete code differently to better reflect user intent. #15885
Parse incomplete code differently to better reflect user intent. #15885
Conversation
Tagging @dotnet/roslyn-compiler @agocke To make sure i didn't break parsing of LocalFunctions. |
@dotnet/roslyn-compiler Where is the best place to put tests for this? |
I'm going to add tests to ExpressionParsingTests for this. |
a90b56a
to
8e82403
Compare
8e82403
to
0851ac8
Compare
Note: in these error situations we use the presence of the newline as a strong indicator of user intent. This works quite well in practice and makes the code parse intuitively as the user thinks it should. |
N(SyntaxKind.IdentifierToken, "Task"); | ||
} | ||
N(SyntaxKind.DotToken); | ||
M(SyntaxKind.IdentifierName); |
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.
note the missing identifier here. we now parse this as Task.<missing>
as would be expected from the given source.
See https://github.com/Microsoft/TypeScript/blob/master/src/compiler/parser.ts#L1923 as an example of how we dealt with this in TypeScript. This issue was more pronounced in TS/JS as the following is legal in their language:
So we would commonly have the first keyword of the next line getting sucked into the parse of the previous unterminated construct. This was an elegant way to address the issue low in the stack so that all higher levels worked well. |
Tagging @gafter for parser changes. |
Ok, tests have been added. |
// X.Y z; | ||
// X.Y z = ... | ||
// X.Y z, ... | ||
// X.Y z( ... (local 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.
There is also the case z[
which is an erroneous local array declaration.
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.
Fort that case, I think the new error handling heuristic will actually produce a better result :-)
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 please add a test case showing that?
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.
sure!
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.
Test added.
@CyrusNajmabadi |
Fairly confident yes. This is a pretty reasonable situation and the experience is currently poor |
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.
This looks good to me.
It would be nice to see the errors associated with the parse tests. I'll enhance the tests with that info in a separate PR.
@dotnet/roslyn-compiler Can we have a second review here, please? |
Why do we need to do this in a separate PR? I think it is appropriate to do this in this PR, this will make it easier to review and sign-off on the issue. @gafter If you want to help @CyrusNajmabadi, you can push commit to this PR. |
I would also be fine with more commits pushed to this. |
@CyrusNajmabadi Don't wait for me. I'm technically on vacation this month, and this is not the next thing I would do. If you and/or @AlekseyTs think that needs to be done as part of this PR (I don't), someone other than me will have to do it. |
Oh ok. I'll do taht and let you know when it's done. |
@AlekseyTs Tests pushed. |
retest windows_eta_open_prtest please |
Tagging @srivatsn For approval once @AlekseyTs Has taken a look and signed off. |
@@ -2830,6 +2833,7 @@ class C | |||
End Using | |||
End Function | |||
|
|||
' <WpfFact, Trait(Traits.Feature, Traits.Features.Completion)> |
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.
@rchande I tried to enable this test but it fails. Can you take a look and fix/enable the test?
{ | ||
var token1 = PeekToken(1); | ||
if (token1.Kind == SyntaxKind.DotToken && | ||
token1.TrailingTrivia.Any((int)SyntaxKind.EndOfLineTrivia)) |
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 this check sensitive to spaces?
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.
LGTM Thanks
token4Kind == SyntaxKind.CommaToken || | ||
token4Kind == SyntaxKind.OpenParenToken || | ||
token4Kind == SyntaxKind.LessThanToken; | ||
} |
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.
Should we simply continue if this condition is true? #Closed
LGTM. However, please consider if it is better to let the old remaining logic in IsPossibleLocalDeclarationStatement to be applied to the situation that the new code recognized as a possible valid declaration. |
You're totally right aleksey. I'll make that change and resubmit |
LGTM |
@gafter Are you ok with me merging this in? You assigned to yourself, so i wasn't sure if there was anything you wanted to do with this. |
Merging in as i see that @gafter LGTMed this. |
…on-error code. Related to dotnet#15885 Fixes dotnet#17683
Fixes #15881
This is a port of a parsing strategy we took in TypeScript to better deal with code as the user is typing it. Note that this is a change in how we parse code in error, and it involves heuristics to make the tree better match the user intent. The code in question is code like:
Today, the C# parser eagerly parses this as a local declaration of the form "Task.await Task". i.e. the "Type" is "Task.await" and the VariableDeclarator is "Task". This clearly doesn't match what the user intends, and it messes up higher layers of the stack.
Specifically, because "Task." is a QualifiedName, that changes how we treat it (when it really should be a MemberAccessExpression). Similarly, because it looks like we're declaring a local called 'Task', we introduce a bogus LocalSymbol into scope, which messes up binding of names like "Task" (it finds the local instead of the type).
The fix is to tweak how we parse here. We specifically look for the pattern:
And we do not think of it as a LocalDeclaration unless we see a following token that more definitely demonstrates that it is local-variable. i.e.
In this case, there is no syntax error, so we have to accept this code as being a local variable declaration.