UseDefaultLiteral and RemoveUnnecessaryCast shouldn't introduce invalid default literal in pattern or case label#25538
Conversation
| // First, check to see if the node ultimately parenting this cast has any syntax errors. | ||
| // If so, we bail. If removing the cast introduces a syntax error, bail out too. | ||
| if (speculationAnalyzer.SemanticRootOfOriginalExpression.ContainsDiagnostics || | ||
| speculationAnalyzer.SemanticRootOfReplacedExpression.ContainsDiagnostics) |
There was a problem hiding this comment.
@CyrusNajmabadi what do you think about this approach? are there any concerns with this? performance concerns possibly?
this seems like generally a good idea but could also hide bugs if for some reason we generate an invalid syntax node
There was a problem hiding this comment.
whatever we do (whtether a check for diagnostics or special-case check for default inside a switch case/pattern) should probably be done inside the speculation analyzer itself in ReplacementChangesSemantics - there are probably other IDE features that might introduce broken code here and doing that would ensure they all behave correctly and don't need to each handle this separately
There was a problem hiding this comment.
another thing i like about this is that it doesn't duplicate the logic in the compiler
There was a problem hiding this comment.
This seems reasonable to me.
There was a problem hiding this comment.
@CyrusNajmabadi well it shouldn't 😄 that's all wrong, forget what i said.
the "replaced" expression does not contain any new diagnostics... all the tests passed because i didn't pass in c# 7.1 to the tests that expect to not get this offered - it simply wasn't offered because it used c# 7.0 where default is always a syntax error
There was a problem hiding this comment.
turns out manual testing is still useful
| } | ||
|
|
||
| if (defaultExpression.WalkUpParentheses().IsParentKind(SyntaxKind.ConstantPattern, SyntaxKind.CaseSwitchLabel)) | ||
| { |
There was a problem hiding this comment.
if it's done in the speculation analyzer, this can be removed
| { | ||
| switch (true) | ||
| { | ||
| case ((bool)default): |
There was a problem hiding this comment.
obviously another bug - it adds unnecessary parentheses
| protected abstract TestWorkspace CreateWorkspaceFromFile(string initialMarkup, TestParameters parameters); | ||
|
|
||
| private TestParameters WithRegularOptions(TestParameters parameters) | ||
| => parameters.WithParseOptions(parameters.parseOptions?.WithKind(SourceCodeKind.Regular)); |
There was a problem hiding this comment.
this is an important change so that TestMissingInRegularAndScriptAsync and the other methods actually use the language version we asked for by giving ParseOptions inside TestParameters, rather than ignoring it
There was a problem hiding this comment.
did this reveal any errant tests that were passing when tehy shouldn't hve been?
There was a problem hiding this comment.
actually no, i was surprised by that
There was a problem hiding this comment.
but it revealed a lot of tests that would have passed even if the code they were supposed to test didn't work
| // We can't have a default literal inside a case label or constant pattern. | ||
| return true; | ||
| } | ||
| } |
There was a problem hiding this comment.
@CyrusNajmabadi having the check here will be the most correct and will work for all IDE features equally well, the only thing I don't like is that this is just a special edge case that is at the very beginning and steals focus from the rest of this method which is far more important
also, this doesn't really feel like a "changing the semantics" all that much
There was a problem hiding this comment.
But I really feel like this code has to be somewhere in the speculation analyzer and not scattered around individual features. Doing this fixes bugs in other features too. One example I found is:
const bool b = default;
switch (true)
{
case b:
break;
}do inline temporary on 'b' - it will use replace 'b' with 'default' and produce the error.
But with this change, it will introduce a cast and the resulting code will be correct:
switch (true)
{
case ((bool)(default)):
break;
}edit: I'm wrong, this particular case actually works with either approach because the simplifier uses the same helper.
There was a problem hiding this comment.
also, this doesn't really feel like a "changing the semantics" all that much
"changes semantics" is just the term we used for "breaks the code" :)
the only thing I don't like is that this is just a special edge case that is at the very beginning and steals focus from the rest of this method which is far more important
The method is just a bunch of checks that was added in the order we found and fixed thigns while doing this feature. I don't see any problem with your addition.
| { | ||
| private static ITypeSymbol GetOuterCastType(ExpressionSyntax expression, SemanticModel semanticModel, out bool parentIsOrAsExpression) | ||
| private static ITypeSymbol GetOuterCastType(ExpressionSyntax expression, SemanticModel semanticModel, | ||
| out bool parentIsIsOrAsExpression) |
There was a problem hiding this comment.
maybe this name doesn't roll off the tongue as well, but at least it makes sense - i couldn't figure out what the old one was supposed to mean until i looked at the implementation
|
@dotnet/roslyn-ide Please review this community PR. Thanks |
|
@dotnet-bot Test this please |
|
@dotnet-bot retest this please |
|
@dotnet-bot retest windows_debug_unit32_prtest please |
|
@jinujoseph You were right to try to retest this, there was the build issue with a missing using again. Thanks |
Customer scenario
Type
case default(bool):, orcase (bool)default:, orb is default(bool), orb is (bool)default.In all those cases, no code fix should be offered to simplify to the
defaultliteral, as this results in a compilation error.Bugs this fixes
fixes #25456
Risk
Performance impact
Low. A few syntax checks were added to the logic that verifies if a change results in different semantics.
Is this a regression from a previous update?
No
Root cause analysis
At least for the
iscase, it was allowed for a short while, then we made a language change to reservedefaultfor use in C# 8matchexpressions. Forcase, I don't remember.How was the bug found?
Reported internally.