|
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.Linq;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.CodeStyle;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.CodeStyle;
using Microsoft.CodeAnalysis.CSharp.ConvertSwitchStatementToExpression;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.Editor.UnitTests.CodeActions;
using Microsoft.CodeAnalysis.Test.Utilities;
using Microsoft.CodeAnalysis.Testing;
using Roslyn.Test.Utilities;
using Xunit;
namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.ConvertSwitchStatementToExpression;
using VerifyCS = CSharpCodeFixVerifier<
ConvertSwitchStatementToExpressionDiagnosticAnalyzer,
ConvertSwitchStatementToExpressionCodeFixProvider>;
[Trait(Traits.Feature, Traits.Features.CodeActionsConvertSwitchStatementToExpression)]
public class ConvertSwitchStatementToExpressionTests
{
private static readonly LanguageVersion CSharp9 = LanguageVersion.CSharp9;
[Theory, CombinatorialData]
public void TestStandardProperty(AnalyzerProperty property)
=> VerifyCS.VerifyStandardProperty(property);
[Fact]
public async Task TestReturn()
{
await VerifyCS.VerifyCodeFixAsync(
"""
class Program
{
int M(int i)
{
[|switch|] (i)
{
case 1:
return 4;
case 2:
return 5;
case 3:
return 6;
default:
return 7;
}
}
}
""",
"""
class Program
{
int M(int i)
{
return i switch
{
1 => 4,
2 => 5,
3 => 6,
_ => 7,
};
}
}
""");
}
[Fact]
public async Task TestReturnAndThrow()
{
await VerifyCS.VerifyCodeFixAsync(
"""
class Program
{
int M(int i)
{
[|switch|] (i)
{
case 1:
return 4;
default:
throw null;
case 2:
return 5;
case 3:
return 6;
}
}
}
""",
"""
class Program
{
int M(int i)
{
return i switch
{
1 => 4,
2 => 5,
3 => 6,
_ => throw null,
};
}
}
""");
}
[Fact]
public async Task TestAssignment_Array()
{
await VerifyCS.VerifyCodeFixAsync(
"""
class Program
{
int[] array = new int[1];
void M(int i)
{
[|switch|] (i)
{
case 1:
array[0] = 4;
break;
case 2:
array[0] = 5;
break;
case 3:
array[0] = 6;
break;
default:
array[0] = 7;
break;
}
}
}
""",
"""
class Program
{
int[] array = new int[1];
void M(int i)
{
array[0] = i switch
{
1 => 4,
2 => 5,
3 => 6,
_ => 7,
};
}
}
""");
}
[Fact]
public async Task TestMissingOnDifferentIndexerArgs()
{
var code = """
class Program
{
int[] array = new int[1];
void M(int i)
{
switch (i)
{
case 1:
array[1] = 4;
break;
case 2:
array[2] = 5;
break;
case 3:
array[2] = 6;
break;
default:
array[2] = 7;
break;
}
}
}
""";
await VerifyCS.VerifyCodeFixAsync(code, code);
}
[Fact]
public async Task TestMissingOnQualifiedName()
{
var code = """
class Program
{
int[] array = new int[1];
void M(int i)
{
switch (i)
{
case 1:
this.array[2] = 4;
break;
case 2:
array[2] = 5;
break;
case 3:
array[2] = 6;
break;
default:
array[2] = 7;
break;
}
}
}
""";
await VerifyCS.VerifyCodeFixAsync(code, code);
}
[Fact]
public async Task TestMissingOnDefaultBreak_01()
{
var code = """
class Program
{
void M(int i)
{
switch (i)
{
default:
break;
}
}
}
""";
await VerifyCS.VerifyCodeFixAsync(code, code);
}
[Fact]
public async Task TestMissingOnDefaultBreak_02()
{
var code = """
class Program
{
void M(int i)
{
switch (i)
{
case { }:
break;
}
}
}
""";
await VerifyCS.VerifyCodeFixAsync(code, code);
}
[Fact]
public async Task TestMissingOnDefaultBreak_03()
{
var code = """
class Program
{
void M(int i)
{
switch (i)
{
case var _:
break;
}
}
}
""";
await VerifyCS.VerifyCodeFixAsync(code, code);
}
[Fact]
public async Task TestMissingOnDefaultBreak_04()
{
var code = """
class Program
{
void M(int i)
{
switch (i)
{
case var x:
break;
}
}
}
""";
await VerifyCS.VerifyCodeFixAsync(code, code);
}
[Fact]
public async Task TestMissingOnAllBreak()
{
var code = """
class Program
{
void M(int i)
{
switch (i)
{
case 1:
break;
case 2:
break;
case 3:
break;
}
}
}
""";
await VerifyCS.VerifyCodeFixAsync(code, code);
}
[Fact]
public async Task TestAllThrow()
{
await VerifyCS.VerifyCodeFixAsync(
"""
using System;
class Program
{
void M(int i)
{
[|switch|] (i)
{
case 1:
throw null;
default:
throw new Exception();
}
}
}
""",
"""
using System;
class Program
{
void M(int i)
{
throw i switch
{
1 => null,
_ => new Exception(),
};
}
}
""");
}
[Fact]
public async Task TestAssignment()
{
await VerifyCS.VerifyCodeFixAsync(
"""
class Program
{
void M(int i)
{
int j;
[|switch|] (i)
{
case 1:
j = 4;
break;
case 2:
j = 5;
break;
case 3:
j = 6;
break;
}
throw null;
}
}
""",
"""
class Program
{
void M(int i)
{
var j = i switch
{
1 => 4,
2 => 5,
3 => 6,
_ => throw null,
};
}
}
""");
}
[Fact]
public async Task TestMissingOnNextStatementMismatch()
{
var code = """
class Program
{
int M(int i)
{
int j = 0;
switch (i)
{
case 1:
j = 4;
break;
case 2:
j = 5;
break;
case 3:
j = 6;
break;
}
return j;
}
}
""";
await VerifyCS.VerifyCodeFixAsync(code, code);
}
[Fact]
public async Task TestMissingOnAssignmentMismatch()
{
var code = """
class Program
{
int M(int i)
{
int j = 0;
switch (i)
{
case 1:
j = 4;
break;
case 2:
j += 5;
break;
}
return j;
}
}
""";
await VerifyCS.VerifyCodeFixAsync(code, code);
}
[Fact]
public async Task TestAssignment_Compound()
{
await VerifyCS.VerifyCodeFixAsync(
"""
class Program
{
void M(int i)
{
int j = 0;
[|switch|] (i)
{
case 1:
j += 4;
break;
case 2:
j += 5;
break;
case 3:
j += 6;
break;
}
throw null;
}
}
""",
"""
class Program
{
void M(int i)
{
int j = 0;
j += i switch
{
1 => 4,
2 => 5,
3 => 6,
_ => throw null,
};
}
}
""");
}
[Fact]
public async Task TestAssignment_UseBeforeAssignment()
{
await VerifyCS.VerifyCodeFixAsync(
"""
class Program
{
void M(int i)
{
int j = 123;
M(i);
[|switch|] (i)
{
case 1:
j = 4;
break;
case 2:
j = 5;
break;
case 3:
j = 6;
break;
}
throw null;
}
}
""",
"""
class Program
{
void M(int i)
{
int j = 123;
M(i);
j = i switch
{
1 => 4,
2 => 5,
3 => 6,
_ => throw null,
};
}
}
""");
}
[Fact]
public async Task TestMissingOnMultiAssignment()
{
var code = """
class Program
{
void M(int i)
{
int j, k;
switch (i)
{
case 1:
j = 4;
k = 5;
break;
case 2:
j = 6;
k = 7;
break;
case 3:
j = 8;
k = 9;
break;
}
throw null;
}
}
""";
await VerifyCS.VerifyCodeFixAsync(code, code);
}
[Fact]
public async Task TestMissingOnMultiCaseSection()
{
var code = """
class Program
{
void M(int i)
{
int j;
switch (i)
{
case 1:
case 2:
j = 4;
break;
}
throw null;
}
}
""";
await VerifyCS.VerifyCodeFixAsync(code, code);
}
[Fact]
public async Task TestMissingOnMultiCaseSectionWithWhenClause_CSharp9()
{
var code = """
class Program
{
void M(int i)
{
int j;
switch (i)
{
case 1:
case 2 when true:
j = 4;
break;
}
throw null;
}
}
""";
await new VerifyCS.Test
{
TestCode = code,
LanguageVersion = CSharp9,
}.RunAsync();
}
[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/42368")]
public async Task TestOnMultiCaseSection_CSharp9()
{
var testCode = """
class Program
{
void M(int i)
{
int j;
[|switch|] (i)
{
case 1:
case 2:
j = 4;
break;
}
throw null;
}
}
""";
var fixedCode = """
class Program
{
void M(int i)
{
var j = i switch
{
1 or 2 => 4,
_ => throw null,
};
}
}
""";
await new VerifyCS.Test
{
TestCode = testCode,
FixedCode = fixedCode,
LanguageVersion = CSharp9,
}.RunAsync();
}
[Fact]
public async Task TestMissingOnMultiCompoundAssignment()
{
var code = """
class Program
{
void M(int i)
{
int j = 0, k = 0;
switch (i)
{
case 1:
j += 4;
k += 5;
break;
case 2:
j += 6;
k += 7;
break;
case 3:
j += 8;
k += 9;
break;
}
throw null;
}
}
""";
await VerifyCS.VerifyCodeFixAsync(code, code);
}
[Fact]
public async Task TestMissingOnGoto()
{
var code = """
class Program
{
int M(int i)
{
switch (i)
{
case 1:
return 0;
case 2:
goto default;
default:
return 2;
}
}
}
""";
await VerifyCS.VerifyCodeFixAsync(code, code);
}
[Fact]
public async Task TestTrivia_01()
{
await VerifyCS.VerifyCodeFixAsync(
"""
class Program
{
int M(int i)
{
// leading switch
[|switch|] (i) // trailing switch
{
// leading label
case 1: // trailing label
// leading body
return 4; // trailing body
case 2:
return 5;
case 3:
return 6;
}
// leading next statement
throw null; // leading next statement
}
}
""",
"""
class Program
{
int M(int i)
{
// leading switch
return i switch // trailing switch
{
// leading label
// trailing label
1 => 4,// leading body
// trailing body
2 => 5,
3 => 6,
// leading next statement
_ => throw null,// leading next statement
};
}
}
""");
}
[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/37873")]
public async Task TestTrivia_02()
{
await VerifyCS.VerifyCodeFixAsync(
"""
class Program
{
static int GetValue(int input)
{
[|switch|] (input)
{
case 1:
// this little piggy went to market
return 42;
case 2:
// this little piggy stayed home
return 50;
case 3:
// this little piggy had roast beef
return 79;
default:
// this little piggy had none
return 80;
}
}
}
""",
"""
class Program
{
static int GetValue(int input)
{
return input switch
{
1 => 42,// this little piggy went to market
2 => 50,// this little piggy stayed home
3 => 79,// this little piggy had roast beef
_ => 80,// this little piggy had none
};
}
}
""");
}
[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/52258")]
public async Task TestTrivia_03()
{
await VerifyCS.VerifyCodeFixAsync(
"""
class Program
{
int M(int i)
{
[|switch|] (i)
{ // Tip-toe through the trailing trivia
case 0: return 123;
case 1: return 234;
default: throw null;
}
}
}
""",
"""
class Program
{
int M(int i)
{
return i switch
{ // Tip-toe through the trailing trivia
0 => 123,
1 => 234,
_ => throw null,
};
}
}
""");
}
[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/36086")]
public async Task TestSeverity()
{
var source =
"""
class Program
{
int M(int i)
{
switch (i)
{
case 1:
return 4;
case 2:
return 5;
case 3:
return 6;
default:
return 7;
}
}
}
""";
var analyzer = new ConvertSwitchStatementToExpressionDiagnosticAnalyzer();
var descriptor = analyzer.SupportedDiagnostics.First(descriptor => descriptor.Id == IDEDiagnosticIds.ConvertSwitchStatementToExpressionDiagnosticId);
await new VerifyCS.Test
{
TestCode = source,
ExpectedDiagnostics =
{
// Test0.cs(5,9): warning IDE0066: Use 'switch' expression
new DiagnosticResult(descriptor).WithSeverity(DiagnosticSeverity.Warning).WithSpan(5, 9, 5, 15).WithSpan(5, 9, 15, 10),
},
Options =
{
{ CSharpCodeStyleOptions.PreferSwitchExpression, true, NotificationOption2.Warning },
},
}.RunAsync();
}
[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/36995")]
public async Task TestAddParenthesesAroundBinaryExpression()
{
await VerifyCS.VerifyCodeFixAsync(
"""
class Program
{
void M(int i)
{
int j = 123;
[|switch|] (i % 10)
{
case 1:
j = 4;
break;
case 2:
j = 5;
break;
}
throw null;
}
}
""",
"""
class Program
{
void M(int i)
{
int j = 123;
j = (i % 10) switch
{
1 => 4,
2 => 5,
_ => throw null,
};
}
}
""");
}
[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/37947")]
public async Task TestMultiLabelWithDefault()
{
await VerifyCS.VerifyCodeFixAsync(
"""
using System;
class Program
{
public static string FromDay(DayOfWeek dayOfWeek)
{
[|switch|] (dayOfWeek)
{
case DayOfWeek.Monday:
return "Monday";
case DayOfWeek.Friday:
default:
return "Other";
}
}
}
""",
"""
using System;
class Program
{
public static string FromDay(DayOfWeek dayOfWeek)
{
return dayOfWeek switch
{
DayOfWeek.Monday => "Monday",
_ => "Other",
};
}
}
""");
}
[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/37949")]
public async Task TestMissingOnUseInNextStatement()
{
var code = """
using System;
class Program
{
public static void Throw(int index)
{
string name = "";
switch (index)
{
case 0: name = "1"; break;
case 1: name = "2"; break;
}
throw new Exception(name);
}
}
""";
await VerifyCS.VerifyCodeFixAsync(code, code);
}
[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/36876")]
public async Task TestDeclarationInOuterScope()
{
await VerifyCS.VerifyCodeFixAsync(
"""
using System;
using System.IO;
class Program
{
static SeekOrigin origin;
static long offset;
static long position;
static long length;
public static void Test()
{
long target;
try
{
[|switch|] (origin)
{
case SeekOrigin.Begin:
target = offset;
break;
case SeekOrigin.Current:
target = checked(offset + position);
break;
case SeekOrigin.End:
target = checked(offset + length);
break;
default:
throw new ArgumentOutOfRangeException(nameof(origin));
}
}
catch (OverflowException)
{
throw new ArgumentOutOfRangeException(nameof(offset));
}
if (target < 0)
{
throw new ArgumentOutOfRangeException(nameof(offset));
}
}
}
""",
"""
using System;
using System.IO;
class Program
{
static SeekOrigin origin;
static long offset;
static long position;
static long length;
public static void Test()
{
long target;
try
{
target = origin switch
{
SeekOrigin.Begin => offset,
SeekOrigin.Current => checked(offset + position),
SeekOrigin.End => checked(offset + length),
_ => throw new ArgumentOutOfRangeException(nameof(origin)),
};
}
catch (OverflowException)
{
throw new ArgumentOutOfRangeException(nameof(offset));
}
if (target < 0)
{
throw new ArgumentOutOfRangeException(nameof(offset));
}
}
}
""");
}
[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/37872")]
public async Task TestMissingFixOnDirectives()
{
var code = """
class Program
{
static void Main() { }
static int GetValue(int input)
{
[|switch|] (input)
{
case 1:
return 42;
case 2:
#if PLATFORM_UNIX
return 50;
#else
return 51;
#endif
case 3:
return 79;
default:
return 80;
}
}
}
""";
await VerifyCS.VerifyCodeFixAsync(code, code);
}
[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/37872")]
public async Task TestMissingFixAllOnDirectives()
{
var code = """
class Program
{
static void Main() { }
static int GetValue(int input)
{
[|switch|] (input)
{
case 1:
return 42;
default:
return 80;
}
[|switch|] (input)
{
case 1:
return 42;
case 2:
#if PLATFORM_UNIX
return 50;
#else
return 51;
#endif
case 3:
return 79;
default:
return 80;
}
}
}
""";
var fixedCode = """
class Program
{
static void Main() { }
static int GetValue(int input)
{
return input switch
{
1 => 42,
_ => 80,
};
[|switch|] (input)
{
case 1:
return 42;
case 2:
#if PLATFORM_UNIX
return 50;
#else
return 51;
#endif
case 3:
return 79;
default:
return 80;
}
}
}
""";
await new VerifyCS.Test
{
TestCode = code,
FixedState =
{
Sources = { fixedCode },
MarkupHandling = MarkupMode.Allow,
},
}.RunAsync();
}
[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/37950")]
public async Task TestShouldNotCastNullOnNullableValueType_ReturnStatement()
{
await VerifyCS.VerifyCodeFixAsync(
"""
class Program
{
public static bool? GetBool(string name)
{
[|switch|] (name)
{
case "a": return true;
case "b": return false;
default: return null;
}
}
}
""",
"""
class Program
{
public static bool? GetBool(string name)
{
return name switch
{
"a" => true,
"b" => false,
_ => null,
};
}
}
""");
}
[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/37950")]
public async Task TestShouldNotCastNullOnNullableValueType_Assignment()
{
await VerifyCS.VerifyCodeFixAsync(
"""
class Program
{
public static void Test(string name)
{
bool? result;
[|switch|] (name)
{
case "a": result = true; break;
case "b": result = false; break;
default: result = null; break;
}
}
}
""",
"""
class Program
{
public static void Test(string name)
{
bool? result = name switch
{
"a" => true,
"b" => false,
_ => null,
};
}
}
""");
}
[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/38771")]
public async Task TestExplicitDeclaration_Interfaces()
{
var input =
"""
using System;
class Program
{
interface IFruit { }
interface IFruit2 { }
class Apple : IFruit, IFruit2 { }
class Banana : IFruit, IFruit2 { }
public static void Test(string name)
{
IFruit2 fruit;
[|switch|] (name)
{
case "apple":
fruit = new Apple();
break;
case "banana":
fruit = new Banana();
break;
default:
throw new InvalidOperationException("Unknown fruit.");
}
}
}
""";
var expected =
"""
using System;
class Program
{
interface IFruit { }
interface IFruit2 { }
class Apple : IFruit, IFruit2 { }
class Banana : IFruit, IFruit2 { }
public static void Test(string name)
{
IFruit2 fruit = name switch
{
"apple" => new Apple(),
"banana" => new Banana(),
_ => throw new InvalidOperationException("Unknown fruit."),
};
}
}
""";
await new VerifyCS.Test
{
TestCode = input,
FixedCode = expected,
Options =
{
{ CSharpCodeStyleOptions.VarElsewhere, true, NotificationOption2.Silent },
},
}.RunAsync();
}
[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/38771")]
public async Task TestExplicitDeclaration_Interfaces2()
{
var input =
"""
using System;
class Program
{
interface IFruit { }
interface IFruit2 { }
class Apple : IFruit, IFruit2 { }
class Banana : IFruit, IFruit2 { }
public static void Test(string name)
{
IFruit2 fruit;
[|switch|] (name)
{
case "banana":
fruit = new Banana();
break;
case "banana2":
fruit = new Banana();
break;
default:
throw new InvalidOperationException("Unknown fruit.");
}
}
}
""";
var expected =
"""
using System;
class Program
{
interface IFruit { }
interface IFruit2 { }
class Apple : IFruit, IFruit2 { }
class Banana : IFruit, IFruit2 { }
public static void Test(string name)
{
IFruit2 fruit = name switch
{
"banana" => new Banana(),
"banana2" => new Banana(),
_ => throw new InvalidOperationException("Unknown fruit."),
};
}
}
""";
await new VerifyCS.Test
{
TestCode = input,
FixedCode = expected,
Options =
{
{ CSharpCodeStyleOptions.VarElsewhere, true, NotificationOption2.Silent },
},
}.RunAsync();
}
[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/38771")]
public async Task TestExplicitDeclaration_Interfaces3()
{
var input =
"""
using System;
class Program
{
interface IFruit { }
interface IFruit2 { }
class Apple : IFruit2 { }
class Banana : IFruit, IFruit2 { }
public static void Test(string name)
{
IFruit2 fruit;
[|switch|] (name)
{
case "apple":
fruit = new Apple();
break;
case "banana":
fruit = new Banana();
break;
default:
throw new InvalidOperationException("Unknown fruit.");
}
}
}
""";
var expected =
"""
using System;
class Program
{
interface IFruit { }
interface IFruit2 { }
class Apple : IFruit2 { }
class Banana : IFruit, IFruit2 { }
public static void Test(string name)
{
IFruit2 fruit = name switch
{
"apple" => new Apple(),
"banana" => new Banana(),
_ => throw new InvalidOperationException("Unknown fruit."),
};
}
}
""";
await new VerifyCS.Test
{
TestCode = input,
FixedCode = expected,
Options =
{
{ CSharpCodeStyleOptions.VarElsewhere, true, NotificationOption2.Silent },
},
}.RunAsync();
}
[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/38771")]
public async Task TestExplicitDeclaration_ClassInheritance()
{
var input =
"""
using System;
class Program
{
interface IFruit { }
interface IFruit2 { }
class Apple : IFruit2 { }
class Banana : IFruit, IFruit2 { }
class OrganicApple : Apple { }
class OrganicBanana : Banana { }
public static void Test(string name)
{
IFruit2 fruit;
[|switch|] (name)
{
case "apple":
fruit = new OrganicApple();
break;
case "banana":
fruit = new OrganicBanana();
break;
default:
throw new InvalidOperationException("Unknown fruit.");
}
}
}
""";
var expected =
"""
using System;
class Program
{
interface IFruit { }
interface IFruit2 { }
class Apple : IFruit2 { }
class Banana : IFruit, IFruit2 { }
class OrganicApple : Apple { }
class OrganicBanana : Banana { }
public static void Test(string name)
{
IFruit2 fruit = name switch
{
"apple" => new OrganicApple(),
"banana" => new OrganicBanana(),
_ => throw new InvalidOperationException("Unknown fruit."),
};
}
}
""";
await new VerifyCS.Test
{
TestCode = input,
FixedCode = expected,
Options =
{
{ CSharpCodeStyleOptions.VarElsewhere, true, NotificationOption2.Silent },
},
}.RunAsync();
}
[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/38771")]
public async Task TestExplicitDeclaration_ClassInheritance2()
{
var input =
"""
using System;
class Program
{
interface IFruit { }
interface IFruit2 { }
class Banana : IFruit, IFruit2 { }
class OrganicBanana : Banana, IFruit { }
public static void Test(string name)
{
IFruit2 fruit;
[|switch|] (name)
{
case "banana":
fruit = new Banana();
break;
case "organic banana":
fruit = new OrganicBanana();
break;
default:
throw new InvalidOperationException("Unknown fruit.");
}
}
}
""";
var expected =
"""
using System;
class Program
{
interface IFruit { }
interface IFruit2 { }
class Banana : IFruit, IFruit2 { }
class OrganicBanana : Banana, IFruit { }
public static void Test(string name)
{
IFruit2 fruit = name switch
{
"banana" => new Banana(),
"organic banana" => new OrganicBanana(),
_ => throw new InvalidOperationException("Unknown fruit."),
};
}
}
""";
await new VerifyCS.Test
{
TestCode = input,
FixedCode = expected,
Options =
{
{ CSharpCodeStyleOptions.VarElsewhere, true, NotificationOption2.Silent },
},
}.RunAsync();
}
[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/38771")]
public async Task TestImplicitDeclaration_ClassInheritance()
{
var input =
"""
using System;
class Program
{
interface IFruit { }
interface IFruit2 { }
class Banana : IFruit, IFruit2 { }
class OrganicBanana : Banana { }
public static void Test(string name)
{
Banana fruit;
[|switch|] (name)
{
case "banana":
fruit = new Banana();
break;
case "organic banana":
fruit = new OrganicBanana();
break;
default:
throw new InvalidOperationException("Unknown fruit.");
}
}
}
""";
var expected =
"""
using System;
class Program
{
interface IFruit { }
interface IFruit2 { }
class Banana : IFruit, IFruit2 { }
class OrganicBanana : Banana { }
public static void Test(string name)
{
var fruit = name switch
{
"banana" => new Banana(),
"organic banana" => new OrganicBanana(),
_ => throw new InvalidOperationException("Unknown fruit."),
};
}
}
""";
await new VerifyCS.Test
{
TestCode = input,
FixedCode = expected,
Options =
{
{ CSharpCodeStyleOptions.VarElsewhere, true, NotificationOption2.Silent },
},
}.RunAsync();
}
[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/38771")]
public async Task TestImplicitDeclaration_ClassInheritance2()
{
var input =
"""
using System;
class Program
{
interface IFruit { }
interface IFruit2 { }
class Banana : IFruit, IFruit2 { }
class OrganicBanana : Banana, IFruit { }
public static void Test(string name)
{
Banana fruit;
[|switch|] (name)
{
case "banana":
fruit = new Banana();
break;
case "organic banana":
fruit = new OrganicBanana();
break;
default:
throw new InvalidOperationException("Unknown fruit.");
}
}
}
""";
var expected =
"""
using System;
class Program
{
interface IFruit { }
interface IFruit2 { }
class Banana : IFruit, IFruit2 { }
class OrganicBanana : Banana, IFruit { }
public static void Test(string name)
{
var fruit = name switch
{
"banana" => new Banana(),
"organic banana" => new OrganicBanana(),
_ => throw new InvalidOperationException("Unknown fruit."),
};
}
}
""";
await new VerifyCS.Test
{
TestCode = input,
FixedCode = expected,
Options =
{
{ CSharpCodeStyleOptions.VarElsewhere, true, NotificationOption2.Silent },
},
}.RunAsync();
}
[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/38771")]
public async Task TestExplicitDeclaration_AllCasesDefaultLiteral()
{
var input =
"""
class Program
{
public static void Test()
{
var a = 0;
object o;
[|switch|] (a)
{
case 0:
o = default;
break;
default:
o = default;
break;
}
}
}
""";
var expected =
"""
class Program
{
public static void Test()
{
var a = 0;
object o = a switch
{
0 => default,
_ => default,
};
}
}
""";
await new VerifyCS.Test
{
TestCode = input,
FixedCode = expected,
Options =
{
{ CSharpCodeStyleOptions.VarForBuiltInTypes, true, NotificationOption2.Silent },
},
}.RunAsync();
}
[Fact]
public async Task TestExplicitDeclaration_MixedDefaultLiteralDefaultParameter()
{
var input =
"""
class Program
{
public static void Test()
{
var a = 0;
object o;
[|switch|] (a)
{
case 0:
o = default(string);
break;
default:
o = default;
break;
}
}
}
""";
var expected = """
class Program
{
public static void Test()
{
var a = 0;
object o = a switch
{
0 => default(string),
_ => default,
};
}
}
""";
await new VerifyCS.Test
{
TestCode = input,
FixedCode = expected,
Options =
{
{ CSharpCodeStyleOptions.VarForBuiltInTypes, true, NotificationOption2.Silent },
},
}.RunAsync();
}
[Fact]
public async Task TestImplicitDeclaration_AllCasesDefaultParameter()
{
var input =
"""
class Program
{
public static void Test()
{
var a = 0;
object o;
[|switch|] (a)
{
case 0:
o = default(object);
break;
default:
o = default(object);
break;
}
}
}
""";
var expected =
"""
class Program
{
public static void Test()
{
var a = 0;
var o = a switch
{
0 => default(object),
_ => default(object),
};
}
}
""";
await new VerifyCS.Test
{
TestCode = input,
FixedCode = expected,
Options =
{
{ CSharpCodeStyleOptions.VarForBuiltInTypes, true, NotificationOption2.Silent },
},
}.RunAsync();
}
[Fact]
public async Task TestExplicitDeclaration_AllCasesDefaultParameter()
{
var input =
"""
class Program
{
public static void Test()
{
var a = 0;
object o;
[|switch|] (a)
{
case 0:
o = default(object);
break;
default:
o = default(object);
break;
}
}
}
""";
var expected =
"""
class Program
{
public static void Test()
{
var a = 0;
object o = a switch
{
0 => default(object),
_ => default(object),
};
}
}
""";
await new VerifyCS.Test
{
TestCode = input,
FixedCode = expected,
Options =
{
{ CSharpCodeStyleOptions.VarForBuiltInTypes, false, NotificationOption2.Silent },
},
}.RunAsync();
}
[Fact]
public async Task TestExplicitDeclaration_DeclarationTypeDifferentFromAllCaseTypes()
{
var input =
"""
class Program
{
public static void Test()
{
var a = 0;
object o;
[|switch|] (a)
{
case 0:
o = "";
break;
default:
o = "";
break;
}
}
}
""";
var expected =
"""
class Program
{
public static void Test()
{
var a = 0;
object o = a switch
{
0 => "",
_ => "",
};
}
}
""";
await new VerifyCS.Test
{
TestCode = input,
FixedCode = expected,
Options =
{
{ CSharpCodeStyleOptions.VarForBuiltInTypes, true, NotificationOption2.Silent },
},
}.RunAsync();
}
[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/40198")]
public async Task TestNotWithRefReturns()
{
var code = """
using System;
class Program
{
static ref int GetRef(int[] mem, int addr, int mode)
{
switch (mode)
{
case 0: return ref mem[mem[addr]];
case 1: return ref mem[addr];
default: throw new Exception();
}
}
}
""";
await VerifyCS.VerifyCodeFixAsync(code, code);
}
[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/40198")]
public async Task TestNotWithRefAssignment()
{
var code = """
using System;
class Program
{
static ref int GetRef(int[] mem, int addr, int mode)
{
ref int i = ref addr;
switch (mode)
{
case 0: i = ref mem[mem[addr]]; break;
default: throw new Exception();
}
return ref mem[addr];
}
}
""";
await VerifyCS.VerifyCodeFixAsync(code, code);
}
[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/40198")]
public async Task TestNotWithRefConditionalAssignment()
{
var code = """
using System;
class Program
{
static ref int GetRef(int[] mem, int addr, int mode)
{
ref int i = ref addr;
switch (mode)
{
case 0: i = ref true ? ref mem[mem[addr]] : ref mem[mem[addr]]; break;
default: throw new Exception();
}
return ref mem[addr];
}
}
""";
await VerifyCS.VerifyCodeFixAsync(code, code);
}
[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/40198")]
public async Task TestWithRefInsideConditionalAssignment()
{
await VerifyCS.VerifyCodeFixAsync(
"""
using System;
class Program
{
static void GetRef(int[] mem, int addr, int mode)
{
ref int i = ref addr;
[|switch|] (mode)
{
case 0: i = true ? ref mem[mem[addr]] : ref mem[mem[addr]]; break;
default: throw new Exception();
}
}
}
""",
"""
using System;
class Program
{
static void GetRef(int[] mem, int addr, int mode)
{
ref int i = ref addr;
i = mode switch
{
0 => true ? ref mem[mem[addr]] : ref mem[mem[addr]],
_ => throw new Exception(),
};
}
}
""");
}
[Fact]
public async Task TopLevelStatement()
{
var source = """
int i = 0;
[|switch|] (i)
{
case 1:
return 4;
default:
return 7;
}
""";
var fixedSource = """
int i = 0;
return i switch
{
1 => 4,
_ => 7,
};
""";
var test = new VerifyCS.Test
{
TestState =
{
OutputKind = OutputKind.ConsoleApplication,
Sources = { source },
},
FixedCode = fixedSource,
LanguageVersion = LanguageVersion.CSharp9,
};
await test.RunAsync();
}
[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/44449")]
public async Task TopLevelStatement_FollowedWithThrow()
{
// We should be rewriting the declaration for 'j' to get 'var j = i switch ...'
var source = """
int i = 0;
int j;
[|switch|] (i)
{
case 1:
j = 4;
break;
case 2:
j = 5;
break;
}
throw null;
""";
var fixedSource = """
int i = 0;
int j;
j = i switch
{
1 => 4,
2 => 5,
_ => throw null,
};
""";
var test = new VerifyCS.Test
{
TestState = {
Sources = { source },
OutputKind = OutputKind.ConsoleApplication,
},
FixedCode = fixedSource,
LanguageVersion = LanguageVersion.CSharp9,
};
await test.RunAsync();
}
[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/48006")]
public async Task TestOnMultiCaseSection_String_CSharp9()
{
var testCode = """
class Program
{
bool M(string s)
{
[|switch|] (s)
{
case "Last":
case "First":
case "Count":
return true;
default:
return false;
}
}
}
""";
var fixedCode = """
class Program
{
bool M(string s)
{
return s switch
{
"Last" or "First" or "Count" => true,
_ => false,
};
}
}
""";
await new VerifyCS.Test
{
TestCode = testCode,
FixedCode = fixedCode,
LanguageVersion = CSharp9,
}.RunAsync();
}
[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/49788")]
public async Task TestParenthesizedExpression1()
{
await VerifyCS.VerifyCodeFixAsync(
"""
class Program
{
int M(object i)
{
[|switch|] (i.GetType())
{
default: return 0;
}
}
}
""",
"""
class Program
{
int M(object i)
{
return i.GetType() switch
{
_ => 0,
};
}
}
""");
}
[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/49788")]
public async Task TestParenthesizedExpression2()
{
await VerifyCS.VerifyCodeFixAsync(
"""
class Program
{
int M()
{
[|switch|] (1 + 1)
{
default: return 0;
}
}
}
""",
"""
class Program
{
int M()
{
return (1 + 1) switch
{
_ => 0,
};
}
}
""");
}
[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/58636")]
public async Task TestRuntimeTypeConversion_Assignment1()
{
await VerifyCS.VerifyCodeFixAsync(
"""
class Program
{
void M(string s)
{
object result;
[|switch|] (s)
{
case "a":
result = 1234;
break;
case "b":
result = 3.14;
break;
default:
throw new System.Exception();
}
}
}
""",
"""
class Program
{
void M(string s)
{
object result = s switch
{
"a" => 1234,
"b" => (object)3.14,
_ => throw new System.Exception(),
};
}
}
""");
}
[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/58636")]
public async Task TestRuntimeTypeConversion_Assignment2()
{
await VerifyCS.VerifyCodeFixAsync(
"""
class Program
{
void M(string s)
{
object result;
[|switch|] (s)
{
case "a":
result = 1234;
break;
case "b":
result = 3.14;
break;
case "c":
result = true;
break;
default:
throw new System.Exception();
}
}
}
""",
"""
class Program
{
void M(string s)
{
object result = s switch
{
"a" => 1234,
"b" => 3.14,
"c" => true,
_ => throw new System.Exception(),
};
}
}
""");
}
[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/58636")]
public async Task TestRuntimeTypeConversion_Return1()
{
await VerifyCS.VerifyCodeFixAsync(
"""
class Program
{
object M(string s)
{
[|switch|] (s)
{
case "a":
return 1234;
case "b":
return 3.14;
default:
throw new System.Exception();
}
}
}
""",
"""
class Program
{
object M(string s)
{
return s switch
{
"a" => 1234,
"b" => (object)3.14,
_ => throw new System.Exception(),
};
}
}
""");
}
[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/58636")]
public async Task TestRuntimeTypeConversion_Return2()
{
await VerifyCS.VerifyCodeFixAsync(
"""
class Program
{
object M(string s)
{
[|switch|] (s)
{
case "a":
return 1234;
case "b":
return 3.14;
case "c":
return true;
default:
throw new System.Exception();
}
}
}
""",
"""
class Program
{
object M(string s)
{
return s switch
{
"a" => 1234,
"b" => 3.14,
"c" => true,
_ => throw new System.Exception(),
};
}
}
""");
}
[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/61278")]
public async Task TestLeadingTrivia1()
{
await new VerifyCS.Test
{
TestCode =
"""
using System;
class C
{
public static int RefactorReplacementDemoMethod(int argument)
{
Console.WriteLine(nameof(RefactorReplacementDemoMethod));
// This comment will get deleted, together with the blank lines around it.
// Very similar issue already filed, but no resolution to this issue so far.
int result;
[|switch|] (argument)
{
case 1: result = 1001; break;
case 2: result = 1002; break;
default: result = -1; break;
}
return result;
}
}
""",
FixedCode =
"""
using System;
class C
{
public static int RefactorReplacementDemoMethod(int argument)
{
Console.WriteLine(nameof(RefactorReplacementDemoMethod));
// This comment will get deleted, together with the blank lines around it.
// Very similar issue already filed, but no resolution to this issue so far.
var result = argument switch
{
1 => 1001,
2 => 1002,
_ => -1,
};
return result;
}
}
""",
LanguageVersion = LanguageVersion.CSharp9,
}.RunAsync();
}
[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/61278")]
public async Task TestLeadingTrivia2()
{
await new VerifyCS.Test
{
TestCode =
"""
using System;
class C
{
public static int RefactorReplacementDemoMethod(int argument)
{
Console.WriteLine(nameof(RefactorReplacementDemoMethod));
// This comment will get deleted, together with the blank lines around it.
// Very similar issue already filed, but no resolution to this issue so far.
int result, x = 0;
[|switch|] (argument)
{
case 1: result = 1001; break;
case 2: result = 1002; break;
default: result = -1; break;
}
return result;
}
}
""",
FixedCode =
"""
using System;
class C
{
public static int RefactorReplacementDemoMethod(int argument)
{
Console.WriteLine(nameof(RefactorReplacementDemoMethod));
// This comment will get deleted, together with the blank lines around it.
// Very similar issue already filed, but no resolution to this issue so far.
int x = 0;
var result = argument switch
{
1 => 1001,
2 => 1002,
_ => -1,
};
return result;
}
}
""",
LanguageVersion = LanguageVersion.CSharp9,
}.RunAsync();
}
}
|