Skip to content

Commit

Permalink
Add suppressor for IDE0058 on StringBuilder and Directory (#762)
Browse files Browse the repository at this point in the history
  • Loading branch information
meziantou authored Oct 18, 2024
1 parent 7a29d04 commit d2be721
Show file tree
Hide file tree
Showing 10 changed files with 130 additions and 19 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -191,5 +191,6 @@ If you are already using other analyzers, you can check [which rules are duplica
|--|---------------|-------------|
|`MAS0001`|[CA1822](https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1822?WT.mc_id=DT-MVP-5003978)|Suppress CA1822 on methods decorated with BenchmarkDotNet attributes.|
|`MAS0002`|[CA1822](https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1822?WT.mc_id=DT-MVP-5003978)|Suppress CA1822 on methods decorated with a System.Text.Json attribute such as \[JsonPropertyName\] or \[JsonInclude\].|
|`MAS0003`|[IDE0058](https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/ide0058?WT.mc_id=DT-MVP-5003978)|Suppress IDE0058 on well-known types|

<!-- suppressions -->
1 change: 1 addition & 0 deletions docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,7 @@
|--|---------------|-------------|
|`MAS0001`|[CA1822](https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1822?WT.mc_id=DT-MVP-5003978)|Suppress CA1822 on methods decorated with BenchmarkDotNet attributes.|
|`MAS0002`|[CA1822](https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1822?WT.mc_id=DT-MVP-5003978)|Suppress CA1822 on methods decorated with a System.Text.Json attribute such as \[JsonPropertyName\] or \[JsonInclude\].|
|`MAS0003`|[IDE0058](https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/ide0058?WT.mc_id=DT-MVP-5003978)|Suppress IDE0058 on well-known types|


# .editorconfig - default values
Expand Down
8 changes: 8 additions & 0 deletions src/DocumentationGenerator/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,14 @@ static string GenerateSuppressorsTable(List<DiagnosticSuppressor> diagnosticSupp
.Append($"https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/").Append(suppression.SuppressedDiagnosticId.ToLowerInvariant()).Append("?WT.mc_id=DT-MVP-5003978")
.Append(')');
}
else if (suppression.SuppressedDiagnosticId.StartsWith("IDE", StringComparison.OrdinalIgnoreCase))
{
sb.Append('[')
.Append(suppression.SuppressedDiagnosticId)
.Append("](")
.Append($"https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/").Append(suppression.SuppressedDiagnosticId.ToLowerInvariant()).Append("?WT.mc_id=DT-MVP-5003978")
.Append(')');
}
else
{
sb.Append('`').Append(suppression.SuppressedDiagnosticId).Append('`');
Expand Down
3 changes: 1 addition & 2 deletions src/Meziantou.Analyzer/Rules/DoNotUseAsyncVoidAnalyzer.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
using System;
using System.Collections.Immutable;
using System.Collections.Immutable;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.Operations;
Expand Down
2 changes: 1 addition & 1 deletion src/Meziantou.Analyzer/Rules/ProcessStartAnalyzer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ public void AnalyzeObjectCreation(OperationAnalysisContext context)
var operation = (IObjectCreationOperation)context.Operation;
if (IsProcessStartInfoCreation(operation))
{
if (operation is { Initializer: {} initializer } )
if (operation is { Initializer: { } initializer })
{
var useShellExecuteInitializer = initializer.Initializers.OfType<ISimpleAssignmentOperation>()
.FirstOrDefault(x => x.Target.Syntax is IdentifierNameSyntax { Identifier.Text: "UseShellExecute" });
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,5 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Collections.Concurrent;
using System.Collections.Immutable;
using System.Linq;
using Meziantou.Analyzer.Internals;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Diagnostics;
Expand Down Expand Up @@ -112,14 +108,8 @@ private void HandleOperation(ISymbol symbol, IOperation operation)
}
}

public void ExcludeSymbol(ISymbol symbol)
{
_ = _symbols.AddOrUpdate(symbol, addValue: false, (_, _) => false);
}
public void ExcludeSymbol(ISymbol symbol) => _symbols.AddOrUpdate(symbol, addValue: false, (_, _) => false);

public void AddPotentialSymbol(ISymbol symbol)
{
_ = _symbols.TryAdd(symbol, value: true);
}
public void AddPotentialSymbol(ISymbol symbol) => _symbols.TryAdd(symbol, value: true);
}
}
54 changes: 54 additions & 0 deletions src/Meziantou.Analyzer/Suppressors/IDE0058Suppressor.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
using System.Collections.Immutable;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.Operations;

namespace Meziantou.Analyzer.Suppressors;

[DiagnosticAnalyzer(LanguageNames.CSharp)]
public sealed class IDE0058Suppressor : DiagnosticSuppressor
{
private static readonly SuppressionDescriptor Descriptor = new(
id: "MAS0003",
suppressedDiagnosticId: "IDE0058",
justification: "Suppress IDE0058 on well-known types"
);

public override ImmutableArray<SuppressionDescriptor> SupportedSuppressions => ImmutableArray.Create(Descriptor);

public override void ReportSuppressions(SuppressionAnalysisContext context)
{
#pragma warning disable IDE1006 // Naming Styles
var System_Text_StringBuilder = context.Compilation.GetBestTypeByMetadataName("System.Text.StringBuilder");
var System_IO_Directory = context.Compilation.GetBestTypeByMetadataName("System.IO.Directory");
#pragma warning restore IDE1006

foreach (var diagnostic in context.ReportedDiagnostics)
{
var tree = diagnostic.Location.SourceTree;
if (tree is null)
continue;

var semanticModel = context.GetSemanticModel(tree);
if (semanticModel is null)
continue;

var node = tree.GetRoot(context.CancellationToken).FindNode(diagnostic.Location.SourceSpan);
var operation = semanticModel.GetOperation(node, context.CancellationToken);
if (operation is IInvocationOperation invocation)
{
// StringBuilder
if (invocation.TargetMethod.Name is "Append" or "AppendLine" or "AppendJoin" or "AppendFormat" or "Clear" or "Remove" or "Insert" or "Replace" && invocation.TargetMethod.ContainingType.IsEqualTo(System_Text_StringBuilder))
{
context.ReportSuppression(Suppression.Create(Descriptor, diagnostic));
}

// Directory.CreateDirectory
if (invocation.TargetMethod.Name is "CreateDirectory" && invocation.TargetMethod.ContainingType.IsEqualTo(System_IO_Directory))
{
context.ReportSuppression(Suppression.Create(Descriptor, diagnostic));
}
}
}
}
}
4 changes: 2 additions & 2 deletions tests/Meziantou.Analyzer.Test/Helpers/ProjectBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -134,14 +134,14 @@ public ProjectBuilder WithAnalyzerFromNuGet(string packageName, string version,
public ProjectBuilder WithMicrosoftCodeAnalysisNetAnalyzers(params string[] ruleIds) =>
WithAnalyzerFromNuGet(
"Microsoft.CodeAnalysis.NetAnalyzers",
"7.0.1",
"9.0.0-preview.24454.1",
paths: ["analyzers/dotnet/cs/Microsoft.CodeAnalysis"],
ruleIds);

public ProjectBuilder WithMicrosoftCodeAnalysisCSharpCodeStyleAnalyzers(params string[] ruleIds) =>
WithAnalyzerFromNuGet(
"Microsoft.CodeAnalysis.CSharp.CodeStyle",
"4.10.0-2.final",
"4.12.0-2.final",
paths: ["analyzers/dotnet/cs/"],
ruleIds);

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.Threading.Tasks;
#if ROSLYN_4_10_OR_GREATER
using System.Threading.Tasks;
using Meziantou.Analyzer.Suppressors;
using TestHelper;
using Xunit;
Expand Down Expand Up @@ -65,3 +66,4 @@ internal sealed class Sample
.ValidateAsync();
}
}
#endif
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
#if ROSLYN_4_10_OR_GREATER
using System.Threading.Tasks;
using Meziantou.Analyzer.Suppressors;
using Microsoft.CodeAnalysis;
using TestHelper;
using Xunit;

