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 lambda attributes #52038

Merged
merged 9 commits into from
Mar 31, 2021
Merged

Parse lambda attributes #52038

merged 9 commits into from
Mar 31, 2021

Conversation

cston
Copy link
Member

@cston cston commented Mar 21, 2021

Parse attributes on lambda expressions and lambda parameters.

Proposal: lambda-improvements.md
Test plan: #52192

@cston cston force-pushed the parse-attributes branch from 10648fb to 6d72d30 Compare March 22, 2021 01:32
@cston cston marked this pull request as ready for review March 22, 2021 01:34
@cston cston requested a review from a team as a code owner March 22, 2021 01:34
@333fred
Copy link
Member

333fred commented Mar 22, 2021

    private bool IsPossibleLambdaExpression(Precedence precedence)

This will need to be updated. #Closed


Refers to: src/Compilers/CSharp/Portable/Parser/LanguageParser.cs:11311 in 6d72d30. [](commit_id = 6d72d30, deletion_comment = False)

@333fred
Copy link
Member

333fred commented Mar 22, 2021

                return IsPossibleAnonymousMethodExpression() || IsPossibleLambdaExpression(Precedence.Expression);

Likely needs a new case for open brackets. #Closed


Refers to: src/Compilers/CSharp/Portable/Parser/LanguageParser.cs:9680 in 6d72d30. [](commit_id = 6d72d30, deletion_comment = False)

@cston
Copy link
Member Author

cston commented Mar 22, 2021

    private bool IsPossibleLambdaExpression(Precedence precedence)

This will need to be updated.

I didn't think so. This method is only called after scanning static or an identifier.


In reply to: 804442264 [](ancestors = 804442264)


Refers to: src/Compilers/CSharp/Portable/Parser/LanguageParser.cs:11311 in 6d72d30. [](commit_id = 6d72d30, deletion_comment = False)

@333fred
Copy link
Member

333fred commented Mar 22, 2021

    private bool IsPossibleLambdaExpression(Precedence precedence)

This method is only called after scanning static or an identifier.

Currently true. Which is why [Test] static () => {} isn't going to parse correctly currently in cases that call this :).


In reply to: 804445349 [](ancestors = 804445349,804442264)


Refers to: src/Compilers/CSharp/Portable/Parser/LanguageParser.cs:11311 in 6d72d30. [](commit_id = 6d72d30, deletion_comment = False)

@333fred
Copy link
Member

333fred commented Mar 22, 2021

Done review pass (commit 2) #Closed

@333fred
Copy link
Member

333fred commented Mar 24, 2021

Done review pass (commit 5) #Closed

@halter73
Copy link
Member

halter73 commented Mar 24, 2021

Proposed changes:

  1. Allow lambdas with attributes
  2. Allow lambdas with explicit return type
  3. Infer a natural delegate type for lambdas and method groups

Of the proposed changes in lambda-improvements.md, does this only cover item 1? If so, is item 3 next? Between 2 and 3, 3 is a lot more important for the usability of an API taking an arbitrary Delegate. Right now, it requires a lot of ugly casting.

@333fred
Copy link
Member

333fred commented Mar 24, 2021

This is just the parsing changes for 1. 3 requires no parsing changes and will come later.

@cston
Copy link
Member Author

cston commented Mar 24, 2021

@halter73

Of the proposed changes in lambda-improvements.md, does this only cover item 1?

This PR covers a part of 1: parsing the attribute syntax. Changes will be needed to bind and emit attributes as well. After that, I'll focus on 3.

@333fred
Copy link
Member

333fred commented Mar 25, 2021

Done review pass (commit 6)

Copy link
Member

@333fred 333fred left a comment

Choose a reason for hiding this comment

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

LGTM (commit 8)

@cston cston requested a review from a team March 26, 2021 02:37
@cston
Copy link
Member Author

cston commented Mar 29, 2021

@dotnet/roslyn-compiler, please review, thanks.

@jaredpar
Copy link
Member

@RikkiGibson

@cston cston requested a review from CyrusNajmabadi March 30, 2021 19:49
@CyrusNajmabadi
Copy link
Member

Looking now.

@@ -216,6 +216,7 @@ internal enum MessageID
IDS_FeatureVarianceSafetyForStaticInterfaceMembers = MessageBase + 12791,
IDS_FeatureConstantInterpolatedStrings = MessageBase + 12792,
IDS_FeatureMixedDeclarationsAndExpressionsInDeconstruction = MessageBase + 12793,
IDS_FeatureLambdaAttributes = MessageBase + 12798,
Copy link
Member

@CyrusNajmabadi CyrusNajmabadi Mar 30, 2021

