Skip to content

Commit 12454c0

Browse files
committed
LuceneDev1001_FloatingPointFormattingCSCodeFixProvider: Refactored to make use of shared TryGetJ2NTypeAndMember() method to reduce duplicated code.
1 parent 089eee3 commit 12454c0

2 files changed

Lines changed: 84 additions & 68 deletions

File tree

src/Lucene.Net.CodeAnalysis.Dev.CodeFixes/LuceneDev1001_FloatingPointFormattingCSCodeFixProvider .cs

Lines changed: 66 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -43,117 +43,118 @@ public override FixAllProvider GetFixAllProvider() =>
4343

4444
public override async Task RegisterCodeFixesAsync(CodeFixContext context)
4545
{
46-
// only handle the first diagnostic for this registration
47-
var diagnostic = context.Diagnostics.FirstOrDefault();
48-
if (diagnostic == null)
46+
Diagnostic? diagnostic = context.Diagnostics.FirstOrDefault();
47+
if (diagnostic is null)
4948
return;
5049

51-
var root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false);
50+
SyntaxNode? root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false);
5251
if (root is null)
5352
return;
5453

5554
// the diagnostic in the analyzer is reported on the member access (e.g. "x.ToString")
5655
// but we need the whole invocation (e.g. "x.ToString(...)"). So find the invocation
5756
// by walking ancestors if needed.
58-
var node = root.FindNode(diagnostic.Location.SourceSpan);
57+
SyntaxNode? node = root.FindNode(diagnostic.Location.SourceSpan);
5958
if (node is null)
6059
return;
6160

62-
var invocation = node as InvocationExpressionSyntax
63-
?? node.AncestorsAndSelf().OfType<InvocationExpressionSyntax>().FirstOrDefault();
64-
65-
if (invocation is null)
61+
if (node is not ExpressionSyntax exprNode)
6662
return;
6763

68-
var semanticModel = await context.Document.GetSemanticModelAsync(context.CancellationToken).ConfigureAwait(false);
64+
SemanticModel? semanticModel = await context.Document.GetSemanticModelAsync(context.CancellationToken).ConfigureAwait(false);
6965
if (semanticModel is null)
7066
return;
7167

72-
var memberAccess = node as MemberAccessExpressionSyntax
73-
?? node.AncestorsAndSelf().OfType<MemberAccessExpressionSyntax>().FirstOrDefault();
74-
75-
if (memberAccess is null)
68+
if (!TryGetJ2NTypeAndMember(semanticModel, exprNode, out var j2nTypeName, out var memberAccess))
7669
return;
7770

78-
// Determine the type name for J2N (Single or Double)
79-
var typeInfo = semanticModel.GetTypeInfo(memberAccess.Expression, context.CancellationToken);
80-
var type = typeInfo.Type;
81-
82-
string? j2nTypeName = type?.SpecialType switch
83-
{
84-
SpecialType.System_Single => "Single",
85-
SpecialType.System_Double => "Double",
86-
_ => null
87-
};
88-
89-
if (j2nTypeName == null)
90-
return;
91-
92-
// Build the code element string
9371
string codeElement = $"J2N.Numerics.{j2nTypeName}.ToString(...)";
9472

95-
// Use the helper to register the code fix
9673
context.RegisterCodeFix(
9774
CodeActionHelper.CreateFromResource(
9875
CodeFixResources.UseX,
99-
c => ReplaceWithJ2NToStringAsync(context.Document, invocation, c),
76+
c => ReplaceWithJ2NToStringAsync(context.Document, memberAccess, c),
10077
"UseJ2NToString",
10178
codeElement),
10279
diagnostic);
103-
10480
}
10581

10682
private async Task<Document> ReplaceWithJ2NToStringAsync(
10783
Document document,
108-
InvocationExpressionSyntax invocation,
84+
MemberAccessExpressionSyntax memberAccess,
10985
CancellationToken cancellationToken)
11086
{
87+
SemanticModel? semanticModel = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false);
88+
if (semanticModel is null)
89+
return document;
11190

112-
if (invocation.Expression is not MemberAccessExpressionSyntax memberAccess)
91+
if (!TryGetJ2NTypeAndMember(semanticModel, memberAccess, out var j2nTypeName, out _))
11392
return document;
11493

115-
var semanticModel = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false);
116-
if (semanticModel is null)
94+
// Build J2N.Numerics.Single/Double.ToString
95+
MemberAccessExpressionSyntax j2nToStringAccess = SyntaxFactory.MemberAccessExpression(
96+
SyntaxKind.SimpleMemberAccessExpression,
97+
SyntaxFactory.MemberAccessExpression(
98+
SyntaxKind.SimpleMemberAccessExpression,
99+
SyntaxFactory.IdentifierName("J2N"),
100+
SyntaxFactory.IdentifierName("Numerics")),
101+
SyntaxFactory.IdentifierName(j2nTypeName))
102+
.WithAdditionalAnnotations(Formatter.Annotation);
103+
104+
MemberAccessExpressionSyntax fullAccess = SyntaxFactory.MemberAccessExpression(
105+
SyntaxKind.SimpleMemberAccessExpression,
106+
j2nToStringAccess,
107+
SyntaxFactory.IdentifierName("ToString"));
108+
109+
// Build invocation: J2N.Numerics.<Single|Double>.ToString(<expr>, <original args...>)
110+
if (memberAccess.Parent is not InvocationExpressionSyntax invocation)
117111
return document;
118112

