Skip to content

Commit

Permalink
Fix RCS1014 (#1350)
Browse files Browse the repository at this point in the history
  • Loading branch information
josefpihrt authored Jan 8, 2024
1 parent 23b3f7d commit 2354f1f
Show file tree
Hide file tree
Showing 9 changed files with 180 additions and 44 deletions.
1 change: 1 addition & 0 deletions ChangeLog.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

- Fix analyzer [RCS0034](https://josefpihrt.github.io/docs/roslynator/analyzers/RCS0034) ([PR](https://github.com/dotnet/roslynator/pull/1351))
- Fix analyzer [RCS0023](https://josefpihrt.github.io/docs/roslynator/analyzers/RCS0023) ([PR](https://github.com/dotnet/roslynator/pull/1352))
- Fix analyzer [RCS1014](https://josefpihrt.github.io/docs/roslynator/analyzers/RCS1014) ([PR](https://github.com/dotnet/roslynator/pull/1350))

## [4.8.0] - 2024-01-02

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,11 @@ public override async Task RegisterCodeFixesAsync(CodeFixContext context)
}
else if (node is ImplicitArrayCreationExpressionSyntax implicitArrayCreation)
{
if (diagnostic.Properties.ContainsKey(DiagnosticPropertyKeys.ImplicitToCollectionExpression))
if (diagnostic.Properties.ContainsKey(DiagnosticPropertyKeys.VarToExplicit))
{
return (ct => ConvertToExplicitAndUseVarAsync(document, implicitArrayCreation, ct), UseCollectionExpressionTitle);
}
else if (diagnostic.Properties.ContainsKey(DiagnosticPropertyKeys.ImplicitToCollectionExpression))
{
return (ct => ConvertToCollectionExpressionAsync(document, implicitArrayCreation, ct), UseCollectionExpressionTitle);
}
Expand Down Expand Up @@ -130,6 +134,28 @@ private static async Task<Document> ConvertToExplicitAsync(
Document document,
ImplicitArrayCreationExpressionSyntax implicitArrayCreation,
CancellationToken cancellationToken)
{
ArrayCreationExpressionSyntax newNode = await CreateArrayCreationAsync(document, implicitArrayCreation, cancellationToken).ConfigureAwait(false);

return await document.ReplaceNodeAsync(implicitArrayCreation, newNode, cancellationToken).ConfigureAwait(false);
}

private static async Task<Document> ConvertToExplicitAndUseVarAsync(
Document document,
ImplicitArrayCreationExpressionSyntax implicitArrayCreation,
CancellationToken cancellationToken)
{
ArrayCreationExpressionSyntax arrayCreation = await CreateArrayCreationAsync(document, implicitArrayCreation, cancellationToken).ConfigureAwait(false);

VariableDeclarationSyntax variableDeclaration = implicitArrayCreation.FirstAncestor<VariableDeclarationSyntax>();

VariableDeclarationSyntax newVariableDeclaration = variableDeclaration.ReplaceNode(implicitArrayCreation, arrayCreation)
.WithType(CSharpFactory.VarType().WithTriviaFrom(variableDeclaration.Type));

return await document.ReplaceNodeAsync(variableDeclaration, newVariableDeclaration, cancellationToken).ConfigureAwait(false);
}

private static async Task<ArrayCreationExpressionSyntax> CreateArrayCreationAsync(Document document, ImplicitArrayCreationExpressionSyntax implicitArrayCreation, CancellationToken cancellationToken)
{
SemanticModel semanticModel = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false);
ITypeSymbol typeSymbol = semanticModel.GetTypeSymbol(implicitArrayCreation, cancellationToken);
Expand All @@ -146,14 +172,12 @@ private static async Task<Document> ConvertToExplicitAsync(
initializer.Expressions,
(node, _) => (node.IsKind(SyntaxKind.CastExpression)) ? node.WithSimplifierAnnotation() : node);

ArrayCreationExpressionSyntax newNode = ArrayCreationExpression(
return ArrayCreationExpression(
newKeyword,
arrayType
.WithLeadingTrivia(implicitArrayCreation.OpenBracketToken.LeadingTrivia)
.WithTrailingTrivia(implicitArrayCreation.CloseBracketToken.TrailingTrivia),
newInitializer);

return await document.ReplaceNodeAsync(implicitArrayCreation, newNode, cancellationToken).ConfigureAwait(false);
}

private static async Task<Document> ConvertToExplicitAsync(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,7 @@ public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context)
predicate: f => f.IsKind(
SyntaxKind.ObjectCreationExpression,
SyntaxKind.ImplicitObjectCreationExpression,
SyntaxKind.CollectionExpression,
SyntaxKind.VariableDeclaration)))
SyntaxKind.CollectionExpression)))
{
return;
}
Expand Down Expand Up @@ -97,7 +96,37 @@ public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context)
}
else if (node is ImplicitObjectCreationExpressionSyntax implicitObjectCreation)
{
if (diagnostic.Properties.ContainsKey(DiagnosticPropertyKeys.ImplicitToCollectionExpression))
if (diagnostic.Properties.ContainsKey(DiagnosticPropertyKeys.VarToExplicit))
{
VariableDeclarationSyntax variableDeclaration = node.FirstAncestor<VariableDeclarationSyntax>();

CodeAction codeAction = CodeAction.Create(
UseExplicitObjectCreationTitle,
ct =>
{
var implicitObjectCreation = (ImplicitObjectCreationExpressionSyntax)variableDeclaration.Variables.Single().Initializer.Value;

SyntaxToken newKeyword = implicitObjectCreation.NewKeyword;

if (!newKeyword.TrailingTrivia.Any())
newKeyword = newKeyword.WithTrailingTrivia(ElasticSpace);

ObjectCreationExpressionSyntax objectCreation = ObjectCreationExpression(
newKeyword,
variableDeclaration.Type.WithoutTrivia(),
implicitObjectCreation.ArgumentList,
implicitObjectCreation.Initializer);

VariableDeclarationSyntax newVariableDeclaration = variableDeclaration.ReplaceNode(implicitObjectCreation, objectCreation)
.WithType(CSharpFactory.VarType().WithTriviaFrom(variableDeclaration.Type));

return document.ReplaceNodeAsync(variableDeclaration, newVariableDeclaration, ct);
},
GetEquivalenceKey(diagnostic));

context.RegisterCodeFix(codeAction, diagnostic);
}
else if (diagnostic.Properties.ContainsKey(DiagnosticPropertyKeys.ImplicitToCollectionExpression))
{
CodeAction codeAction = CodeAction.Create(
UseCollectionExpressionTitle,
Expand Down Expand Up @@ -199,35 +228,5 @@ public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context)
context.RegisterCodeFix(codeAction, diagnostic);
}
}
else
{
var variableDeclaration = (VariableDeclarationSyntax)node;

CodeAction codeAction = CodeAction.Create(
UseExplicitObjectCreationTitle,
ct =>
{
var implicitObjectCreation = (ImplicitObjectCreationExpressionSyntax)variableDeclaration.Variables.Single().Initializer.Value;

SyntaxToken newKeyword = implicitObjectCreation.NewKeyword;

if (!newKeyword.TrailingTrivia.Any())
newKeyword = newKeyword.WithTrailingTrivia(ElasticSpace);

ObjectCreationExpressionSyntax objectCreation = ObjectCreationExpression(
newKeyword,
variableDeclaration.Type.WithoutTrivia(),
implicitObjectCreation.ArgumentList,
implicitObjectCreation.Initializer);

VariableDeclarationSyntax newVariableDeclaration = variableDeclaration.ReplaceNode(implicitObjectCreation, objectCreation)
.WithType(CSharpFactory.VarType().WithTriviaFrom(variableDeclaration.Type));

return document.ReplaceNodeAsync(variableDeclaration, newVariableDeclaration, ct);
},
GetEquivalenceKey(diagnostic));

context.RegisterCodeFix(codeAction, diagnostic);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,11 @@ internal abstract class ImplicitOrExplicitCreationAnalysis
new KeyValuePair<string, string>(DiagnosticPropertyKeys.ExplicitToCollectionExpression, null)
});

