-
-
Notifications
You must be signed in to change notification settings - Fork 17
EC90 Use 'Cast' instead of 'Select' to cast: MA0078 #52
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Closed
Closed
Changes from all commits
Commits
Show all changes
10 commits
Select commit
Hold shift + click to select a range
0c0b2ab
EC90 Use 'Cast' instead of 'Select' to cast: MA0078
ElyesFakhar d626358
Merge branch 'green-code-initiative:main' into main
ElyesFakhar a47f2f9
condenser tout a en 2 lignes, avec un if et un report
ElyesFakhar a5ac19d
Mets des arrow functions pour cohérence avec le reste
ElyesFakhar 53258cc
Fais plutôt FirstOrDefault et on return s'il est nul, y a des cas lim…
ElyesFakhar 35cbc15
""" pour cohérence tjs
ElyesFakhar 1903a2e
compléter avec ceux qui renvoient un warning, plus leur code fix
ElyesFakhar 32cddb7
Merge branch 'green-code-initiative:main' into main
ElyesFakhar 03bb312
Fix build
ElyesFakhar fc83e22
Merge branch 'main' into main
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
106 changes: 106 additions & 0 deletions
106
src/EcoCode.Core/Analyzers/EC90.UseCastInsteadOfSelectToCast.Fixer.cs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,106 @@ | ||
| namespace EcoCode.Core.Analyzers | ||
| { | ||
| using System.Linq; | ||
| using System.Xml.Linq; | ||
|
|
||
| /// <summary> | ||
| /// Provides a code fix for the UseCastInsteadOfSelectToCast analyzer. | ||
| /// </summary> | ||
| [ExportCodeFixProvider(LanguageNames.CSharp, Name = nameof(UseCastInsteadOfSelectToCastFixer)), Shared] | ||
| public sealed class UseCastInsteadOfSelectToCastFixer : CodeFixProvider | ||
| { | ||
| /// <summary> | ||
| /// Gets the diagnostic IDs that this provider can fix. | ||
| /// </summary> | ||
| public sealed override ImmutableArray<string> FixableDiagnosticIds => ImmutableArray.Create(Rule.Ids.EC90_UseCastInsteadOfSelectToCast); | ||
|
|
||
| /// <summary> | ||
| /// Gets the provider that can fix all occurrences of diagnostics. | ||
| /// </summary> | ||
| public override FixAllProvider GetFixAllProvider() => WellKnownFixAllProviders.BatchFixer; | ||
|
|
||
| /// <summary> | ||
| /// Registers the code fixes provided by this provider. | ||
| /// </summary> | ||
|
|
||
| public override async Task RegisterCodeFixesAsync(CodeFixContext context) | ||
| { | ||
| if (context.Diagnostics.Length == 0) return; | ||
|
|
||
| var document = context.Document; | ||
| var root = await document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false); | ||
| if (root is null) return; | ||
|
|
||
| var nodeToFix = root.FindNode(context.Span, getInnermostNodeForTie: true); | ||
| context.RegisterCodeFix( | ||
| CodeAction.Create( | ||
| title: "Use nameof", | ||
| createChangedDocument: token => RefactorAsync(document, context.Diagnostics.First(), token), | ||
| equivalenceKey: "Use nameof"), | ||
| context.Diagnostics); | ||
| } | ||
|
|
||
| private async Task<Document> RefactorAsync(Document document, Diagnostic diagnostic, CancellationToken cancellationToken) | ||
| { | ||
| var root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false); | ||
|
|
||
| if (root == null) | ||
| { | ||
| return document; | ||
| } | ||
|
|
||
| // Find the Select invocation node | ||
| var diagnosticSpan = diagnostic.Location.SourceSpan; | ||
| var selectInvocation = root.FindToken(diagnosticSpan.Start).Parent?.AncestorsAndSelf().OfType<InvocationExpressionSyntax>().FirstOrDefault(); | ||
|
|
||
| if (selectInvocation is null) | ||
| { | ||
| return document; | ||
| } | ||
|
|
||
| // Get the lambda expression from the Select method call | ||
| var selectArgument = selectInvocation.ArgumentList.Arguments[0]; | ||
| var lambdaExpression = selectArgument.Expression as SimpleLambdaExpressionSyntax; | ||
|
|
||
| if (lambdaExpression is null) | ||
| { | ||
| return document; | ||
| } | ||
|
|
||
| // Get the type from the cast expression within the lambda expression | ||
| var castExpression = lambdaExpression.Body as CastExpressionSyntax; | ||
|
|
||
| if (castExpression is null) | ||
| { | ||
| return document; | ||
| } | ||
|
|
||
| var castType = castExpression.Type; | ||
|
|
||
| // Create a new Cast invocation node | ||
| var memberAccess = selectInvocation.Expression as MemberAccessExpressionSyntax; | ||
| if (memberAccess?.Expression == null) | ||
| { | ||
| return document; | ||
| } | ||
|
|
||
| var castInvocation = SyntaxFactory.InvocationExpression( | ||
| SyntaxFactory.MemberAccessExpression( | ||
| SyntaxKind.SimpleMemberAccessExpression, | ||
| memberAccess.Expression, | ||
| SyntaxFactory.GenericName( | ||
| SyntaxFactory.Identifier("Cast"), | ||
| SyntaxFactory.TypeArgumentList(SyntaxFactory.SingletonSeparatedList(castType)) | ||
| ) | ||
| ) | ||
| ); | ||
|
|
||
| // Replace only the Select invocation with the new Cast invocation | ||
| var newRoot = root.ReplaceNode(selectInvocation, castInvocation); | ||
|
|
||
| // Return the new document | ||
| return document.WithSyntaxRoot(newRoot); | ||
| } | ||
|
|
||
| } | ||
| } |
41 changes: 41 additions & 0 deletions
41
src/EcoCode.Core/Analyzers/EC90.UseCastInsteadOfSelectToCast.cs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,41 @@ | ||
| namespace EcoCode.Core.Analyzers | ||
| { | ||
| [DiagnosticAnalyzer(LanguageNames.CSharp)] | ||
| public sealed class UseCastInsteadOfSelectToCast : DiagnosticAnalyzer | ||
| { | ||
| public static DiagnosticDescriptor Descriptor { get; } = Rule.CreateDescriptor( | ||
| id: Rule.Ids.EC90_UseCastInsteadOfSelectToCast, | ||
| title: "Use Cast instead of Select to cast", | ||
| message: "A Select method is used for casting instead of the Cast method", | ||
| category: Rule.Categories.Performance, | ||
| severity: DiagnosticSeverity.Warning, | ||
| description: "The Cast method should be used instead of Select for casting to improve performance."); | ||
|
|
||
| public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => _supportedDiagnostics; | ||
| private static readonly ImmutableArray<DiagnosticDescriptor> _supportedDiagnostics = ImmutableArray.Create(Descriptor); | ||
|
|
||
| public override void Initialize(AnalysisContext context) | ||
| { | ||
| context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None); | ||
| context.EnableConcurrentExecution(); | ||
| context.RegisterSyntaxNodeAction(static context => AnalyzeSelectNode(context), SyntaxKind.InvocationExpression); | ||
| } | ||
|
|
||
| private static void AnalyzeSelectNode(SyntaxNodeAnalysisContext context) | ||
| { | ||
| var invocation = (InvocationExpressionSyntax)context.Node; | ||
| var memberAccess = invocation.Expression as MemberAccessExpressionSyntax; | ||
|
|
||
| // Check if the method being called is 'Select' | ||
| if (memberAccess?.Name.Identifier.Text != "Select") return; | ||
|
|
||
| // Check if the argument to 'Select' is a cast operation | ||
| if ((invocation.ArgumentList.Arguments.FirstOrDefault()?.Expression as SimpleLambdaExpressionSyntax)?.Body is CastExpressionSyntax) | ||
| { | ||
| context.ReportDiagnostic(Diagnostic.Create(Descriptor, invocation.GetLocation())); | ||
| } | ||
|
|
||
| } | ||
|
|
||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
136 changes: 136 additions & 0 deletions
136
src/EcoCode.Tests/Tests/EC90.UseCastInsteadOfSelectToCast.Tests.cs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,136 @@ | ||
| using EcoCode.Core.Analyzers; | ||
|
|
||
| namespace EcoCode.Tests; | ||
|
|
||
| [TestClass] | ||
| public sealed class UseCastInsteadOfSelectToCastTests | ||
| { | ||
| private static readonly CodeFixerDlg VerifyAsync = TestRunner.VerifyAsync<UseCastInsteadOfSelectToCast, UseCastInsteadOfSelectToCastFixer>; | ||
|
|
||
| [TestMethod] | ||
| public async Task EmptyCodeAsync() => await VerifyAsync("").ConfigureAwait(false); | ||
|
|
||
| [TestMethod] | ||
| public async Task SelectMethodUsedForStringArrayAsync() => await VerifyAsync(""" | ||
| using System.Linq; | ||
| using System.Collections.Generic; | ||
| public class Program | ||
| { | ||
| public static void Main() | ||
| { | ||
| var strings = GetStrings(); | ||
| var stringsAsObjects = [|strings.Select(s => (object)s)|].ToList(); | ||
| } | ||
|
|
||
| private static IEnumerable<string> GetStrings() | ||
| { | ||
| return new List<string> { "Hello", "World" }; | ||
| } | ||
| } | ||
| """, """ | ||
| using System.Linq; | ||
| using System.Collections.Generic; | ||
| public class Program | ||
| { | ||
| public static void Main() | ||
| { | ||
| var strings = GetStrings(); | ||
| var stringsAsObjects = strings.Cast<object>().ToList(); | ||
| } | ||
|
|
||
| private static IEnumerable<string> GetStrings() | ||
| { | ||
| return new List<string> { "Hello", "World" }; | ||
| } | ||
| } | ||
| """).ConfigureAwait(false); | ||
|
|
||
| [TestMethod] | ||
| public async Task CastMethodUsedForNumberArrayAsync() => await VerifyAsync(""" | ||
| using System.Linq; | ||
| public class Test | ||
| { | ||
| public void Run() | ||
| { | ||
| var numbers = new int[] { 1, 2, 3, 4, 5 }; | ||
| var castedNumbers = numbers.Cast<double>().ToList(); | ||
|
|
||
| var numbers2 = new int[] { 6, 7, 8, 9, 10 }; | ||
| var correctlyCastedNumbers = numbers2.Cast<double>().ToList(); | ||
| } | ||
| } | ||
| """).ConfigureAwait(false); | ||
|
|
||
| [TestMethod] | ||
| public async Task CastMethodUsedForStringArrayAsync() => await VerifyAsync(""" | ||
| using System.Linq; | ||
| using System.Collections.Generic; | ||
| public class Test | ||
| { | ||
| public void Run() | ||
| { | ||
| IEnumerable<string> strings = GetStrings(); | ||
| var stringsAsObjects = strings.Cast<object>().ToList(); | ||
| } | ||
|
|
||
| private IEnumerable<string> GetStrings() | ||
| { | ||
| return new List<string> { "Hello", "World" }; | ||
| } | ||
| } | ||
| """).ConfigureAwait(false); | ||
|
|
||
|
|
||
| [TestMethod] | ||
| public async Task CastMethodUsedForEmptyArrayAsync() => await VerifyAsync(""" | ||
| using System.Linq; | ||
| public class Test | ||
| { | ||
| public void Run() | ||
| { | ||
| var numbers = new int[] { }; | ||
| var castedNumbers = numbers.Cast<double>().ToList(); | ||
| } | ||
| } | ||
| """).ConfigureAwait(false); | ||
|
|
||
| [TestMethod] | ||
| public async Task CastMethodUsedForArrayWithNullValuesAsync() => await VerifyAsync(""" | ||
| using System.Linq; | ||
| public class Test | ||
| { | ||
| public void Run() | ||
| { | ||
| var strings = new string[] { "Hello", null, "World" }; | ||
| var castedStrings = strings.Cast<object>().ToList(); | ||
| } | ||
| } | ||
| """).ConfigureAwait(false); | ||
|
|
||
|
|
||
| [TestMethod] | ||
| public async Task CastMethodUsedForBoolArrayAsync() => await VerifyAsync(""" | ||
| using System.Linq; | ||
| public class Test | ||
| { | ||
| public void Run() | ||
| { | ||
| var booleans = new bool[] { true, false, true }; | ||
| var castedBooleans = booleans.Cast<object>().ToList(); | ||
| } | ||
| } | ||
| """).ConfigureAwait(false); | ||
|
|
||
| [TestMethod] | ||
| public async Task CastMethodUsedForNestedArrayAsync() => await VerifyAsync(""" | ||
| using System.Linq; | ||
| public class Test | ||
| { | ||
| public void Run() | ||
| { | ||
| var nestedArrays = new int[][] { new int[] {1, 2}, new int[] {3, 4} }; | ||
| var castedNestedArrays = nestedArrays.Cast<object>().ToList(); | ||
| } | ||
| } | ||
| """).ConfigureAwait(false); | ||
| } | ||
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.