Skip to content

Commit f1b1761

Browse files
committed
Add refactoring helpers
1 parent d1d8ee9 commit f1b1761

18 files changed

+1536
-0
lines changed

StyleCop.Analyzers/StyleCop.Analyzers.PrivateCodeFixes/Helpers/AbstractRefactoringHelpers`3.cs

Lines changed: 527 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
// <auto-generated/>
2+
// https://raw.githubusercontent.com/dotnet/roslyn-analyzers/84fb81c27e0554eadf6b12f97eb52c7cd2803c7e/src/Utilities/Refactoring/AbstractSyntaxFacts.cs
3+
4+
#nullable enable
5+
6+
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the MIT license. See License.txt in the project root for license information.
7+
8+
using System;
9+
using System.Collections.Immutable;
10+
using System.Diagnostics;
11+
using System.Linq;
12+
using Analyzer.Utilities.Extensions;
13+
using Microsoft.CodeAnalysis;
14+
using Microsoft.CodeAnalysis.Text;
15+
16+
namespace Analyzer.Utilities
17+
{
18+
internal abstract class AbstractSyntaxFacts
19+
{
20+
public abstract ISyntaxKinds SyntaxKinds { get; }
21+
22+
public bool IsOnHeader(SyntaxNode root, int position, SyntaxNode ownerOfHeader, SyntaxNodeOrToken lastTokenOrNodeOfHeader)
23+
=> IsOnHeader(root, position, ownerOfHeader, lastTokenOrNodeOfHeader, ImmutableArray<SyntaxNode>.Empty);
24+
25+
public bool IsOnHeader<THoleSyntax>(
26+
SyntaxNode root,
27+
int position,
28+
SyntaxNode ownerOfHeader,
29+
SyntaxNodeOrToken lastTokenOrNodeOfHeader,
30+
ImmutableArray<THoleSyntax> holes)
31+
where THoleSyntax : SyntaxNode
32+
{
33+
Debug.Assert(ownerOfHeader.FullSpan.Contains(lastTokenOrNodeOfHeader.Span));
34+
35+
var headerSpan = TextSpan.FromBounds(
36+
start: GetStartOfNodeExcludingAttributes(root, ownerOfHeader),
37+
end: lastTokenOrNodeOfHeader.FullSpan.End);
38+
39+
// Is in header check is inclusive, being on the end edge of an header still counts
40+
if (!headerSpan.IntersectsWith(position))
41+
{
42+
return false;
43+
}
44+
45+
// Holes are exclusive:
46+
// To be consistent with other 'being on the edge' of Tokens/Nodes a position is
47+
// in a hole (not in a header) only if it's inside _inside_ a hole, not only on the edge.
48+
if (holes.Any(h => h.Span.Contains(position) && position > h.Span.Start))
49+
{
50+
return false;
51+
}
52+
53+
return true;
54+
}
55+
56+
/// <summary>
57+
/// Tries to get an ancestor of a Token on current position or of Token directly to left:
58+
/// e.g.: tokenWithWantedAncestor[||]tokenWithoutWantedAncestor
59+
/// </summary>
60+
protected TNode? TryGetAncestorForLocation<TNode>(SyntaxNode root, int position)
61+
where TNode : SyntaxNode
62+
{
63+
var tokenToRightOrIn = root.FindToken(position);
64+
var nodeToRightOrIn = tokenToRightOrIn.GetAncestor<TNode>();
65+
if (nodeToRightOrIn != null)
66+
{
67+
return nodeToRightOrIn;
68+
}
69+
70+
// not at the beginning of a Token -> no (different) token to the left
71+
if (tokenToRightOrIn.FullSpan.Start != position && tokenToRightOrIn.RawKind != SyntaxKinds.EndOfFileToken)
72+
{
73+
return null;
74+
}
75+
76+
return tokenToRightOrIn.GetPreviousToken().GetAncestor<TNode>();
77+
}
78+
79+
protected int GetStartOfNodeExcludingAttributes(SyntaxNode root, SyntaxNode node)
80+
{
81+
var attributeList = GetAttributeLists(node);
82+
if (attributeList.Any())
83+
{
84+
var endOfAttributeLists = attributeList.Last().Span.End;
85+
var afterAttributesToken = root.FindTokenOnRightOfPosition(endOfAttributeLists);
86+
87+
return Math.Min(afterAttributesToken.Span.Start, node.Span.End);
88+
}
89+
90+
return node.SpanStart;
91+
}
92+
93+
public abstract SyntaxList<SyntaxNode> GetAttributeLists(SyntaxNode node);
94+
}
95+
}
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
// <auto-generated/>
2+
// https://raw.githubusercontent.com/dotnet/roslyn-analyzers/84fb81c27e0554eadf6b12f97eb52c7cd2803c7e/src/Utilities/Refactoring.CSharp/CSharpRefactoringHelpers.cs
3+
4+
#nullable enable
5+
6+
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the MIT license. See License.txt in the project root for license information.
7+
8+
using System.Collections.Generic;
9+
using Microsoft.CodeAnalysis;
10+
using Microsoft.CodeAnalysis.CSharp.Syntax;
11+
12+
namespace Analyzer.Utilities
13+
{
14+
internal sealed class CSharpRefactoringHelpers : AbstractRefactoringHelpers<ExpressionSyntax, ArgumentSyntax, ExpressionStatementSyntax>
15+
{
16+
public static CSharpRefactoringHelpers Instance { get; } = new CSharpRefactoringHelpers();
17+
18+
private CSharpRefactoringHelpers()
19+
{
20+
}
21+
22+
protected override ISyntaxFacts SyntaxFacts => CSharpSyntaxFacts.Instance;
23+
24+
protected override IEnumerable<SyntaxNode> ExtractNodesSimple(SyntaxNode? node, ISyntaxFacts syntaxFacts)
25+
{
26+
if (node == null)
27+
{
28+
yield break;
29+
}
30+
31+
foreach (var extractedNode in base.ExtractNodesSimple(node, syntaxFacts))
32+
{
33+
yield return extractedNode;
34+
}
35+
36+
// `var a = b;`
37+
// -> `var a = b`;
38+
if (node is LocalDeclarationStatementSyntax localDeclaration)
39+
{
40+
yield return localDeclaration.Declaration;
41+
}
42+
43+
// var `a = b`;
44+
if (node is VariableDeclaratorSyntax declarator)
45+
{
46+
var declaration = declarator.Parent;
47+
if (declaration?.Parent is LocalDeclarationStatementSyntax localDeclarationStatement)
48+
{
49+
var variables = syntaxFacts.GetVariablesOfLocalDeclarationStatement(localDeclarationStatement);
50+
if (variables.Count == 1)
51+
{
52+
// -> `var a = b`;
53+
yield return declaration;
54+
55+
// -> `var a = b;`
56+
yield return localDeclarationStatement;
57+
}
58+
}
59+
}
60+
}
61+
}
62+
}
Lines changed: 177 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,177 @@
1+
// <auto-generated/>
2+
// https://raw.githubusercontent.com/dotnet/roslyn-analyzers/84fb81c27e0554eadf6b12f97eb52c7cd2803c7e/src/Utilities/Refactoring.CSharp/CSharpSyntaxFacts.cs
3+
4+
#nullable enable
5+
6+
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the MIT license. See License.txt in the project root for license information.
7+
8+
#nullable disable warnings
9+
10+
using System.Collections.Immutable;
11+
using System.Diagnostics.CodeAnalysis;
12+
using System.Linq;
13+
using Analyzer.Utilities.Extensions;
14+
using Microsoft.CodeAnalysis;
15+
using Microsoft.CodeAnalysis.CSharp;
16+
using Microsoft.CodeAnalysis.CSharp.Syntax;
17+
18+
namespace Analyzer.Utilities
19+
{
20+
internal sealed class CSharpSyntaxFacts : AbstractSyntaxFacts, ISyntaxFacts
21+
{
22+
public static CSharpSyntaxFacts Instance { get; } = new CSharpSyntaxFacts();
23+
24+
private CSharpSyntaxFacts()
25+
{
26+
}
27+
28+
public override ISyntaxKinds SyntaxKinds => CSharpSyntaxKinds.Instance;
29+
30+
public SyntaxNode GetExpressionOfExpressionStatement(SyntaxNode node)
31+
=> ((ExpressionStatementSyntax)node).Expression;
32+
33+
public bool IsSimpleAssignmentStatement(SyntaxNode statement)
34+
{
35+
return statement is ExpressionStatementSyntax exprStatement
36+
&& exprStatement.Expression.IsKind(SyntaxKind.SimpleAssignmentExpression);
37+
}
38+
39+
public void GetPartsOfAssignmentExpressionOrStatement(SyntaxNode statement, out SyntaxNode left, out SyntaxToken operatorToken, out SyntaxNode right)
40+
{
41+
var expression = statement;
42+
if (statement is ExpressionStatementSyntax expressionStatement)
43+
{
44+
expression = expressionStatement.Expression;
45+
}
46+
47+
var assignment = (AssignmentExpressionSyntax)expression;
48+
left = assignment.Left;
49+
operatorToken = assignment.OperatorToken;
50+
right = assignment.Right;
51+
}
52+
53+
public override SyntaxList<SyntaxNode> GetAttributeLists(SyntaxNode node)
54+
=> node.GetAttributeLists();
55+
56+
public SeparatedSyntaxList<SyntaxNode> GetVariablesOfLocalDeclarationStatement(SyntaxNode node)
57+
=> ((LocalDeclarationStatementSyntax)node).Declaration.Variables;
58+
59+
public SyntaxNode GetInitializerOfVariableDeclarator(SyntaxNode node)
60+
=> ((VariableDeclaratorSyntax)node).Initializer;
61+
62+
public SyntaxNode? GetValueOfEqualsValueClause(SyntaxNode? node)
63+
=> ((EqualsValueClauseSyntax?)node)?.Value;
64+
65+
public bool IsOnTypeHeader(SyntaxNode root, int position, bool fullHeader, [NotNullWhen(true)] out SyntaxNode? typeDeclaration)
66+
{
67+
var node = TryGetAncestorForLocation<BaseTypeDeclarationSyntax>(root, position);
68+
if (node is null)
69+
{
70+
typeDeclaration = null;
71+
return false;
72+
}
73+
74+
typeDeclaration = node;
75+
var lastToken = (node as TypeDeclarationSyntax)?.TypeParameterList?.GetLastToken() ?? node.Identifier;
76+
if (fullHeader)
77+
lastToken = node.BaseList?.GetLastToken() ?? lastToken;
78+
79+
return IsOnHeader(root, position, node, lastToken);
80+
}
81+
82+
public bool IsOnPropertyDeclarationHeader(SyntaxNode root, int position, [NotNullWhen(true)] out SyntaxNode? propertyDeclaration)
83+
{
84+
var node = TryGetAncestorForLocation<PropertyDeclarationSyntax>(root, position);
85+
if (node is null)
86+
{
87+
propertyDeclaration = null;
88+
return false;
89+
}
90+
91+
propertyDeclaration = node;
92+
return IsOnHeader(root, position, node, node.Identifier);
93+
}
94+
95+
public bool IsOnParameterHeader(SyntaxNode root, int position, [NotNullWhen(true)] out SyntaxNode? parameter)
96+
{
97+
var node = TryGetAncestorForLocation<ParameterSyntax>(root, position);
98+
if (node is null)
99+
{
100+
parameter = null;
101+
return false;
102+
}
103+
104+
parameter = node;
105+
return IsOnHeader(root, position, node, node);
106+
}
107+
108+
public bool IsOnMethodHeader(SyntaxNode root, int position, [NotNullWhen(true)] out SyntaxNode? method)
109+
{
110+
var node = TryGetAncestorForLocation<MethodDeclarationSyntax>(root, position);
111+
if (node is null)
112+
{
113+
method = null;
114+
return false;
115+
}
116+
117+
method = node;
118+
return IsOnHeader(root, position, node, node.ParameterList);
119+
}
120+
121+
public bool IsOnLocalFunctionHeader(SyntaxNode root, int position, [NotNullWhen(true)] out SyntaxNode? localFunction)
122+
{
123+
var node = TryGetAncestorForLocation<LocalFunctionStatementSyntax>(root, position);
124+
if (node is null)
125+
{
126+
localFunction = null;
127+
return false;
128+
}
129+
130+
localFunction = node;
131+
return IsOnHeader(root, position, node, node.ParameterList);
132+
}
133+
134+
public bool IsOnLocalDeclarationHeader(SyntaxNode root, int position, [NotNullWhen(true)] out SyntaxNode? localDeclaration)
135+
{
136+
var node = TryGetAncestorForLocation<LocalDeclarationStatementSyntax>(root, position);
137+
if (node is null)
138+
{
139+
localDeclaration = null;
140+
return false;
141+
}
142+
143+
localDeclaration = node;
144+
var initializersExpressions = node.Declaration.Variables
145+
.Where(v => v.Initializer != null)
146+
.Select(initializedV => initializedV.Initializer.Value)
147+
.ToImmutableArray();
148+
return IsOnHeader(root, position, node, node, holes: initializersExpressions);
149+
}
150+
151+
public bool IsOnIfStatementHeader(SyntaxNode root, int position, [NotNullWhen(true)] out SyntaxNode? ifStatement)
152+
{
153+
var node = TryGetAncestorForLocation<IfStatementSyntax>(root, position);
154+
if (node is null)
155+
{
156+
ifStatement = null;
157+
return false;
158+
}
159+
160+
ifStatement = node;
161+
return IsOnHeader(root, position, node, node.CloseParenToken);
162+
}
163+
164+
public bool IsOnForeachHeader(SyntaxNode root, int position, [NotNullWhen(true)] out SyntaxNode? foreachStatement)
165+
{
166+
var node = TryGetAncestorForLocation<ForEachStatementSyntax>(root, position);
167+
if (node is null)
168+
{
169+
foreachStatement = null;
170+
return false;
171+
}
172+
173+
foreachStatement = node;
174+
return IsOnHeader(root, position, node, node.CloseParenToken);
175+
}
176+
}
177+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
// <auto-generated/>
2+
// https://raw.githubusercontent.com/dotnet/roslyn-analyzers/84fb81c27e0554eadf6b12f97eb52c7cd2803c7e/src/Utilities/Refactoring.CSharp/CSharpSyntaxKinds.cs
3+
4+
#nullable enable
5+
6+
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the MIT license. See License.txt in the project root for license information.
7+
8+
using Microsoft.CodeAnalysis.CSharp;
9+
10+
namespace Analyzer.Utilities
11+
{
12+
internal sealed class CSharpSyntaxKinds : ISyntaxKinds
13+
{
14+
public static CSharpSyntaxKinds Instance { get; } = new CSharpSyntaxKinds();
15+
16+
private CSharpSyntaxKinds()
17+
{
18+
}
19+
20+
public int EndOfFileToken => (int)SyntaxKind.EndOfFileToken;
21+
22+
public int ExpressionStatement => (int)SyntaxKind.ExpressionStatement;
23+
public int LocalDeclarationStatement => (int)SyntaxKind.LocalDeclarationStatement;
24+
25+
public int VariableDeclarator => (int)SyntaxKind.VariableDeclarator;
26+
}
27+
}

0 commit comments

Comments
 (0)