protected static readonly ImmutableDictionary<string, string> _varToExplicit = ImmutableDictionary.CreateRange(new[]
{
new KeyValuePair<string, string>(DiagnosticPropertyKeys.VarToExplicit, null)
});

public abstract TypeStyle GetTypeStyle(ref SyntaxNodeAnalysisContext context);

protected abstract void ReportExplicitToImplicit(ref SyntaxNodeAnalysisContext context);
Expand All @@ -37,6 +42,8 @@ internal abstract class ImplicitOrExplicitCreationAnalysis

protected abstract void ReportImplicitToExplicit(ref SyntaxNodeAnalysisContext context);

protected abstract void ReportVarToExplicit(ref SyntaxNodeAnalysisContext context, TypeSyntax type);

protected abstract void ReportImplicitToCollectionExpression(ref SyntaxNodeAnalysisContext context);

protected abstract void ReportCollectionExpressionToImplicit(ref SyntaxNodeAnalysisContext context);
Expand Down Expand Up @@ -343,7 +350,7 @@ private void AnalyzeImplicit(ref SyntaxNodeAnalysisContext context)
&& !isVar
&& context.UseVarInsteadOfImplicitObjectCreation() == true)
{
DiagnosticHelpers.ReportDiagnostic(context, DiagnosticRules.UseImplicitOrExplicitObjectCreation, variableDeclaration, "explicit");
ReportVarToExplicit(ref context, variableDeclaration.Type);
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -343,6 +343,29 @@ protected override void ReportImplicitToExplicit(ref SyntaxNodeAnalysisContext c
"Use explicitly typed array");
}