namespace Meziantou.Analyzer.Test.Suppressors;
public sealed class IDE0058SuppressorTests
{
private static ProjectBuilder CreateProjectBuilder()
=> new ProjectBuilder()
.WithMicrosoftCodeAnalysisCSharpCodeStyleAnalyzers("IDE0058")
.WithAnalyzer<IDE0058Suppressor>()
.WithOutputKind(OutputKind.ConsoleApplication);

// Ensure the diagnostic is reported without the suppressor
[Fact]
public async Task IDE0058IsReported()
=> await new ProjectBuilder()
.WithMicrosoftCodeAnalysisCSharpCodeStyleAnalyzers("IDE0058")
.WithOutputKind(OutputKind.ConsoleApplication)
.WithSourceCode("""
static void A()
{
[|new System.Text.StringBuilder().Append("Hello")|];
[|System.IO.Directory.CreateDirectory("dir")|];
}
""")
.ValidateAsync();

[Fact]
public async Task StringBuilder_Append()
=> await CreateProjectBuilder()
.WithSourceCode("""
static void A()
{
var sb = new System.Text.StringBuilder();
sb.Append("Hello");
System.Console.WriteLine(sb.ToString());
}
""")
.ValidateAsync();

[Fact]
public async Task Directory_CreateDirectory()
=> await CreateProjectBuilder()
.WithSourceCode("""
static void A()
{
System.IO.Directory.CreateDirectory("dir");
}
""")
.ValidateAsync();
}
#endif

0 comments on commit d2be721

Please sign in to comment.