-
Notifications
You must be signed in to change notification settings - Fork 12
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[EC86] GC.Collect should not be called (#37)
- Loading branch information
1 parent
ecf4b0c
commit 7d012b4
Showing
4 changed files
with
203 additions
and
0 deletions.
There are no files selected for viewing
15 changes: 15 additions & 0 deletions
15
src/EcoCode.Core/Analyzers/EC86_GCCollectShouldNotBeCalled.Fixer.cs
This file contains 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,15 @@ | ||
namespace EcoCode.Analyzers; | ||
|
||
/// <summary>The code fix provider for avoid async void methods.</summary> | ||
[ExportCodeFixProvider(LanguageNames.CSharp, Name = nameof(GCCollectShouldNotBeCalledFixer)), Shared] | ||
public sealed class GCCollectShouldNotBeCalledFixer: CodeFixProvider | ||
{ | ||
/// <inheritdoc/> | ||
public override ImmutableArray<string> FixableDiagnosticIds => [GCCollectShouldNotBeCalled.Descriptor.Id]; | ||
|
||
/// <inheritdoc/> | ||
public override FixAllProvider GetFixAllProvider() => WellKnownFixAllProviders.BatchFixer; | ||
|
||
/// <inheritdoc/> | ||
public override Task RegisterCodeFixesAsync(CodeFixContext context) => Task.CompletedTask; | ||
} |
66 changes: 66 additions & 0 deletions
66
src/EcoCode.Core/Analyzers/EC86_GCCollectShouldNotBeCalled.cs
This file contains 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,66 @@ | ||
using System.Linq; | ||
|
||
namespace EcoCode.Analyzers; | ||
|
||
/// <summary>Analyzer for avoid async void methods.</summary> | ||
[DiagnosticAnalyzer(LanguageNames.CSharp)] | ||
public sealed class GCCollectShouldNotBeCalled: DiagnosticAnalyzer | ||
{ | ||
/// <summary>The diagnostic descriptor.</summary> | ||
public static DiagnosticDescriptor Descriptor { get; } = new( | ||
Rule.Ids.EC86_GCCollectShouldNotBeCalled, | ||
title: "Avoid calling GC.Collect() method", | ||
messageFormat: "Avoid calling GC.Collect() method", | ||
Rule.Categories.Performance, | ||
DiagnosticSeverity.Warning, | ||
isEnabledByDefault: true, | ||
description: null, | ||
helpLinkUri: Rule.GetHelpUri(Rule.Ids.EC86_GCCollectShouldNotBeCalled)); | ||
|
||
/// <inheritdoc/> | ||
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => [Descriptor]; | ||
|
||
/// <inheritdoc/> | ||
public override void Initialize(AnalysisContext context) | ||
{ | ||
context.EnableConcurrentExecution(); | ||
context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None); | ||
context.RegisterSyntaxNodeAction(static context => AnalyzeMethod(context), SyntaxKind.InvocationExpression); | ||
} | ||
|
||
private static void AnalyzeMethod(SyntaxNodeAnalysisContext context) | ||
{ | ||
var invocationExpression = (InvocationExpressionSyntax)context.Node; | ||
|
||
//if the expression is not a method or method name is not GC.Collect or Containing type is not System.GC, return | ||
if (context.SemanticModel.GetSymbolInfo(invocationExpression).Symbol is not IMethodSymbol methodSymbol | ||
|| methodSymbol.Name != nameof(GC.Collect) | ||
|| !SymbolEqualityComparer.Default.Equals(methodSymbol.ContainingType, | ||
context.SemanticModel.Compilation.GetTypeByMetadataName("System.GC"))) | ||
{ | ||
return; | ||
} | ||
|
||
//If there is no arguments or the "generation" argument is not 0, raise report | ||
bool report = !invocationExpression.ArgumentList.Arguments.Any(); | ||
if (!report) | ||
{ | ||
var firstArgument = invocationExpression.ArgumentList.Arguments[0]; | ||
if (firstArgument.NameColon is not null) // Named argument, may not be the one we want | ||
{ | ||
string firstParameterName = methodSymbol.Parameters[0].Name; // Parameter name from the method signature | ||
if (firstArgument.NameColon.Name.Identifier.Text != firstParameterName) | ||
{ | ||
firstArgument = invocationExpression.ArgumentList.Arguments | ||
.First(arg => arg.NameColon?.Name.Identifier.Text == firstParameterName); | ||
} | ||
} | ||
var constantValue = context.SemanticModel.GetConstantValue(firstArgument.Expression); | ||
if (constantValue.Value is not int intValue || intValue != 0) | ||
report = true; | ||
} | ||
|
||
if(report) | ||
context.ReportDiagnostic(Diagnostic.Create(Descriptor, invocationExpression.GetLocation())); | ||
} | ||
} |
This file contains 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
121 changes: 121 additions & 0 deletions
121
src/EcoCode.Tests/Tests/EC86_GCCollectShouldNotBeCalledUnitTests.cs
This file contains 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,121 @@ | ||
namespace EcoCode.Tests; | ||
|
||
[TestClass] | ||
public class GCCollectShouldNotBeCalledUnitTests | ||
{ | ||
private static readonly CodeFixerDlg VerifyAsync = TestRunner.VerifyAsync< | ||
GCCollectShouldNotBeCalled, | ||
GCCollectShouldNotBeCalledFixer>; | ||
|
||
[TestMethod] | ||
public async Task EmptyCodeAsync() => await VerifyAsync("").ConfigureAwait(false); | ||
|
||
[TestMethod] | ||
public async Task GCCollectShouldNotBeCalledAsync() => await VerifyAsync(""" | ||
using System; | ||
public static class Program | ||
{ | ||
public static async void Main() | ||
{ | ||
[|GC.Collect()|]; | ||
} | ||
} | ||
""").ConfigureAwait(false); | ||
|
||
[TestMethod] | ||
public async Task GCCollectShouldNotBeCalledSystemAsync() => await VerifyAsync(""" | ||
using System; | ||
public static class Program | ||
{ | ||
public static async void Main() | ||
{ | ||
[|System.GC.Collect()|]; | ||
} | ||
} | ||
""").ConfigureAwait(false); | ||
|
||
[TestMethod] | ||
public async Task GCCollectShouldNotBeCalledNamedArgumentsAsync() => await VerifyAsync(""" | ||
using System; | ||
public static class Program | ||
{ | ||
public static async void Main() | ||
{ | ||
[|GC.Collect(mode: GCCollectionMode.Optimized, generation: 1)|]; | ||
} | ||
} | ||
""").ConfigureAwait(false); | ||
|
||
[TestMethod] | ||
public async Task GCCollectShouldNotBeCalledMultipleCodeAsync() => await VerifyAsync(""" | ||
using System; | ||
public static class Program | ||
{ | ||
public static async void Main() | ||
{ | ||
string text=""; [|GC.Collect()|]; string text2 = ""; | ||
} | ||
} | ||
""").ConfigureAwait(false); | ||
|
||
[TestMethod] | ||
public async Task GCCollectShouldNotBeCalledCommentedAsync() => await VerifyAsync(""" | ||
using System; | ||
public static class Program | ||
{ | ||
public static async void Main() | ||
{ | ||
//GC.Collect(); | ||
} | ||
} | ||
""").ConfigureAwait(false); | ||
|
||
[TestMethod] | ||
public async Task GCCollectShouldNotBeCalledGeneration0Async() => await VerifyAsync(""" | ||
using System; | ||
public static class Program | ||
{ | ||
public static async void Main() | ||
{ | ||
GC.Collect(0); | ||
} | ||
} | ||
""").ConfigureAwait(false); | ||
|
||
[TestMethod] | ||
public async Task GCCollectShouldNotBeCalledGeneration10Async() => await VerifyAsync(""" | ||
using System; | ||
public static class Program | ||
{ | ||
public static async void Main() | ||
{ | ||
[|GC.Collect(10)|]; | ||
} | ||
} | ||
""").ConfigureAwait(false); | ||
|
||
[TestMethod] | ||
public async Task GCCollectShouldNotBeCalledGeneratio0CollectionModeAsync() => await VerifyAsync(""" | ||
using System; | ||
public static class Program | ||
{ | ||
public static async void Main() | ||
{ | ||
GC.Collect(0, GCCollectionMode.Forced); | ||
} | ||
} | ||
""").ConfigureAwait(false); | ||
|
||
[TestMethod] | ||
public async Task GCCollectShouldNotBeCalledGeneration10CollectionModeAsync() => await VerifyAsync(""" | ||
using System; | ||
public static class Program | ||
{ | ||
public static async void Main() | ||
{ | ||
[|GC.Collect(10, GCCollectionMode.Forced)|]; | ||
} | ||
} | ||
""").ConfigureAwait(false); | ||
|
||
} |