protected override void ReportVarToExplicit(ref SyntaxNodeAnalysisContext context, TypeSyntax type)
{
if (context.Node.IsKind(SyntaxKind.CollectionExpression))
return;

ITypeSymbol typeSymbol = context.SemanticModel.GetTypeSymbol(type, context.CancellationToken);

if (typeSymbol?.IsErrorType() != false)
return;

ITypeSymbol expressionTypeSymbol = context.SemanticModel.GetTypeSymbol(context.Node, context.CancellationToken);

if (!SymbolEqualityComparer.IncludeNullability.Equals(typeSymbol, expressionTypeSymbol))
return;

DiagnosticHelpers.ReportDiagnostic(
context,
DiagnosticRules.UseExplicitlyOrImplicitlyTypedArray,
GetLocationFromImplicit(ref context),
properties: _varToExplicit,
"Use explicitly typed array");
}

protected override void ReportImplicitToCollectionExpression(ref SyntaxNodeAnalysisContext context)
{
DiagnosticHelpers.ReportDiagnostic(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ protected override void ReportExplicitToImplicit(ref SyntaxNodeAnalysisContext c
context,
DiagnosticRules.UseImplicitOrExplicitObjectCreation,
objectCreation.Type.GetLocation(),
"Simplify array creation");
"Simplify object creation");
}

protected override void ReportExplicitToCollectionExpression(ref SyntaxNodeAnalysisContext context)
Expand All @@ -41,7 +41,7 @@ protected override void ReportExplicitToCollectionExpression(ref SyntaxNodeAnaly
DiagnosticRules.UseImplicitOrExplicitObjectCreation,
objectCreation.Type.GetLocation(),
properties: _explicitToCollectionExpression,
"Simplify array creation");
"Simplify object creation");
}

protected override void ReportImplicitToExplicit(ref SyntaxNodeAnalysisContext context)
Expand All @@ -53,14 +53,24 @@ protected override void ReportImplicitToExplicit(ref SyntaxNodeAnalysisContext c
"Use explicit object creation");
}

protected override void ReportVarToExplicit(ref SyntaxNodeAnalysisContext context, TypeSyntax type)
{
DiagnosticHelpers.ReportDiagnostic(
context,
DiagnosticRules.UseImplicitOrExplicitObjectCreation,
context.Node.GetLocation(),
properties: _varToExplicit,
"Use explicit object creation");
}

