src/WebJobs.Extensions.DurableTask.Analyzers/Analyzers/EntityInterface/InterfaceAnalyzer.cs (96 lines of code) (raw):

// Copyright (c) .NET Foundation. All rights reserved. // Licensed under the MIT License. See LICENSE in the project root for license information. using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Diagnostics; using System.Collections.Generic; using System.Collections.Immutable; using System.Linq; namespace Microsoft.Azure.WebJobs.Extensions.DurableTask.Analyzers { [DiagnosticAnalyzer(Microsoft.CodeAnalysis.LanguageNames.CSharp)] public class InterfaceAnalyzer : DiagnosticAnalyzer { public const string DiagnosticId = "DF0301"; private static readonly LocalizableString Title = new LocalizableResourceString(nameof(Resources.SignalEntityAnalyzerTitle), Resources.ResourceManager, typeof(Resources)); private static readonly LocalizableString MessageFormat = new LocalizableResourceString(nameof(Resources.SignalEntityAnalyzerMessageFormat), Resources.ResourceManager, typeof(Resources)); private static readonly LocalizableString Description = new LocalizableResourceString(nameof(Resources.SignalEntityAnalyzerDescription), Resources.ResourceManager, typeof(Resources)); private const string Category = SupportedCategories.EntityInterface; public const DiagnosticSeverity Severity = DiagnosticSeverity.Warning; private static readonly DiagnosticDescriptor Rule = new DiagnosticDescriptor(DiagnosticId, Title, MessageFormat, Category, Severity, isEnabledByDefault: true, description: Description); private List<EntityInterface> entityInterfacesList = new List<EntityInterface>(); public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics { get { return ImmutableArray.Create( Rule, InterfaceContentAnalyzer.NoMethodsRule, InterfaceContentAnalyzer.NotAMethodRule, ParameterAnalyzer.Rule, EntityInterfaceReturnTypeAnalyzer.Rule); } } public override void Initialize(AnalysisContext context) { context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.Analyze | GeneratedCodeAnalysisFlags.ReportDiagnostics); InterfaceAnalyzer interfaceAnalyzer = new InterfaceAnalyzer(); context.RegisterCompilationStartAction(compilation => { compilation.RegisterSyntaxNodeAction(interfaceAnalyzer.FindEntityCalls, SyntaxKind.SimpleMemberAccessExpression); compilation.RegisterCompilationEndAction(interfaceAnalyzer.RegisterAnalyzers); }); } private void RegisterAnalyzers(CompilationAnalysisContext context) { foreach (EntityInterface entityInterface in entityInterfacesList) { InterfaceContentAnalyzer.ReportProblems(context, entityInterface); ParameterAnalyzer.ReportProblems(context, entityInterface); EntityInterfaceReturnTypeAnalyzer.ReportProblems(context, entityInterface); } } public void FindEntityCalls(SyntaxNodeAnalysisContext context) { if (context.Node is MemberAccessExpressionSyntax expression && SyntaxNodeUtils.IsInsideFunction(context.SemanticModel, expression)) { var name = expression.Name; if (name.ToString().StartsWith("SignalEntityAsync")) { if (!SyntaxNodeUtils.TryGetTypeArgumentIdentifier(expression, out SyntaxNode typeArgument)) { return; } if (TryFindEntityInterface(context, typeArgument, out EntityInterface entityInterface)) { entityInterfacesList.Add(entityInterface); } else { var diagnostic = Diagnostic.Create(Rule, typeArgument.GetLocation(), typeArgument); context.ReportDiagnostic(diagnostic); } } } } private bool TryFindEntityInterface(SyntaxNodeAnalysisContext context, SyntaxNode identifierName, out EntityInterface entityInterface) { if (SyntaxNodeUtils.TryGetDeclaredSyntaxNode(context.SemanticModel, identifierName, out SyntaxNode declaration)) { if (IsInterface(declaration)) { entityInterface = new EntityInterface { Name = identifierName.ToString(), InterfaceDeclaration = declaration }; return true; } } entityInterface = null; return false; } private bool IsInterface(SyntaxNode declaration) { var interfaceKeyword = declaration.ChildTokens().FirstOrDefault(x => x.IsKind(SyntaxKind.InterfaceKeyword)); return interfaceKeyword != null && !interfaceKeyword.IsKind(SyntaxKind.None); } } }