Skip to content

Commit

Permalink
Support implicit object creation expressions in SA1129 code fix
Browse files Browse the repository at this point in the history
Fixes #3277
  • Loading branch information
sharwell committed Dec 21, 2020
1 parent be71d50 commit 29362fd
Show file tree
Hide file tree
Showing 2 changed files with 37 additions and 23 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,9 @@ namespace StyleCop.Analyzers.ReadabilityRules
using Microsoft.CodeAnalysis.CodeFixes;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Editing;
using StyleCop.Analyzers.Helpers;
using StyleCop.Analyzers.Lightup;

/// <summary>
/// Implements a code fix for <see cref="SA1129DoNotUseDefaultValueTypeConstructor"/>.
Expand Down Expand Up @@ -54,17 +56,19 @@ private static async Task<Document> GetTransformedDocumentAsync(Document documen
var semanticModel = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false);

var newExpression = syntaxRoot.FindNode(diagnostic.Location.SourceSpan, getInnermostNodeForTie: true);
var newSyntaxRoot = syntaxRoot.ReplaceNode(newExpression, GetReplacementNode(newExpression, semanticModel, cancellationToken));
var newSyntaxRoot = syntaxRoot.ReplaceNode(newExpression, GetReplacementNode(document.Project, newExpression, semanticModel, cancellationToken));

return document.WithSyntaxRoot(newSyntaxRoot);
}

private static SyntaxNode GetReplacementNode(SyntaxNode node, SemanticModel semanticModel, CancellationToken cancellationToken)
private static SyntaxNode GetReplacementNode(Project project, SyntaxNode node, SemanticModel semanticModel, CancellationToken cancellationToken)
{
var newExpression = (ObjectCreationExpressionSyntax)node;
var newExpression = (BaseObjectCreationExpressionSyntaxWrapper)node;

var symbolInfo = semanticModel.GetSymbolInfo(newExpression.Type, cancellationToken);
var namedTypeSymbol = symbolInfo.Symbol as INamedTypeSymbol;
var symbolInfo = semanticModel.GetSymbolInfo(newExpression, cancellationToken);
var namedTypeSymbol = (symbolInfo.Symbol as IMethodSymbol)?.ContainingType;

var type = GetOrCreateTypeSyntax(project, newExpression, namedTypeSymbol);

SyntaxNode replacement;

Expand All @@ -75,7 +79,7 @@ private static SyntaxNode GetReplacementNode(SyntaxNode node, SemanticModel sema
{
if (IsDefaultParameterValue(newExpression))
{
replacement = SyntaxFactory.DefaultExpression(newExpression.Type);
replacement = SyntaxFactory.DefaultExpression(type);
}
else
{
Expand All @@ -98,21 +102,34 @@ private static SyntaxNode GetReplacementNode(SyntaxNode node, SemanticModel sema
fieldName = nameof(Guid.Empty);
}

replacement = ConstructMemberAccessSyntax(newExpression.Type, fieldName);
replacement = ConstructMemberAccessSyntax(type, fieldName);
}
}
else if (IsEnumWithDefaultMember(namedTypeSymbol, out string memberName))
{
replacement = ConstructMemberAccessSyntax(newExpression.Type, memberName);
replacement = ConstructMemberAccessSyntax(type, memberName);
}
else
{
replacement = SyntaxFactory.DefaultExpression(newExpression.Type);
replacement = SyntaxFactory.DefaultExpression(type);
}

return replacement
.WithLeadingTrivia(newExpression.GetLeadingTrivia())
.WithTrailingTrivia(newExpression.GetTrailingTrivia());
.WithLeadingTrivia(newExpression.SyntaxNode.GetLeadingTrivia())
.WithTrailingTrivia(newExpression.SyntaxNode.GetTrailingTrivia());
}

private static TypeSyntax GetOrCreateTypeSyntax(Project project, BaseObjectCreationExpressionSyntaxWrapper baseObjectCreationExpression, INamedTypeSymbol constructedType)
{
if (baseObjectCreationExpression.SyntaxNode is ObjectCreationExpressionSyntax objectCreationExpressionSyntax)
{
return objectCreationExpressionSyntax.Type;
}
else
{
SyntaxGenerator generator = SyntaxGenerator.GetGenerator(project);
return (TypeSyntax)generator.TypeExpression(constructedType);
}
}

/// <summary>
Expand Down Expand Up @@ -146,9 +163,9 @@ private static bool IsType<T>(INamedTypeSymbol namedTypeSymbol)
return true;
}

private static bool IsDefaultParameterValue(ObjectCreationExpressionSyntax expression)
private static bool IsDefaultParameterValue(BaseObjectCreationExpressionSyntaxWrapper expression)
{
if (expression.Parent.Parent is ParameterSyntax parameterSyntax)
if (expression.SyntaxNode.Parent.Parent is ParameterSyntax parameterSyntax)
{
return parameterSyntax.Parent.Parent is BaseMethodDeclarationSyntax;
}
Expand Down Expand Up @@ -221,7 +238,7 @@ protected override async Task<SyntaxNode> FixAllInDocumentAsync(FixAllContext fi

var nodes = diagnostics.Select(diagnostic => syntaxRoot.FindNode(diagnostic.Location.SourceSpan, getInnermostNodeForTie: true));

return syntaxRoot.ReplaceNodes(nodes, (originalNode, rewrittenNode) => GetReplacementNode(rewrittenNode, semanticModel, fixAllContext.CancellationToken));
return syntaxRoot.ReplaceNodes(nodes, (originalNode, rewrittenNode) => GetReplacementNode(document.Project, rewrittenNode, semanticModel, fixAllContext.CancellationToken));
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ namespace StyleCop.Analyzers.Test.CSharp9.ReadabilityRules
using StyleCop.Analyzers.Test.CSharp8.ReadabilityRules;
using Xunit;
using static StyleCop.Analyzers.Test.Verifiers.StyleCopCodeFixVerifier<
StyleCop.Analyzers.ReadabilityRules.SA1129DoNotUseDefaultValueTypeConstructor,
StyleCop.Analyzers.ReadabilityRules.SA1129CodeFixProvider>;
StyleCop.Analyzers.ReadabilityRules.SA1129DoNotUseDefaultValueTypeConstructor,
StyleCop.Analyzers.ReadabilityRules.SA1129CodeFixProvider>;

public class SA1129CSharp9UnitTests : SA1129CSharp8UnitTests
{
Expand All @@ -19,13 +19,14 @@ public class SA1129CSharp9UnitTests : SA1129CSharp8UnitTests
/// </summary>
/// <returns>A <see cref="Task"/> representing the asynchronous unit test.</returns>
[Fact]
[WorkItem(3277, "https://github.com/DotNetAnalyzers/StyleCopAnalyzers/issues/3277")]
public async Task VerifyValueTypeWithTargetTypeNewAsync()
{
var testCode = @"struct S
{
internal static S F()
{
S s = {|#0:new()|};
S s = [|new()|];
return s;
}
}
Expand All @@ -35,17 +36,13 @@ internal static S F()
{
internal static S F()
{
S s = default;
S s = default(S);
return s;
}
}
";
DiagnosticResult[] expected =
{
Diagnostic().WithLocation(0),
};

await VerifyCSharpFixAsync(testCode, expected, fixedTestCode, CancellationToken.None).ConfigureAwait(false);
await VerifyCSharpFixAsync(testCode, DiagnosticResult.EmptyDiagnosticResults, fixedTestCode, CancellationToken.None).ConfigureAwait(false);
}
}
}

0 comments on commit 29362fd

Please sign in to comment.