Skip to content

Commit

Permalink
feature: warn on usage of method groups (#253)
Browse files Browse the repository at this point in the history
  • Loading branch information
dpvreony authored Aug 30, 2024
1 parent 5eb2616 commit a0b886c
Show file tree
Hide file tree
Showing 2 changed files with 104 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
// Copyright (c) 2019 DHGMS Solutions and Contributors. All rights reserved.
// This file is licensed to you under the MIT license.
// See the LICENSE file in the project root for full license information.

using System.Collections.Immutable;
using Dhgms.GripeWithRoslyn.Analyzer.CodeCracker.Extensions;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Diagnostics;

namespace Dhgms.GripeWithRoslyn.Analyzer.Analyzers.Language
{
/// <summary>
/// Analyzer for warning on usages of method groups.
/// </summary>
[DiagnosticAnalyzer(LanguageNames.CSharp)]
public sealed class DoNotUseMethodGroupsAnalyzer : DiagnosticAnalyzer
{
internal const string Title = "Do not use method groups. For readability and consistency consider using lambda with manual caching if performance is an issue.";
private readonly DiagnosticDescriptor _rule;

/// <summary>
/// Initializes a new instance of the <see cref="DoNotUseMethodGroupsAnalyzer"/> class.
/// </summary>
public DoNotUseMethodGroupsAnalyzer()
{
_rule = new DiagnosticDescriptor(
DiagnosticIdsHelper.DoNotUseMethodGroups,
Title,
Title,
SupportedCategories.Maintainability,
DiagnosticSeverity.Warning,
isEnabledByDefault: true,
description: Title);
}

/// <inheritdoc/>
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => ImmutableArray.Create(_rule);

/// <inheritdoc/>
public override void Initialize(AnalysisContext context)
{
context.EnableConcurrentExecution();
context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.Analyze | GeneratedCodeAnalysisFlags.ReportDiagnostics);

context.RegisterSyntaxNodeAction(AnalyzeIdentifierName, SyntaxKind.IdentifierName);
context.RegisterSyntaxNodeAction(AnalyzeInvocationExpression, SyntaxKind.InvocationExpression);
}

private static bool IsMethodGroupInvocation(SyntaxNodeAnalysisContext context, IdentifierNameSyntax identifierName)
{
// Check if the identifier refers to a method symbol and is used as a delegate
var symbolInfo = context.SemanticModel.GetSymbolInfo(identifierName);
if (symbolInfo.Symbol is IMethodSymbol _)
{
// Ensure it's not followed by parentheses (indicating an invocation)
var parent = identifierName.Parent;
if (parent is ArgumentSyntax)
{
return true;
}
}

return false;
}

private void AnalyzeIdentifierName(SyntaxNodeAnalysisContext context)
{
var identifierNode = (IdentifierNameSyntax)context.Node;

if (!IsMethodGroupInvocation(context, identifierNode))
{
return;
}

var diagnostic = Diagnostic.Create(_rule, identifierNode.GetLocation(), identifierNode.Identifier.Text);
context.ReportDiagnostic(diagnostic);
}

private void AnalyzeInvocationExpression(SyntaxNodeAnalysisContext context)
{
var invocationExpression = (InvocationExpressionSyntax)context.Node;

foreach (var argument in invocationExpression.ArgumentList.Arguments)
{
if (argument.Expression is not IdentifierNameSyntax identifierName)
{
continue;
}

if (!IsMethodGroupInvocation(context, identifierName))
{
continue;
}

var diagnostic = Diagnostic.Create(_rule, identifierName.GetLocation(), identifierName.Identifier.Text);
context.ReportDiagnostic(diagnostic);
}
}
}
}
2 changes: 2 additions & 0 deletions src/Dhgms.GripeWithRoslyn.Analyzer/DiagnosticIdsHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -95,5 +95,7 @@ internal static class DiagnosticIdsHelper
internal static string ProjectShouldEnableNullableReferenceTypes => "GR0044";

internal static string MicrosoftAppCenterShouldNotBeUsed => "GR0045";

internal static string DoNotUseMethodGroups => "GR0046";
}
}

0 comments on commit a0b886c

Please sign in to comment.