Choose a reason for hiding this comment

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

curious why the +5 here. #Resolved

Copy link
Member Author

Choose a reason for hiding this comment

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

curious why the +5 here.

To reduce the chance of conflict with other features.


In reply to: 604497031 [](ancestors = 604497031)

@@ -11056,6 +11072,8 @@ private bool ScanExplicitlyTypedLambda(Precedence precedence)
// Advance past the open paren or comma.
this.EatToken();

_ = ParseAttributeDeclarations();
Copy link
Member

Choose a reason for hiding this comment

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

this is definitely so weird. i don't expect you to change this in this pr, but at this point, i think we need to rethink the whole 'scan then parse' approach we take. Now that scanning is effectively just running the parser to make its determination, we should think about just swithcing to a "just parse" approach. We'd still have rewind points, we just wouldn't be trying to duplicate the logic between these guys.

default:
result = false;
break;
}
Copy link
Member

@CyrusNajmabadi CyrusNajmabadi Mar 30, 2021

Choose a reason for hiding this comment

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

could just be a switch expression. #Resolved

result = IsPossibleLambdaExpressionCore(precedence);
break;
case SyntaxKind.OpenParenToken:
result = ScanParenthesizedImplicitlyTypedLambda(precedence) || ScanExplicitlyTypedLambda(precedence);
Copy link
Member

@CyrusNajmabadi CyrusNajmabadi Mar 30, 2021

Choose a reason for hiding this comment

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

should this go into a single ScanParenthesizedLambda helper? #Resolved

}

this.Reset(ref resetPoint);
this.Release(ref resetPoint);
Copy link
Member

@CyrusNajmabadi CyrusNajmabadi Mar 30, 2021

Choose a reason for hiding this comment

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

we generally put these in finallys. is it ok that these aren't? #Resolved

Copy link
Member

@CyrusNajmabadi CyrusNajmabadi Mar 30, 2021

Choose a reason for hiding this comment

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

also, if you do a try/finally, the body of this helper becomes much simpler,a nd less likely to have a problem later on (for example, if someone errantly returns). #Resolved

{
case SyntaxKind.StaticKeyword:
case SyntaxKind.IdentifierToken:
result = IsPossibleLambdaExpressionCore(precedence);
Copy link
Member

Choose a reason for hiding this comment

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

i don't love the assymetry here with the check above (though i grant it is correct). i would prefer another case of "== Static || IsTrueIdentifier" to make it clear tehse chunks are in sync, and aren't dependent on an internal detail of IsTrueIdentifier implying that this case is sufficient.

@@ -12233,6 +12291,7 @@ private SyntaxList<SyntaxToken> ParseAnonymousFunctionModifiers()

private LambdaExpressionSyntax ParseLambdaExpression()
{
var attributes = ParseAttributeDeclarations(checkLambdaAttributesFeature: true);
Copy link
Member

Choose a reason for hiding this comment

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

so this is technically a bit unclean, but hopefully not a problem. THe main issue is that this makes attribute parsing context sensitive. We generally like avoiding that as it makes incremental difficult. Specifically, it means we couldn't jsut say "ah, we can reuse the attributes from a previous parse that are available in this location".

In your case, i think you're ok. that's bacause you'll have one of the two cases:

  1. the attribute decl is on a lambda before. so it will have the feature diagnostic on it, so it will crumble if we try to reparse it.
  2. the attribute decl is not on a lambda. it is reused in ParseAttributeDecl, but we still add the diagnostic on afterwards.

This is subtle though and easy to get wrong. in general, it can be preferable to just do the parse normally and have the caller (i.e. this method) do the check.

Copy link
Member Author

Choose a reason for hiding this comment

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

this makes attribute parsing context sensitive. We generally like avoiding that as it makes incremental difficult.

I'm interested to understand how to handle the feature check in this method. Specifically, what would the resulting error be attached to that would simplify incremental parsing? (Perhaps I can address this in a subsequent PR.)


In reply to: 604500799 [](ancestors = 604500799)

Copy link
Member

@CyrusNajmabadi CyrusNajmabadi left a comment

Choose a reason for hiding this comment

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

Parsing looks correct. I couldn't think of any cases that would be a problem. I do have some suggestions on potential simplification/safety. But as they are not necessary, it's up to you if you want to do any of them.

@RikkiGibson RikkiGibson self-assigned this Mar 31, 2021
@cston cston merged commit 4463994 into dotnet:features/lambdas Mar 31, 2021
@cston cston deleted the parse-attributes branch March 31, 2021 15:02
@jcouv jcouv added this to the C# 10 milestone Apr 6, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants