Skip to content

Commit

Permalink
Add MA0020
Browse files Browse the repository at this point in the history
  • Loading branch information
meziantou committed Feb 16, 2019
1 parent 85ad742 commit ca769a3
Show file tree
Hide file tree
Showing 5 changed files with 118 additions and 0 deletions.
1 change: 1 addition & 0 deletions docs/Rules/MA0019.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# MA0019 - Use `EventArgs.Empty` instead of `new EventArgs()`
1 change: 1 addition & 0 deletions docs/Rules/MA0020.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# MA0020 - Use `List<T>.Find` instead of `Enumerable.FirstOrDefault`
1 change: 1 addition & 0 deletions src/Meziantou.Analyzer/RuleIdentifiers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ internal static class RuleIdentifiers
public const string AbstractTypesShouldNotHaveConstructors = "MA0017";
public const string DoNotDeclareStaticMembersOnGenericTypes = "MA0018";
public const string UseEventArgsEmpty = "MA0019";
public const string UseListOfTMethodsInsteadOfEnumerableExtensionMethods = "MA0020";

public static string GetHelpUri(string idenfifier)
{
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
using System.Collections.Immutable;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.Operations;

namespace Meziantou.Analyzer.Rules
{
[DiagnosticAnalyzer(LanguageNames.CSharp)]
public class UseListOfTMethodsInsteadOfEnumerableExtensionMethodsAnalyzer : DiagnosticAnalyzer
{
private static readonly DiagnosticDescriptor s_rule = new DiagnosticDescriptor(
RuleIdentifiers.UseListOfTMethodsInsteadOfEnumerableExtensionMethods,
title: "Use List<T> methods instead of extension methods",
messageFormat: "Use '{0}' instead of '{1}'",
RuleCategories.Performance,
DiagnosticSeverity.Info,
isEnabledByDefault: true,
description: "",
helpLinkUri: RuleIdentifiers.GetHelpUri(RuleIdentifiers.UseListOfTMethodsInsteadOfEnumerableExtensionMethods));

public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => ImmutableArray.Create(s_rule);

public override void Initialize(AnalysisContext context)
{
context.EnableConcurrentExecution();
context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None);

context.RegisterOperationAction(Analyze, OperationKind.Invocation);
}

private void Analyze(OperationAnalysisContext context)
{
var operation = (IInvocationOperation)context.Operation;
if (operation == null)
return;

var enumerableSymbol = context.Compilation.GetTypeByMetadataName("System.Linq.Enumerable");
if (enumerableSymbol == null)
return;

if (operation.Arguments.Length != 2)
return;

var methodName = operation.TargetMethod.Name;
if (string.Equals(methodName, "FirstOrDefault", System.StringComparison.Ordinal))
{
if (!operation.TargetMethod.ContainingType.IsEqualsTo(enumerableSymbol))
return;

var listSymbol = context.Compilation.GetTypeByMetadataName("System.Collections.Generic.List`1");
if (GetActualType(operation.Arguments[0]).OriginalDefinition.IsEqualsTo(listSymbol))
{
context.ReportDiagnostic(Diagnostic.Create(s_rule, operation.Syntax.GetLocation(), "Find", methodName));
}
}
}

private static ITypeSymbol GetActualType(IArgumentOperation argument)
{
var value = argument.Value;
if (value is IConversionOperation conversionOperation)
{
value = conversionOperation.Operand;
}

return value.Type;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
using System.Collections.Generic;
using System.Linq;
using Meziantou.Analyzer.Rules;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using TestHelper;

namespace Meziantou.Analyzer.Test.Rules
{
[TestClass]
public class UseListOfTMethodsInsteadOfEnumerableExtensionMethodsAnalyzerTests : CodeFixVerifier
{
protected override DiagnosticAnalyzer GetCSharpDiagnosticAnalyzer() => new UseListOfTMethodsInsteadOfEnumerableExtensionMethodsAnalyzer();
protected override string ExpectedDiagnosticId => "MA0020";
protected override DiagnosticSeverity ExpectedDiagnosticSeverity => DiagnosticSeverity.Info;

[TestMethod]
public void FirstOrDefault()
{
var project = new ProjectBuilder()
.AddReference(typeof(IEnumerable<>))
.AddReference(typeof(Enumerable))
.WithSource(@"using System.Linq;
class Test
{
public Test()
{
var enumerable = System.Linq.Enumerable.Empty<int>();
var list = new System.Collections.Generic.List<int>();
list.FirstOrDefault();
list.FirstOrDefault(x => x == 0); // Error
enumerable.FirstOrDefault();
enumerable.FirstOrDefault(x => x == 0);
}
}
");

var expected = new[]
{
CreateDiagnosticResult(line: 9, column: 9, message: "Use 'Find' instead of 'FirstOrDefault'"),
};
VerifyDiagnostic(project, expected);
}
}
}

0 comments on commit ca769a3

Please sign in to comment.