protected override void ReportImplicitToCollectionExpression(ref SyntaxNodeAnalysisContext context)
{
DiagnosticHelpers.ReportDiagnostic(
context,
DiagnosticRules.UseImplicitOrExplicitObjectCreation,
context.Node.GetLocation(),
properties: _implicitToCollectionExpression,
"Simplify array creation");
"Simplify object creation");
}

protected override void ReportCollectionExpressionToImplicit(ref SyntaxNodeAnalysisContext context)
Expand All @@ -70,6 +80,6 @@ protected override void ReportCollectionExpressionToImplicit(ref SyntaxNodeAnaly
DiagnosticRules.UseImplicitOrExplicitObjectCreation,
context.Node.GetLocation(),
properties: _collectionExpressionToImplicit,
"Simplify array creation");
"Simplify object creation");
}
}
1 change: 1 addition & 0 deletions src/Common/CSharp/Analysis/DiagnosticPropertyKeys.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,5 @@ internal static class DiagnosticPropertyKeys
internal static readonly string ImplicitToCollectionExpression = nameof(ImplicitToCollectionExpression);
internal static readonly string CollectionExpressionToImplicit = nameof(CollectionExpressionToImplicit);
internal static readonly string ExplicitToCollectionExpression = nameof(ExplicitToCollectionExpression);
internal static readonly string VarToExplicit = nameof(VarToExplicit);
}
71 changes: 71 additions & 0 deletions src/Tests/Analyzers.Tests/RCS1014UseImplicitlyTypedArrayTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -196,4 +196,75 @@ void M()
", options: Options.AddConfigOption(ConfigOptionKeys.ArrayCreationTypeStyle, ConfigOptionValues.ArrayCreationTypeStyle_Implicit)
.AddConfigOption(ConfigOptionKeys.UseVarInsteadOfImplicitObjectCreation, true));
}

[Fact, Trait(Traits.Analyzer, DiagnosticIdentifiers.UseImplicitOrExplicitObjectCreation)]
public async Task Test_UseVarInsteadOfImplicitCreation()
{
await VerifyDiagnosticAndFixAsync(@"
using System.Collections.Generic;
class C
{
void M()
{
string[] addresses = [|new[]|]
{
""address 1"",
""address 2""
};
}
}
", @"
using System.Collections.Generic;
class C
{
void M()
{
var addresses = new string[]
{
""address 1"",
""address 2""
};
}
}
", options: Options.AddConfigOption(ConfigOptionKeys.ObjectCreationTypeStyle, ConfigOptionValues.ArrayCreationTypeStyle_Implicit)
.AddConfigOption(ConfigOptionKeys.UseVarInsteadOfImplicitObjectCreation, true));
}

[Fact, Trait(Traits.Analyzer, DiagnosticIdentifiers.UseImplicitOrExplicitObjectCreation)]
public async Task TestNoDiagnostic_UseVarInsteadOfImplicitCreation()
{
await VerifyNoDiagnosticAsync(@"
using System.Collections.Generic;
class C
{
void M()
{
ICollection<string> addresses = new[]
{
""a"",
""b""
};
}
}
", options: Options.AddConfigOption(ConfigOptionKeys.UseVarInsteadOfImplicitObjectCreation, true));
}

[Fact, Trait(Traits.Analyzer, DiagnosticIdentifiers.UseImplicitOrExplicitObjectCreation)]
public async Task TestNoDiagnostic_UseVarInsteadOfImplicitCreation_CollectionExpression()
{
await VerifyNoDiagnosticAsync(@"
using System.Collections.Generic;
class C
{
void M()
{
string[] addresses = [ ""a"", ""b"" ];
}
}
", options: Options.AddConfigOption(ConfigOptionKeys.UseVarInsteadOfImplicitObjectCreation, true));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -1525,7 +1525,7 @@ class C
{
void M()
{
[|string s = new(' ', 1)|];
string s = [|new(' ', 1)|];
}
}
", @"
Expand Down Expand Up @@ -1563,7 +1563,7 @@ class C
{
void M()
{
using ([|StringReader s = new("""")|])
using (StringReader s = [|new("""")|])
{
}
}
Expand Down

0 comments on commit 2354f1f

Please sign in to comment.