-
Notifications
You must be signed in to change notification settings - Fork 4k
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
Bugfixes for ConvertSwitchStatementToExpression #38007
Conversation
...res/CSharpTest/ConvertSwitchStatementToExpression/ConvertSwitchStatementToExpressionTests.cs
Show resolved
Hide resolved
...ertSwitchStatementToExpression/ConvertSwitchStatementToExpressionCodeFixProvider.Rewriter.cs
Outdated
Show resolved
Hide resolved
case 0: name = ""1""; break; | ||
case 1: name = ""2""; break; | ||
} | ||
throw new Exception(name); |
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 like it will result in non-intuitive behavior. I'd prefer we still support this and refactor it correctly.
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 fixes the incorrect behavior by not suggesting the fix. However, for this particular case we can use the initializer value for the last arm expression as suggested in #37949. we can keep it open to track that case.
if (shouldRemoveNextStatement && | ||
semanticModel.AnalyzeDataFlow(node.GetNextStatement()).DataFlowsIn.Contains(symbol)) | ||
{ | ||
// Bail out if data flows into the next statement that we want to move |
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.
Instead of bailing, can we just not remove the next statement and leave it outside the switch?
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.
Just looking through the AnalyzeSwitchStatement
implementation, we're setting shouldRemoveNextStatement
always to true if there is no default. That's potentially wrong, especially in the case noted in #37949
Maybe we can check if there is a default that makes sense before assuming we need to remove next?
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.
Instead of bailing, can we just not remove the next statement and leave it outside the switch?
At that point, we must have a viable statement after switch, otherwise this is not an exhaustive switch and we can't offer the fix
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.
#37949 fix still is lacking. I'd rather we fix that or leave open and take another stab in a separate PR.
Everything else looks good to me ✔
...ertSwitchStatementToExpression/ConvertSwitchStatementToExpressionCodeFixProvider.Rewriter.cs
Show resolved
Hide resolved
...ertSwitchStatementToExpression/ConvertSwitchStatementToExpressionCodeFixProvider.Rewriter.cs
Outdated
Show resolved
Hide resolved
Should we rebase this bugfix PR to 16.4-preview1 as well? |
@alrz Hmm, didn't realize this was out or I would have reviewed it. Don't rebase (the branch is switching around) but I might actually want to get this to go into 16.3... |
...SwitchStatementToExpression/ConvertSwitchStatementToExpressionDiagnosticAnalyzer.Analyzer.cs
Outdated
Show resolved
Hide resolved
var declaration = declarator.GetAncestor<StatementSyntax>(); | ||
if (declaration.Parent == node.Parent && declarator.Initializer is null) | ||
{ | ||
var beforeSwitch = node.GetPreviousStatement() is {} previousStatement |
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.
i'd honestly just prefer something like is StatementSyntax previousStatement
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 StatementSyntax doesn't scream null check? this is a usage that's been anticipated for recursive patterns afterall.
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 StatementSyntax doesn't scream null check?
It does to me. Because it just matches is Whatever
which has been in the language forever and does a null check.
If we get a not null
pattern in the future i'd also be ok with that. I think that {}
works, but just doesn't read well. It makes me feel as if it's missing something and trying to say "i need some props...i just forgot what they were :)"
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.
It does to me. Because it just matches is Whatever which has been in the language forever
So does is object
, perhaps I should quote #37539 (comment) from Cyrus
I agree. I'm actually not a huge fan of is object primarily because, for me, it heavily evokes a type check, when in reality all i'm really trying to convey is the null check in a safe way
I'd agree too.
If we get a not null pattern in the future i'd also be ok with that
Sadly, not null
pattern only works when you don't need a variable, you'd need to say
not null and var x
to also introduce a variable of a null-checked expression that would err if it's not a reference type - {}
doesn't.
I think that {} works, but just doesn't read well. It makes me feel as if it's missing something and trying to say "i need some props...i just forgot what they were :)
There were many requests to have a null-checked var-pattern before C# 8.0 and the answer was to use {}
, so I'd say that's pretty idiomatic.
I myself proposed var x?
similar to swift's "optional patterns" - but it got superseded by {}
which is a completely accidental feature of recursive patterns.
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.
basically, i'd rather wait until we have a good pattern here tbh.
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.
I'm with @CyrusNajmabadi and also detest this pattern: it feels like somebody realized you could use a screwdriver to hammer in a nail, and because we've all decided hammers aren't the new hotness we're all trying to use screwdrivers. :-)
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: i'm also very amenable to the idea that i'm being curmudgeonly here :) I'm not opposed to us migrating to new patterns and practices (lord knows you can see how often i massively do that to some part of the IDE).
That said, it's sometimes good to have a period were we dip our toes in and decide how we feel as a whole and come to some general consensus on a good balance.
i.e. i like patterns. i like them a lot. BUt i also think they can become unreadable. So finding a good balance on how they can be used is great. But we shouldn't necessarily try to make 100% of all code into a pattern.
This is similar to if we get sequence-expression, and things like return-expressoins. We'd likely be able to make tons and tons of statements into pure expressions. But should we? Probably not. It will really depend on the code and what the team as a whole feels is a good balance between brevity and readability.
So, in this case, i would use another pattern. but know that maybe after a few months to a year, the team may start liking this more and more and it may eventually move back :)
#37950 will be resolved on the compiler side, should revert the change here. |
@CyrusNajmabadi @ryzngard do you have any more comments? |
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 with plan to resolve #37949 with a different change
@CyrusNajmabadi can you take another pass? |
Sure. Will try to do tonight or tomorrow! |
LGTM. Thanks for taking care of all of these cases! |
{ | ||
return input switch | ||
{ | ||
1 => 42,// this little piggy went to market |
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.
nit. probably want space after the comma.
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.
(super super minor nit)
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.
that's just me not knowing how trivia works. there is no resource and I should just infer from code. not an easy task for something this subtle.
return dayOfWeek switch | ||
{ | ||
DayOfWeek.Monday => ""Monday"", | ||
_ => ""Other"", |
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.
nice!
@ryzngard I'm good here! |
Thanks for contributing @alrz ! |
Fixes #37949 (leave open for offering the fix instead of bail out)
Fixes #37950(will be resolved on the compiler side)Fixes #36876
Fixes #37873
Fixes #37872
Fixes #37907
Fixes #37947
Closes #36877 (duplicate of #37873)