Move features over to new "simplifier + var" system.#26237
Move features over to new "simplifier + var" system.#26237heejaechang merged 109 commits intodotnet:masterfrom
Conversation
|
Tagging @DustinCampbell as i had to make a couple of interesting behavioral choices. I've tagged you where i made them so you can weigh in if the change is ok. |
| ImmutableArray.Create<AbstractReducer>( | ||
| new CSharpCastReducer(), | ||
| new CSharpNameReducer(), | ||
| new CSharpCastReducer(), |
There was a problem hiding this comment.
@DustinCampbell This was the interesting starting point. One of our features (convert foreach to for) generates code like so:
IList<int> list = (IList<int>)collection;Previous behavior was that it wanted this to change into:
var list = (IList<int>)collection;However, after my change it converted to:
IList<int> list = collection;This was due to the 'cast' reducer running before the name reducer. IMO, if someone expresses a 'var' preference, i think that should take preference, so i changed to have name-reduction happen before cast-reduction. However, this had observable downstream impacts in a few tests that i've called out.
Take a look at the changed tests and let me know waht you think. Should name reduction come before cast reduction, or vice versa?
There was a problem hiding this comment.
I think this is fine.
There was some reason why we reduced casts twice here, but I don't recall what it was and it doesn't appear to have been problematic for any existing tests. Regardless, it always felt like a hack.
I kind of wish the generic method argument simplification was implemented as a separate reducer rather than part of the name reducer so we could just move it below the cast reducer, but I think it's fine for now.
| { | ||
| byte z = 0; | ||
| Goo<byte, byte>({|Rename:NewMethod|}(), y => { return 0; }, z, z); | ||
| Goo({|Rename:NewMethod|}(), y => { return (byte)0; }, z, z); |
There was a problem hiding this comment.
@DustinCampbell see https://github.com/dotnet/roslyn/pull/26237/files#r182592374
Now, we attempt to simplify the name first rather than the cast first, so we get this behavior. IMO, i think the new code is preferable to the old. Using inference for generic methods is very preferred (especially as you might end up having to name a lot of type parameters), so i think we should attempt to preserve inference first, even if it means we have to keep some casts.
There was a problem hiding this comment.
Yes, this is definitely better.
| static void M() | ||
| { | ||
| IComparable<long> c = Goo<long>(1, 1); | ||
| IComparable<long> c = Goo(1, (long)1); |
There was a problem hiding this comment.
@DustinCampbell see https://github.com/dotnet/roslyn/pull/26237/files#r182592374
This is the same as the above test. Humorously, this test is called "InsertCastToKeepGenericMethodInference". So it sounds like we did actually intend for their to be a cast, rather than keeping the type parameters.
There was a problem hiding this comment.
Yeah, simplification of generic method arguments was added to the system later. I agree that the new version is preferred (at least to my eye). Maybe someday, we'll add simplification to type characters (as an option of course!).
IComparable<long> c = Goo(1, 1L);😄
| byte z = 0; | ||
| Func<byte, byte> {|Rename:p|} = x => 0; | ||
| Goo<byte, byte>(p, y => 0, z, z); | ||
| Goo(p, y => (byte)0, z, z); |
There was a problem hiding this comment.
@DustinCampbell see https://github.com/dotnet/roslyn/pull/26237/files#r182592374
This is the same as the above test. Humorously, this test is called "InsertNeededCast2". So it sounds like we did actually intend for their to be a cast, rather than keeping the type parameters.
There was a problem hiding this comment.
Yup. It was originally a Nikov case. I like the change here.
| public Enumerator GetEnumerator() { return default; } | ||
|
|
||
| public struct Enumerator { public object Current { get; } } | ||
| public struct Enumerator { public object Current { get; } public bool MoveNext() => true; } |
There was a problem hiding this comment.
needed to make this change, otherwise the compiler felt that the type didn't properly match the 'foreach' pattern, and didn't want to consider hte types the same for reduction.
| ? foreachInfo.ExplicitCastInterface | ||
| : model.GetTypeInfo(foreachCollectionExpression).Type ?? model.Compilation.GetSpecialType(SpecialType.System_Object); | ||
|
|
||
| var collectionStatementType = typeSymbol.GenerateTypeSyntax(); |
There was a problem hiding this comment.
these are the places where i updated our features to now just use 'GenerateTypeSyntax()' confident that the simplifier would now use 'var' if htat's what the user wanted.
There was a problem hiding this comment.
Awesome. It's definitely simpler.
|
Tagging @dotnet/roslyn-ide @jcouv @heejaechang |
|
Tagging @ivanbasov this will likely impact your foreach/linq work. |
|
nice! we should have other code styles as well such as having "this." or not and etc. |
Indeed, IMO, all the codestyle stuff should be enforced on nodes marked with the Simplifier Annotatoin when passed through the simplifier. |
This was always the original intent. |
DustinCampbell
left a comment
There was a problem hiding this comment.
I'm definitely in favor of this change. The changes to the unit tests represent simpler, yet still correct, code which was always the intent of the feature. Thanks @CyrusNajmabadi!
| static void M() | ||
| { | ||
| IComparable<long> c = Goo<long>(1, 1); | ||
| IComparable<long> c = Goo(1, (long)1); |
There was a problem hiding this comment.
Yeah, simplification of generic method arguments was added to the system later. I agree that the new version is preferred (at least to my eye). Maybe someday, we'll add simplification to type characters (as an option of course!).
IComparable<long> c = Goo(1, 1L);😄
| { | ||
| byte z = 0; | ||
| Goo<byte, byte>({|Rename:NewMethod|}(), y => { return 0; }, z, z); | ||
| Goo({|Rename:NewMethod|}(), y => { return (byte)0; }, z, z); |
There was a problem hiding this comment.
Yes, this is definitely better.
| byte z = 0; | ||
| Func<byte, byte> {|Rename:p|} = x => 0; | ||
| Goo<byte, byte>(p, y => 0, z, z); | ||
| Goo(p, y => (byte)0, z, z); |
There was a problem hiding this comment.
Yup. It was originally a Nikov case. I like the change here.
| public Enumerator GetEnumerator() { return default; } | ||
|
|
||
| public struct Enumerator { public object Current { get; } } | ||
| public struct Enumerator { public object Current { get; } public bool MoveNext() => true; } |
| ? foreachInfo.ExplicitCastInterface | ||
| : model.GetTypeInfo(foreachCollectionExpression).Type ?? model.Compilation.GetSpecialType(SpecialType.System_Object); | ||
|
|
||
| var collectionStatementType = typeSymbol.GenerateTypeSyntax(); |
There was a problem hiding this comment.
Awesome. It's definitely simpler.
| ImmutableArray.Create<AbstractReducer>( | ||
| new CSharpCastReducer(), | ||
| new CSharpNameReducer(), | ||
| new CSharpCastReducer(), |
There was a problem hiding this comment.
I think this is fine.
There was some reason why we reduced casts twice here, but I don't recall what it was and it doesn't appear to have been problematic for any existing tests. Regardless, it always felt like a hack.
I kind of wish the generic method argument simplification was implemented as a separate reducer rather than part of the name reducer so we could just move it below the cast reducer, but I think it's fine for now.
|
Thanks @DustinCampbell I'll see if i can start pushing more and more stuff to the simplifier. |
|
@heejaechang @jmarolf @DustinCampbell can one of you merge in? |
|
Thanks! |
No description provided.