diff --git a/src/Analyzers/CSharp/Tests/UseImplicitOrExplicitType/UseImplicitTypeTests.cs b/src/Analyzers/CSharp/Tests/UseImplicitOrExplicitType/UseImplicitTypeTests.cs index db68ed935ddf43d28bb0dcd635b42badc91d4917..cb1a014f4e5f67797cdc8d5018589b5f596de64e 100644 --- a/src/Analyzers/CSharp/Tests/UseImplicitOrExplicitType/UseImplicitTypeTests.cs +++ b/src/Analyzers/CSharp/Tests/UseImplicitOrExplicitType/UseImplicitTypeTests.cs @@ -2735,5 +2735,35 @@ static void Main(string[] args) }", options: ImplicitTypeEverywhere()); } + + [WpfFact, Trait(Traits.Feature, Traits.Features.CodeActionsUseExplicitType)] + [WorkItem(44507, "https://github.com/dotnet/roslyn/issues/44507")] + public async Task DoNotSuggestVarInAmbiguousSwitchExpression() + { + await TestMissingAsync( +@"using System; + +class C +{ + void M() + { + var i = 1; + [||]C x = i switch + { + 0 => new A(), + 1 => new B(), + _ => throw new ArgumentException(), + }; + } +} + +class A : C +{ +} + +class B : C +{ +}", parameters: new TestParameters(options: ImplicitTypeEverywhere())); + } } } diff --git a/src/Features/CSharp/Portable/ChangeSignature/CSharpChangeSignatureService.cs b/src/Features/CSharp/Portable/ChangeSignature/CSharpChangeSignatureService.cs index f92d6b4c46dcd17f2d75361ba94dc5bada6a9d06..ea0605747fc8af56a184079245745fbe8a0efcc5 100644 --- a/src/Features/CSharp/Portable/ChangeSignature/CSharpChangeSignatureService.cs +++ b/src/Features/CSharp/Portable/ChangeSignature/CSharpChangeSignatureService.cs @@ -560,10 +560,7 @@ private static bool IsParamsArrayExpanded(SemanticModel semanticModel, SyntaxNod } else { -#pragma warning disable IDE0007 // Use implicit type - Using 'var' causes "error CS8506: No best type was found for the switch expression" - // TODO: File a bug on IDE0007 analyzer BaseArgumentListSyntax? argumentList = node switch -#pragma warning restore IDE0007 // Use implicit type { InvocationExpressionSyntax invocation => invocation.ArgumentList, ObjectCreationExpressionSyntax objectCreation => objectCreation.ArgumentList, diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Utilities/TypeStyle/CSharpUseImplicitTypeHelper.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Utilities/TypeStyle/CSharpUseImplicitTypeHelper.cs index 40eb121a7715e3cfc43a18544435e559e015026a..e0ab17d77c92e15f95c774a085d120ba049aabe8 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Utilities/TypeStyle/CSharpUseImplicitTypeHelper.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Utilities/TypeStyle/CSharpUseImplicitTypeHelper.cs @@ -15,6 +15,7 @@ using Microsoft.CodeAnalysis.Shared.Utilities; using Microsoft.CodeAnalysis.Simplification; using Microsoft.CodeAnalysis.Operations; +using Microsoft.CodeAnalysis.PooledObjects; #if CODE_STYLE using OptionSet = Microsoft.CodeAnalysis.Diagnostics.AnalyzerConfigOptions; @@ -362,13 +363,15 @@ private static bool IsSwitchExpressionAndCannotUseVar(TypeSyntax typeName, Expre if (initializer.IsKind(SyntaxKind.SwitchExpression)) { // We compare the variable declaration type to each arm's type to see if there is an exact match, or if the - // arm type inherits from the variable declaration type. If not, we must use the explicit type instead of var. + // arm type inherits from the variable declaration type. We also must verify that the arm types are all + // in the same line of inheritance. If not, we must use the explicit type instead of var. // Even if 'true' is returned from this method, it is not guaranteed that we can use var. Further checks should occur // after this method is called, such as checking if multiple implicit coversions exist. var declarationType = semanticModel.GetTypeInfo(typeName).Type; var noValidTypeExpressions = true; if (declarationType != null) { + using var _ = ArrayBuilder.GetInstance(out var seenTypes); foreach (var arm in ((SwitchExpressionSyntax)initializer).Arms) { var expression = arm.Expression; @@ -381,10 +384,43 @@ private static bool IsSwitchExpressionAndCannotUseVar(TypeSyntax typeName, Expre { noValidTypeExpressions = false; var expressionType = semanticModel.GetTypeInfo(expression).Type; - if (expressionType != null && !expressionType.InheritsFromOrEquals(declarationType)) + if (expressionType == null) + { + continue; + } + + if (!expressionType.InheritsFromOrEquals(declarationType)) { return true; } + + // All arms must be in the same direct line of inheritance. + // e.g. Given the tree: + // C + // / \ + // A B + // + // We cannot substitute var for 'x' in the following switch expression, + // as it will introduce a compiler error. + // C x = i switch + // { + // 0 => new A(), + // 1 => new B(), + // _ => throw new ArgumentException(), + // }; + if (expressionType.Equals(declarationType) || seenTypes.Contains(expressionType)) + { + continue; + } + + var invalidType = seenTypes.Any( + t => !t.InheritsFromOrEquals(expressionType) && !expressionType.InheritsFromOrEquals(t)); + if (invalidType) + { + return true; + } + + seenTypes.Add(expressionType); } } }