Skip to content

Commit

Permalink
Merge pull request #466 from TimeWarpEngineering/Cramer/2024-08-04/Ac…
Browse files Browse the repository at this point in the history
…tionSetMethodSourceGen

Add Analyzer to make sure States implement specific CRTP pattern
  • Loading branch information
StevenTCramer authored Aug 12, 2024
2 parents 527e1ae + cdd18b7 commit 4914b69
Show file tree
Hide file tree
Showing 3 changed files with 54 additions and 1 deletion.
2 changes: 1 addition & 1 deletion Directory.Build.props
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project>
<!-- Set common properties regarding assembly information and nuget packages -->
<PropertyGroup>
<TimeWarpStateVersion>11.0.0-beta.81+8.0.303</TimeWarpStateVersion>
<TimeWarpStateVersion>11.0.0-beta.82+8.0.303</TimeWarpStateVersion>
<Authors>Steven T. Cramer</Authors>
<Product>TimeWarp State</Product>
<PackageVersion>$(TimeWarpStateVersion)</PackageVersion>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,6 @@

Rule ID | Category | Severity | Notes
--------|----------|----------|-------
StateInheritanceRule | Design | Error | StateInheritanceAnalyzer
TW0001 | TimeWarp.State | Error | TimeWarpStateActionAnalyzer
TWD001 | Debug | Info | TimeWarpStateActionAnalyzer
52 changes: 52 additions & 0 deletions Source/TimeWarp.State.Analyzer/StateInheritanceAnalyzer.cs
Original file line number Diff line number Diff line change
@@ -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<T> inheritance";
private static readonly LocalizableString MessageFormat = "The type argument for State<T> must be the derived class itself";
private static readonly LocalizableString Description = "When inheriting from State<T>, 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<DiagnosticDescriptor> 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);
}
}
}

0 comments on commit 4914b69

Please sign in to comment.