From 928e332e4b1c19051afc20af8a17e8450ab4bd97 Mon Sep 17 00:00:00 2001 From: "Steven T. Cramer" Date: Tue, 13 Aug 2024 02:06:07 +0700 Subject: [PATCH 1/2] Add anlyzer to make sure States are declared correctly. --- .../AnalyzerReleases.Unshipped.md | 1 + .../StateInheritanceAnalyzer.cs | 52 +++++++++++++++++++ 2 files changed, 53 insertions(+) create mode 100644 Source/TimeWarp.State.Analyzer/StateInheritanceAnalyzer.cs diff --git a/Source/TimeWarp.State.Analyzer/AnalyzerReleases.Unshipped.md b/Source/TimeWarp.State.Analyzer/AnalyzerReleases.Unshipped.md index db712857e..92c2decd8 100644 --- a/Source/TimeWarp.State.Analyzer/AnalyzerReleases.Unshipped.md +++ b/Source/TimeWarp.State.Analyzer/AnalyzerReleases.Unshipped.md @@ -5,5 +5,6 @@ Rule ID | Category | Severity | Notes --------|----------|----------|------- +StateInheritanceRule | Design | Error | StateInheritanceAnalyzer TW0001 | TimeWarp.State | Error | TimeWarpStateActionAnalyzer TWD001 | Debug | Info | TimeWarpStateActionAnalyzer diff --git a/Source/TimeWarp.State.Analyzer/StateInheritanceAnalyzer.cs b/Source/TimeWarp.State.Analyzer/StateInheritanceAnalyzer.cs new file mode 100644 index 000000000..b6c4e6ba0 --- /dev/null +++ b/Source/TimeWarp.State.Analyzer/StateInheritanceAnalyzer.cs @@ -0,0 +1,52 @@ +namespace TimeWarp.State.Analyzer; + +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Diagnostics; +using System.Collections.Immutable; + +[DiagnosticAnalyzer(LanguageNames.CSharp)] +public class StateInheritanceAnalyzer : DiagnosticAnalyzer +{ + public const string DiagnosticId = "StateInheritanceRule"; + + private static readonly LocalizableString Title = "Incorrect State inheritance"; + private static readonly LocalizableString MessageFormat = "The type argument for State must be the derived class itself"; + private static readonly LocalizableString Description = "When inheriting from State, T must be the name of the derived class."; + private const string Category = "Design"; + + private static readonly DiagnosticDescriptor Rule = new(DiagnosticId, Title, MessageFormat, Category, DiagnosticSeverity.Error, isEnabledByDefault: true, description: Description); + + public override ImmutableArray SupportedDiagnostics { get { return ImmutableArray.Create(Rule); } } + + public override void Initialize(AnalysisContext context) + { + context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None); + context.EnableConcurrentExecution(); + context.RegisterSyntaxNodeAction(AnalyzeNode, SyntaxKind.ClassDeclaration); + } + + private static void AnalyzeNode(SyntaxNodeAnalysisContext context) + { + var classDeclaration = (ClassDeclarationSyntax)context.Node; + BaseTypeSyntax? baseType = classDeclaration.BaseList?.Types.FirstOrDefault(); + + if (baseType == null) return; + + var baseTypeSymbol = context.SemanticModel.GetSymbolInfo(baseType.Type).Symbol as INamedTypeSymbol; + if (baseTypeSymbol == null || !baseTypeSymbol.Name.Equals("State")) return; + + ITypeSymbol? typeArg = baseTypeSymbol.TypeArguments.FirstOrDefault(); + if (typeArg == null) return; + + INamedTypeSymbol? derivedTypeSymbol = context.SemanticModel.GetDeclaredSymbol(classDeclaration); + if (derivedTypeSymbol == null) return; + + if (!SymbolEqualityComparer.Default.Equals(typeArg, derivedTypeSymbol)) + { + var diagnostic = Diagnostic.Create(Rule, classDeclaration.Identifier.GetLocation()); + context.ReportDiagnostic(diagnostic); + } + } +} From cdd18b71cee3b3517bbf6795bc7806280472da28 Mon Sep 17 00:00:00 2001 From: "Steven T. Cramer" Date: Tue, 13 Aug 2024 02:06:52 +0700 Subject: [PATCH 2/2] Bump version --- Directory.Build.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Build.props b/Directory.Build.props index f97e7ca36..7b2a5e57b 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,7 +1,7 @@ - 11.0.0-beta.81+8.0.303 + 11.0.0-beta.82+8.0.303 Steven T. Cramer TimeWarp State $(TimeWarpStateVersion)