From 405c6150017022eaf970b44dcaa8c013542e1522 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 22 Nov 2025 04:20:46 +0000 Subject: [PATCH 1/2] Initial plan From ea5298d98d7cb53710ff532b840bae6a9e5fd572 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 22 Nov 2025 04:25:59 +0000 Subject: [PATCH 2/2] Use IOperation to detect both explicit and implicit integer-to-enum conversions Co-authored-by: agocke <515774+agocke@users.noreply.github.com> --- src/StaticCs/EnumClosedConversionAnalyzer.cs | 29 ++++++++++++-------- test/test/ClosedTests.cs | 6 ++-- 2 files changed, 22 insertions(+), 13 deletions(-) diff --git a/src/StaticCs/EnumClosedConversionAnalyzer.cs b/src/StaticCs/EnumClosedConversionAnalyzer.cs index 5edda05..6fd4332 100644 --- a/src/StaticCs/EnumClosedConversionAnalyzer.cs +++ b/src/StaticCs/EnumClosedConversionAnalyzer.cs @@ -1,9 +1,8 @@ using System.Collections.Immutable; using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.CSharp; -using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.Operations; namespace StaticCs; @@ -24,22 +23,30 @@ public override void Initialize(AnalysisContext ctx) { ctx.EnableConcurrentExecution(); ctx.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.ReportDiagnostics); - ctx.RegisterSyntaxNodeAction(ctx => + ctx.RegisterOperationAction(ctx => { - var castSyntax = (CastExpressionSyntax)ctx.Node; - var model = ctx.SemanticModel; - var targetTypeInfo = model.GetTypeInfo(castSyntax.Type); - - if (targetTypeInfo.Type is { TypeKind: TypeKind.Enum } type) + var conversion = (IConversionOperation)ctx.Operation; + + // Check if the conversion is from an integer type to an enum type + if (conversion.Type is { TypeKind: TypeKind.Enum } targetType && + conversion.Operand.Type?.SpecialType is + SpecialType.System_SByte or + SpecialType.System_Byte or + SpecialType.System_Int16 or + SpecialType.System_UInt16 or + SpecialType.System_Int32 or + SpecialType.System_UInt32 or + SpecialType.System_Int64 or + SpecialType.System_UInt64) { - foreach (var attr in type.GetAttributes()) + foreach (var attr in targetType.GetAttributes()) { if (attr.AttributeClass?.ToDisplayString() == "StaticCs.ClosedAttribute") { - ctx.ReportDiagnostic(Diagnostic.Create(s_descriptor, castSyntax.GetLocation(), type)); + ctx.ReportDiagnostic(Diagnostic.Create(s_descriptor, conversion.Syntax.GetLocation(), targetType)); } } } - }, SyntaxKind.CastExpression); + }, OperationKind.Conversion); } } \ No newline at end of file diff --git a/test/test/ClosedTests.cs b/test/test/ClosedTests.cs index bd03399..74995ec 100644 --- a/test/test/ClosedTests.cs +++ b/test/test/ClosedTests.cs @@ -80,7 +80,7 @@ await VerifyDiagnostics(src, } [Fact] - public async Task ImplicitConversionToClosedLoophole() + public async Task ImplicitConversionToClosedError() { var src = """ using System; @@ -96,7 +96,9 @@ void M() } } """; - await VerifyDiagnostics(src); + await VerifyDiagnostics(src, + // /0/Test0.cs(9,19): error STATICCS002: Integer conversions to [Closed] enum Rgb are disallowed + ClosedEnumConversion.WithSpan(9, 19, 9, 20).WithArguments("Rgb")); } [Fact]