Skip to content

Instantly share code, notes, and snippets.

@SolarianZ
Created June 25, 2024 11:53
Show Gist options
  • Save SolarianZ/234ab549a570f4775cf5ce037c1b958d to your computer and use it in GitHub Desktop.
Save SolarianZ/234ab549a570f4775cf5ce037c1b958d to your computer and use it in GitHub Desktop.
{"category": "C#/Analyzer", "keywords": "C#, Roslyn, Analyzer, override, call, base"} A Roslyn analyzer used to prevent calling the base class method implementation when overriding a method.
// < !--Add to YOUR_PROJECT.csproj-- >
// < Project Sdk = "Microsoft.NET.Sdk" >
//
// < !--... -->
//
// < ItemGroup >
// < Analyzer Include = "ABSOLUTE_OR_RELATIVE_FOLDER\DontCallBaseImplementation.dll" />
// </ ItemGroup >
//
// < !--... -->
//
// </ Project >
//
// https://learn.microsoft.com/en-us/dotnet/csharp/roslyn-sdk/tutorials/how-to-write-csharp-analyzer-code-fix
using System;
using System.Collections.Immutable;
using System.Linq;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Diagnostics;
namespace DontCallBaseImplementation
{
[AttributeUsage(AttributeTargets.Method, Inherited = false)]
public class DontCallBaseImplementationAttribute : Attribute { }
[DiagnosticAnalyzer(LanguageNames.CSharp)]
public class DontCallBaseImplementationAnalyzer : DiagnosticAnalyzer
{
public const string DiagnosticId = "DontCallBaseImplementation";
private static readonly LocalizableString Title = "Invalid base method implementation call";
private static readonly LocalizableString MessageFormat = "Method '{0}' with [DontCallBaseImplementationAttribute] should not call base method implementation";
private static readonly LocalizableString Description = "The method in the derived class calls the base method implementation which is marked with [DontCallBaseImplementationAttribute].";
private const string Category = "Usage";
private static readonly DiagnosticDescriptor Rule = new DiagnosticDescriptor(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(AnalyzeSyntax, SyntaxKind.InvocationExpression);
}
private static void AnalyzeSyntax(SyntaxNodeAnalysisContext context)
{
InvocationExpressionSyntax invocationExpression = (InvocationExpressionSyntax)context.Node;
if (invocationExpression.Expression is MemberAccessExpressionSyntax memberAccess &&
memberAccess.Expression is BaseExpressionSyntax)
{
IMethodSymbol invokedSymbol = context.SemanticModel.GetSymbolInfo(invocationExpression).Symbol as IMethodSymbol;
if (invokedSymbol == null) return;
IMethodSymbol currentMethod = context.ContainingSymbol as IMethodSymbol;
IMethodSymbol baseMethod = currentMethod?.OverriddenMethod;
if (!SymbolEqualityComparer.Default.Equals(invokedSymbol, baseMethod)) return;
AttributeData dontCallBaseImplAttr = baseMethod.GetAttributes()
.FirstOrDefault(attr => attr.AttributeClass.Name == nameof(DontCallBaseImplementationAttribute));
if (dontCallBaseImplAttr != null)
{
Diagnostic diagnostic = Diagnostic.Create(Rule, invocationExpression.GetLocation(), memberAccess.Name);
context.ReportDiagnostic(diagnostic);
}
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment