未验证 提交 c8746efe 编写于 作者: J Jared Parsons 提交者: GitHub

Merge pull request #33125 from JoeRobich/place-usings

Treat normal using and static usings separately when adding imports.
......@@ -5048,5 +5048,438 @@ static class Extensions
}
}");
}
[WorkItem(30734, "https://github.com/dotnet/roslyn/issues/30734")]
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsAddImport)]
public async Task UsingPlacedWithStaticUsingInNamespace_WhenNoExistingUsings()
{
await TestAsync(
@"
namespace N
{
using static System.Math;
class C
{
public [|List<int>|] F;
}
}
",
@"
namespace N
{
using System.Collections.Generic;
using static System.Math;
class C
{
public List<int> F;
}
}
");
}
[WorkItem(30734, "https://github.com/dotnet/roslyn/issues/30734")]
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsAddImport)]
public async Task UsingPlacedWithStaticUsingInInnerNestedNamespace_WhenNoExistingUsings()
{
await TestAsync(
@"
namespace N
{
namespace M
{
using static System.Math;
class C
{
public [|List<int>|] F;
}
}
}
",
@"
namespace N
{
namespace M
{
using System.Collections.Generic;
using static System.Math;
class C
{
public List<int> F;
}
}
}
");
}
[WorkItem(30734, "https://github.com/dotnet/roslyn/issues/30734")]
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsAddImport)]
public async Task UsingPlacedWithStaticUsingInOuterNestedNamespace_WhenNoExistingUsings()
{
await TestAsync(
@"
namespace N
{
using static System.Math;
namespace M
{
class C
{
public [|List<int>|] F;
}
}
}
",
@"
namespace N
{
using System.Collections.Generic;
using static System.Math;
namespace M
{
class C
{
public List<int> F;
}
}
}
");
}
[WorkItem(30734, "https://github.com/dotnet/roslyn/issues/30734")]
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsAddImport)]
public async Task UsingPlacedWithExistingUsingInCompilationUnit_WhenStaticUsingInNamespace()
{
await TestAsync(
@"
using System;
namespace N
{
using static System.Math;
class C
{
public [|List<int>|] F;
}
}
",
@"
using System;
using System.Collections.Generic;
namespace N
{
using static System.Math;
class C
{
public List<int> F;
}
}
");
}
[WorkItem(30734, "https://github.com/dotnet/roslyn/issues/30734")]
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsAddImport)]
public async Task UsingPlacedWithExistingUsing_WhenStaticUsingInInnerNestedNamespace()
{
await TestAsync(
@"
namespace N
{
using System;
namespace M
{
using static System.Math;
class C
{
public [|List<int>|] F;
}
}
}
",
@"
namespace N
{
using System;
using System.Collections.Generic;
namespace M
{
using static System.Math;
class C
{
public List<int> F;
}
}
}
");
}
[WorkItem(30734, "https://github.com/dotnet/roslyn/issues/30734")]
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsAddImport)]
public async Task UsingPlacedWithExistingUsing_WhenStaticUsingInOuterNestedNamespace()
{
await TestAsync(
@"
namespace N
{
using static System.Math;
namespace M
{
using System;
class C
{
public [|List<int>|] F;
}
}
}
",
@"
namespace N
{
using static System.Math;
namespace M
{
using System;
using System.Collections.Generic;
class C
{
public List<int> F;
}
}
}
");
}
[WorkItem(30734, "https://github.com/dotnet/roslyn/issues/30734")]
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsAddImport)]
public async Task UsingPlacedWithUsingAliasInNamespace_WhenNoExistingUsing()
{
await TestAsync(
@"
namespace N
{
using SAction = System.Action;
class C
{
public [|List<int>|] F;
}
}
",
@"
namespace N
{
using System.Collections.Generic;
using SAction = System.Action;
class C
{
public List<int> F;
}
}
");
}
[WorkItem(30734, "https://github.com/dotnet/roslyn/issues/30734")]
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsAddImport)]
public async Task UsingPlacedWithUsingAliasInInnerNestedNamespace_WhenNoExistingUsing()
{
await TestAsync(
@"
namespace N
{
namespace M
{
using SAction = System.Action;
class C
{
public [|List<int>|] F;
}
}
}
",
@"
namespace N
{
namespace M
{
using System.Collections.Generic;
using SAction = System.Action;
class C
{
public List<int> F;
}
}
}
");
}
[WorkItem(30734, "https://github.com/dotnet/roslyn/issues/30734")]
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsAddImport)]
public async Task UsingPlacedWithUsingAliasInOuterNestedNamespace_WhenNoExistingUsing()
{
await TestAsync(
@"
namespace N
{
using SAction = System.Action;
namespace M
{
class C
{
public [|List<int>|] F;
}
}
}
",
@"
namespace N
{
using System.Collections.Generic;
using SAction = System.Action;
namespace M
{
class C
{
public List<int> F;
}
}
}
");
}
[WorkItem(30734, "https://github.com/dotnet/roslyn/issues/30734")]
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsAddImport)]
public async Task UsingPlacedWithExistingUsingInCompilationUnit_WhenUsingAliasInNamespace()
{
await TestAsync(
@"
using System;
namespace N
{
using SAction = System.Action;
class C
{
public [|List<int>|] F;
}
}
",
@"
using System;
using System.Collections.Generic;
namespace N
{
using SAction = System.Action;
class C
{
public List<int> F;
}
}
");
}
[WorkItem(30734, "https://github.com/dotnet/roslyn/issues/30734")]
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsAddImport)]
public async Task UsingPlacedWithExistingUsing_WhenUsingAliasInInnerNestedNamespace()
{
await TestAsync(
@"
namespace N
{
using System;
namespace M
{
using SAction = System.Action;
class C
{
public [|List<int>|] F;
}
}
}
",
@"
namespace N
{
using System;
using System.Collections.Generic;
namespace M
{
using SAction = System.Action;
class C
{
public [|List<int>|] F;
}
}
}
");
}
[WorkItem(30734, "https://github.com/dotnet/roslyn/issues/30734")]
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsAddImport)]
public async Task UsingPlacedWithExistingUsing_WhenUsingAliasInOuterNestedNamespace()
{
await TestAsync(
@"
namespace N
{
using SAction = System.Action;
namespace M
{
using System;
class C
{
public [|List<int>|] F;
}
}
}
",
@"
namespace N
{
using SAction = System.Action;
namespace M
{
using System;
using System.Collections.Generic;
class C
{
public [|List<int>|] F;
}
}
}
");
}
}
}
......@@ -20,20 +20,25 @@ protected override ImmutableArray<SyntaxNode> GetGlobalImports(Compilation compi
protected override SyntaxNode GetAlias(UsingDirectiveSyntax usingOrAlias)
=> usingOrAlias.Alias;
protected override bool IsStaticUsing(UsingDirectiveSyntax usingOrAlias)
=> usingOrAlias.StaticKeyword != default;
protected override SyntaxNode Rewrite(
ExternAliasDirectiveSyntax[] externAliases,
UsingDirectiveSyntax[] usingDirectives,
UsingDirectiveSyntax[] staticUsingDirectives,
UsingDirectiveSyntax[] aliasDirectives,
SyntaxNode externContainer,
SyntaxNode usingContainer,
SyntaxNode staticUsingContainer,
SyntaxNode aliasContainer,
bool placeSystemNamespaceFirst,
SyntaxNode root)
{
var rewriter = new Rewriter(
externAliases, usingDirectives, aliasDirectives,
externContainer, usingContainer, aliasContainer,
placeSystemNamespaceFirst);
externAliases, usingDirectives, staticUsingDirectives,
aliasDirectives, externContainer, usingContainer,
staticUsingContainer, aliasContainer, placeSystemNamespaceFirst);
var newRoot = rewriter.Visit(root);
return newRoot;
......@@ -65,26 +70,32 @@ private class Rewriter : CSharpSyntaxRewriter
private readonly SyntaxNode _externContainer;
private readonly SyntaxNode _usingContainer;
private readonly SyntaxNode _aliasContainer;
private readonly SyntaxNode _staticUsingContainer;
private readonly UsingDirectiveSyntax[] _aliasDirectives;
private readonly ExternAliasDirectiveSyntax[] _externAliases;
private readonly UsingDirectiveSyntax[] _usingDirectives;
private readonly UsingDirectiveSyntax[] _staticUsingDirectives;
public Rewriter(
ExternAliasDirectiveSyntax[] externAliases,
UsingDirectiveSyntax[] usingDirectives,
UsingDirectiveSyntax[] staticUsingDirectives,
UsingDirectiveSyntax[] aliasDirectives,
SyntaxNode externContainer,
SyntaxNode usingContainer,
SyntaxNode aliasContainer,
SyntaxNode staticUsingContainer,
bool placeSystemNamespaceFirst)
{
_externAliases = externAliases;
_usingDirectives = usingDirectives;
_staticUsingDirectives = staticUsingDirectives;
_aliasDirectives = aliasDirectives;
_externContainer = externContainer;
_usingContainer = usingContainer;
_aliasContainer = aliasContainer;
_staticUsingContainer = staticUsingContainer;
_placeSystemNamespaceFirst = placeSystemNamespaceFirst;
}
......@@ -103,6 +114,11 @@ public override SyntaxNode VisitNamespaceDeclaration(NamespaceDeclarationSyntax
rewritten = rewritten.AddUsingDirectives(_usingDirectives, _placeSystemNamespaceFirst);
}
if (node == _staticUsingContainer)
{
rewritten = rewritten.AddUsingDirectives(_staticUsingDirectives, _placeSystemNamespaceFirst);
}
if (node == _externContainer)
{
rewritten = rewritten.AddExterns(_externAliases);
......@@ -126,6 +142,11 @@ public override SyntaxNode VisitCompilationUnit(CompilationUnitSyntax node)
rewritten = rewritten.AddUsingDirectives(_usingDirectives, _placeSystemNamespaceFirst);
}
if (node == _staticUsingContainer)
{
rewritten = rewritten.AddUsingDirectives(_staticUsingDirectives, _placeSystemNamespaceFirst);
}
if (node == _externContainer)
{
rewritten = rewritten.AddExterns(_externAliases);
......
......@@ -23,11 +23,13 @@ protected AbstractAddImportsService()
protected abstract ImmutableArray<SyntaxNode> GetGlobalImports(Compilation compilation);
protected abstract SyntaxList<TUsingOrAliasSyntax> GetUsingsAndAliases(SyntaxNode node);
protected abstract SyntaxList<TExternSyntax> GetExterns(SyntaxNode node);
protected abstract bool IsStaticUsing(TUsingOrAliasSyntax usingOrAlias);
private bool IsUsing(TUsingOrAliasSyntax usingOrAlias) => GetAlias(usingOrAlias) == null;
private bool IsSimpleUsing(TUsingOrAliasSyntax usingOrAlias) => !IsAlias(usingOrAlias) && !IsStaticUsing(usingOrAlias);
private bool IsAlias(TUsingOrAliasSyntax usingOrAlias) => GetAlias(usingOrAlias) != null;
private bool HasAliases(SyntaxNode node) => GetUsingsAndAliases(node).Any(IsAlias);
private bool HasUsings(SyntaxNode node) => GetUsingsAndAliases(node).Any(IsUsing);
private bool HasUsings(SyntaxNode node) => GetUsingsAndAliases(node).Any(IsSimpleUsing);
private bool HasStaticUsings(SyntaxNode node) => GetUsingsAndAliases(node).Any(IsStaticUsing);
private bool HasExterns(SyntaxNode node) => GetExterns(node).Any();
private bool HasAnyImports(SyntaxNode node) => GetUsingsAndAliases(node).Any() || GetExterns(node).Any();
......@@ -79,12 +81,23 @@ public SyntaxNode GetImportContainer(SyntaxNode root, SyntaxNode contextLocation
{
contextLocation = contextLocation ?? root;
GetContainers(root, contextLocation,
out var externContainer, out var usingContainer, out var aliasContainer);
out var externContainer, out var usingContainer, out var staticUsingContainer, out var aliasContainer);
switch (import)
{
case TExternSyntax e: return externContainer;
case TUsingOrAliasSyntax u: return IsAlias(u) ? aliasContainer : usingContainer;
case TUsingOrAliasSyntax u:
if (IsAlias(u))
{
return aliasContainer;
}
if (IsStaticUsing(u))
{
return staticUsingContainer;
}
return usingContainer;
}
throw new InvalidOperationException();
......@@ -104,26 +117,28 @@ public SyntaxNode GetImportContainer(SyntaxNode root, SyntaxNode contextLocation
var filteredImports = newImports.Where(i => !HasExistingImport(i, containers, globalImports)).ToArray();
var externAliases = filteredImports.OfType<TExternSyntax>().ToArray();
var usingDirectives = filteredImports.OfType<TUsingOrAliasSyntax>().Where(IsUsing).ToArray();
var usingDirectives = filteredImports.OfType<TUsingOrAliasSyntax>().Where(IsSimpleUsing).ToArray();
var staticUsingDirectives = filteredImports.OfType<TUsingOrAliasSyntax>().Where(IsStaticUsing).ToArray();
var aliasDirectives = filteredImports.OfType<TUsingOrAliasSyntax>().Where(IsAlias).ToArray();
GetContainers(root, contextLocation,
out var externContainer, out var usingContainer, out var aliasContainer);
out var externContainer, out var usingContainer, out var aliasContainer, out var staticUsingContainer);
var newRoot = Rewrite(
externAliases, usingDirectives, aliasDirectives,
externContainer, usingContainer, aliasContainer,
externAliases, usingDirectives, staticUsingDirectives,
aliasDirectives, externContainer, usingContainer,
staticUsingContainer, aliasContainer,
placeSystemNamespaceFirst, root);
return newRoot;
}
protected abstract SyntaxNode Rewrite(
TExternSyntax[] externAliases, TUsingOrAliasSyntax[] usingDirectives, TUsingOrAliasSyntax[] aliasDirectives,
SyntaxNode externContainer, SyntaxNode usingContainer, SyntaxNode aliasContainer,
bool placeSystemNamespaceFirst, SyntaxNode root);
TExternSyntax[] externAliases, TUsingOrAliasSyntax[] usingDirectives, TUsingOrAliasSyntax[] staticUsingDirectives,
TUsingOrAliasSyntax[] aliasDirectives, SyntaxNode externContainer, SyntaxNode usingContainer,
SyntaxNode staticUsingContainer, SyntaxNode aliasContainer, bool placeSystemNamespaceFirst, SyntaxNode root);
private void GetContainers(SyntaxNode root, SyntaxNode contextLocation, out SyntaxNode externContainer, out SyntaxNode usingContainer, out SyntaxNode aliasContainer)
private void GetContainers(SyntaxNode root, SyntaxNode contextLocation, out SyntaxNode externContainer, out SyntaxNode usingContainer, out SyntaxNode staticUsingContainer, out SyntaxNode aliasContainer)
{
var applicableContainer = GetFirstApplicableContainer(contextLocation);
var contextSpine = applicableContainer.GetAncestorsOrThis<SyntaxNode>().ToImmutableArray();
......@@ -139,6 +154,7 @@ private void GetContainers(SyntaxNode root, SyntaxNode contextLocation, out Synt
// innermost node with any imports.
externContainer = contextSpine.FirstOrDefault(HasExterns) ?? fallbackNode;
usingContainer = contextSpine.FirstOrDefault(HasUsings) ?? fallbackNode;
staticUsingContainer = contextSpine.FirstOrDefault(HasStaticUsings) ?? fallbackNode;
aliasContainer = contextSpine.FirstOrDefault(HasAliases) ?? fallbackNode;
}
......
......@@ -36,6 +36,11 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.AddImports
FirstOrDefault()?.Alias
End Function
Protected Overrides Function IsStaticUsing(usingOrAlias As ImportsStatementSyntax) As Boolean
' Visual Basic doesn't support static imports
Return False
End Function
Protected Overrides Function GetExterns(node As SyntaxNode) As SyntaxList(Of ImportsStatementSyntax)
Return Nothing
End Function
......@@ -51,9 +56,11 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.AddImports
Protected Overrides Function Rewrite(
externAliases() As ImportsStatementSyntax,
usingDirectives() As ImportsStatementSyntax,
staticUsingDirectives() As ImportsStatementSyntax,
aliasDirectives() As ImportsStatementSyntax,
externContainer As SyntaxNode,
usingContainer As SyntaxNode,
staticUsingContainer As SyntaxNode,
aliasContainer As SyntaxNode,
placeSystemNamespaceFirst As Boolean,
root As SyntaxNode) As SyntaxNode
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册