119-
var typeInfo = semanticModel.GetTypeInfo(memberAccess.Expression, cancellationToken);
120-
var type = typeInfo.Type;
113+
var newArgs = new List<ArgumentSyntax> { SyntaxFactory.Argument(memberAccess.Expression) };
114+
if (invocation.ArgumentList != null)
115+
newArgs.AddRange(invocation.ArgumentList.Arguments);
121116

122-
string? j2nTypeName = type?.SpecialType switch
123-
{
124-
SpecialType.System_Single => "Single",
125-
SpecialType.System_Double => "Double",
126-
_ => null
127-
};
117+
InvocationExpressionSyntax newInvocation = SyntaxFactory.InvocationExpression(
118+
fullAccess,
119+
SyntaxFactory.ArgumentList(SyntaxFactory.SeparatedList(newArgs)))
120+
.WithTriviaFrom(invocation) // safe now
121+
.WithAdditionalAnnotations(Formatter.Annotation);
128122

129-
if (j2nTypeName == null)
130-
return document; // unsupported type
123+
DocumentEditor editor = await DocumentEditor.CreateAsync(document, cancellationToken).ConfigureAwait(false);
124+
editor.ReplaceNode(memberAccess.Parent, newInvocation);
131125

132-
// Build J2N.Numerics.Single.ToString
133-
var j2n = SyntaxFactory.IdentifierName("J2N");
134-
var numerics = SyntaxFactory.IdentifierName("Numerics");
135-
var single = SyntaxFactory.IdentifierName(j2nTypeName);
136-
var toString = SyntaxFactory.IdentifierName("ToString");
126+
return editor.GetChangedDocument();
127+
}
137128

138-
// Build the chain: J2N.Numerics.Single.ToString
139-
var j2nNumerics = SyntaxFactory.MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, j2n, numerics);
140-
var j2nNumericsSingle = SyntaxFactory.MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, j2nNumerics, single);
141-
var j2nToStringAccess = SyntaxFactory.MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, j2nNumericsSingle, toString);
129+
private static bool TryGetJ2NTypeAndMember(
130+
SemanticModel semanticModel,
131+
ExpressionSyntax expr,
132+
out string j2nTypeName,
133+
out MemberAccessExpressionSyntax memberAccess)
134+
{
135+
memberAccess = expr as MemberAccessExpressionSyntax
136+
?? expr.AncestorsAndSelf().OfType<MemberAccessExpressionSyntax>().FirstOrDefault();
142137

138+
if (memberAccess is null)
139+
{
140+
j2nTypeName = null!; // we always return false when the value is null, so we can ignore it here.
141+
return false;
142+
}
143143

144-
// Build invocation: J2N.Numerics.<Single|Double>.ToString(<expr>, <original args...>)
145-
var newArgs = new List<ArgumentSyntax> { SyntaxFactory.Argument(memberAccess.Expression) };
146-
if (invocation.ArgumentList != null)
147-
newArgs.AddRange(invocation.ArgumentList.Arguments);
144+
var typeInfo = semanticModel.GetTypeInfo(memberAccess.Expression);
145+
var type = typeInfo.Type;
148146

149-
var newInvocation = SyntaxFactory.InvocationExpression(j2nToStringAccess, SyntaxFactory.ArgumentList(SyntaxFactory.SeparatedList(newArgs)))
150-
.WithTriviaFrom(invocation)
151-
.WithAdditionalAnnotations(Formatter.Annotation);
147+
j2nTypeName = type?.SpecialType switch
148+
{
149+
SpecialType.System_Single => "Single",
150+
SpecialType.System_Double => "Double",
151+
_ => null! // we always return false when the value is null, so we can ignore it here.
152+
};
152153

153-
var editor = await DocumentEditor.CreateAsync(document, cancellationToken).ConfigureAwait(false);
154-
editor.ReplaceNode(invocation, newInvocation);
154+
if (j2nTypeName is null)
155+
return false;
155156

156-
return editor.GetChangedDocument();
157+
return true;
157158
}
158159
}
159160
}

src/Lucene.Net.CodeAnalysis.Dev.CodeFixes/Utility/CodeActionHelper.cs

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,23 @@
1-
using Microsoft.CodeAnalysis;
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one or more
3+
* contributor license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright ownership.
5+
* The ASF licenses this file to You under the Apache License, Version 2.0
6+
* (the "License"); you may not use this file except in compliance with
7+
* the License. You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
using Microsoft.CodeAnalysis;
219
using Microsoft.CodeAnalysis.CodeActions;
320
using System;
4-
using System.Collections.Generic;
5-
using System.Text;
621
using System.Threading;
722
using System.Threading.Tasks;
823

0 commit comments

Comments
 (0)