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

Introduce local variable supporting target-type new syntax #76342

Conversation

MartyIX
Copy link
Contributor

@MartyIX MartyIX commented Dec 9, 2024

Our project uses target-type new syntax. Whenever, I have a line like this one:

int sum = Sum([|new Numbers()|]);

and I want to extract a new variable from the marked expression, I get

Numbers numbers = new Numbers();
int sum = Sum(numbers);

but I would like to get

Numbers numbers = new(); // i.e. `new()` here and not `new Numbers()`.
int sum = Sum(numbers);

That's why I created this PR. Are you interested in such feature?1 Could you give me some pointers how to finish this PR (as it does not work at the moment)?

Footnotes

  1. I sure am as I hit this daily. :)

@dotnet-issue-labeler dotnet-issue-labeler bot added Area-IDE untriaged Issues and PRs which have not yet been triaged by a lead labels Dec 9, 2024
@dotnet-policy-service dotnet-policy-service bot added Community The pull request was submitted by a contributor who is not a Microsoft employee. VSCode labels Dec 9, 2024
@@ -54,7 +55,7 @@ protected override Document IntroduceLocal(
[VariableDeclarator(
newLocalNameToken.WithAdditionalAnnotations(RenameAnnotation.Create()),
argumentList: null,
EqualsValueClause(expression.WithoutTrivia()))]));
EqualsValueClause(expression.WithAdditionalAnnotations(Simplifier.Annotation)).WithoutTrivia())]));
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Is there any chance that this might work? It does not but I'm not exactly sure why it doesn't. I thought it might.

Copy link
Member

Choose a reason for hiding this comment

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

i would not use the simplifier annotation. I would just generate the right expected results based ont he user's options specified.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I took the time to debug TestIntroduceLocalInExpressionBodiedIndexer. It is my understanding that var in that test (instead of explicit type name) is added in a postprocessing step. However, that postprocessing is not able to convert it to "target-type form".

So then your suggestion is a reasonably easy solution here.

@CyrusNajmabadi
Copy link
Member

Happy to help with this. And we would accept a PR for this as logn as the the following holds:

  1. if the user has 'prefer var when apparent/elsewhere' we should generate var numbers = new Numbers();
  2. if the user does not have those options on, but does have csharp_style_implicit_object_creation_when_type_is_apparent, then we should do Numbers numbers = new();
  3. otherwise, we should generate Numbers numbers = new Numbers();

If you can confirm this is the logic the PR maintains,a nd you have tests for this, i can review asap. I can also review any questions blocking you if you call them out. Thanks!

@CyrusNajmabadi CyrusNajmabadi self-assigned this Dec 9, 2024
@MartyIX
Copy link
Contributor Author

MartyIX commented Dec 9, 2024

Yes, that's I would like to achieve.

I added a few comments to specific lines of code where some help would be great

@MartyIX MartyIX force-pushed the feature/2024-12-09-TargetTypeNew-for-IntroduceLocalVariable branch from b01eca3 to 12a96b8 Compare December 22, 2024 20:13
@MartyIX MartyIX changed the title [wip] Introduce local variable supporting target-type new syntax Introduce local variable supporting target-type new syntax Dec 22, 2024
@MartyIX MartyIX marked this pull request as ready for review December 22, 2024 20:16
@MartyIX MartyIX requested a review from a team as a code owner December 22, 2024 20:16
@CyrusNajmabadi
Copy link
Member

FYI, it's vacation time. So things may be slow to respond. Feel free to ping me on jan2 if you haven't heard from anyone else!

@MartyIX MartyIX force-pushed the feature/2024-12-09-TargetTypeNew-for-IntroduceLocalVariable branch from 12a96b8 to 42634bd Compare December 22, 2024 20:38
@MartyIX
Copy link
Contributor Author

MartyIX commented Dec 22, 2024

FYI, it's vacation time. So things may be slow to respond. Feel free to ping me on jan2 if you haven't heard from anyone else!

Thank you for the heads up. It would be great if at least tests could be run for the PR, I would get some feedback. If not, no problem, I can wait.

Merry Christmas!

@CyrusNajmabadi
Copy link
Member

It would be great if at least tests could be run for the PR

Tears will automatically run for you pr as long as it builds successfully :-)

@MartyIX
Copy link
Contributor Author

MartyIX commented Dec 23, 2024

Hm, I'm not too sure what that CI error

Caught exception querying ADO for test runs: System.InvalidOperationException: Sequence contains more than one matching element
   at System.Linq.ThrowHelper.ThrowMoreThanOneMatchException()
   at System.Linq.Enumerable.TryGetSingle[TSource](IEnumerable`1 source, Func`2 predicate, Boolean& found)
   at System.Linq.Enumerable.SingleOrDefault[TSource](IEnumerable`1 source, Func`2 predicate)
   at RunTests.TestHistoryManager.GetRunForStageAsync(Build build, String phaseName, TestResultsHttpClient testClient, CancellationToken cancellationToken) in /_/src/Tools/Source/RunTests/TestHistoryManager.cs:line 177
