Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions docs/Rules/MA0099.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,19 @@ class Test
}
}
````

## Configuration

```editorconfig
# Only report the diagnostic when the enum has a named member with value 0.
# Default: false (report for all implicit 0 → enum conversions)
[*.cs]
MA0099.exclude_enum_without_zero_member = true
```

```editorconfig
# Restrict reporting to method/constructor arguments only (where accidental
# overload selection is the primary risk). Values: all (default), argument.
[*.cs]
MA0099.report_on = argument
```
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
using System.Collections.Immutable;
using System.Linq;
using Meziantou.Analyzer.Configurations;
using Meziantou.Analyzer.Internals;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Diagnostics;
Expand Down Expand Up @@ -35,7 +37,7 @@ private static void AnalyzeConversion(OperationAnalysisContext context)
if (!operation.IsImplicit)
return;

if (operation.Type is not INamedTypeSymbol { EnumUnderlyingType: not null and var enumType })
if (operation.Type is not INamedTypeSymbol { EnumUnderlyingType: not null and var enumType } enumSymbol)
return;

if (operation.Operand is IDefaultValueOperation)
Expand All @@ -49,11 +51,22 @@ private static void AnalyzeConversion(OperationAnalysisContext context)
return;
#endif

if (operation.ConstantValue is { HasValue: true, Value: not null and var value } && IsZero(enumType, value))
if (operation.ConstantValue is not { HasValue: true, Value: not null and var value } || !IsZero(enumType, value))
return;

var reportOn = context.Options.GetConfigurationValue(operation, RuleIdentifiers.DoNotUseZeroToInitializeAnEnumValue + ".report_on", defaultValue: "all");
if (string.Equals(reportOn, "argument", StringComparison.OrdinalIgnoreCase))
{
context.ReportDiagnostic(Rule, operation, operation.Type.ToDisplayString(SymbolDisplayFormat.CSharpShortErrorMessageFormat));
if (operation.Parent is not IArgumentOperation)
return;
}

var excludeEnumWithoutZeroMember = context.Options.GetConfigurationValue(operation, RuleIdentifiers.DoNotUseZeroToInitializeAnEnumValue + ".exclude_enum_without_zero_member", defaultValue: false);
if (excludeEnumWithoutZeroMember && !HasZeroMember(enumSymbol, enumType))
return;

context.ReportDiagnostic(Rule, operation, operation.Type.ToDisplayString(SymbolDisplayFormat.CSharpShortErrorMessageFormat));

static bool IsZero(ITypeSymbol enumType, object value)
{
return enumType.SpecialType switch
Expand All @@ -69,5 +82,13 @@ static bool IsZero(ITypeSymbol enumType, object value)
_ => false,
};
}

static bool HasZeroMember(INamedTypeSymbol enumSymbol, ITypeSymbol enumType)
{
return enumSymbol.GetMembers()
.OfType<IFieldSymbol>()
.Where(f => f.HasConstantValue)
.Any(f => IsZero(enumType, f.ConstantValue!));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -453,6 +453,108 @@ public class MyClass
{
public MyClass(MyEnum foo = MyEnum.None) { }
}
""")
.ValidateAsync();
}

[Fact]
[Trait("Issue", "https://github.com/meziantou/Meziantou.Analyzer/issues/1210")]
public async Task ExcludeEnumWithoutZeroMember_NoZeroMember_NoDiagnostic()
{
await CreateProjectBuilder()
.AddAnalyzerConfiguration("MA0099.exclude_enum_without_zero_member", "true")
.WithSourceCode("""
enum MyEnum { A = 1, B = 2 }

class Test
{
void M()
{
MyEnum a = 0;
}
}
""")
.ValidateAsync();
}

[Fact]
[Trait("Issue", "https://github.com/meziantou/Meziantou.Analyzer/issues/1210")]
public async Task ExcludeEnumWithoutZeroMember_HasZeroMember_Diagnostic()
{
await CreateProjectBuilder()
.AddAnalyzerConfiguration("MA0099.exclude_enum_without_zero_member", "true")
.WithSourceCode("""
enum MyEnum { A = 0, B = 1 }

class Test
{
void M()
{
MyEnum a = [|0|];
}
}
""")
.ValidateAsync();
}

[Fact]
[Trait("Issue", "https://github.com/meziantou/Meziantou.Analyzer/issues/1210")]
public async Task ReportOnArgument_ZeroInArgument_Diagnostic()
{
await CreateProjectBuilder()
.AddAnalyzerConfiguration("MA0099.report_on", "argument")
.WithSourceCode("""
enum MyEnum { A = 0, B = 1 }

class Test
{
void M(MyEnum x) { }

void A()
{
M([|0|]);
}
}
""")
.ValidateAsync();
}

[Fact]
[Trait("Issue", "https://github.com/meziantou/Meziantou.Analyzer/issues/1210")]
public async Task ReportOnArgument_ZeroInAssignment_NoDiagnostic()
{
await CreateProjectBuilder()
.AddAnalyzerConfiguration("MA0099.report_on", "argument")
.WithSourceCode("""
enum MyEnum { A = 0, B = 1 }

class Test
{
void M()
{
MyEnum a = 0;
}
}
""")
.ValidateAsync();
}

[Fact]
[Trait("Issue", "https://github.com/meziantou/Meziantou.Analyzer/issues/1210")]
public async Task ReportOnArgument_ZeroInOptionalParameterDefault_NoDiagnostic()
{
await CreateProjectBuilder()
.AddAnalyzerConfiguration("MA0099.report_on", "argument")
.WithSourceCode("""
enum MyEnum { A = 0, B = 1 }

class Test
{
void M(MyEnum x = 0)
{
M();
}
}
""")
.ValidateAsync();
}
Expand Down