Skip to content

Commit e73109e

Browse files
committed
Merge remote-tracking branch 'DotNetAnalyzers/master' into #3926
2 parents f51d23e + 27446fa commit e73109e

File tree

90 files changed

+4845
-250
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

90 files changed

+4845
-250
lines changed
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
// Copyright (c) Tunnel Vision Laboratories, LLC. All Rights Reserved.
2+
// Licensed under the MIT License. See LICENSE in the project root for license information.
3+
4+
namespace StyleCop.Analyzers.PrivateCodeFixes;
5+
6+
using System.IO;
7+
using System.Text;
8+
using System.Threading.Tasks;
9+
using Analyzer.Utilities;
10+
using Microsoft.CodeAnalysis;
11+
using Microsoft.CodeAnalysis.CodeActions;
12+
using Microsoft.CodeAnalysis.CodeRefactorings;
13+
using Microsoft.CodeAnalysis.CSharp;
14+
using Microsoft.CodeAnalysis.CSharp.Syntax;
15+
using Microsoft.CodeAnalysis.Text;
16+
17+
[ExportCodeRefactoringProvider(LanguageNames.CSharp, Name = nameof(GeneratePartCodeRefactoringProvider))]
18+
internal sealed class GeneratePartCodeRefactoringProvider
19+
: CodeRefactoringProvider
20+
{
21+
public override async Task ComputeRefactoringsAsync(CodeRefactoringContext context)
22+
{
23+
var partialType = await context.TryGetRelevantNodeAsync<ClassDeclarationSyntax>(CSharpRefactoringHelpers.Instance).ConfigureAwait(false);
24+
if (partialType is not { Modifiers: var modifiers }
25+
|| !modifiers.Any(SyntaxKind.PartialKeyword))
26+
{
27+
return;
28+
}
29+
30+
context.RegisterRefactoring(CodeAction.Create(
31+
"Generate additional part",
32+
async cancellationToken =>
33+
{
34+
var namespaceDeclaration = partialType.FirstAncestorOrSelf<BaseNamespaceDeclarationSyntax>();
35+
if (namespaceDeclaration is null)
36+
{
37+
return context.Document.Project.Solution;
38+
}
39+
40+
var firstUsing = namespaceDeclaration.Usings.FirstOrDefault()?.Name.ToString();
41+
42+
var namespaceName = namespaceDeclaration.Name.ToString();
43+
var subNamespace = namespaceName;
44+
var rootNamespace = context.Document.Project.DefaultNamespace;
45+
if (!string.IsNullOrEmpty(rootNamespace) && namespaceName.StartsWith(rootNamespace + "."))
46+
{
47+
subNamespace = namespaceName[(rootNamespace.Length + 1)..];
48+
}
49+
50+
var content = $@"// Copyright (c) Tunnel Vision Laboratories, LLC. All Rights Reserved.
51+
// Licensed under the MIT License. See LICENSE in the project root for license information.
52+
53+
namespace {namespaceName};
54+
55+
using {firstUsing};
56+
57+
public partial class {partialType.Identifier.ValueText} {partialType.BaseList}
58+
{{
59+
}}
60+
";
61+
62+
var fileName = partialType.Identifier.ValueText + ".cs";
63+
var directory = Path.GetDirectoryName(context.Document.Project.FilePath)!;
64+
var existingText = await context.Document.GetTextAsync(cancellationToken).ConfigureAwait(false);
65+
66+
var addedDocument = context.Document.Project.AddDocument(
67+
fileName,
68+
SourceText.From(content, new UTF8Encoding(true), existingText.ChecksumAlgorithm),
69+
folders: subNamespace.Split('.'),
70+
filePath: Path.Combine(directory, Path.Combine(subNamespace.Split('.')), fileName));
71+
72+
return addedDocument.Project.Solution;
73+
},
74+
nameof(GeneratePartCodeRefactoringProvider)));
75+
}
76+
}

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

Lines changed: 526 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+
}

0 commit comments

Comments
 (0)