Skip to content

Commit acd7b22

Browse files
committed
Merge pull request #2148 from tmaczynski/sa1139
2 parents 09075ff + e8d9684 commit acd7b22

13 files changed

Lines changed: 705 additions & 0 deletions

File tree

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
// Copyright (c) Tunnel Vision Laboratories, LLC. All Rights Reserved.
2+
// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information.
3+
4+
/* Contributor: Tomasz Maczyński */
5+
6+
namespace StyleCop.Analyzers.ReadabilityRules
7+
{
8+
using System.Collections.Generic;
9+
using System.Collections.Immutable;
10+
using System.Composition;
11+
using System.Linq;
12+
using System.Threading;
13+
using System.Threading.Tasks;
14+
using Helpers;
15+
using Microsoft.CodeAnalysis;
16+
using Microsoft.CodeAnalysis.CodeActions;
17+
using Microsoft.CodeAnalysis.CodeFixes;
18+
using Microsoft.CodeAnalysis.CSharp;
19+
using Microsoft.CodeAnalysis.CSharp.Syntax;
20+
using Microsoft.CodeAnalysis.Text;
21+
22+
/// <summary>
23+
/// Implements a code fix for <see cref="SA1139UseLiteralSuffixNotationInsteadOfCasting"/>.
24+
/// </summary>
25+
[ExportCodeFixProvider(LanguageNames.CSharp, Name = nameof(SA1139CodeFixProvider))]
26+
[Shared]
27+
internal class SA1139CodeFixProvider : CodeFixProvider
28+
{
29+
private static readonly Dictionary<SyntaxKind, string> LiteralSyntaxKindToSuffix = new Dictionary<SyntaxKind, string>()
30+
{
31+
{ SyntaxKind.IntKeyword, string.Empty },
32+
{ SyntaxKind.LongKeyword, "L" },
33+
{ SyntaxKind.ULongKeyword, "UL" },
34+
{ SyntaxKind.UIntKeyword, "U" },
35+
{ SyntaxKind.FloatKeyword, "F"},
36+
{ SyntaxKind.DoubleKeyword, "D" },
37+
{ SyntaxKind.DecimalKeyword, "M" }
38+
};
39+
40+
private static readonly char[] LettersAllowedInLiteralSuffix = LiteralSyntaxKindToSuffix.Values
41+
.SelectMany(s => s.ToCharArray()).Distinct()
42+
.SelectMany(c => new[] { char.ToLowerInvariant(c), c })
43+
.ToArray();
44+
45+
/// <inheritdoc/>
46+
public override ImmutableArray<string> FixableDiagnosticIds { get; } =
47+
ImmutableArray.Create(SA1139UseLiteralSuffixNotationInsteadOfCasting.DiagnosticId);
48+
49+
/// <inheritdoc/>
50+
public override Task RegisterCodeFixesAsync(CodeFixContext context)
51+
{
52+
foreach (var diagnostic in context.Diagnostics)
53+
{
54+
context.RegisterCodeFix(
55+
CodeAction.Create(
56+
ReadabilityResources.SA1139CodeFix,
57+
cancellationToken => GetTransformedDocumentAsync(context.Document, diagnostic, cancellationToken),
58+
nameof(SA1139CodeFixProvider)),
59+
diagnostic);
60+
}
61+
62+
return SpecializedTasks.CompletedTask;
63+
}
64+
65+
private static async Task<Document> GetTransformedDocumentAsync(Document document, Diagnostic diagnostic, CancellationToken cancellationToken)
66+
{
67+
var syntaxRoot = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false);
68+
var oldSemanticModel = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false);
69+
70+
var node = syntaxRoot.FindNode(diagnostic.Location.SourceSpan, getInnermostNodeForTie: true) as CastExpressionSyntax;
71+
if (node == null)
72+
{
73+
return document;
74+
}
75+
76+
var replacementNode = GenerateReplacementNode(node);
77+
var newSyntaxRoot = syntaxRoot.ReplaceNode(node, replacementNode);
78+
var newDocument = document.WithSyntaxRoot(newSyntaxRoot);
79+
var newSemanticModel = await newDocument.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false);
80+
var newNode = newSemanticModel.SyntaxTree.GetRoot().FindNode(
81+
span: new TextSpan(start: node.FullSpan.Start, length: replacementNode.FullSpan.Length),
82+
getInnermostNodeForTie: true);
83+
84+
var oldConstantValue = oldSemanticModel.GetConstantValue(node).Value;
85+
var newConstantValueOption = newSemanticModel.GetConstantValue(newNode, cancellationToken);
86+
if (newConstantValueOption.HasValue && oldConstantValue.Equals(newConstantValueOption.Value))
87+
{
88+
return newDocument;
89+
}
90+
else
91+
{
92+
var newNodeBasedOnValue = GenerateReplacementNodeBasedOnValue(node, oldConstantValue);
93+
newSyntaxRoot = syntaxRoot.ReplaceNode(node, newNodeBasedOnValue);
94+
return document.WithSyntaxRoot(newSyntaxRoot);
95+
}
96+
}
97+
98+
private static SyntaxNode GenerateReplacementNode(CastExpressionSyntax node)
99+
{
100+
var plusMinusSyntax = node.Expression as PrefixUnaryExpressionSyntax;
101+
var literalExpressionSyntax =
102+
plusMinusSyntax == null ?
103+
(LiteralExpressionSyntax)node.Expression :
104+
(LiteralExpressionSyntax)plusMinusSyntax.Operand;
105+
var typeToken = node.Type.GetFirstToken();
106+
var prefix = plusMinusSyntax == null
107+
? string.Empty
108+
: plusMinusSyntax.OperatorToken.Text;
109+
var literalWithoutSuffix = literalExpressionSyntax.StripLiteralSuffix();
110+
var correspondingSuffix = LiteralSyntaxKindToSuffix[typeToken.Kind()];
111+
var fixedCodePreservingText = SyntaxFactory.ParseExpression(prefix + literalWithoutSuffix + correspondingSuffix);
112+
113+
return fixedCodePreservingText.WithTriviaFrom(node);
114+
}
115+
116+
private static SyntaxNode GenerateReplacementNodeBasedOnValue(CastExpressionSyntax node, object desiredValue)
117+
{
118+
var typeToken = node.Type.GetFirstToken();
119+
var correspondingSuffix = LiteralSyntaxKindToSuffix[typeToken.Kind()];
120+
var fixedCodePreservingText = SyntaxFactory.ParseExpression(desiredValue + correspondingSuffix);
121+
122+
return fixedCodePreservingText.WithTriviaFrom(node);
123+
}
124+
}
125+
}

StyleCop.Analyzers/StyleCop.Analyzers.CodeFixes/StyleCop.Analyzers.CodeFixes.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,7 @@
133133
<Compile Include="ReadabilityRules\SA1133CodeFixProvider.cs" />
134134
<Compile Include="ReadabilityRules\SA1134CodeFixProvider.cs" />
135135
<Compile Include="ReadabilityRules\SA1136CodeFixProvider.cs" />
136+
<Compile Include="ReadabilityRules\SA1139CodeFixProvider.cs" />
136137
<Compile Include="ReadabilityRules\SX1101CodeFixProvider.cs" />
137138
<Compile Include="Settings\SettingsFileCodeFixProvider.cs" />
138139
<Compile Include="SpacingRules\SA1003CodeFixProvider.cs" />

0 commit comments

Comments
 (0)