Unable to get a run with name Test_Windows_CoreClr_Debug from build https://dev.azure.com/dnceng-public/cbb18261-c48f-4abb-8651-8cdcb5474649/_apis/build/Builds/900968.

is about.

var updatedExpression = expression.WithoutTrivia();

if (options.SimplifierOptions is CSharpSimplifierOptions csOptions && csOptions.ImplicitObjectCreationWhenTypeIsApparent.Value
&& csOptions.GetUseVarPreference() == UseVarPreference.None)
Copy link
Member

Choose a reason for hiding this comment

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

needs docs explaining what is going on.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I documented my approach. It is a really conservative one (either target-type new syntax is used exclusively, or codebase opts into using "var" in any case). My reasoning is as follows:

  • It's a very conservative change, so it should not introduce some unwanted behavior.
  • It's hard for me to tell what codebases in the wild actually use and want (i.e., how complex the implementation should be), so I basically wanted to cover my use-case because I understand it well (and hopefully, it's what other customers will like).
  • With my PR, it should be easy to add missing cases, if people actually want it.

That being said, I'm certainly open to modify it.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I think it is what #76342 (comment) mentions.

@CyrusNajmabadi
Copy link
Member

@dotnet/roslyn-infrastructure can someone help this community member with their PR? something has gone wonky with the test machines. Thanks!

@MartyIX
Copy link
Contributor Author

MartyIX commented Dec 23, 2024

@dotnet/roslyn-infrastructure can someone help this community member with their PR? something has gone wonky with the test machines. Thanks!

Perhaps it was my fault. I fixed a few failing tests.

@MartyIX
Copy link
Contributor Author

MartyIX commented Jan 2, 2025

FYI, it's vacation time. So things may be slow to respond. Feel free to ping me on jan2 if you haven't heard from anyone else!

@CyrusNajmabadi Pinging :)

@@ -47,14 +50,25 @@ protected override Document IntroduceLocal(
? TokenList(ConstKeyword)
: default;

var updatedExpression = expression.WithoutTrivia();
var csOptions = (CSharpSimplifierOptions)options.SimplifierOptions;
Copy link
Member

Choose a reason for hiding this comment

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

minor, but we do not tend to use things like cs in our code for abbreviations. We'd either use csharpOptions here, or just simplifierOptions (no need for the 'csharp' name to distringuish it.

@@ -47,14 +50,25 @@ protected override Document IntroduceLocal(
? TokenList(ConstKeyword)
: default;

var updatedExpression = expression.WithoutTrivia();
var csOptions = (CSharpSimplifierOptions)options.SimplifierOptions;
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
var csOptions = (CSharpSimplifierOptions)options.SimplifierOptions;
var simplifierOptions = (CSharpSimplifierOptions)options.SimplifierOptions;

if (csOptions.ImplicitObjectCreationWhenTypeIsApparent.Value && csOptions.GetUseVarPreference() == UseVarPreference.None
&& expression is ObjectCreationExpressionSyntax objectCreationExpression)
{
updatedExpression = ImplicitObjectCreationExpression(objectCreationExpression.ArgumentList, objectCreationExpression.Initializer);
Copy link
Member

Choose a reason for hiding this comment

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

you removed the trivia from expression when making updatedExpression at the top of the method. but not here. consider either doign the same here, or doign updatedExpression is ObjectCreationExpressionSyntax ... to ensure that original op wasn't lost.

@@ -935,7 +935,7 @@ class Program
{
static void Main()
{
G<int>.@class {|Rename:@class|} = new G<int>.@class();
G<int>.@class {|Rename:@class|} = new();
Copy link
Member

Choose a reason for hiding this comment

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

can you add sibling tests that have the same input, but which have the ImplicitObjectCreationWhenTypeIsApparent set to false, to show we get the old output in that case?

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.

everything about the code change looks good. there are some small tweaks i'd like you to make, and some test additions. Preemptive approving assuming that happens. when that change goes in and everything is green, ping me and i can merge. thanks!

@CyrusNajmabadi
Copy link
Member

Note: if you're low on time, and you'd prefer it, i'm happy to make the above change requests for you. Up to you.

@MartyIX
Copy link
Contributor Author

MartyIX commented Jan 2, 2025

Note: if you're low on time, and you'd prefer it, i'm happy to make the above change requests for you. Up to you.

If you have some time to do it, it would be great. First days after holidays are busy for me, so I cannot do the work immediately.

@CyrusNajmabadi
Copy link
Member

Thanks for the contribution :)

@CyrusNajmabadi CyrusNajmabadi merged commit a7ff9bf into dotnet:main Jan 2, 2025
25 checks passed
@dotnet-policy-service dotnet-policy-service bot added this to the Next milestone Jan 2, 2025
@CyrusNajmabadi
Copy link
Member

Merged. Thanks very much!

@MartyIX
Copy link
Contributor Author

MartyIX commented Jan 2, 2025

Merged. Thanks very much!

Thank you for finishing the work! I'm very excited to try this feature in some new Visual Studio version! :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Area-IDE Community The pull request was submitted by a contributor who is not a Microsoft employee. untriaged Issues and PRs which have not yet been triaged by a lead VSCode
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants