Skip to content
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

Merged
merged 11 commits into from
Dec 19, 2016

Conversation

CyrusNajmabadi
Copy link
Member

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:

Task.
await Task.Delay(...);

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:

Id dot new-line
Id id

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.

X.
Y z;

In this case, there is no syntax error, so we have to accept this code as being a local variable declaration.

@CyrusNajmabadi
Copy link
Member Author

Tagging @dotnet/roslyn-compiler

@agocke To make sure i didn't break parsing of LocalFunctions.

@CyrusNajmabadi
Copy link
Member Author

@dotnet/roslyn-compiler Where is the best place to put tests for this?

@CyrusNajmabadi
Copy link
Member Author

I'm going to add tests to ExpressionParsingTests for this.

@CyrusNajmabadi
Copy link
Member Author

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);
Copy link
Member Author

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.

@CyrusNajmabadi
Copy link
Member Author

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:

foo.any_keyword

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.

@CyrusNajmabadi
Copy link
Member Author

Tagging @gafter for parser changes.

@CyrusNajmabadi
Copy link
Member Author

Ok, tests have been added.

// X.Y z;
// X.Y z = ...
// X.Y z, ...
// X.Y z( ... (local function)
Copy link
Member

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.

Copy link
Member Author

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 :-)

Copy link
Member

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?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sure!

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Test added.

@gafter gafter self-assigned this Dec 14, 2016
@gafter gafter added Area-Compilers Concept-Diagnostic Clarity The issues deals with the ease of understanding of errors and warnings. Feature Request labels Dec 14, 2016
@gafter
Copy link
Member

gafter commented Dec 14, 2016

@CyrusNajmabadi master is in ask mode for RC3. Are you confident this would pass ask mode?

@gafter gafter added this to the 2.0 (RC.3) milestone Dec 14, 2016
@CyrusNajmabadi
Copy link
Member Author

Fairly confident yes. This is a pretty reasonable situation and the experience is currently poor

Copy link
Member

@gafter gafter left a 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.

@gafter
Copy link
Member

gafter commented Dec 14, 2016

@dotnet/roslyn-compiler Can we have a second review here, please?

@AlekseyTs
Copy link
Contributor

AlekseyTs commented Dec 14, 2016

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.

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.

@CyrusNajmabadi
Copy link
Member Author

I would also be fine with more commits pushed to this.

@gafter
Copy link
Member

gafter commented Dec 16, 2016

@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.

@CyrusNajmabadi
Copy link
Member Author

Oh ok. I'll do taht and let you know when it's done.

@CyrusNajmabadi
Copy link
Member Author

@AlekseyTs Tests pushed.

@CyrusNajmabadi
Copy link
Member Author

retest windows_eta_open_prtest please

@CyrusNajmabadi
Copy link
Member Author

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)>
Copy link
Member Author

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))
Copy link
Member

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?

Copy link
Member

@jcouv jcouv left a 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;
}
Copy link
Contributor

@AlekseyTs AlekseyTs Dec 17, 2016

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

@AlekseyTs
Copy link
Contributor

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.

@CyrusNajmabadi
Copy link
Member Author

You're totally right aleksey. I'll make that change and resubmit

@AlekseyTs
Copy link
Contributor

LGTM

@CyrusNajmabadi
Copy link
Member Author

@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.

@CyrusNajmabadi
Copy link
Member Author

Merging in as i see that @gafter LGTMed this.

@CyrusNajmabadi CyrusNajmabadi merged commit 1338adc into dotnet:master Dec 19, 2016
@CyrusNajmabadi CyrusNajmabadi deleted the awaitCompletion2 branch December 19, 2016 21:34
gafter added a commit to gafter/roslyn that referenced this pull request Mar 10, 2017
gafter added a commit that referenced this pull request Mar 15, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Approved to merge Area-Compilers cla-already-signed Concept-Diagnostic Clarity The issues deals with the ease of understanding of errors and warnings. Feature Request
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Cannot get intellisense off of "Task." when followed by 'await'.
6 participants