diff --git a/Roslyn.sln b/Roslyn.sln index 3a0c0c701d85ab72ce30fe49025d256f8bdc48ce..7136254ca82c2610ae97f20779bc79ae03226642 100644 --- a/Roslyn.sln +++ b/Roslyn.sln @@ -450,8 +450,8 @@ Global {1EE8CAD3-55F9-4D91-96B2-084641DA9A6C}.Debug|ARM.Build.0 = Debug|Any CPU {1EE8CAD3-55F9-4D91-96B2-084641DA9A6C}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU {1EE8CAD3-55F9-4D91-96B2-084641DA9A6C}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU - {1EE8CAD3-55F9-4D91-96B2-084641DA9A6C}.Debug|x64.ActiveCfg = Debug|x64 - {1EE8CAD3-55F9-4D91-96B2-084641DA9A6C}.Debug|x64.Build.0 = Debug|x64 + {1EE8CAD3-55F9-4D91-96B2-084641DA9A6C}.Debug|x64.ActiveCfg = Debug|Any CPU + {1EE8CAD3-55F9-4D91-96B2-084641DA9A6C}.Debug|x64.Build.0 = Debug|Any CPU {1EE8CAD3-55F9-4D91-96B2-084641DA9A6C}.Debug|x86.ActiveCfg = Debug|Any CPU {1EE8CAD3-55F9-4D91-96B2-084641DA9A6C}.Debug|x86.Build.0 = Debug|Any CPU {1EE8CAD3-55F9-4D91-96B2-084641DA9A6C}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -460,8 +460,8 @@ Global {1EE8CAD3-55F9-4D91-96B2-084641DA9A6C}.Release|ARM.Build.0 = Release|Any CPU {1EE8CAD3-55F9-4D91-96B2-084641DA9A6C}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU {1EE8CAD3-55F9-4D91-96B2-084641DA9A6C}.Release|Mixed Platforms.Build.0 = Release|Any CPU - {1EE8CAD3-55F9-4D91-96B2-084641DA9A6C}.Release|x64.ActiveCfg = Release|x64 - {1EE8CAD3-55F9-4D91-96B2-084641DA9A6C}.Release|x64.Build.0 = Release|x64 + {1EE8CAD3-55F9-4D91-96B2-084641DA9A6C}.Release|x64.ActiveCfg = Release|Any CPU + {1EE8CAD3-55F9-4D91-96B2-084641DA9A6C}.Release|x64.Build.0 = Release|Any CPU {1EE8CAD3-55F9-4D91-96B2-084641DA9A6C}.Release|x86.ActiveCfg = Release|Any CPU {1EE8CAD3-55F9-4D91-96B2-084641DA9A6C}.Release|x86.Build.0 = Release|Any CPU {9508F118-F62E-4C16-A6F4-7C3B56E166AD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU @@ -486,22 +486,22 @@ Global {9508F118-F62E-4C16-A6F4-7C3B56E166AD}.Release|x86.Build.0 = Release|Any CPU {F5CE416E-B906-41D2-80B9-0078E887A3F6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {F5CE416E-B906-41D2-80B9-0078E887A3F6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F5CE416E-B906-41D2-80B9-0078E887A3F6}.Debug|ARM.ActiveCfg = Debug|ARM - {F5CE416E-B906-41D2-80B9-0078E887A3F6}.Debug|ARM.Build.0 = Debug|ARM + {F5CE416E-B906-41D2-80B9-0078E887A3F6}.Debug|ARM.ActiveCfg = Debug|Any CPU + {F5CE416E-B906-41D2-80B9-0078E887A3F6}.Debug|ARM.Build.0 = Debug|Any CPU {F5CE416E-B906-41D2-80B9-0078E887A3F6}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU {F5CE416E-B906-41D2-80B9-0078E887A3F6}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU - {F5CE416E-B906-41D2-80B9-0078E887A3F6}.Debug|x64.ActiveCfg = Debug|x64 - {F5CE416E-B906-41D2-80B9-0078E887A3F6}.Debug|x64.Build.0 = Debug|x64 + {F5CE416E-B906-41D2-80B9-0078E887A3F6}.Debug|x64.ActiveCfg = Debug|Any CPU + {F5CE416E-B906-41D2-80B9-0078E887A3F6}.Debug|x64.Build.0 = Debug|Any CPU {F5CE416E-B906-41D2-80B9-0078E887A3F6}.Debug|x86.ActiveCfg = Debug|Any CPU {F5CE416E-B906-41D2-80B9-0078E887A3F6}.Debug|x86.Build.0 = Debug|Any CPU {F5CE416E-B906-41D2-80B9-0078E887A3F6}.Release|Any CPU.ActiveCfg = Release|Any CPU {F5CE416E-B906-41D2-80B9-0078E887A3F6}.Release|Any CPU.Build.0 = Release|Any CPU - {F5CE416E-B906-41D2-80B9-0078E887A3F6}.Release|ARM.ActiveCfg = Release|ARM - {F5CE416E-B906-41D2-80B9-0078E887A3F6}.Release|ARM.Build.0 = Release|ARM + {F5CE416E-B906-41D2-80B9-0078E887A3F6}.Release|ARM.ActiveCfg = Release|Any CPU + {F5CE416E-B906-41D2-80B9-0078E887A3F6}.Release|ARM.Build.0 = Release|Any CPU {F5CE416E-B906-41D2-80B9-0078E887A3F6}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU {F5CE416E-B906-41D2-80B9-0078E887A3F6}.Release|Mixed Platforms.Build.0 = Release|Any CPU - {F5CE416E-B906-41D2-80B9-0078E887A3F6}.Release|x64.ActiveCfg = Release|x64 - {F5CE416E-B906-41D2-80B9-0078E887A3F6}.Release|x64.Build.0 = Release|x64 + {F5CE416E-B906-41D2-80B9-0078E887A3F6}.Release|x64.ActiveCfg = Release|Any CPU + {F5CE416E-B906-41D2-80B9-0078E887A3F6}.Release|x64.Build.0 = Release|Any CPU {F5CE416E-B906-41D2-80B9-0078E887A3F6}.Release|x86.ActiveCfg = Release|Any CPU {F5CE416E-B906-41D2-80B9-0078E887A3F6}.Release|x86.Build.0 = Release|Any CPU {4B45CA0C-03A0-400F-B454-3D4BCB16AF38}.Debug|Any CPU.ActiveCfg = Debug|Any CPU @@ -530,8 +530,8 @@ Global {B501A547-C911-4A05-AC6E-274A50DFF30E}.Debug|ARM.Build.0 = Debug|Any CPU {B501A547-C911-4A05-AC6E-274A50DFF30E}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU {B501A547-C911-4A05-AC6E-274A50DFF30E}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU - {B501A547-C911-4A05-AC6E-274A50DFF30E}.Debug|x64.ActiveCfg = Debug|x64 - {B501A547-C911-4A05-AC6E-274A50DFF30E}.Debug|x64.Build.0 = Debug|x64 + {B501A547-C911-4A05-AC6E-274A50DFF30E}.Debug|x64.ActiveCfg = Debug|Any CPU + {B501A547-C911-4A05-AC6E-274A50DFF30E}.Debug|x64.Build.0 = Debug|Any CPU {B501A547-C911-4A05-AC6E-274A50DFF30E}.Debug|x86.ActiveCfg = Debug|Any CPU {B501A547-C911-4A05-AC6E-274A50DFF30E}.Debug|x86.Build.0 = Debug|Any CPU {B501A547-C911-4A05-AC6E-274A50DFF30E}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -540,8 +540,8 @@ Global {B501A547-C911-4A05-AC6E-274A50DFF30E}.Release|ARM.Build.0 = Release|Any CPU {B501A547-C911-4A05-AC6E-274A50DFF30E}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU {B501A547-C911-4A05-AC6E-274A50DFF30E}.Release|Mixed Platforms.Build.0 = Release|Any CPU - {B501A547-C911-4A05-AC6E-274A50DFF30E}.Release|x64.ActiveCfg = Release|x64 - {B501A547-C911-4A05-AC6E-274A50DFF30E}.Release|x64.Build.0 = Release|x64 + {B501A547-C911-4A05-AC6E-274A50DFF30E}.Release|x64.ActiveCfg = Release|Any CPU + {B501A547-C911-4A05-AC6E-274A50DFF30E}.Release|x64.Build.0 = Release|Any CPU {B501A547-C911-4A05-AC6E-274A50DFF30E}.Release|x86.ActiveCfg = Release|Any CPU {B501A547-C911-4A05-AC6E-274A50DFF30E}.Release|x86.Build.0 = Release|Any CPU {50D26304-0961-4A51-ABF6-6CAD1A56D203}.Debug|Any CPU.ActiveCfg = Debug|Any CPU @@ -646,22 +646,22 @@ Global {50D26304-0961-4A51-ABF6-6CAD1A56D202}.Release|x86.Build.0 = Release|Any CPU {7FE6B002-89D8-4298-9B1B-0B5C247DD1FD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {7FE6B002-89D8-4298-9B1B-0B5C247DD1FD}.Debug|Any CPU.Build.0 = Debug|Any CPU - {7FE6B002-89D8-4298-9B1B-0B5C247DD1FD}.Debug|ARM.ActiveCfg = Debug|ARM - {7FE6B002-89D8-4298-9B1B-0B5C247DD1FD}.Debug|ARM.Build.0 = Debug|ARM + {7FE6B002-89D8-4298-9B1B-0B5C247DD1FD}.Debug|ARM.ActiveCfg = Debug|Any CPU + {7FE6B002-89D8-4298-9B1B-0B5C247DD1FD}.Debug|ARM.Build.0 = Debug|Any CPU {7FE6B002-89D8-4298-9B1B-0B5C247DD1FD}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU {7FE6B002-89D8-4298-9B1B-0B5C247DD1FD}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU - {7FE6B002-89D8-4298-9B1B-0B5C247DD1FD}.Debug|x64.ActiveCfg = Debug|x64 - {7FE6B002-89D8-4298-9B1B-0B5C247DD1FD}.Debug|x64.Build.0 = Debug|x64 + {7FE6B002-89D8-4298-9B1B-0B5C247DD1FD}.Debug|x64.ActiveCfg = Debug|Any CPU + {7FE6B002-89D8-4298-9B1B-0B5C247DD1FD}.Debug|x64.Build.0 = Debug|Any CPU {7FE6B002-89D8-4298-9B1B-0B5C247DD1FD}.Debug|x86.ActiveCfg = Debug|Any CPU {7FE6B002-89D8-4298-9B1B-0B5C247DD1FD}.Debug|x86.Build.0 = Debug|Any CPU {7FE6B002-89D8-4298-9B1B-0B5C247DD1FD}.Release|Any CPU.ActiveCfg = Release|Any CPU {7FE6B002-89D8-4298-9B1B-0B5C247DD1FD}.Release|Any CPU.Build.0 = Release|Any CPU - {7FE6B002-89D8-4298-9B1B-0B5C247DD1FD}.Release|ARM.ActiveCfg = Release|ARM - {7FE6B002-89D8-4298-9B1B-0B5C247DD1FD}.Release|ARM.Build.0 = Release|ARM + {7FE6B002-89D8-4298-9B1B-0B5C247DD1FD}.Release|ARM.ActiveCfg = Release|Any CPU + {7FE6B002-89D8-4298-9B1B-0B5C247DD1FD}.Release|ARM.Build.0 = Release|Any CPU {7FE6B002-89D8-4298-9B1B-0B5C247DD1FD}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU {7FE6B002-89D8-4298-9B1B-0B5C247DD1FD}.Release|Mixed Platforms.Build.0 = Release|Any CPU - {7FE6B002-89D8-4298-9B1B-0B5C247DD1FD}.Release|x64.ActiveCfg = Release|x64 - {7FE6B002-89D8-4298-9B1B-0B5C247DD1FD}.Release|x64.Build.0 = Release|x64 + {7FE6B002-89D8-4298-9B1B-0B5C247DD1FD}.Release|x64.ActiveCfg = Release|Any CPU + {7FE6B002-89D8-4298-9B1B-0B5C247DD1FD}.Release|x64.Build.0 = Release|Any CPU {7FE6B002-89D8-4298-9B1B-0B5C247DD1FD}.Release|x86.ActiveCfg = Release|Any CPU {7FE6B002-89D8-4298-9B1B-0B5C247DD1FD}.Release|x86.Build.0 = Release|Any CPU {F7712928-1175-47B3-8819-EE086753DEE2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU @@ -2178,21 +2178,21 @@ Global {FCFA8808-A1B6-48CC-A1EA-0B8CA8AEDA8E}.Release|x86.Build.0 = Release|Any CPU {1DFEA9C5-973C-4179-9B1B-0F32288E1EF2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {1DFEA9C5-973C-4179-9B1B-0F32288E1EF2}.Debug|Any CPU.Build.0 = Debug|Any CPU - {1DFEA9C5-973C-4179-9B1B-0F32288E1EF2}.Debug|ARM.ActiveCfg = Debug|ARM - {1DFEA9C5-973C-4179-9B1B-0F32288E1EF2}.Debug|ARM.Build.0 = Debug|ARM + {1DFEA9C5-973C-4179-9B1B-0F32288E1EF2}.Debug|ARM.ActiveCfg = Debug|Any CPU + {1DFEA9C5-973C-4179-9B1B-0F32288E1EF2}.Debug|ARM.Build.0 = Debug|Any CPU {1DFEA9C5-973C-4179-9B1B-0F32288E1EF2}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU {1DFEA9C5-973C-4179-9B1B-0F32288E1EF2}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU - {1DFEA9C5-973C-4179-9B1B-0F32288E1EF2}.Debug|x64.ActiveCfg = Debug|x64 - {1DFEA9C5-973C-4179-9B1B-0F32288E1EF2}.Debug|x64.Build.0 = Debug|x64 + {1DFEA9C5-973C-4179-9B1B-0F32288E1EF2}.Debug|x64.ActiveCfg = Debug|Any CPU + {1DFEA9C5-973C-4179-9B1B-0F32288E1EF2}.Debug|x64.Build.0 = Debug|Any CPU {1DFEA9C5-973C-4179-9B1B-0F32288E1EF2}.Debug|x86.ActiveCfg = Debug|Any CPU {1DFEA9C5-973C-4179-9B1B-0F32288E1EF2}.Release|Any CPU.ActiveCfg = Release|Any CPU {1DFEA9C5-973C-4179-9B1B-0F32288E1EF2}.Release|Any CPU.Build.0 = Release|Any CPU - {1DFEA9C5-973C-4179-9B1B-0F32288E1EF2}.Release|ARM.ActiveCfg = Release|ARM - {1DFEA9C5-973C-4179-9B1B-0F32288E1EF2}.Release|ARM.Build.0 = Release|ARM + {1DFEA9C5-973C-4179-9B1B-0F32288E1EF2}.Release|ARM.ActiveCfg = Release|Any CPU + {1DFEA9C5-973C-4179-9B1B-0F32288E1EF2}.Release|ARM.Build.0 = Release|Any CPU {1DFEA9C5-973C-4179-9B1B-0F32288E1EF2}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU {1DFEA9C5-973C-4179-9B1B-0F32288E1EF2}.Release|Mixed Platforms.Build.0 = Release|Any CPU - {1DFEA9C5-973C-4179-9B1B-0F32288E1EF2}.Release|x64.ActiveCfg = Release|x64 - {1DFEA9C5-973C-4179-9B1B-0F32288E1EF2}.Release|x64.Build.0 = Release|x64 + {1DFEA9C5-973C-4179-9B1B-0F32288E1EF2}.Release|x64.ActiveCfg = Release|Any CPU + {1DFEA9C5-973C-4179-9B1B-0F32288E1EF2}.Release|x64.Build.0 = Release|Any CPU {1DFEA9C5-973C-4179-9B1B-0F32288E1EF2}.Release|x86.ActiveCfg = Release|Any CPU {76242A2D-2600-49DD-8C15-FEA07ECB1842}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {76242A2D-2600-49DD-8C15-FEA07ECB1842}.Debug|Any CPU.Build.0 = Debug|Any CPU diff --git a/src/Compilers/Core/Portable/Compilation.EmitStreamProvider.cs b/src/Compilers/Core/Portable/Compilation.EmitStreamProvider.cs index 4da4f651669b4f390704d62179964e45f5e0f017..bb37b18b33ffba4282f437b080e85dbc203c4ee3 100644 --- a/src/Compilers/Core/Portable/Compilation.EmitStreamProvider.cs +++ b/src/Compilers/Core/Portable/Compilation.EmitStreamProvider.cs @@ -59,4 +59,4 @@ public override Stream CreateStream(DiagnosticBag diagnostics) } } } -} +} \ No newline at end of file diff --git a/src/EditorFeatures/CSharpTest/Diagnostics/AddUsing/AddUsingTests.cs b/src/EditorFeatures/CSharpTest/Diagnostics/AddUsing/AddUsingTests.cs index d964bde15a95eb3302a08763ac5d5fe2ec105fa3..43de5c64e655358e9c65197fafc0885d0d46fadd 100644 --- a/src/EditorFeatures/CSharpTest/Diagnostics/AddUsing/AddUsingTests.cs +++ b/src/EditorFeatures/CSharpTest/Diagnostics/AddUsing/AddUsingTests.cs @@ -4373,6 +4373,43 @@ class C compareTokens: false); } + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsAddUsing)] + public async Task TestPlaceUsingWithUsings_NotWithAliases() + { + await TestAsync( +@" +using System; + +namespace N +{ + using C = System.Collections; + + class Class + { + [|List|] Method() + { + Foo(); + } + } +}", +@" +using System; +using System.Collections.Generic; + +namespace N +{ + using C = System.Collections; + + class Class + { + List Method() + { + Foo(); + } + } +}"); + } + public partial class AddUsingTestsWithAddImportDiagnosticProvider : AbstractCSharpDiagnosticProviderBasedUserDiagnosticTest { internal override Tuple CreateDiagnosticProviderAndFixer(Workspace workspace) diff --git a/src/Features/CSharp/Portable/CodeFixes/AddImport/CSharpAddImportCodeFixProvider.cs b/src/Features/CSharp/Portable/CodeFixes/AddImport/CSharpAddImportCodeFixProvider.cs index 98d57203048bcbe0d9ffb49742b805d26201b983..d12bf2b1231652445e4977ebee8c12236e41cae6 100644 --- a/src/Features/CSharp/Portable/CodeFixes/AddImport/CSharpAddImportCodeFixProvider.cs +++ b/src/Features/CSharp/Portable/CodeFixes/AddImport/CSharpAddImportCodeFixProvider.cs @@ -7,6 +7,7 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.AddImport; using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeFixes; using Microsoft.CodeAnalysis.CodeFixes.AddImport; @@ -395,6 +396,7 @@ protected override string GetDescription(IReadOnlyList nameParts) } protected override string TryGetDescription( + Document document, INamespaceOrTypeSymbol namespaceOrTypeSymbol, SemanticModel semanticModel, SyntaxNode contextNode, @@ -415,7 +417,7 @@ protected override string GetDescription(IReadOnlyList nameParts) } var usingDirective = TryGetUsingDirective( - namespaceOrTypeSymbol, semanticModel, root, contextNode); + document, namespaceOrTypeSymbol, semanticModel, root, contextNode); if (usingDirective != null) { @@ -465,63 +467,33 @@ protected override string GetDescription(IReadOnlyList nameParts) { var semanticModel = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false); - var firstContainingNamespaceWithUsings = GetFirstContainingNamespaceWithUsings(contextNode); - var namespaceToUpdate = firstContainingNamespaceWithUsings; - var externAliasDirective = TryGetExternAliasDirective( namespaceOrTypeSymbol, semanticModel, contextNode, checkForExistingExternAlias: true); var usingDirective = TryGetUsingDirective( - namespaceOrTypeSymbol, semanticModel, root, contextNode); + document, namespaceOrTypeSymbol, semanticModel, root, contextNode); - if (externAliasDirective != null) + var newImports = ArrayBuilder.GetInstance(); + try { - AddExterns(ref root, ref namespaceToUpdate, externAliasDirective); - } - - if (usingDirective != null) - { - AddUsingDirective(ref root, ref namespaceToUpdate, - placeSystemNamespaceFirst, usingDirective); - } - - return firstContainingNamespaceWithUsings != null - ? root.ReplaceNode(firstContainingNamespaceWithUsings, namespaceToUpdate) - : root; - } + if (externAliasDirective != null) + { + newImports.Add(externAliasDirective); + } - private void AddUsingDirective( - ref CompilationUnitSyntax root, - ref NamespaceDeclarationSyntax namespaceToUpdate, - bool placeSystemNamespaceFirst, - UsingDirectiveSyntax usingDirective) - { - IList directives = new[] { usingDirective }; - if (namespaceToUpdate != null) - { - namespaceToUpdate = namespaceToUpdate.AddUsingDirectives( - directives, placeSystemNamespaceFirst); - } - else - { - root = root.AddUsingDirectives( - directives, placeSystemNamespaceFirst); - } - } + if (usingDirective != null) + { + newImports.Add(usingDirective); + } - private void AddExterns( - ref CompilationUnitSyntax root, - ref NamespaceDeclarationSyntax namespaceToUpdate, - ExternAliasDirectiveSyntax externAliasDirective) - { - if (namespaceToUpdate != null) - { - namespaceToUpdate = namespaceToUpdate.AddExterns(externAliasDirective); + var addImportService = document.GetLanguageService(); + var newRoot = addImportService.AddImports(root, contextNode, newImports, placeSystemNamespaceFirst); + return (CompilationUnitSyntax)newRoot; } - else + finally { - root = root.AddExterns(externAliasDirective); + newImports.Free(); } } @@ -581,15 +553,19 @@ private NameSyntax CreateNameSyntax(IReadOnlyList namespaceParts, int in } private UsingDirectiveSyntax TryGetUsingDirective( + Document document, INamespaceOrTypeSymbol namespaceOrTypeSymbol, SemanticModel semanticModel, CompilationUnitSyntax root, SyntaxNode contextNode) { - var namespaceToAddTo = GetFirstContainingNamespaceWithUsings(contextNode); - var usingDirectives = namespaceToAddTo?.Usings ?? root.Usings; + var addImportService = document.GetLanguageService(); var nameSyntax = namespaceOrTypeSymbol.GenerateNameSyntax(); + var dummyUsing = SyntaxFactory.UsingDirective(nameSyntax); + + var container = addImportService.GetImportContainer(root, contextNode, dummyUsing); + var namespaceToAddTo = container as NamespaceDeclarationSyntax; // Replace the alias that GenerateTypeSyntax added if we want this to be looked // up off of an extern alias. @@ -694,19 +670,6 @@ private NameSyntax CreateNameSyntax(IReadOnlyList namespaceParts, int in return aliasName.WithAlias(alias); } - private NamespaceDeclarationSyntax GetFirstContainingNamespaceWithUsings(SyntaxNode contextNode) - { - var usingDirective = contextNode.GetAncestor(); - if (usingDirective != null) - { - contextNode = usingDirective.Parent; - } - - return contextNode.GetAncestors() - .Where(n => n.Usings.Count > 0) - .FirstOrDefault(); - } - private static bool TryGetExternAliasString( INamespaceOrTypeSymbol namespaceSymbol, SemanticModel semanticModel, diff --git a/src/Features/Core/Portable/AddImport/AbstractAddImportCodeFixProvider.cs b/src/Features/Core/Portable/AddImport/AbstractAddImportCodeFixProvider.cs index 216bfb521f815bc51358625c70bf3091c097fbba..e3ecc98cff23a55b82fc8a66cde92a1b4cc6a6cc 100644 --- a/src/Features/Core/Portable/AddImport/AbstractAddImportCodeFixProvider.cs +++ b/src/Features/Core/Portable/AddImport/AbstractAddImportCodeFixProvider.cs @@ -52,7 +52,7 @@ internal abstract partial class AbstractAddImportCodeFixProvider nameParts); - protected abstract string TryGetDescription(INamespaceOrTypeSymbol symbol, SemanticModel semanticModel, SyntaxNode root, bool checkForExistingImport, CancellationToken cancellationToken); + protected abstract string TryGetDescription(Document document, INamespaceOrTypeSymbol symbol, SemanticModel semanticModel, SyntaxNode root, bool checkForExistingImport, CancellationToken cancellationToken); public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context) { diff --git a/src/Features/Core/Portable/AddImport/References/MetadataSymbolReference.cs b/src/Features/Core/Portable/AddImport/References/MetadataSymbolReference.cs index b61d8410a5ffe943acab1b828f8c8b590daca9d0..527b771b746479d7d9cd3ac45cd45ba2c5dab9e1 100644 --- a/src/Features/Core/Portable/AddImport/References/MetadataSymbolReference.cs +++ b/src/Features/Core/Portable/AddImport/References/MetadataSymbolReference.cs @@ -23,12 +23,12 @@ private partial class MetadataSymbolReference : SymbolReference } protected override string TryGetDescription( - Project project, SyntaxNode node, + Document document, SyntaxNode node, SemanticModel semanticModel, CancellationToken cancellationToken) { // If 'TryGetDescription' returns 'null' then that means that we don't actually want to add a reference // in this case. As such, just continue to return the 'null' outwards. - var description = base.TryGetDescription(project, node, semanticModel, cancellationToken); + var description = base.TryGetDescription(document, node, semanticModel, cancellationToken); if (description == null) { return null; diff --git a/src/Features/Core/Portable/AddImport/References/ProjectSymbolReference.cs b/src/Features/Core/Portable/AddImport/References/ProjectSymbolReference.cs index 797f2992427e2b9d8fdab1f184b9386b11124f2d..266d75b3e1bf6462a662dcb56316d2cfff81428d 100644 --- a/src/Features/Core/Portable/AddImport/References/ProjectSymbolReference.cs +++ b/src/Features/Core/Portable/AddImport/References/ProjectSymbolReference.cs @@ -73,15 +73,16 @@ protected override Solution GetUpdatedSolution(Document newDocument) } protected override string TryGetDescription( - Project project, SyntaxNode node, + Document document, SyntaxNode node, SemanticModel semanticModel, CancellationToken cancellationToken) { - var description = base.TryGetDescription(project, node, semanticModel, cancellationToken); + var description = base.TryGetDescription(document, node, semanticModel, cancellationToken); if (description == null) { return null; } + var project = document.Project; return project.Id == _project.Id ? description : string.Format(FeaturesResources.Add_reference_to_0, _project.Name); diff --git a/src/Features/Core/Portable/AddImport/References/SymbolReference.cs b/src/Features/Core/Portable/AddImport/References/SymbolReference.cs index 286c41aa11cb54a35ec196be5f5d40128c07fb5e..03d43f26f763f463e2b4edf616cdda857d5a1af8 100644 --- a/src/Features/Core/Portable/AddImport/References/SymbolReference.cs +++ b/src/Features/Core/Portable/AddImport/References/SymbolReference.cs @@ -73,7 +73,7 @@ protected virtual Solution GetUpdatedSolution(Document newDocument) bool placeSystemNamespaceFirst, CancellationToken cancellationToken) { var semanticModel = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false); - string description = TryGetDescription(document.Project, node, semanticModel, cancellationToken); + string description = TryGetDescription(document, node, semanticModel, cancellationToken); if (description == null) { return null; @@ -104,12 +104,12 @@ protected virtual Solution GetUpdatedSolution(Document newDocument) } protected virtual string TryGetDescription( - Project project, SyntaxNode node, + Document document, SyntaxNode node, SemanticModel semanticModel, CancellationToken cancellationToken) { return provider.TryGetDescription( - SymbolResult.Symbol, semanticModel, node, - this.CheckForExistingImport(project), cancellationToken); + document, SymbolResult.Symbol, semanticModel, node, + this.CheckForExistingImport(document.Project), cancellationToken); } } } diff --git a/src/Features/VisualBasic/Portable/CodeFixes/AddImport/VisualBasicAddImportCodeFixProvider.vb b/src/Features/VisualBasic/Portable/CodeFixes/AddImport/VisualBasicAddImportCodeFixProvider.vb index 695326565848f3819c6407a9b93e0d73226c906d..a84b7d3c9d1709e02afb8f62c38fb85b5020d9ad 100644 --- a/src/Features/VisualBasic/Portable/CodeFixes/AddImport/VisualBasicAddImportCodeFixProvider.vb +++ b/src/Features/VisualBasic/Portable/CodeFixes/AddImport/VisualBasicAddImportCodeFixProvider.vb @@ -248,6 +248,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.CodeFixes.AddImport End Function Protected Overrides Function TryGetDescription( + document As Document, namespaceSymbol As INamespaceOrTypeSymbol, semanticModel As SemanticModel, root As SyntaxNode, @@ -313,7 +314,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.CodeFixes.AddImport Return symbol IsNot Nothing AndAlso symbol.Locations.Length > 0 End Function - Protected Overloads Overrides Async Function AddImportAsync( + Protected Overloads Overrides Function AddImportAsync( contextNode As SyntaxNode, symbol As INamespaceOrTypeSymbol, document As Document, @@ -322,9 +323,9 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.CodeFixes.AddImport Dim nameSyntax = DirectCast(symbol.GenerateTypeSyntax(addGlobal:=False), NameSyntax) - Return Await AddImportsAsync( + Return AddImportsAsync( contextNode, document, placeSystemNamespaceFirst, nameSyntax, - additionalAnnotation:=Nothing, cancellationToken:=cancellationToken).ConfigureAwait(False) + additionalAnnotation:=Nothing, cancellationToken:=cancellationToken) End Function Private Shared Async Function AddImportsAsync( diff --git a/src/VisualStudio/CSharp/Impl/Snippets/SnippetExpansionClient.cs b/src/VisualStudio/CSharp/Impl/Snippets/SnippetExpansionClient.cs index 107871c4a46e4648577b15f8d45563b85002d9b5..36986f2d9ef56f9e8ff674121bc2be3c1e8c974c 100644 --- a/src/VisualStudio/CSharp/Impl/Snippets/SnippetExpansionClient.cs +++ b/src/VisualStudio/CSharp/Impl/Snippets/SnippetExpansionClient.cs @@ -6,6 +6,7 @@ using System.Threading; using System.Xml.Linq; using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.AddImport; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Extensions; using Microsoft.CodeAnalysis.CSharp.Syntax; @@ -93,7 +94,10 @@ public override int GetExpansionFunction(IXMLDOMNode xmlFunctionNode, string bst return document; } - var newUsingDirectives = GetUsingDirectivesToAdd(document, snippetNode, importsNode, cancellationToken); + var root = document.GetSyntaxRootSynchronously(cancellationToken); + var contextLocation = root.FindToken(position).Parent; + + var newUsingDirectives = GetUsingDirectivesToAdd(contextLocation, snippetNode, importsNode, cancellationToken); if (!newUsingDirectives.Any()) { return document; @@ -106,15 +110,9 @@ public override int GetExpansionFunction(IXMLDOMNode xmlFunctionNode, string bst return document; } - var root = document.GetSyntaxRootSynchronously(cancellationToken); - var node = root.FindToken(position).Parent; - var container = node.GetInnermostNamespaceDeclarationWithUsings() ?? (SyntaxNode)node.GetAncestorOrThis(); - - var newContainer = container is NamespaceDeclarationSyntax n - ? (SyntaxNode)n.AddUsingDirectives(newUsingDirectives, placeSystemNamespaceFirst, Formatter.Annotation) - : ((CompilationUnitSyntax)container).AddUsingDirectives(newUsingDirectives, placeSystemNamespaceFirst); + var addImportService = document.GetLanguageService(); + var newRoot = addImportService.AddImports(root, contextLocation, newUsingDirectives, placeSystemNamespaceFirst); - var newRoot = root.ReplaceNode(container, newContainer); var newDocument = document.WithSyntaxRoot(newRoot); var formattedDocument = Formatter.FormatAsync(newDocument, Formatter.Annotation, cancellationToken: cancellationToken).WaitAndGetResult(cancellationToken); @@ -123,11 +121,11 @@ public override int GetExpansionFunction(IXMLDOMNode xmlFunctionNode, string bst return formattedDocument; } - private static IList GetUsingDirectivesToAdd(Document document, XElement snippetNode, XElement importsNode, CancellationToken cancellationToken) + private static IList GetUsingDirectivesToAdd( + SyntaxNode contextLocation, XElement snippetNode, XElement importsNode, CancellationToken cancellationToken) { var namespaceXmlName = XName.Get("Namespace", snippetNode.Name.NamespaceName); - var root = document.GetSyntaxRootSynchronously(cancellationToken); - var existingUsings = ((CompilationUnitSyntax)root).Usings; + var existingUsings = contextLocation.GetEnclosingUsingDirectives(); var newUsings = new List(); foreach (var import in importsNode.Elements(XName.Get("Import", snippetNode.Name.NamespaceName))) @@ -150,7 +148,7 @@ private static IList GetUsingDirectivesToAdd(Document docu continue; } - if (!existingUsings.Any(u => UsingsMatch(u, candidateUsing))) + if (!existingUsings.Any(u => u.IsEquivalentTo(candidateUsing, topLevel: false))) { newUsings.Add(candidateUsing.WithAdditionalAnnotations(Formatter.Annotation).WithAppendedTrailingTrivia(SyntaxFactory.CarriageReturnLineFeed)); } @@ -159,11 +157,6 @@ private static IList GetUsingDirectivesToAdd(Document docu return newUsings; } - private static bool UsingsMatch(UsingDirectiveSyntax usingDirective1, UsingDirectiveSyntax usingDirective2) - { - return usingDirective1.Name.ToString() == usingDirective2.Name.ToString() && GetAliasName(usingDirective1) == GetAliasName(usingDirective2); - } - private static string GetAliasName(UsingDirectiveSyntax usingDirective) { return (usingDirective.Alias == null || usingDirective.Alias.Name == null) ? null : usingDirective.Alias.Name.ToString(); diff --git a/src/Workspaces/CSharp/Portable/AddImport/CSharpAddImportService.cs b/src/Workspaces/CSharp/Portable/AddImport/CSharpAddImportService.cs new file mode 100644 index 0000000000000000000000000000000000000000..2133ff39cdc21ec449cda9a5d83e7ab9777b5244 --- /dev/null +++ b/src/Workspaces/CSharp/Portable/AddImport/CSharpAddImportService.cs @@ -0,0 +1,194 @@ +// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Composition; +using System.Linq; +using Microsoft.CodeAnalysis.AddImport; +using Microsoft.CodeAnalysis.CSharp.Extensions; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Host.Mef; +using Microsoft.CodeAnalysis.Shared.Extensions; +using Roslyn.Utilities; + +namespace Microsoft.CodeAnalysis.CSharp.AddImport +{ + [ExportLanguageService(typeof(IAddImportService), LanguageNames.CSharp), Shared] + internal class CSharpAddImportService : IAddImportService + { + private static readonly Func s_isUsing = u => u.Alias == null; + private static readonly Func s_isAlias = u => u.Alias != null; + + private static readonly Func s_hasAliases = n => GetUsings(n).Any(s_isAlias); + private static readonly Func s_hasUsings = n => GetUsings(n).Any(s_isUsing); + private static readonly Func s_hasExterns = n => GetExterns(n).Any(); + + private static readonly Func s_hasAnyImports = n => GetUsings(n).Any() || GetExterns(n).Any(); + + public SyntaxNode GetImportContainer(SyntaxNode root, SyntaxNode contextLocation, SyntaxNode import) + { + contextLocation = contextLocation ?? root; + GetContainers(root, contextLocation, + out var externContainer, out var usingContainer, out var aliasContainer); + + switch (import) + { + case ExternAliasDirectiveSyntax e: return externContainer; + case UsingDirectiveSyntax u: return s_isAlias(u) ? aliasContainer : usingContainer; + } + + throw new InvalidOperationException(); + } + + public SyntaxNode AddImports( + SyntaxNode root, + SyntaxNode contextLocation, + IEnumerable newImports, + bool placeSystemNamespaceFirst) + { + contextLocation = contextLocation ?? root; + GetContainers(root, contextLocation, + out var externContainer, out var usingContainer, out var aliasContainer); + + var externAliases = newImports.OfType().ToArray(); + var usingDirectives = newImports.OfType().Where(u => u.Alias == null).ToArray(); + var aliasDirectives = newImports.OfType().Where(u => u.Alias != null).ToArray(); + + var rewriter = new Rewriter( + externAliases, usingDirectives, aliasDirectives, + externContainer, usingContainer, aliasContainer, + placeSystemNamespaceFirst); + var newRoot = rewriter.Visit(root); + + return newRoot; + } + + private static void GetContainers(SyntaxNode root, SyntaxNode contextLocation, out SyntaxNode externContainer, out SyntaxNode usingContainer, out SyntaxNode aliasContainer) + { + var applicableContainer = GetFirstApplicableContainer(contextLocation); + var contextSpine = applicableContainer.GetAncestorsOrThis().ToImmutableArray(); + + // The node we'll add to if we can't find a specific namespace with imports of + // the type we're trying to add. This will be the closest namespace with any + // imports in it, or the root if there are no such namespaces. + var fallbackNode = contextSpine.FirstOrDefault(s_hasAnyImports) ?? root; + + // The specific container to add each type of import to. We look for a container + // that already has an import of the same type as the node we want to add to. + // If we can find one, we add to that container. If not, we call back to the + // innermost node with any imports. + externContainer = contextSpine.FirstOrDefault(s_hasExterns) ?? fallbackNode; + usingContainer = contextSpine.FirstOrDefault(s_hasUsings) ?? fallbackNode; + aliasContainer = contextSpine.FirstOrDefault(s_hasAliases) ?? fallbackNode; + } + + private static SyntaxList GetUsings(SyntaxNode node) + { + switch (node) + { + case CompilationUnitSyntax c: return c.Usings; + case NamespaceDeclarationSyntax n: return n.Usings; + default: return default(SyntaxList); + } + } + + private static SyntaxList GetExterns(SyntaxNode node) + { + switch (node) + { + case CompilationUnitSyntax c: return c.Externs; + case NamespaceDeclarationSyntax n: return n.Externs; + default: return default(SyntaxList); + } + } + + private static SyntaxNode GetFirstApplicableContainer(SyntaxNode contextNode) + { + var usingDirective = contextNode.GetAncestor(); + if (usingDirective != null) + { + contextNode = usingDirective.Parent; + } + + return contextNode.GetAncestor() ?? + (SyntaxNode)contextNode.GetAncestor(); + } + + private class Rewriter : CSharpSyntaxRewriter + { + private readonly bool _placeSystemNamespaceFirst; + private readonly SyntaxNode _externContainer; + private readonly SyntaxNode _usingContainer; + private readonly SyntaxNode _aliasContainer; + + private readonly UsingDirectiveSyntax[] _aliasDirectives; + private readonly ExternAliasDirectiveSyntax[] _externAliases; + private readonly UsingDirectiveSyntax[] _usingDirectives; + + public Rewriter( + ExternAliasDirectiveSyntax[] externAliases, + UsingDirectiveSyntax[] usingDirectives, + UsingDirectiveSyntax[] aliasDirectives, + SyntaxNode externContainer, + SyntaxNode usingContainer, + SyntaxNode aliasContainer, + bool placeSystemNamespaceFirst) + { + _externAliases = externAliases; + _usingDirectives = usingDirectives; + _aliasDirectives = aliasDirectives; + _externContainer = externContainer; + _usingContainer = usingContainer; + _aliasContainer = aliasContainer; + _placeSystemNamespaceFirst = placeSystemNamespaceFirst; + } + + public override SyntaxNode VisitNamespaceDeclaration(NamespaceDeclarationSyntax node) + { + // recurse downwards so we visit inner namespaces first. + var rewritten = (NamespaceDeclarationSyntax)base.VisitNamespaceDeclaration(node); + + if (node == _aliasContainer) + { + rewritten = rewritten.AddUsingDirectives(_aliasDirectives, _placeSystemNamespaceFirst); + } + + if (node == _usingContainer) + { + rewritten = rewritten.AddUsingDirectives(_usingDirectives, _placeSystemNamespaceFirst); + } + + if (node == _externContainer) + { + rewritten = rewritten.AddExterns(_externAliases); + } + + return rewritten; + } + + public override SyntaxNode VisitCompilationUnit(CompilationUnitSyntax node) + { + // recurse downwards so we visit inner namespaces first. + var rewritten = (CompilationUnitSyntax)base.VisitCompilationUnit(node); + + if (node == _aliasContainer) + { + rewritten = rewritten.AddUsingDirectives(_aliasDirectives, _placeSystemNamespaceFirst); + } + + if (node == _usingContainer) + { + rewritten = rewritten.AddUsingDirectives(_usingDirectives, _placeSystemNamespaceFirst); + } + + if (node == _externContainer) + { + rewritten = rewritten.AddExterns(_externAliases); + } + + return rewritten; + } + } + } +} \ No newline at end of file diff --git a/src/Workspaces/CSharp/Portable/CSharpWorkspace.csproj b/src/Workspaces/CSharp/Portable/CSharpWorkspace.csproj index 900fc6b37608b0828bfb1b4d1804e7a1efc3df36..f92b0d35fdb8e26b395a8d92a6856f8e10983c0a 100644 --- a/src/Workspaces/CSharp/Portable/CSharpWorkspace.csproj +++ b/src/Workspaces/CSharp/Portable/CSharpWorkspace.csproj @@ -51,6 +51,7 @@ + @@ -246,4 +247,4 @@ - + \ No newline at end of file diff --git a/src/Workspaces/CSharp/Portable/Extensions/NamespaceDeclarationSyntaxExtensions.cs b/src/Workspaces/CSharp/Portable/Extensions/NamespaceDeclarationSyntaxExtensions.cs index 339c257f24c42804d9d4d6c05479a9dc717fba3a..48095b846ad312b966a3b8b9d7b14c282e4ce514 100644 --- a/src/Workspaces/CSharp/Portable/Extensions/NamespaceDeclarationSyntaxExtensions.cs +++ b/src/Workspaces/CSharp/Portable/Extensions/NamespaceDeclarationSyntaxExtensions.cs @@ -22,7 +22,7 @@ internal static class NamespaceDeclarationSyntaxExtensions bool placeSystemNamespaceFirst, params SyntaxAnnotation[] annotations) { - if (!usingDirectives.Any()) + if (usingDirectives.Count == 0) { return namespaceDeclaration; } diff --git a/src/Workspaces/Core/Portable/AddImport/IAddImportService.cs b/src/Workspaces/Core/Portable/AddImport/IAddImportService.cs new file mode 100644 index 0000000000000000000000000000000000000000..14e74301abc0648c27fe71e58d367eca4a45c058 --- /dev/null +++ b/src/Workspaces/Core/Portable/AddImport/IAddImportService.cs @@ -0,0 +1,32 @@ +// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.Collections.Generic; +using Microsoft.CodeAnalysis.Host; +using Roslyn.Utilities; + +namespace Microsoft.CodeAnalysis.AddImport +{ + internal interface IAddImportService : ILanguageService + { + SyntaxNode AddImports( + SyntaxNode root, SyntaxNode contextLocation, + IEnumerable newImports, bool placeSystemNamespaceFirst); + + /// + /// Given a context location in a provided syntax tree, returns the appropriate container + /// that should should be added to. + /// + SyntaxNode GetImportContainer(SyntaxNode root, SyntaxNode contextLocation, SyntaxNode import); + } + + internal static class IAddImportServiceExtensions + { + public static SyntaxNode AddImport( + this IAddImportService service, SyntaxNode root, SyntaxNode contextLocation, + SyntaxNode newImport, bool placeSystemNamespaceFirst) + { + return service.AddImports(root, contextLocation, + SpecializedCollections.SingletonEnumerable(newImport), placeSystemNamespaceFirst); + } + } +} \ No newline at end of file diff --git a/src/Workspaces/Core/Portable/Workspaces.csproj b/src/Workspaces/Core/Portable/Workspaces.csproj index cf90188c6311b0c4fca682f43af449823d084ed8..5ab6db568ba4517870a0c2b94be744984ced017f 100644 --- a/src/Workspaces/Core/Portable/Workspaces.csproj +++ b/src/Workspaces/Core/Portable/Workspaces.csproj @@ -294,6 +294,7 @@ InternalUtilities\UnicodeCharacterUtilities.cs + @@ -1090,7 +1091,8 @@ - + + @@ -1099,4 +1101,4 @@ - + \ No newline at end of file