Skip to content

Commit f4e3909

Browse files
committed
Merge pull request #1976 from nbarbettini/fix-1973
Add special cases to SA1129 code fix
2 parents f8ee188 + 16da6b8 commit f4e3909

2 files changed

Lines changed: 392 additions & 4 deletions

File tree

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

Lines changed: 103 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
namespace StyleCop.Analyzers.ReadabilityRules
55
{
6+
using System;
67
using System.Collections.Immutable;
78
using System.Composition;
89
using System.Linq;
@@ -49,21 +50,118 @@ public override Task RegisterCodeFixesAsync(CodeFixContext context)
4950
private static async Task<Document> GetTransformedDocumentAsync(Document document, Diagnostic diagnostic, CancellationToken cancellationToken)
5051
{
5152
var syntaxRoot = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false);
53+
var semanticModel = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false);
5254

5355
var newExpression = syntaxRoot.FindNode(diagnostic.Location.SourceSpan, getInnermostNodeForTie: true);
54-
var newSyntaxRoot = syntaxRoot.ReplaceNode(newExpression, GetReplacementNode(newExpression));
56+
var newSyntaxRoot = syntaxRoot.ReplaceNode(newExpression, GetReplacementNode(newExpression, semanticModel, cancellationToken));
57+
5558
return document.WithSyntaxRoot(newSyntaxRoot);
5659
}
5760

58-
private static SyntaxNode GetReplacementNode(SyntaxNode node)
61+
private static SyntaxNode GetReplacementNode(SyntaxNode node, SemanticModel semanticModel, CancellationToken cancellationToken)
5962
{
6063
var newExpression = (ObjectCreationExpressionSyntax)node;
6164

62-
return SyntaxFactory.DefaultExpression(newExpression.Type)
65+
var symbolInfo = semanticModel.GetSymbolInfo(newExpression.Type, cancellationToken);
66+
var namedTypeSymbol = symbolInfo.Symbol as INamedTypeSymbol;
67+
68+
SyntaxNode replacement = null;
69+
string memberName = null;
70+
71+
if (IsType<CancellationToken>(namedTypeSymbol))
72+
{
73+
replacement = ConstructMemberAccessSyntax(newExpression.Type, nameof(CancellationToken.None));
74+
}
75+
else if (IsEnumWithDefaultMember(namedTypeSymbol, out memberName))
76+
{
77+
replacement = ConstructMemberAccessSyntax(newExpression.Type, memberName);
78+
}
79+
else
80+
{
81+
replacement = SyntaxFactory.DefaultExpression(newExpression.Type);
82+
}
83+
84+
return replacement
6385
.WithLeadingTrivia(newExpression.GetLeadingTrivia())
6486
.WithTrailingTrivia(newExpression.GetTrailingTrivia());
6587
}
6688

89+
/// <summary>
90+
/// Determines whether a symbol is an instance of a given <see cref="Type"/>.
91+
/// </summary>
92+
/// <typeparam name="T">The type to match.</typeparam>
93+
/// <param name="namedTypeSymbol">The symbol.</param>
94+
/// <returns><see langword="true"/> if the syntax matches the type; <see langword="false"/> otherwise.</returns>
95+
private static bool IsType<T>(INamedTypeSymbol namedTypeSymbol)
96+
{
97+
if (namedTypeSymbol == null)
98+
{
99+
return false;
100+
}
101+
102+
var expectedType = typeof(T);
103+
104+
if (!string.Equals(expectedType.Name, namedTypeSymbol.Name, StringComparison.Ordinal))
105+
{
106+
return false;
107+
}
108+
109+
if (!string.Equals(
110+
expectedType.Namespace,
111+
namedTypeSymbol.ContainingNamespace?.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat.WithGlobalNamespaceStyle(SymbolDisplayGlobalNamespaceStyle.Omitted)),
112+
StringComparison.Ordinal))
113+
{
114+
return false;
115+
}
116+
117+
return true;
118+
}
119+
120+
/// <summary>
121+
/// Determines whether a given enumeration symbol contains a member with value <c>0</c>.
122+
/// </summary>
123+
/// <param name="namedTypeSymbol">The symbol.</param>
124+
/// <param name="foundMemberName">Will be set to the string name of the member, if one is found.</param>
125+
/// <returns><see langword="true"/> if the syntax is an enumeration with a value of <c>0</c>; <see langword="false"/> otherwise.</returns>
126+
private static bool IsEnumWithDefaultMember(INamedTypeSymbol namedTypeSymbol, out string foundMemberName)
127+
{
128+
foundMemberName = null;
129+
130+
if (namedTypeSymbol == null || namedTypeSymbol.TypeKind != TypeKind.Enum)
131+
{
132+
return false;
133+
}
134+
135+
var foundMembers = namedTypeSymbol
136+
.GetMembers()
137+
.Where(m => m.Kind == SymbolKind.Field)
138+
.OfType<IFieldSymbol>()
139+
.Where(fs => fs.ConstantValue.Equals(0))
140+
.ToList();
141+
142+
if (foundMembers.Count != 1)
143+
{
144+
return false;
145+
}
146+
147+
foundMemberName = foundMembers[0].Name;
148+
return true;
149+
}
150+
151+
/// <summary>
152+
/// Gets a qualified member access expression for the given <paramref name="typeSyntax"/>.
153+
/// </summary>
154+
/// <param name="typeSyntax">The type syntax from the original constructor.</param>
155+
/// <param name="memberName">The member name.</param>
156+
/// <returns>A new member access expression.</returns>
157+
private static SyntaxNode ConstructMemberAccessSyntax(TypeSyntax typeSyntax, string memberName)
158+
{
159+
return SyntaxFactory.MemberAccessExpression(
160+
SyntaxKind.SimpleMemberAccessExpression,
161+
typeSyntax,
162+
SyntaxFactory.IdentifierName(memberName));
163+
}
164+
67165
private class FixAll : DocumentBasedFixAllProvider
68166
{
69167
public static FixAllProvider Instance { get; } =
@@ -80,10 +178,11 @@ protected override async Task<SyntaxNode> FixAllInDocumentAsync(FixAllContext fi
80178
}
81179

82180
var syntaxRoot = await document.GetSyntaxRootAsync(fixAllContext.CancellationToken).ConfigureAwait(false);
181+
var semanticModel = await document.GetSemanticModelAsync(fixAllContext.CancellationToken).ConfigureAwait(false);
83182

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

86-
return syntaxRoot.ReplaceNodes(nodes, (originalNode, rewrittenNode) => GetReplacementNode(rewrittenNode));
185+
return syntaxRoot.ReplaceNodes(nodes, (originalNode, rewrittenNode) => GetReplacementNode(rewrittenNode, semanticModel, fixAllContext.CancellationToken));
87186
}
88187
}
89188
}

0 commit comments

Comments
 (0)