diff --git a/src/REstate.Analyzers/REstateAnalyzer.cs b/src/REstate.Analyzers/REstateAnalyzer.cs index b30c33a..f87b76f 100644 --- a/src/REstate.Analyzers/REstateAnalyzer.cs +++ b/src/REstate.Analyzers/REstateAnalyzer.cs @@ -13,52 +13,46 @@ namespace REstate.Analyzers [DiagnosticAnalyzer(LanguageNames.CSharp)] public class REstateAnalyzer : DiagnosticAnalyzer { - public const string DiagnosticId = nameof(REstateAnalyzer); - // You can change these strings in the Resources.resx file. If you do not want your analyzer to be localize-able, you can use regular strings for Title and MessageFormat. // See https://github.com/dotnet/roslyn/blob/master/docs/analyzers/Localizing%20Analyzers.md for more on localization - private static readonly LocalizableString Title = new LocalizableResourceString(nameof(Resources.AnalyzerTitle), Resources.ResourceManager, typeof(Resources)); - private static readonly LocalizableString MessageFormat = new LocalizableResourceString(nameof(Resources.AnalyzerMessageFormat), Resources.ResourceManager, typeof(Resources)); + private static readonly LocalizableString Title = new LocalizableResourceString(nameof(Resources.RESTATE001_Title), Resources.ResourceManager, typeof(Resources)); + private static readonly LocalizableString MessageFormat = new LocalizableResourceString(nameof(Resources.RESTATE001_Format), Resources.ResourceManager, typeof(Resources)); private static readonly LocalizableString Description = new LocalizableResourceString(nameof(Resources.AnalyzerDescription), Resources.ResourceManager, typeof(Resources)); private const string Category = "Usage"; - private static DiagnosticDescriptor Rule = new DiagnosticDescriptor(DiagnosticId, Title, MessageFormat, Category, DiagnosticSeverity.Warning, isEnabledByDefault: true, description: Description); + private static DiagnosticDescriptor RESTATE001 = new DiagnosticDescriptor("RESTATE001", Title, MessageFormat, Category, DiagnosticSeverity.Warning, isEnabledByDefault: true, description: Description); + - public override ImmutableArray SupportedDiagnostics { get { return ImmutableArray.Create(Rule); } } + public override ImmutableArray SupportedDiagnostics { get { return ImmutableArray.Create(RESTATE001); } } public override void Initialize(AnalysisContext context) { context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.Analyze | GeneratedCodeAnalysisFlags.ReportDiagnostics); context.EnableConcurrentExecution(); - context.RegisterSyntaxNodeAction(AnalyzeNode, SyntaxKind.LocalDeclarationStatement); + context.RegisterSyntaxNodeAction(AnalyzeNode, SyntaxKind.SimpleBaseType); } private void AnalyzeNode(SyntaxNodeAnalysisContext context) { + var baseDeclaration = (SimpleBaseTypeSyntax)context.Node; + + var baseItem = baseDeclaration.ChildNodes().OfType().FirstOrDefault(); + + // only consider IAcceptSignal + if (baseItem?.Identifier.ValueText != "IAcceptSignal") return; - //const check example. - //var localDeclaration = (LocalDeclarationStatementSyntax)context.Node; + var symbol = context.SemanticModel.GetSymbolInfo(baseItem); + if (symbol.Symbol?.OriginalDefinition?.ToDisplayString() != "REstate.Natural.IAcceptSignal") return; - //// make sure the declaration isn't already const: - //if (localDeclaration.Modifiers.Any(SyntaxKind.ConstKeyword)) - //{ - // return; - //} + var signalNode = baseItem.TypeArgumentList.ChildNodes().OfType().FirstOrDefault(); - //// Perform data flow analysis on the local declaration. - //var dataFlowAnalysis = context.SemanticModel.AnalyzeDataFlow(localDeclaration); + if(signalNode is null) return; - //// Retrieve the local symbol for each variable in the local declaration - //// and ensure that it is not written outside of the data flow analysis region. - //var variable = localDeclaration.Declaration.Variables.Single(); - //var variableSymbol = context.SemanticModel.GetDeclaredSymbol(variable); - //if (dataFlowAnalysis.WrittenOutside.Contains(variableSymbol)) - //{ - // return; - //} + var signalType = context.SemanticModel.GetTypeInfo(signalNode); + if(signalType.Type?.TypeKind != TypeKind.Interface) return; - //context.ReportDiagnostic(Diagnostic.Create(Rule, context.Node.GetLocation())); + context.ReportDiagnostic(Diagnostic.Create(RESTATE001, context.Node.GetLocation(), signalType.Type.ToDisplayString())); } } } diff --git a/src/REstate.Analyzers/REstateCodeFixProvider.cs b/src/REstate.Analyzers/REstateCodeFixProvider.cs index dc54c67..2a8d78b 100644 --- a/src/REstate.Analyzers/REstateCodeFixProvider.cs +++ b/src/REstate.Analyzers/REstateCodeFixProvider.cs @@ -22,7 +22,7 @@ public class REstateCodeFixProvider : CodeFixProvider public sealed override ImmutableArray FixableDiagnosticIds { - get { return ImmutableArray.Create(REstateAnalyzer.DiagnosticId); } + get { return ImmutableArray.Create("RESTATE001"); } } public sealed override FixAllProvider GetFixAllProvider() @@ -43,12 +43,12 @@ public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context) var declaration = root.FindToken(diagnosticSpan.Start).Parent.AncestorsAndSelf().OfType().First(); // Register a code action that will invoke the fix. - context.RegisterCodeFix( - CodeAction.Create( - title: title, - createChangedSolution: c => MakeUppercaseAsync(context.Document, declaration, c), - equivalenceKey: title), - diagnostic); + //context.RegisterCodeFix( + // CodeAction.Create( + // title: title, + // createChangedSolution: c => MakeUppercaseAsync(context.Document, declaration, c), + // equivalenceKey: title), + // diagnostic); } private async Task MakeUppercaseAsync(Document document, TypeDeclarationSyntax typeDecl, CancellationToken cancellationToken) diff --git a/src/REstate.Analyzers/Resources.Designer.cs b/src/REstate.Analyzers/Resources.Designer.cs index e0835fe..2301756 100644 --- a/src/REstate.Analyzers/Resources.Designer.cs +++ b/src/REstate.Analyzers/Resources.Designer.cs @@ -61,7 +61,7 @@ internal Resources() { } /// - /// Looks up a localized string similar to locals should be const if possible. + /// Looks up a localized string similar to Transitioning on Interfaces may cause unexpected actions and paths when signals are casted to an interface they implement.. /// internal static string AnalyzerDescription { get { @@ -70,20 +70,20 @@ internal static string AnalyzerDescription { } /// - /// Looks up a localized string similar to variable '{0}' can be const. + /// Looks up a localized string similar to State accepts the interface {0} as a signal. /// - internal static string AnalyzerMessageFormat { + internal static string RESTATE001_Format { get { - return ResourceManager.GetString("AnalyzerMessageFormat", resourceCulture); + return ResourceManager.GetString("RESTATE001_Format", resourceCulture); } } /// - /// Looks up a localized string similar to Can be const. + /// Looks up a localized string similar to Accepting interfaces as signals may be unsafe. /// - internal static string AnalyzerTitle { + internal static string RESTATE001_Title { get { - return ResourceManager.GetString("AnalyzerTitle", resourceCulture); + return ResourceManager.GetString("RESTATE001_Title", resourceCulture); } } } diff --git a/src/REstate.Analyzers/Resources.resx b/src/REstate.Analyzers/Resources.resx index 94ba501..17f61e5 100644 --- a/src/REstate.Analyzers/Resources.resx +++ b/src/REstate.Analyzers/Resources.resx @@ -118,15 +118,15 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - locals should be const if possible + Transitioning on Interfaces may cause unexpected actions and paths when signals are casted to an interface they implement. An optional longer localizable description of the diagnostic. - - variable '{0}' can be const + + State accepts the interface {0} as a signal The format-able message the diagnostic displays. - - Can be const + + Accepting interfaces as signals may be unsafe The title of the diagnostic. \ No newline at end of file