Skip to content

Commit

Permalink
Merge pull request #4 from ShaharPrishMSFT/shaharp/regularswitch
Browse files Browse the repository at this point in the history
Fix issues with SE1050 - exhaustive switch
  • Loading branch information
ShaharPrishMSFT authored Sep 29, 2024
2 parents fcc4f7c + 206483a commit f819f44
Show file tree
Hide file tree
Showing 2 changed files with 249 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,251 @@ namespace SubtleEngineering.Analyzers.Tests.ExhaustiveEnumSwitch
{
public class ExhaustiveEnumSwitchAnalyzerTests
{
[Fact]
public async Task NormalSwitchDoesNothing()
{
const string code = """
using SubtleEngineering.Analyzers.Decorators;
enum MyEnum { A, B, C }
class TestClass
{
void TestMethod()
{
var obj = new object();
var x = obj switch
{
int v => MyInvocation(MyEnum.A),
double v => MyInvocation(MyEnum.B),
float v => MyInvocation(MyEnum.C),
_ => (object)null,
};
}
public static object MyInvocation<T>(T o) => o;
}
""";

var sut = CreateSut(code, new List<DiagnosticResult>());
await sut.RunAsync();
}

[Fact]
public async Task NestedSwitchWorks()
{
const string code = """
using SubtleEngineering.Analyzers.Decorators;
enum MyEnum { A, B }
enum MyEnum2 { C, D }
class TestClass
{
void TestMethod()
{
var e = MyEnum.A;
var e2 = MyEnum2.C;
var x = e.ForceExhaustive() switch
{
MyEnum.A => 1,
MyEnum.B => e2.ForceExhaustive() switch
{
MyEnum2.C => 3,
MyEnum2.D => 4,
_ => 0,
},
_ => (object)null,
};
}
public static object MyInvocation<T>(T o) => o;
}
""";

var sut = CreateSut(code, new List<DiagnosticResult>());
await sut.RunAsync();
}

[Fact]
public async Task NestedSwitchWhereTopHasForceAndMissingValuesWorks()
{
const string code = """
using SubtleEngineering.Analyzers.Decorators;
enum MyEnum { A, B }
enum MyEnum2 { C, D }
class TestClass
{
void TestMethod()
{
var e = MyEnum.A;
var e2 = MyEnum2.C;
var x = e.ForceExhaustive() switch
{
MyEnum.A => 1,
MyEnum.B => e2 switch
{
MyEnum2.C => 3,
MyEnum2.D => 4,
_ => 0,
},
_ => (object)null,
};
}
public static object MyInvocation<T>(T o) => o;
}
""";

var sut = CreateSut(code, new List<DiagnosticResult>());
await sut.RunAsync();
}

[Fact]
public async Task NestedSwitchWhereInnerHasForceWorks()
{
const string code = """
using SubtleEngineering.Analyzers.Decorators;
enum MyEnum { A, B }
enum MyEnum2 { C, D }
class TestClass
{
void TestMethod()
{
var e = MyEnum.A;
var e2 = MyEnum2.C;
var x = e switch
{
MyEnum.A => 1,
MyEnum.B => e2.ForceExhaustive() switch
{
MyEnum2.C => 3,
MyEnum2.D => 4,
_ => 0,
},
_ => (object)null,
};
}
public static object MyInvocation<T>(T o) => o;
}
""";

var sut = CreateSut(code, new List<DiagnosticResult>());
await sut.RunAsync();
}

[Fact]
public async Task NestedSwitchWhereTopHasForceWorks()
{
const string code = """
using SubtleEngineering.Analyzers.Decorators;
enum MyEnum { A, B }
enum MyEnum2 { C, D }
class TestClass
{
void TestMethod()
{
var e = MyEnum.A;
var e2 = MyEnum2.C;
var x = e.ForceExhaustive() switch
{
MyEnum.A => 1,
MyEnum.B => e2 switch
{
MyEnum2.C => 3,
MyEnum2.D => 4,
_ => 0,
},
_ => (object)null,
};
}
public static object MyInvocation<T>(T o) => o;
}
""";

var sut = CreateSut(code, new List<DiagnosticResult>());
await sut.RunAsync();
}

[Fact]
public async Task NestedSwitchWhereInnerHasForceAndMissingWorks()
{
const string code = """
using SubtleEngineering.Analyzers.Decorators;
enum MyEnum { A, B }
enum MyEnum2 { C, D }
class TestClass
{
void TestMethod()
{
var e = MyEnum.A;
var e2 = MyEnum2.C;
var x = e switch
{
MyEnum.A => 1,
MyEnum.B => e2.ForceExhaustive() switch
{
MyEnum2.C => 3,
_ => 0,
},
_ => (object)null,
};
}
public static object MyInvocation<T>(T o) => o;
}
""";

var expected = VerifyAn.Diagnostic(ExhaustiveEnumSwitchAnalyzer.Rules[0])
.WithLocation(14, 25)
.WithArguments("MyEnum2", "D");

var sut = CreateSut(code, new List<DiagnosticResult>() { expected });
await sut.RunAsync();
}

[Fact]
public async Task NestedSwitchWhereOuterHasForceAndMissingWorks()
{
const string code = """
using SubtleEngineering.Analyzers.Decorators;
enum MyEnum { A, B }
enum MyEnum2 { C, D }
class TestClass
{
void TestMethod()
{
var e = MyEnum.A;
var e2 = MyEnum2.C;
var x = e.ForceExhaustive() switch
{
MyEnum.B => e2 switch
{
MyEnum2.C => 3,
MyEnum2.D => 4,
_ => 0,
},
_ => (object)null,
};
}
public static object MyInvocation<T>(T o) => o;
}
""";

var expected = VerifyAn.Diagnostic(ExhaustiveEnumSwitchAnalyzer.Rules[0])
.WithLocation(11, 17)
.WithArguments("MyEnum", "A");

var sut = CreateSut(code, new List<DiagnosticResult>() { expected });
await sut.RunAsync();
}

[Fact]
public async Task SwitchStatement_AllEnumValuesCovered_NoDiagnostic()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,10 @@ private void AnalyzeInvocation(OperationAnalysisContext context)
var invocation = (IInvocationOperation)context.Operation;

// Check if the method being called is the 'Exhaustive' extension method.
IsExhaustive(invocation);
if (!IsExhaustive(invocation))
{
return;
}

// Get the enum type being passed to the 'Exhaustive' method.
var enumType = invocation.Arguments.FirstOrDefault()?.Value?.Type as INamedTypeSymbol;
Expand Down

0 comments on commit f819f44

Please sign in to comment.