Skip to content

Commit 46d2e37

Browse files
committed
Support implicit object creation expressions in SA1129 code fix
Fixes #3277
1 parent f851c85 commit 46d2e37

File tree

2 files changed

+37
-23
lines changed

2 files changed

+37
-23
lines changed

StyleCop.Analyzers/StyleCop.Analyzers.CodeFixes/ReadabilityRules/SA1129CodeFixProvider.cs

Lines changed: 31 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,9 @@ namespace StyleCop.Analyzers.ReadabilityRules
1515
using Microsoft.CodeAnalysis.CodeFixes;
1616
using Microsoft.CodeAnalysis.CSharp;
1717
using Microsoft.CodeAnalysis.CSharp.Syntax;
18+
using Microsoft.CodeAnalysis.Editing;
1819
using StyleCop.Analyzers.Helpers;
20+
using StyleCop.Analyzers.Lightup;
1921

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

5658
var newExpression = syntaxRoot.FindNode(diagnostic.Location.SourceSpan, getInnermostNodeForTie: true);
57-
var newSyntaxRoot = syntaxRoot.ReplaceNode(newExpression, GetReplacementNode(newExpression, semanticModel, cancellationToken));
59+
var newSyntaxRoot = syntaxRoot.ReplaceNode(newExpression, GetReplacementNode(document.Project, newExpression, semanticModel, cancellationToken));
5860

5961
return document.WithSyntaxRoot(newSyntaxRoot);
6062
}
6163

62-
private static SyntaxNode GetReplacementNode(SyntaxNode node, SemanticModel semanticModel, CancellationToken cancellationToken)
64+
private static SyntaxNode GetReplacementNode(Project project, SyntaxNode node, SemanticModel semanticModel, CancellationToken cancellationToken)
6365
{
64-
var newExpression = (ObjectCreationExpressionSyntax)node;
66+
var newExpression = (BaseObjectCreationExpressionSyntaxWrapper)node;
6567

66-
var symbolInfo = semanticModel.GetSymbolInfo(newExpression.Type, cancellationToken);
67-
var namedTypeSymbol = symbolInfo.Symbol as INamedTypeSymbol;
68+
var symbolInfo = semanticModel.GetSymbolInfo(newExpression, cancellationToken);
69+
var namedTypeSymbol = (symbolInfo.Symbol as IMethodSymbol)?.ContainingType;
70+
71+
var type = GetOrCreateTypeSyntax(project, newExpression, namedTypeSymbol);
6872

6973
SyntaxNode replacement;
7074

@@ -75,7 +79,7 @@ private static SyntaxNode GetReplacementNode(SyntaxNode node, SemanticModel sema
7579
{
7680
if (IsDefaultParameterValue(newExpression))
7781
{
78-
replacement = SyntaxFactory.DefaultExpression(newExpression.Type);
82+
replacement = SyntaxFactory.DefaultExpression(type);
7983
}
8084
else
8185
{
@@ -98,21 +102,34 @@ private static SyntaxNode GetReplacementNode(SyntaxNode node, SemanticModel sema
98102
fieldName = nameof(Guid.Empty);
99103
}
100104

101-
replacement = ConstructMemberAccessSyntax(newExpression.Type, fieldName);
105+
replacement = ConstructMemberAccessSyntax(type, fieldName);
102106
}
103107
}
104108
else if (IsEnumWithDefaultMember(namedTypeSymbol, out string memberName))
105109
{
106-
replacement = ConstructMemberAccessSyntax(newExpression.Type, memberName);
110+
replacement = ConstructMemberAccessSyntax(type, memberName);
107111
}
108112
else
109113
{
110-
replacement = SyntaxFactory.DefaultExpression(newExpression.Type);
114+
replacement = SyntaxFactory.DefaultExpression(type);
111115
}
112116

113117
return replacement
114-
.WithLeadingTrivia(newExpression.GetLeadingTrivia())
115-
.WithTrailingTrivia(newExpression.GetTrailingTrivia());
118+
.WithLeadingTrivia(newExpression.SyntaxNode.GetLeadingTrivia())
119+
.WithTrailingTrivia(newExpression.SyntaxNode.GetTrailingTrivia());
120+
}
121+
122+
private static TypeSyntax GetOrCreateTypeSyntax(Project project, BaseObjectCreationExpressionSyntaxWrapper baseObjectCreationExpression, INamedTypeSymbol constructedType)
123+
{
124+
if (baseObjectCreationExpression.SyntaxNode is ObjectCreationExpressionSyntax objectCreationExpressionSyntax)
125+
{
126+
return objectCreationExpressionSyntax.Type;
127+
}
128+
else
129+
{
130+
SyntaxGenerator generator = SyntaxGenerator.GetGenerator(project);
131+
return (TypeSyntax)generator.TypeExpression(constructedType);
132+
}
116133
}
117134

118135
/// <summary>
@@ -146,9 +163,9 @@ private static bool IsType<T>(INamedTypeSymbol namedTypeSymbol)
146163
return true;
147164
}
148165

