33
44namespace 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