149-
private static bool IsDefaultParameterValue(ObjectCreationExpressionSyntax expression)
166+
private static bool IsDefaultParameterValue(BaseObjectCreationExpressionSyntaxWrapper expression)
150167
{
151-
if (expression.Parent.Parent is ParameterSyntax parameterSyntax)
168+
if (expression.SyntaxNode.Parent.Parent is ParameterSyntax parameterSyntax)
152169
{
153170
return parameterSyntax.Parent.Parent is BaseMethodDeclarationSyntax;
154171
}
@@ -221,7 +238,7 @@ protected override async Task<SyntaxNode> FixAllInDocumentAsync(FixAllContext fi
221238

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

224-
return syntaxRoot.ReplaceNodes(nodes, (originalNode, rewrittenNode) => GetReplacementNode(rewrittenNode, semanticModel, fixAllContext.CancellationToken));
241+
return syntaxRoot.ReplaceNodes(nodes, (originalNode, rewrittenNode) => GetReplacementNode(document.Project, rewrittenNode, semanticModel, fixAllContext.CancellationToken));
225242
}
226243
}
227244
}

StyleCop.Analyzers/StyleCop.Analyzers.Test.CSharp9/ReadabilityRules/SA1129CSharp9UnitTests.cs

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@ namespace StyleCop.Analyzers.Test.CSharp9.ReadabilityRules
99
using StyleCop.Analyzers.Test.CSharp8.ReadabilityRules;
1010
using Xunit;
1111
using static StyleCop.Analyzers.Test.Verifiers.StyleCopCodeFixVerifier<
12-
StyleCop.Analyzers.ReadabilityRules.SA1129DoNotUseDefaultValueTypeConstructor,
13-
StyleCop.Analyzers.ReadabilityRules.SA1129CodeFixProvider>;
12+
StyleCop.Analyzers.ReadabilityRules.SA1129DoNotUseDefaultValueTypeConstructor,
13+
StyleCop.Analyzers.ReadabilityRules.SA1129CodeFixProvider>;
1414

1515
public class SA1129CSharp9UnitTests : SA1129CSharp8UnitTests
1616
{
@@ -19,13 +19,14 @@ public class SA1129CSharp9UnitTests : SA1129CSharp8UnitTests
1919
/// </summary>
2020
/// <returns>A <see cref="Task"/> representing the asynchronous unit test.</returns>
2121
[Fact]
22+
[WorkItem(3277, "https://github.com/DotNetAnalyzers/StyleCopAnalyzers/issues/3277")]
2223
public async Task VerifyValueTypeWithTargetTypeNewAsync()
2324
{
2425
var testCode = @"struct S
2526
{
2627
internal static S F()
2728
{
28-
S s = {|#0:new()|};
29+
S s = [|new()|];
2930
return s;
3031
}
3132
}
@@ -35,17 +36,13 @@ internal static S F()
3536
{
3637
internal static S F()
3738
{
38-
S s = default;
39+
S s = default(S);
3940
return s;
4041
}
4142
}
4243
";
43-
DiagnosticResult[] expected =
44-
{
45-
Diagnostic().WithLocation(0),
46-
};
4744

48-
await VerifyCSharpFixAsync(testCode, expected, fixedTestCode, CancellationToken.None).ConfigureAwait(false);
45+
await VerifyCSharpFixAsync(testCode, DiagnosticResult.EmptyDiagnosticResults, fixedTestCode, CancellationToken.None).ConfigureAwait(false);
4946
}
5047
}
5148
}

0 commit comments

Comments
 (0)