From 6ba1ac58a2f0ca72b3e903280fc5d63b5f4eeac4 Mon Sep 17 00:00:00 2001 From: Martin Strecker Date: Fri, 29 Dec 2017 23:16:44 +0100 Subject: [PATCH 01/26] Initial commit. --- .../Ambiguity/AmbiguousTypeTests.cs | 251 ++++++++++++++++++ src/EditorFeatures/TestUtilities/Traits.cs | 1 + .../CSharpAmbiguousTypeCodeFixProvider.cs | 69 +++++ .../AbstractAmbiguousTypeCodeFixProvider.cs | 79 ++++++ .../PredefinedCodeFixProviderNames.cs | 1 + 5 files changed, 401 insertions(+) create mode 100644 src/EditorFeatures/CSharpTest/Ambiguity/AmbiguousTypeTests.cs create mode 100644 src/Features/CSharp/Portable/AmbiguityCodeFixProvider/CSharpAmbiguousTypeCodeFixProvider.cs create mode 100644 src/Features/Core/Portable/AmbiguityCodeFixProvider/AbstractAmbiguousTypeCodeFixProvider.cs diff --git a/src/EditorFeatures/CSharpTest/Ambiguity/AmbiguousTypeTests.cs b/src/EditorFeatures/CSharpTest/Ambiguity/AmbiguousTypeTests.cs new file mode 100644 index 0000000000000..37c8508f4d4d3 --- /dev/null +++ b/src/EditorFeatures/CSharpTest/Ambiguity/AmbiguousTypeTests.cs @@ -0,0 +1,251 @@ +// 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.Threading.Tasks; +using Microsoft.CodeAnalysis.CodeFixes; +using Microsoft.CodeAnalysis.CSharp.AmbiguityCodeFixProvider; +using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.Diagnostics; +using Roslyn.Test.Utilities; +using Xunit; + +namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.Ambiguity +{ + public class AmbiguousTypeTests : AbstractCSharpDiagnosticProviderBasedUserDiagnosticTest + { + internal override (DiagnosticAnalyzer, CodeFixProvider) CreateDiagnosticProviderAndFixer(Workspace workspace) + => (null, new CSharpAmbiguousTypeCodeFixProvider()); + + private string GetAmbiguousDefinition(string typeDefinion) + => $@" +namespace N1 +{{ + { typeDefinion } +}} +namespace N2 +{{ + { typeDefinion } +}}"; + + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsAliasType)] + public async Task TestAmbiguousClassObjectCreationUsingsInNamespace() + { + var classDef = GetAmbiguousDefinition("public class Ambiguous { }"); + var initialMarkup = classDef + @" +namespace Test +{ + using N1; + using N2; + class C + { + void M() + { + var a = new [|Ambiguous|](); + } + } +}"; + var expectedMarkupTemplate = classDef + @" +namespace Test +{ + using N1; + using N2; + # + + class C + { + void M() + { + var a = new Ambiguous(); + } + } +}"; + await TestInRegularAndScriptAsync(initialMarkup, expectedMarkupTemplate.Replace("#", "using Ambiguous = N1.Ambiguous;"), 0); + await TestInRegularAndScriptAsync(initialMarkup, expectedMarkupTemplate.Replace("#", "using Ambiguous = N2.Ambiguous;"), 1); + } + + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsAliasType)] + public async Task TestAmbiguousClassObjectCreationUsingsInCompilationUnit() + { + var classDef = GetAmbiguousDefinition("public class Ambiguous { }"); + await TestInRegularAndScriptAsync(@" +using N1; +using N2; +" + classDef + @" +namespace Test +{ + class C + { + void M() + { + var a = new [|Ambiguous|](); + } + } +}", @" +using N1; +using N2; +using Ambiguous = N1.Ambiguous; +" + classDef + @" +namespace Test +{ + class C + { + void M() + { + var a = new Ambiguous(); + } + } +}"); + } + + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsAliasType)] + public async Task TestAmbiguousClassObjectCreationGenericsDontOfferDiagnostic() + { + var genericAmbiguousClassDefinition = GetAmbiguousDefinition("public class Ambiguous { }"); + await TestMissingAsync(@" +using N1; +using N2; +" + genericAmbiguousClassDefinition + @" +namespace Test +{ + class C + { + void M() + { + var a = new [|Ambiguous|](); + } + } +}"); + } + + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsAliasType)] + public async Task TestAmbiguousAttribute() + { + var classDef = GetAmbiguousDefinition("public class AmbiguousAttribute: System.Attribute { }"); + await TestInRegularAndScriptAsync(@" +using N1; +using N2; +" + classDef + @" +namespace Test +{ + [[|Ambiguous|]] + class C + { + } +}", @" +using N1; +using N2; +using Ambiguous = N1.AmbiguousAttribute; +" + classDef + @" +namespace Test +{ + [Ambiguous] + class C + { + } +}"); + } + + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsAliasType)] + public async Task TestNamespaceAndTypenameIdenticalOffersNoDiagnostics() + { + // This gives CS0433: The type 'Ambiguous' exists in both 'Assembly1' and 'Assembly2' + // Couldn't get a CS0104 in this situation. Keep the test anyway if someone finds a way to force CS0104 here. + await TestMissingAsync(@" + + + +namespace N +{ + public class Ambiguous { } +} + + + + +namespace N +{ + public class Ambiguous { } +} + + + + Assembly1 + Assembly2 + +extern alias A1; +extern alias A2; +using A1::N; +using A2::N; +namespace N1 +{ + public class C + { + void M() + { + var a = new [|Ambiguous|](); + } + } +} + + + +"); + } + + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsAliasType)] + public async Task TestAmbiguousAliasNoDiagnostics() + { + await TestMissingAsync(@" +extern alias alias; +using alias=alias; +class myClass : [|alias|]::Uri + { + } +"); + } + + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsAliasType)] + public async Task TestAmbiguousNestedClass() + { + await TestInRegularAndScriptAsync(@" +using static Static; +using static Static; + +public static class Static +{ + public class Nested + { + public void M() { } + } +} + +class D +{ + static void Main(string[] args) + { + var c = new [|Nested|](); + c.M(); + } +}", @" +using static Static; +using static Static; +using Nested = Static.Nested; + +public static class Static +{ + public class Nested + { + public void M() { } + } +} + +class D +{ + static void Main(string[] args) + { + var c = new Nested(); + c.M(); + } +}"); + } + } +} diff --git a/src/EditorFeatures/TestUtilities/Traits.cs b/src/EditorFeatures/TestUtilities/Traits.cs index 74e22f5f27424..e8b780c60dd0a 100644 --- a/src/EditorFeatures/TestUtilities/Traits.cs +++ b/src/EditorFeatures/TestUtilities/Traits.cs @@ -38,6 +38,7 @@ public static class Features public const string CodeActionsAddImport = "CodeActions.AddImport"; public const string CodeActionsAddMissingReference = "CodeActions.AddMissingReference"; public const string CodeActionsAddParameter = "CodeActions.AddParameter"; + public const string CodeActionsAliasType = "CodeActions.AliasType"; public const string CodeActionsChangeToAsync = "CodeActions.ChangeToAsync"; public const string CodeActionsChangeToIEnumerable = "CodeActions.ChangeToIEnumerable"; public const string CodeActionsChangeToYield = "CodeActions.ChangeToYield"; diff --git a/src/Features/CSharp/Portable/AmbiguityCodeFixProvider/CSharpAmbiguousTypeCodeFixProvider.cs b/src/Features/CSharp/Portable/AmbiguityCodeFixProvider/CSharpAmbiguousTypeCodeFixProvider.cs new file mode 100644 index 0000000000000..a726914a57ef3 --- /dev/null +++ b/src/Features/CSharp/Portable/AmbiguityCodeFixProvider/CSharpAmbiguousTypeCodeFixProvider.cs @@ -0,0 +1,69 @@ +// 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.Immutable; +using System.Composition; +using System.Diagnostics; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.AmbiguityCodeFixProvider; +using Microsoft.CodeAnalysis.CodeFixes; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Shared.Extensions; + +namespace Microsoft.CodeAnalysis.CSharp.AmbiguityCodeFixProvider +{ + [ExportCodeFixProvider(LanguageNames.CSharp, Name = PredefinedCodeFixProviderNames.AliasType), Shared] + [ExtensionOrder(After = PredefinedCodeFixProviderNames.FullyQualify)] + internal class CSharpAmbiguousTypeCodeFixProvider : AbstractAmbiguousTypeCodeFixProvider + { + /// + /// 'reference' is an ambiguous reference between 'identifier' and 'identifier' + /// + private const string CS0104 = nameof(CS0104); + + public override ImmutableArray FixableDiagnosticIds + => ImmutableArray.Create(CS0104); + + protected override SyntaxNode GetAliasDirective(string typeName, ISymbol symbol) + => SyntaxFactory.UsingDirective(SyntaxFactory.NameEquals(typeName), + SyntaxFactory.IdentifierName(symbol.ToNameDisplayString())); + + protected override async Task InsertAliasDirective(Document document, SyntaxNode nodeReferencingType, SyntaxNode aliasDirectiveToInsert, CancellationToken cancellationToken) + { + Debug.Assert(aliasDirectiveToInsert.IsKind(SyntaxKind.UsingDirective)); + var (parent, usings, withUsings) = GetNearestUsingBlock(nodeReferencingType); + if (parent != null) + { + var newUsingList = InsertNewAliasDirectiveInUsingList((UsingDirectiveSyntax)aliasDirectiveToInsert, usings); + var newUsingParent = withUsings(newUsingList); + return await document.ReplaceNodeAsync(parent, newUsingParent, cancellationToken).ConfigureAwait(false); + } + + return document; + } + + private static SyntaxList InsertNewAliasDirectiveInUsingList(UsingDirectiveSyntax aliasDirectiveToInsert, SyntaxList usings) + => usings.Add(aliasDirectiveToInsert); + + private (SyntaxNode parent, SyntaxList usings, Func, SyntaxNode> withUsingFunc) GetNearestUsingBlock(SyntaxNode nodeReferencingType) + { + // Look for the nearest using block that is not empty. + var node = nodeReferencingType; + while (node != null) + { + switch (node) + { + case NamespaceDeclarationSyntax namespaceDeclaration when namespaceDeclaration.Usings.Count > 0: + return (namespaceDeclaration, namespaceDeclaration.Usings, newUsings => namespaceDeclaration.WithUsings(newUsings)); + case CompilationUnitSyntax compilationUnit: + return (compilationUnit, compilationUnit.Usings, newUsings => compilationUnit.WithUsings(newUsings)); + } + + node = node.Parent; + } + + return default; + } + } +} diff --git a/src/Features/Core/Portable/AmbiguityCodeFixProvider/AbstractAmbiguousTypeCodeFixProvider.cs b/src/Features/Core/Portable/AmbiguityCodeFixProvider/AbstractAmbiguousTypeCodeFixProvider.cs new file mode 100644 index 0000000000000..38c03e0b404e6 --- /dev/null +++ b/src/Features/Core/Portable/AmbiguityCodeFixProvider/AbstractAmbiguousTypeCodeFixProvider.cs @@ -0,0 +1,79 @@ +// 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.Immutable; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.CodeActions; +using Microsoft.CodeAnalysis.CodeFixes; +using Microsoft.CodeAnalysis.LanguageServices; +using Microsoft.CodeAnalysis.Shared.Extensions; +using static Microsoft.CodeAnalysis.CodeActions.CodeAction; + +namespace Microsoft.CodeAnalysis.AmbiguityCodeFixProvider +{ + internal abstract class AbstractAmbiguousTypeCodeFixProvider : CodeFixProvider + { + protected abstract SyntaxNode GetAliasDirective(string typeName, ISymbol symbol); + + protected abstract Task InsertAliasDirective(Document document, SyntaxNode nodeReferencingType, SyntaxNode aliasDirectiveToInsert, CancellationToken cancellationToken); + + public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context) + { + var cancellationToken = context.CancellationToken; + var document = context.Document; + var syntaxFacts = document.Project.LanguageServices.GetService(); + var span = context.Span; + var diagnostics = context.Diagnostics; + var diagnostic = diagnostics.First(); + var root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false); + var diagnosticNode = root.FindNode(span); + var typeName = diagnosticNode.ToString(); + var semanticModel = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false); + var symbolInfo = semanticModel.GetSymbolInfo(diagnosticNode, cancellationToken); + if (SymbolInfoContainesSupportedSymbols(symbolInfo)) + { + var codeActionsBuilder = ImmutableArray.CreateBuilder(symbolInfo.CandidateSymbols.Length); + foreach (var symbol in symbolInfo.CandidateSymbols) + { + var aliasDirective = GetAliasDirective(typeName, symbol); + codeActionsBuilder.Add(new MyCodeAction(symbol.ContainingNamespace.Name, + c => InsertAliasDirective(document, diagnosticNode, aliasDirective, c))); + } + context.RegisterFixes(codeActionsBuilder.ToImmutable(), diagnostic); + } + } + + private bool SymbolInfoContainesSupportedSymbols(SymbolInfo symbolInfo) + => symbolInfo.CandidateReason == CandidateReason.Ambiguous && + // Arity: Aliases can not name unbound generic types. Only closed constructed types can be aliased. + // Aliasing as a closed constructed type is possible but would require to remove the type arguments from the diagnosed node. + // It is unlikely that the user wants that and so generic types are not supported. + // SymbolKind.Namespace: see test method TestAmbiguousAliasNoDiagnostics + symbolInfo.CandidateSymbols.All(symbol => symbol.GetArity() == 0 && + !symbol.IsKind(SymbolKind.Namespace)); + + private static string GetNodeName(ISyntaxFactsService syntaxFacts, SyntaxNode node) + { + syntaxFacts.GetNameAndArityOfSimpleName(node, out var name, out var arity); + return name; + } + + private class MyCodeAction : DocumentChangeAction + { + public MyCodeAction(string title, Func> createChangedDocument) : + base(title, createChangedDocument, equivalenceKey: title) + { + } + } + + private class GroupingCodeAction : CodeActionWithNestedActions + { + public GroupingCodeAction(string title, ImmutableArray nestedActions) + : base(title, nestedActions, isInlinable: true) + { + } + } + } +} diff --git a/src/Features/Core/Portable/CodeFixes/PredefinedCodeFixProviderNames.cs b/src/Features/Core/Portable/CodeFixes/PredefinedCodeFixProviderNames.cs index b7efb93409941..94923a909dd80 100644 --- a/src/Features/Core/Portable/CodeFixes/PredefinedCodeFixProviderNames.cs +++ b/src/Features/Core/Portable/CodeFixes/PredefinedCodeFixProviderNames.cs @@ -8,6 +8,7 @@ internal static class PredefinedCodeFixProviderNames public const string AddAwait = nameof(AddAwait); public const string AddAsync = nameof(AddAsync); public const string AddParameter = nameof(AddParameter); + public const string AliasType = nameof(AliasType); public const string ApplyNamingStyle = nameof(ApplyNamingStyle); public const string AddBraces = nameof(AddBraces); public const string ChangeReturnType = nameof(ChangeReturnType); From 0f8fd4d522fa56a332cb934e3f568d177f9798da Mon Sep 17 00:00:00 2001 From: Martin Strecker Date: Sat, 30 Dec 2017 13:18:20 +0100 Subject: [PATCH 02/26] Use IAddImportsService. --- .../Ambiguity/AmbiguousTypeTests.cs | 2 +- .../CSharpAmbiguousTypeCodeFixProvider.cs | 37 ------------------- .../AbstractAmbiguousTypeCodeFixProvider.cs | 20 ++++++---- 3 files changed, 13 insertions(+), 46 deletions(-) diff --git a/src/EditorFeatures/CSharpTest/Ambiguity/AmbiguousTypeTests.cs b/src/EditorFeatures/CSharpTest/Ambiguity/AmbiguousTypeTests.cs index 37c8508f4d4d3..665c0f7acb2aa 100644 --- a/src/EditorFeatures/CSharpTest/Ambiguity/AmbiguousTypeTests.cs +++ b/src/EditorFeatures/CSharpTest/Ambiguity/AmbiguousTypeTests.cs @@ -203,7 +203,7 @@ class myClass : [|alias|]::Uri "); } - [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsAliasType)] + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsAliasType + "1")] public async Task TestAmbiguousNestedClass() { await TestInRegularAndScriptAsync(@" diff --git a/src/Features/CSharp/Portable/AmbiguityCodeFixProvider/CSharpAmbiguousTypeCodeFixProvider.cs b/src/Features/CSharp/Portable/AmbiguityCodeFixProvider/CSharpAmbiguousTypeCodeFixProvider.cs index a726914a57ef3..7b1a812a94f33 100644 --- a/src/Features/CSharp/Portable/AmbiguityCodeFixProvider/CSharpAmbiguousTypeCodeFixProvider.cs +++ b/src/Features/CSharp/Portable/AmbiguityCodeFixProvider/CSharpAmbiguousTypeCodeFixProvider.cs @@ -28,42 +28,5 @@ public override ImmutableArray FixableDiagnosticIds protected override SyntaxNode GetAliasDirective(string typeName, ISymbol symbol) => SyntaxFactory.UsingDirective(SyntaxFactory.NameEquals(typeName), SyntaxFactory.IdentifierName(symbol.ToNameDisplayString())); - - protected override async Task InsertAliasDirective(Document document, SyntaxNode nodeReferencingType, SyntaxNode aliasDirectiveToInsert, CancellationToken cancellationToken) - { - Debug.Assert(aliasDirectiveToInsert.IsKind(SyntaxKind.UsingDirective)); - var (parent, usings, withUsings) = GetNearestUsingBlock(nodeReferencingType); - if (parent != null) - { - var newUsingList = InsertNewAliasDirectiveInUsingList((UsingDirectiveSyntax)aliasDirectiveToInsert, usings); - var newUsingParent = withUsings(newUsingList); - return await document.ReplaceNodeAsync(parent, newUsingParent, cancellationToken).ConfigureAwait(false); - } - - return document; - } - - private static SyntaxList InsertNewAliasDirectiveInUsingList(UsingDirectiveSyntax aliasDirectiveToInsert, SyntaxList usings) - => usings.Add(aliasDirectiveToInsert); - - private (SyntaxNode parent, SyntaxList usings, Func, SyntaxNode> withUsingFunc) GetNearestUsingBlock(SyntaxNode nodeReferencingType) - { - // Look for the nearest using block that is not empty. - var node = nodeReferencingType; - while (node != null) - { - switch (node) - { - case NamespaceDeclarationSyntax namespaceDeclaration when namespaceDeclaration.Usings.Count > 0: - return (namespaceDeclaration, namespaceDeclaration.Usings, newUsings => namespaceDeclaration.WithUsings(newUsings)); - case CompilationUnitSyntax compilationUnit: - return (compilationUnit, compilationUnit.Usings, newUsings => compilationUnit.WithUsings(newUsings)); - } - - node = node.Parent; - } - - return default; - } } } diff --git a/src/Features/Core/Portable/AmbiguityCodeFixProvider/AbstractAmbiguousTypeCodeFixProvider.cs b/src/Features/Core/Portable/AmbiguityCodeFixProvider/AbstractAmbiguousTypeCodeFixProvider.cs index 38c03e0b404e6..96c3ca2de8f8a 100644 --- a/src/Features/Core/Portable/AmbiguityCodeFixProvider/AbstractAmbiguousTypeCodeFixProvider.cs +++ b/src/Features/Core/Portable/AmbiguityCodeFixProvider/AbstractAmbiguousTypeCodeFixProvider.cs @@ -5,8 +5,10 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; +using Microsoft.CodeAnalysis.AddImports; using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeFixes; +using Microsoft.CodeAnalysis.Editing; using Microsoft.CodeAnalysis.LanguageServices; using Microsoft.CodeAnalysis.Shared.Extensions; using static Microsoft.CodeAnalysis.CodeActions.CodeAction; @@ -17,29 +19,31 @@ internal abstract class AbstractAmbiguousTypeCodeFixProvider : CodeFixProvider { protected abstract SyntaxNode GetAliasDirective(string typeName, ISymbol symbol); - protected abstract Task InsertAliasDirective(Document document, SyntaxNode nodeReferencingType, SyntaxNode aliasDirectiveToInsert, CancellationToken cancellationToken); - public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context) { var cancellationToken = context.CancellationToken; var document = context.Document; - var syntaxFacts = document.Project.LanguageServices.GetService(); var span = context.Span; - var diagnostics = context.Diagnostics; - var diagnostic = diagnostics.First(); + var diagnostic = context.Diagnostics.First(); + var syntaxFacts = document.GetLanguageService(); + var addImportService = document.GetLanguageService(); + var optionSet = await document.GetOptionsAsync(cancellationToken).ConfigureAwait(false); + var placeSystemNamespaceFirst = optionSet.GetOption(GenerationOptions.PlaceSystemNamespaceFirst, document.Project.Language); + var root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false); var diagnosticNode = root.FindNode(span); - var typeName = diagnosticNode.ToString(); var semanticModel = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false); var symbolInfo = semanticModel.GetSymbolInfo(diagnosticNode, cancellationToken); if (SymbolInfoContainesSupportedSymbols(symbolInfo)) { var codeActionsBuilder = ImmutableArray.CreateBuilder(symbolInfo.CandidateSymbols.Length); + var typeName = diagnosticNode.ToString(); foreach (var symbol in symbolInfo.CandidateSymbols) { var aliasDirective = GetAliasDirective(typeName, symbol); + var newRoot = addImportService.AddImport(semanticModel.Compilation, root, diagnosticNode, aliasDirective, placeSystemNamespaceFirst); codeActionsBuilder.Add(new MyCodeAction(symbol.ContainingNamespace.Name, - c => InsertAliasDirective(document, diagnosticNode, aliasDirective, c))); + c => Task.FromResult(document.WithSyntaxRoot(newRoot)))); } context.RegisterFixes(codeActionsBuilder.ToImmutable(), diagnostic); } @@ -52,7 +56,7 @@ private bool SymbolInfoContainesSupportedSymbols(SymbolInfo symbolInfo) // It is unlikely that the user wants that and so generic types are not supported. // SymbolKind.Namespace: see test method TestAmbiguousAliasNoDiagnostics symbolInfo.CandidateSymbols.All(symbol => symbol.GetArity() == 0 && - !symbol.IsKind(SymbolKind.Namespace)); + !symbol.IsKind(SymbolKind.Namespace)); private static string GetNodeName(ISyntaxFactsService syntaxFacts, SyntaxNode node) { From 3e6b04e34d4a35721fb2064d49fc426c57eb9172 Mon Sep 17 00:00:00 2001 From: Martin Strecker Date: Sat, 30 Dec 2017 13:53:55 +0100 Subject: [PATCH 03/26] Failed Use IAddImportFeatureService. --- .../AddImport/CSharpAddImportFeatureService.cs | 2 +- .../AddImport/AbstractAddImportFeatureService.cs | 2 +- .../Portable/AddImport/IAddImportFeatureService.cs | 7 +++++++ .../AbstractAmbiguousTypeCodeFixProvider.cs | 11 +++++++---- .../AddImport/VisualBasicAddImportFeatureService.vb | 2 +- 5 files changed, 17 insertions(+), 7 deletions(-) diff --git a/src/Features/CSharp/Portable/AddImport/CSharpAddImportFeatureService.cs b/src/Features/CSharp/Portable/AddImport/CSharpAddImportFeatureService.cs index 791aa7deeb9ba..4c6a817925701 100644 --- a/src/Features/CSharp/Portable/AddImport/CSharpAddImportFeatureService.cs +++ b/src/Features/CSharp/Portable/AddImport/CSharpAddImportFeatureService.cs @@ -323,7 +323,7 @@ private string GetUsingDirectiveString(INamespaceOrTypeSymbol namespaceOrTypeSym : $"using static {displayString};"; } - protected override async Task AddImportAsync( + public override async Task AddImportAsync( SyntaxNode contextNode, INamespaceOrTypeSymbol namespaceOrTypeSymbol, Document document, diff --git a/src/Features/Core/Portable/AddImport/AbstractAddImportFeatureService.cs b/src/Features/Core/Portable/AddImport/AbstractAddImportFeatureService.cs index 1f8c3b0765d9c..38e12613bde35 100644 --- a/src/Features/Core/Portable/AddImport/AbstractAddImportFeatureService.cs +++ b/src/Features/Core/Portable/AddImport/AbstractAddImportFeatureService.cs @@ -45,7 +45,7 @@ protected AbstractAddImportFeatureService() protected abstract ITypeSymbol GetQueryClauseInfo(SemanticModel semanticModel, SyntaxNode node, CancellationToken cancellationToken); protected abstract bool IsViableExtensionMethod(IMethodSymbol method, SyntaxNode expression, SemanticModel semanticModel, ISyntaxFactsService syntaxFacts, CancellationToken cancellationToken); - protected abstract Task AddImportAsync(SyntaxNode contextNode, INamespaceOrTypeSymbol symbol, Document document, bool specialCaseSystem, CancellationToken cancellationToken); + public abstract Task AddImportAsync(SyntaxNode contextNode, INamespaceOrTypeSymbol symbol, Document document, bool specialCaseSystem, CancellationToken cancellationToken); protected abstract Task AddImportAsync(SyntaxNode contextNode, IReadOnlyList nameSpaceParts, Document document, bool specialCaseSystem, CancellationToken cancellationToken); protected abstract bool IsAddMethodContext(SyntaxNode node, SemanticModel semanticModel); diff --git a/src/Features/Core/Portable/AddImport/IAddImportFeatureService.cs b/src/Features/Core/Portable/AddImport/IAddImportFeatureService.cs index 9bca80bacb916..fd72666f79005 100644 --- a/src/Features/Core/Portable/AddImport/IAddImportFeatureService.cs +++ b/src/Features/Core/Portable/AddImport/IAddImportFeatureService.cs @@ -16,5 +16,12 @@ Task> GetFixesAsync( Document document, TextSpan span, string diagnosticId, bool placeSystemNamespaceFirst, ISymbolSearchService symbolSearchService, bool searchReferenceAssemblies, ImmutableArray packageSources, CancellationToken cancellationToken); + + Task AddImportAsync( + SyntaxNode contextNode, + INamespaceOrTypeSymbol namespaceOrTypeSymbol, + Document document, + bool placeSystemNamespaceFirst, + CancellationToken cancellationToken); } } diff --git a/src/Features/Core/Portable/AmbiguityCodeFixProvider/AbstractAmbiguousTypeCodeFixProvider.cs b/src/Features/Core/Portable/AmbiguityCodeFixProvider/AbstractAmbiguousTypeCodeFixProvider.cs index 96c3ca2de8f8a..588a3744fb5a6 100644 --- a/src/Features/Core/Portable/AmbiguityCodeFixProvider/AbstractAmbiguousTypeCodeFixProvider.cs +++ b/src/Features/Core/Portable/AmbiguityCodeFixProvider/AbstractAmbiguousTypeCodeFixProvider.cs @@ -5,6 +5,7 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; +using Microsoft.CodeAnalysis.AddImport; using Microsoft.CodeAnalysis.AddImports; using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeFixes; @@ -26,7 +27,7 @@ public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context) var span = context.Span; var diagnostic = context.Diagnostics.First(); var syntaxFacts = document.GetLanguageService(); - var addImportService = document.GetLanguageService(); + var addImportService = document.GetLanguageService(); var optionSet = await document.GetOptionsAsync(cancellationToken).ConfigureAwait(false); var placeSystemNamespaceFirst = optionSet.GetOption(GenerationOptions.PlaceSystemNamespaceFirst, document.Project.Language); @@ -40,10 +41,12 @@ public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context) var typeName = diagnosticNode.ToString(); foreach (var symbol in symbolInfo.CandidateSymbols) { - var aliasDirective = GetAliasDirective(typeName, symbol); - var newRoot = addImportService.AddImport(semanticModel.Compilation, root, diagnosticNode, aliasDirective, placeSystemNamespaceFirst); codeActionsBuilder.Add(new MyCodeAction(symbol.ContainingNamespace.Name, - c => Task.FromResult(document.WithSyntaxRoot(newRoot)))); + c => addImportService.AddImportAsync(diagnosticNode, + (INamespaceOrTypeSymbol)symbol, + document, + placeSystemNamespaceFirst, + c))); } context.RegisterFixes(codeActionsBuilder.ToImmutable(), diagnostic); } diff --git a/src/Features/VisualBasic/Portable/AddImport/VisualBasicAddImportFeatureService.vb b/src/Features/VisualBasic/Portable/AddImport/VisualBasicAddImportFeatureService.vb index 00188eda79a3c..b664fc8690a6e 100644 --- a/src/Features/VisualBasic/Portable/AddImport/VisualBasicAddImportFeatureService.vb +++ b/src/Features/VisualBasic/Portable/AddImport/VisualBasicAddImportFeatureService.vb @@ -261,7 +261,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.AddImport Return symbol IsNot Nothing AndAlso symbol.Locations.Length > 0 End Function - Protected Overloads Overrides Async Function AddImportAsync( + Public Overloads Overrides Async Function AddImportAsync( contextNode As SyntaxNode, symbol As INamespaceOrTypeSymbol, document As Document, From f1833f81b237a39e0bd7306136e5c26f31bc83e3 Mon Sep 17 00:00:00 2001 From: Martin Strecker Date: Sat, 30 Dec 2017 22:34:07 +0100 Subject: [PATCH 04/26] Revert "Failed Use IAddImportFeatureService." This reverts commit 3e6b04e34d4a35721fb2064d49fc426c57eb9172. --- .../AddImport/CSharpAddImportFeatureService.cs | 2 +- .../AddImport/AbstractAddImportFeatureService.cs | 2 +- .../Portable/AddImport/IAddImportFeatureService.cs | 7 ------- .../AbstractAmbiguousTypeCodeFixProvider.cs | 11 ++++------- .../AddImport/VisualBasicAddImportFeatureService.vb | 2 +- 5 files changed, 7 insertions(+), 17 deletions(-) diff --git a/src/Features/CSharp/Portable/AddImport/CSharpAddImportFeatureService.cs b/src/Features/CSharp/Portable/AddImport/CSharpAddImportFeatureService.cs index 4c6a817925701..791aa7deeb9ba 100644 --- a/src/Features/CSharp/Portable/AddImport/CSharpAddImportFeatureService.cs +++ b/src/Features/CSharp/Portable/AddImport/CSharpAddImportFeatureService.cs @@ -323,7 +323,7 @@ private string GetUsingDirectiveString(INamespaceOrTypeSymbol namespaceOrTypeSym : $"using static {displayString};"; } - public override async Task AddImportAsync( + protected override async Task AddImportAsync( SyntaxNode contextNode, INamespaceOrTypeSymbol namespaceOrTypeSymbol, Document document, diff --git a/src/Features/Core/Portable/AddImport/AbstractAddImportFeatureService.cs b/src/Features/Core/Portable/AddImport/AbstractAddImportFeatureService.cs index 38e12613bde35..1f8c3b0765d9c 100644 --- a/src/Features/Core/Portable/AddImport/AbstractAddImportFeatureService.cs +++ b/src/Features/Core/Portable/AddImport/AbstractAddImportFeatureService.cs @@ -45,7 +45,7 @@ protected AbstractAddImportFeatureService() protected abstract ITypeSymbol GetQueryClauseInfo(SemanticModel semanticModel, SyntaxNode node, CancellationToken cancellationToken); protected abstract bool IsViableExtensionMethod(IMethodSymbol method, SyntaxNode expression, SemanticModel semanticModel, ISyntaxFactsService syntaxFacts, CancellationToken cancellationToken); - public abstract Task AddImportAsync(SyntaxNode contextNode, INamespaceOrTypeSymbol symbol, Document document, bool specialCaseSystem, CancellationToken cancellationToken); + protected abstract Task AddImportAsync(SyntaxNode contextNode, INamespaceOrTypeSymbol symbol, Document document, bool specialCaseSystem, CancellationToken cancellationToken); protected abstract Task AddImportAsync(SyntaxNode contextNode, IReadOnlyList nameSpaceParts, Document document, bool specialCaseSystem, CancellationToken cancellationToken); protected abstract bool IsAddMethodContext(SyntaxNode node, SemanticModel semanticModel); diff --git a/src/Features/Core/Portable/AddImport/IAddImportFeatureService.cs b/src/Features/Core/Portable/AddImport/IAddImportFeatureService.cs index fd72666f79005..9bca80bacb916 100644 --- a/src/Features/Core/Portable/AddImport/IAddImportFeatureService.cs +++ b/src/Features/Core/Portable/AddImport/IAddImportFeatureService.cs @@ -16,12 +16,5 @@ Task> GetFixesAsync( Document document, TextSpan span, string diagnosticId, bool placeSystemNamespaceFirst, ISymbolSearchService symbolSearchService, bool searchReferenceAssemblies, ImmutableArray packageSources, CancellationToken cancellationToken); - - Task AddImportAsync( - SyntaxNode contextNode, - INamespaceOrTypeSymbol namespaceOrTypeSymbol, - Document document, - bool placeSystemNamespaceFirst, - CancellationToken cancellationToken); } } diff --git a/src/Features/Core/Portable/AmbiguityCodeFixProvider/AbstractAmbiguousTypeCodeFixProvider.cs b/src/Features/Core/Portable/AmbiguityCodeFixProvider/AbstractAmbiguousTypeCodeFixProvider.cs index 588a3744fb5a6..96c3ca2de8f8a 100644 --- a/src/Features/Core/Portable/AmbiguityCodeFixProvider/AbstractAmbiguousTypeCodeFixProvider.cs +++ b/src/Features/Core/Portable/AmbiguityCodeFixProvider/AbstractAmbiguousTypeCodeFixProvider.cs @@ -5,7 +5,6 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; -using Microsoft.CodeAnalysis.AddImport; using Microsoft.CodeAnalysis.AddImports; using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeFixes; @@ -27,7 +26,7 @@ public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context) var span = context.Span; var diagnostic = context.Diagnostics.First(); var syntaxFacts = document.GetLanguageService(); - var addImportService = document.GetLanguageService(); + var addImportService = document.GetLanguageService(); var optionSet = await document.GetOptionsAsync(cancellationToken).ConfigureAwait(false); var placeSystemNamespaceFirst = optionSet.GetOption(GenerationOptions.PlaceSystemNamespaceFirst, document.Project.Language); @@ -41,12 +40,10 @@ public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context) var typeName = diagnosticNode.ToString(); foreach (var symbol in symbolInfo.CandidateSymbols) { + var aliasDirective = GetAliasDirective(typeName, symbol); + var newRoot = addImportService.AddImport(semanticModel.Compilation, root, diagnosticNode, aliasDirective, placeSystemNamespaceFirst); codeActionsBuilder.Add(new MyCodeAction(symbol.ContainingNamespace.Name, - c => addImportService.AddImportAsync(diagnosticNode, - (INamespaceOrTypeSymbol)symbol, - document, - placeSystemNamespaceFirst, - c))); + c => Task.FromResult(document.WithSyntaxRoot(newRoot)))); } context.RegisterFixes(codeActionsBuilder.ToImmutable(), diagnostic); } diff --git a/src/Features/VisualBasic/Portable/AddImport/VisualBasicAddImportFeatureService.vb b/src/Features/VisualBasic/Portable/AddImport/VisualBasicAddImportFeatureService.vb index b664fc8690a6e..00188eda79a3c 100644 --- a/src/Features/VisualBasic/Portable/AddImport/VisualBasicAddImportFeatureService.vb +++ b/src/Features/VisualBasic/Portable/AddImport/VisualBasicAddImportFeatureService.vb @@ -261,7 +261,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.AddImport Return symbol IsNot Nothing AndAlso symbol.Locations.Length > 0 End Function - Public Overloads Overrides Async Function AddImportAsync( + Protected Overloads Overrides Async Function AddImportAsync( contextNode As SyntaxNode, symbol As INamespaceOrTypeSymbol, document As Document, From a2f8c459d49b4489203a991d62e7df2beb0c7e63 Mon Sep 17 00:00:00 2001 From: Martin Strecker Date: Sun, 31 Dec 2017 00:52:07 +0100 Subject: [PATCH 05/26] Added support for VB BC30561 --- .../Ambiguity/AmbiguousTypeTests.cs | 2 +- .../Ambiguity/AmbiguousTypeTests.vb | 60 +++++++++++++++++++ ...VisualBasicAmbiguousTypeCodeFixProvider.vb | 31 ++++++++++ 3 files changed, 92 insertions(+), 1 deletion(-) create mode 100644 src/EditorFeatures/VisualBasicTest/Diagnostics/Ambiguity/AmbiguousTypeTests.vb create mode 100644 src/Features/VisualBasic/Portable/AmbiguityCodeFixProvider/VisualBasicAmbiguousTypeCodeFixProvider.vb diff --git a/src/EditorFeatures/CSharpTest/Ambiguity/AmbiguousTypeTests.cs b/src/EditorFeatures/CSharpTest/Ambiguity/AmbiguousTypeTests.cs index 665c0f7acb2aa..37c8508f4d4d3 100644 --- a/src/EditorFeatures/CSharpTest/Ambiguity/AmbiguousTypeTests.cs +++ b/src/EditorFeatures/CSharpTest/Ambiguity/AmbiguousTypeTests.cs @@ -203,7 +203,7 @@ class myClass : [|alias|]::Uri "); } - [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsAliasType + "1")] + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsAliasType)] public async Task TestAmbiguousNestedClass() { await TestInRegularAndScriptAsync(@" diff --git a/src/EditorFeatures/VisualBasicTest/Diagnostics/Ambiguity/AmbiguousTypeTests.vb b/src/EditorFeatures/VisualBasicTest/Diagnostics/Ambiguity/AmbiguousTypeTests.vb new file mode 100644 index 0000000000000..f79cc3fd6758b --- /dev/null +++ b/src/EditorFeatures/VisualBasicTest/Diagnostics/Ambiguity/AmbiguousTypeTests.vb @@ -0,0 +1,60 @@ +' Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +Imports Microsoft.CodeAnalysis +Imports Microsoft.CodeAnalysis.CodeFixes +Imports Microsoft.CodeAnalysis.Diagnostics +Imports Microsoft.CodeAnalysis.Editor.VisualBasic.UnitTests.Diagnostics + +Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.UnitTests.Diagnostics.Ambiguity + Public Class AmbiguousTypeTests + Inherits AbstractVisualBasicDiagnosticProviderBasedUserDiagnosticTest + + Friend Overrides Function CreateDiagnosticProviderAndFixer(workspace As Workspace) As (DiagnosticAnalyzer, CodeFixProvider) + Return (Nothing, New VisualBasicAmbiguousTypeCodeFixProvider()) + End Function + + Private Function GetAmbiguousDefinition(ByVal typeDefinion As String) As String + Return $" +Namespace N1 + {typeDefinion} +End Namespace +Namespace N2 + {typeDefinion} +End Namespace" + End Function + + + Public Async Function TestAmbiguousClassObjectCreationUsingsInNamespace() As Task + Dim classDef = GetAmbiguousDefinition(" +Public Class Ambiguous +End Class") + Dim initialMarkup = " +Imports N1 +Imports N2 +" & classDef & " + +Namespace N3 + Class C + Private Sub M() + Dim a = New [|Ambiguous|]() + End Sub + End Class +End Namespace" + Dim expectedMarkupTemplate = " +Imports N1 +Imports N2 +# +" & classDef & " + +Namespace N3 + Class C + Private Sub M() + Dim a = New Ambiguous() + End Sub + End Class +End Namespace" + Await TestInRegularAndScriptAsync(initialMarkup, expectedMarkupTemplate.Replace("#", "Imports Ambiguous = N1.Ambiguous"), 0) + Await TestInRegularAndScriptAsync(initialMarkup, expectedMarkupTemplate.Replace("#", "Imports Ambiguous = N2.Ambiguous"), 1) + End Function + End Class +End Namespace diff --git a/src/Features/VisualBasic/Portable/AmbiguityCodeFixProvider/VisualBasicAmbiguousTypeCodeFixProvider.vb b/src/Features/VisualBasic/Portable/AmbiguityCodeFixProvider/VisualBasicAmbiguousTypeCodeFixProvider.vb new file mode 100644 index 0000000000000..0ac874f5d138a --- /dev/null +++ b/src/Features/VisualBasic/Portable/AmbiguityCodeFixProvider/VisualBasicAmbiguousTypeCodeFixProvider.vb @@ -0,0 +1,31 @@ +' Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +Imports System.Collections.Immutable +Imports System.Composition +Imports Microsoft.CodeAnalysis +Imports Microsoft.CodeAnalysis.AmbiguityCodeFixProvider +Imports Microsoft.CodeAnalysis.CodeFixes +Imports Microsoft.CodeAnalysis.VisualBasic +Imports Microsoft.CodeAnalysis.VisualBasic.Syntax + + + +Friend Class VisualBasicAmbiguousTypeCodeFixProvider + Inherits AbstractAmbiguousTypeCodeFixProvider + + 'BC30561: '' is ambiguous, imported from the namespaces or types '' + Private Const BC30561 As String = NameOf(BC30561) + + Public Overrides ReadOnly Property FixableDiagnosticIds As ImmutableArray(Of String) + Get + Return ImmutableArray.Create(BC30561) + End Get + End Property + + Protected Overrides Function GetAliasDirective(ByVal typeName As String, ByVal symbol As ISymbol) As SyntaxNode + Return SyntaxFactory.ImportsStatement(SyntaxFactory.SeparatedList(Of ImportsClauseSyntax).Add( + SyntaxFactory.SimpleImportsClause( + SyntaxFactory.ImportAliasClause(typeName), + SyntaxFactory.IdentifierName(symbol.ToNameDisplayString())))) + End Function +End Class From 5a3c419b2d997baf29f674bff82546deb3f20779 Mon Sep 17 00:00:00 2001 From: Martin Strecker Date: Mon, 1 Jan 2018 19:36:09 +0100 Subject: [PATCH 06/26] More VB tests and some alignments between C#and VB. --- .../Ambiguity/AmbiguousTypeTests.cs | 13 +- .../Ambiguity/AmbiguousTypeTests.vb | 134 +++++++++++++++++- .../AbstractAmbiguousTypeCodeFixProvider.cs | 24 +++- ...VisualBasicAmbiguousTypeCodeFixProvider.vb | 31 ++++ ...VisualBasicAmbiguousTypeCodeFixProvider.vb | 31 ---- 5 files changed, 190 insertions(+), 43 deletions(-) create mode 100644 src/Features/VisualBasic/Portable/Ambiguity/VisualBasicAmbiguousTypeCodeFixProvider.vb delete mode 100644 src/Features/VisualBasic/Portable/AmbiguityCodeFixProvider/VisualBasicAmbiguousTypeCodeFixProvider.vb diff --git a/src/EditorFeatures/CSharpTest/Ambiguity/AmbiguousTypeTests.cs b/src/EditorFeatures/CSharpTest/Ambiguity/AmbiguousTypeTests.cs index 37c8508f4d4d3..e32f9ec2aa9a9 100644 --- a/src/EditorFeatures/CSharpTest/Ambiguity/AmbiguousTypeTests.cs +++ b/src/EditorFeatures/CSharpTest/Ambiguity/AmbiguousTypeTests.cs @@ -133,7 +133,7 @@ class C }", @" using N1; using N2; -using Ambiguous = N1.AmbiguousAttribute; +using AmbiguousAttribute = N1.AmbiguousAttribute; " + classDef + @" namespace Test { @@ -206,7 +206,7 @@ class myClass : [|alias|]::Uri [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsAliasType)] public async Task TestAmbiguousNestedClass() { - await TestInRegularAndScriptAsync(@" + var initialMarkup = @" using static Static; using static Static; @@ -225,10 +225,11 @@ static void Main(string[] args) var c = new [|Nested|](); c.M(); } -}", @" +}"; + var expectedMarkup = @" using static Static; using static Static; -using Nested = Static.Nested; +# public static class Static { @@ -245,7 +246,9 @@ static void Main(string[] args) var c = new Nested(); c.M(); } -}"); +}"; + await TestInRegularAndScriptAsync(initialMarkup, expectedMarkup.Replace("#", "using Nested = Static.Nested;"), 0); + await TestInRegularAndScriptAsync(initialMarkup, expectedMarkup.Replace("#", "using Nested = Static.Nested;"), 1); } } } diff --git a/src/EditorFeatures/VisualBasicTest/Diagnostics/Ambiguity/AmbiguousTypeTests.vb b/src/EditorFeatures/VisualBasicTest/Diagnostics/Ambiguity/AmbiguousTypeTests.vb index f79cc3fd6758b..9b9570d9b0c92 100644 --- a/src/EditorFeatures/VisualBasicTest/Diagnostics/Ambiguity/AmbiguousTypeTests.vb +++ b/src/EditorFeatures/VisualBasicTest/Diagnostics/Ambiguity/AmbiguousTypeTests.vb @@ -1,9 +1,8 @@ ' Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. -Imports Microsoft.CodeAnalysis Imports Microsoft.CodeAnalysis.CodeFixes Imports Microsoft.CodeAnalysis.Diagnostics -Imports Microsoft.CodeAnalysis.Editor.VisualBasic.UnitTests.Diagnostics +Imports Microsoft.CodeAnalysis.VisualBasic.Ambiguity Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.UnitTests.Diagnostics.Ambiguity Public Class AmbiguousTypeTests @@ -24,7 +23,7 @@ End Namespace" End Function - Public Async Function TestAmbiguousClassObjectCreationUsingsInNamespace() As Task + Public Async Function TestAmbiguousClassObjectCreationGlobalImports() As Task Dim classDef = GetAmbiguousDefinition(" Public Class Ambiguous End Class") @@ -56,5 +55,134 @@ End Namespace" Await TestInRegularAndScriptAsync(initialMarkup, expectedMarkupTemplate.Replace("#", "Imports Ambiguous = N1.Ambiguous"), 0) Await TestInRegularAndScriptAsync(initialMarkup, expectedMarkupTemplate.Replace("#", "Imports Ambiguous = N2.Ambiguous"), 1) End Function + + + Public Async Function TestAmbiguousAttribute() As Task + Dim classDef = GetAmbiguousDefinition(" + Class AAttribute + Inherits System.Attribute End Class +") + Dim initialMarkup = " +Imports N1 +Imports N2 +" & classDef & " + +<[|A|]()> +Class C +End Class" + Dim expectedMarkupTemplate = " +Imports N1 +Imports N2 +Imports AAttribute = N1.AAttribute +" & classDef & " + +<[|A|]()> +Class C +End Class" + Await TestInRegularAndScriptAsync(initialMarkup, expectedMarkupTemplate) + End Function + + + Public Async Function TestAmbiguousBug4817() As Task + Dim initialMarkup = " +Imports A +Imports B +Class A + Shared Sub Goo() + End Sub +End Class +Class B + Inherits A + Overloads Shared Sub Goo(x As Integer) + End Sub +End Class +Module C + Sub Main() + [|Goo|]() + End Sub +End Module +" + Await TestMissingAsync(initialMarkup) + End Function + + + Public Async Function TestAmbiguousClassInModule() As Task + Dim initialMarkup = " +Imports N1, N2 +Namespace N1 + Module K + Class Goo + End Class + End Module End Namespace +Namespace N2 + Module L + Class Goo + End Class + End Module +End Namespace +Class A + Public d As [|Goo|] +End Class +" + Dim expectedMarkup = " +Imports N1, N2 +Imports Goo = N1.K.Goo + +Namespace N1 + Module K + Class Goo + End Class + End Module +End Namespace +Namespace N2 + Module L + Class Goo + End Class + End Module +End Namespace +Class A + Public d As Goo +End Class +" + Await TestInRegularAndScriptAsync(initialMarkup, expectedMarkup) + End Function + + + Public Async Function TestAmbiguousInterfaceNameReferencedInSmallCaps() As Task + Dim initialMarkup = " +Imports N1, N2 +Namespace N1 + Interface I1 + End Interface +End Namespace +Namespace N2 + Interface I1 + End Interface +End Namespace +Public Class Cls2 + Implements [|i1|] +End Class +" + Dim expectedMarkup = " +Imports N1, N2 +Imports i1 = N1.I1 + +Namespace N1 + Interface I1 + End Interface +End Namespace +Namespace N2 + Interface I1 + End Interface +End Namespace +Public Class Cls2 + Implements i1 +End Class +" + Await TestInRegularAndScriptAsync(initialMarkup, expectedMarkup) + End Function + End Class +End Namespace + diff --git a/src/Features/Core/Portable/AmbiguityCodeFixProvider/AbstractAmbiguousTypeCodeFixProvider.cs b/src/Features/Core/Portable/AmbiguityCodeFixProvider/AbstractAmbiguousTypeCodeFixProvider.cs index 96c3ca2de8f8a..268f9fd67d788 100644 --- a/src/Features/Core/Portable/AmbiguityCodeFixProvider/AbstractAmbiguousTypeCodeFixProvider.cs +++ b/src/Features/Core/Portable/AmbiguityCodeFixProvider/AbstractAmbiguousTypeCodeFixProvider.cs @@ -37,7 +37,7 @@ public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context) if (SymbolInfoContainesSupportedSymbols(symbolInfo)) { var codeActionsBuilder = ImmutableArray.CreateBuilder(symbolInfo.CandidateSymbols.Length); - var typeName = diagnosticNode.ToString(); + var typeName = GetAliasFromDiagnsoticNode(syntaxFacts, diagnosticNode); foreach (var symbol in symbolInfo.CandidateSymbols) { var aliasDirective = GetAliasDirective(typeName, symbol); @@ -49,14 +49,30 @@ public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context) } } + private static string GetAliasFromDiagnsoticNode(ISyntaxFactsService syntaxFacts, SyntaxNode diagnosticNode) + { + // The content of the node is a good candidate for the alias + // For attributes VB requires that the alias ends with 'Attribute' while C# is fine with or without the suffix. + var nodeText = diagnosticNode.ToString(); + if (syntaxFacts.IsAttribute(diagnosticNode) || syntaxFacts.IsAttribute(diagnosticNode.Parent)) + { + if (!nodeText.EndsWith("Attribute")) + { + nodeText += "Attribute"; + } + } + + return nodeText; + } + private bool SymbolInfoContainesSupportedSymbols(SymbolInfo symbolInfo) => symbolInfo.CandidateReason == CandidateReason.Ambiguous && // Arity: Aliases can not name unbound generic types. Only closed constructed types can be aliased. // Aliasing as a closed constructed type is possible but would require to remove the type arguments from the diagnosed node. // It is unlikely that the user wants that and so generic types are not supported. - // SymbolKind.Namespace: see test method TestAmbiguousAliasNoDiagnostics - symbolInfo.CandidateSymbols.All(symbol => symbol.GetArity() == 0 && - !symbol.IsKind(SymbolKind.Namespace)); + // SymbolKind.NamedType: only types can be aliased by this fix. + symbolInfo.CandidateSymbols.All(symbol => symbol.IsKind(SymbolKind.NamedType) && + symbol.GetArity() == 0); private static string GetNodeName(ISyntaxFactsService syntaxFacts, SyntaxNode node) { diff --git a/src/Features/VisualBasic/Portable/Ambiguity/VisualBasicAmbiguousTypeCodeFixProvider.vb b/src/Features/VisualBasic/Portable/Ambiguity/VisualBasicAmbiguousTypeCodeFixProvider.vb new file mode 100644 index 0000000000000..6a8f2f2e4910b --- /dev/null +++ b/src/Features/VisualBasic/Portable/Ambiguity/VisualBasicAmbiguousTypeCodeFixProvider.vb @@ -0,0 +1,31 @@ +' Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +Imports System.Collections.Immutable +Imports System.Composition +Imports Microsoft.CodeAnalysis.AmbiguityCodeFixProvider +Imports Microsoft.CodeAnalysis.CodeFixes +Imports Microsoft.CodeAnalysis.VisualBasic.Syntax + +Namespace Microsoft.CodeAnalysis.VisualBasic.Ambiguity + + + Friend Class VisualBasicAmbiguousTypeCodeFixProvider + Inherits AbstractAmbiguousTypeCodeFixProvider + + 'BC30561: '' is ambiguous, imported from the namespaces or types '' + Private Const BC30561 As String = NameOf(BC30561) + + Public Overrides ReadOnly Property FixableDiagnosticIds As ImmutableArray(Of String) + Get + Return ImmutableArray.Create(BC30561) + End Get + End Property + + Protected Overrides Function GetAliasDirective(ByVal typeName As String, ByVal symbol As ISymbol) As SyntaxNode + Return SyntaxFactory.ImportsStatement(SyntaxFactory.SeparatedList(Of ImportsClauseSyntax).Add( + SyntaxFactory.SimpleImportsClause( + SyntaxFactory.ImportAliasClause(typeName), + SyntaxFactory.IdentifierName(symbol.ToNameDisplayString())))) + End Function + End Class +End Namespace diff --git a/src/Features/VisualBasic/Portable/AmbiguityCodeFixProvider/VisualBasicAmbiguousTypeCodeFixProvider.vb b/src/Features/VisualBasic/Portable/AmbiguityCodeFixProvider/VisualBasicAmbiguousTypeCodeFixProvider.vb deleted file mode 100644 index 0ac874f5d138a..0000000000000 --- a/src/Features/VisualBasic/Portable/AmbiguityCodeFixProvider/VisualBasicAmbiguousTypeCodeFixProvider.vb +++ /dev/null @@ -1,31 +0,0 @@ -' Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -Imports System.Collections.Immutable -Imports System.Composition -Imports Microsoft.CodeAnalysis -Imports Microsoft.CodeAnalysis.AmbiguityCodeFixProvider -Imports Microsoft.CodeAnalysis.CodeFixes -Imports Microsoft.CodeAnalysis.VisualBasic -Imports Microsoft.CodeAnalysis.VisualBasic.Syntax - - - -Friend Class VisualBasicAmbiguousTypeCodeFixProvider - Inherits AbstractAmbiguousTypeCodeFixProvider - - 'BC30561: '' is ambiguous, imported from the namespaces or types '' - Private Const BC30561 As String = NameOf(BC30561) - - Public Overrides ReadOnly Property FixableDiagnosticIds As ImmutableArray(Of String) - Get - Return ImmutableArray.Create(BC30561) - End Get - End Property - - Protected Overrides Function GetAliasDirective(ByVal typeName As String, ByVal symbol As ISymbol) As SyntaxNode - Return SyntaxFactory.ImportsStatement(SyntaxFactory.SeparatedList(Of ImportsClauseSyntax).Add( - SyntaxFactory.SimpleImportsClause( - SyntaxFactory.ImportAliasClause(typeName), - SyntaxFactory.IdentifierName(symbol.ToNameDisplayString())))) - End Function -End Class From 4d569f32a87fd83f2833b8d5c81371f1169a947a Mon Sep 17 00:00:00 2001 From: Martin Strecker Date: Mon, 1 Jan 2018 21:49:18 +0100 Subject: [PATCH 07/26] Small changes. --- .../Test/Semantic/Properties/launchSettings.json | 7 ++++++- .../CSharpTest/Properties/launchSettings.json | 7 ++++++- .../Test2/My Project/launchSettings.json | 7 ++++++- .../VisualBasicTest/My Project/launchSettings.json | 7 ++++++- .../AbstractAmbiguousTypeCodeFixProvider.cs | 11 +++-------- .../VisualBasicAmbiguousTypeCodeFixProvider.vb | 12 ++++++++---- .../CSharpTest/Properties/launchSettings.json | 7 ++++++- 7 files changed, 41 insertions(+), 17 deletions(-) rename src/Features/Core/Portable/{AmbiguityCodeFixProvider => Ambiguity}/AbstractAmbiguousTypeCodeFixProvider.cs (92%) diff --git a/src/Compilers/CSharp/Test/Semantic/Properties/launchSettings.json b/src/Compilers/CSharp/Test/Semantic/Properties/launchSettings.json index f84436625a97d..7ae7003d107af 100644 --- a/src/Compilers/CSharp/Test/Semantic/Properties/launchSettings.json +++ b/src/Compilers/CSharp/Test/Semantic/Properties/launchSettings.json @@ -7,6 +7,11 @@ "xUnit.net WPF Runner": { "executablePath": "$(XUnitWpfPath)", "commandLineArgs": "$(XUnitWpfArguments)" + }, + "Run Method": { + "commandName": "Executable", + "executablePath": "C:\\Users\\MartinStrecker\\.nuget\\packages\\xunit.runner.console\\2.2.0-beta4-build3444\\tools\\xunit.console.x86.exe", + "commandLineArgs": "\"C:\\IFP\\OS\\roslyn\\Binaries\\Debug\\UnitTests\\CSharpCompilerSemanticTest\\UnitTests\\CSharpCompilerSemanticTest\\Roslyn.Compilers.CSharp.Semantic.UnitTests.dll\" -wait -method Microsoft.CodeAnalysis.CSharp.UnitTests.QueryTests.FromOrderBySelectQueryTranslation" } } -} +} \ No newline at end of file diff --git a/src/EditorFeatures/CSharpTest/Properties/launchSettings.json b/src/EditorFeatures/CSharpTest/Properties/launchSettings.json index f84436625a97d..52e3082a55b25 100644 --- a/src/EditorFeatures/CSharpTest/Properties/launchSettings.json +++ b/src/EditorFeatures/CSharpTest/Properties/launchSettings.json @@ -7,6 +7,11 @@ "xUnit.net WPF Runner": { "executablePath": "$(XUnitWpfPath)", "commandLineArgs": "$(XUnitWpfArguments)" + }, + "RunTestMethod": { + "commandName": "Executable", + "executablePath": "C:\\Users\\MartinStrecker\\.nuget\\packages\\xunit.runner.console\\2.2.0-beta4-build3444\\tools\\xunit.console.x86.exe", + "commandLineArgs": "C:\\IFP\\OS\\roslyn\\Binaries\\Debug\\UnitTests\\CSharpEditorServicesTest\\Roslyn.Services.Editor.CSharp.UnitTests.dll -wait -trait \"Feature=CodeActions.AliasType\"" } } -} +} \ No newline at end of file diff --git a/src/EditorFeatures/Test2/My Project/launchSettings.json b/src/EditorFeatures/Test2/My Project/launchSettings.json index f84436625a97d..0fed0ab96fb50 100644 --- a/src/EditorFeatures/Test2/My Project/launchSettings.json +++ b/src/EditorFeatures/Test2/My Project/launchSettings.json @@ -7,6 +7,11 @@ "xUnit.net WPF Runner": { "executablePath": "$(XUnitWpfPath)", "commandLineArgs": "$(XUnitWpfArguments)" + }, + "TestMethod": { + "commandName": "Executable", + "executablePath": "C:\\Users\\MartinStrecker\\.nuget\\packages\\xunit.runner.console\\2.2.0\\tools\\xunit.console.x86.exe", + "commandLineArgs": "C:\\IFP\\OS\\roslyn\\Binaries\\Debug\\UnitTests\\EditorServicesTest2\\Roslyn.Services.Editor2.UnitTests.dll -wait -trait Feature=GoToDefinition" } } -} +} \ No newline at end of file diff --git a/src/EditorFeatures/VisualBasicTest/My Project/launchSettings.json b/src/EditorFeatures/VisualBasicTest/My Project/launchSettings.json index f84436625a97d..9dc9c8a8b4708 100644 --- a/src/EditorFeatures/VisualBasicTest/My Project/launchSettings.json +++ b/src/EditorFeatures/VisualBasicTest/My Project/launchSettings.json @@ -7,6 +7,11 @@ "xUnit.net WPF Runner": { "executablePath": "$(XUnitWpfPath)", "commandLineArgs": "$(XUnitWpfArguments)" + }, + "RunVBtest": { + "commandName": "Executable", + "executablePath": "C:\\Users\\MartinStrecker\\.nuget\\packages\\xunit.runner.console\\2.2.0-beta4-build3444\\tools\\xunit.console.x86.exe", + "commandLineArgs": "C:\\IFP\\OS\\roslyn\\Binaries\\Debug\\UnitTests\\BasicEditorServicesTest\\Roslyn.Services.Editor.VisualBasic.UnitTests.dll -wait -trait \"Feature=CodeActions.AliasType1\"" } } -} +} \ No newline at end of file diff --git a/src/Features/Core/Portable/AmbiguityCodeFixProvider/AbstractAmbiguousTypeCodeFixProvider.cs b/src/Features/Core/Portable/Ambiguity/AbstractAmbiguousTypeCodeFixProvider.cs similarity index 92% rename from src/Features/Core/Portable/AmbiguityCodeFixProvider/AbstractAmbiguousTypeCodeFixProvider.cs rename to src/Features/Core/Portable/Ambiguity/AbstractAmbiguousTypeCodeFixProvider.cs index 268f9fd67d788..18467e97d1e08 100644 --- a/src/Features/Core/Portable/AmbiguityCodeFixProvider/AbstractAmbiguousTypeCodeFixProvider.cs +++ b/src/Features/Core/Portable/Ambiguity/AbstractAmbiguousTypeCodeFixProvider.cs @@ -42,10 +42,11 @@ public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context) { var aliasDirective = GetAliasDirective(typeName, symbol); var newRoot = addImportService.AddImport(semanticModel.Compilation, root, diagnosticNode, aliasDirective, placeSystemNamespaceFirst); - codeActionsBuilder.Add(new MyCodeAction(symbol.ContainingNamespace.Name, + codeActionsBuilder.Add(new MyCodeAction( symbol.ContainingNamespace.Name, c => Task.FromResult(document.WithSyntaxRoot(newRoot)))); } - context.RegisterFixes(codeActionsBuilder.ToImmutable(), diagnostic); + var groupedCodeAction = new GroupingCodeAction("Test", codeActionsBuilder.ToImmutable()); + context.RegisterCodeFix(groupedCodeAction, diagnostic); } } @@ -74,12 +75,6 @@ private bool SymbolInfoContainesSupportedSymbols(SymbolInfo symbolInfo) symbolInfo.CandidateSymbols.All(symbol => symbol.IsKind(SymbolKind.NamedType) && symbol.GetArity() == 0); - private static string GetNodeName(ISyntaxFactsService syntaxFacts, SyntaxNode node) - { - syntaxFacts.GetNameAndArityOfSimpleName(node, out var name, out var arity); - return name; - } - private class MyCodeAction : DocumentChangeAction { public MyCodeAction(string title, Func> createChangedDocument) : diff --git a/src/Features/VisualBasic/Portable/Ambiguity/VisualBasicAmbiguousTypeCodeFixProvider.vb b/src/Features/VisualBasic/Portable/Ambiguity/VisualBasicAmbiguousTypeCodeFixProvider.vb index 6a8f2f2e4910b..0530576c00d11 100644 --- a/src/Features/VisualBasic/Portable/Ambiguity/VisualBasicAmbiguousTypeCodeFixProvider.vb +++ b/src/Features/VisualBasic/Portable/Ambiguity/VisualBasicAmbiguousTypeCodeFixProvider.vb @@ -22,10 +22,14 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Ambiguity End Property Protected Overrides Function GetAliasDirective(ByVal typeName As String, ByVal symbol As ISymbol) As SyntaxNode - Return SyntaxFactory.ImportsStatement(SyntaxFactory.SeparatedList(Of ImportsClauseSyntax).Add( - SyntaxFactory.SimpleImportsClause( - SyntaxFactory.ImportAliasClause(typeName), - SyntaxFactory.IdentifierName(symbol.ToNameDisplayString())))) + Return SyntaxFactory.ImportsStatement( + SyntaxFactory.SeparatedList(Of ImportsClauseSyntax).Add( + SyntaxFactory.SimpleImportsClause( + SyntaxFactory.ImportAliasClause(typeName), + SyntaxFactory.IdentifierName(symbol.ToNameDisplayString()) + ) + ) + ) End Function End Class End Namespace diff --git a/src/Workspaces/CSharpTest/Properties/launchSettings.json b/src/Workspaces/CSharpTest/Properties/launchSettings.json index f84436625a97d..676038c8a5733 100644 --- a/src/Workspaces/CSharpTest/Properties/launchSettings.json +++ b/src/Workspaces/CSharpTest/Properties/launchSettings.json @@ -7,6 +7,11 @@ "xUnit.net WPF Runner": { "executablePath": "$(XUnitWpfPath)", "commandLineArgs": "$(XUnitWpfArguments)" + }, + "Test": { + "commandName": "Executable", + "executablePath": "C:\\Users\\MartinStrecker\\.nuget\\packages\\xunit.runner.console\\2.2.0-beta4-build3444\\tools\\xunit.console.x86.exe", + "commandLineArgs": "C:\\IFP\\OS\\roslyn\\Binaries\\Debug\\UnitTests\\CSharpServicesTest\\Roslyn.Services.CSharp.UnitTests.dll -wait -class Microsoft.CodeAnalysis.CSharp.UnitTests.Formatting.FormattingEngineTests" } } -} +} \ No newline at end of file From 2491170b34f8b7f43d99c0ad65d88d75e3df2369 Mon Sep 17 00:00:00 2001 From: Martin Strecker Date: Mon, 1 Jan 2018 22:33:47 +0100 Subject: [PATCH 08/26] Better Title for CodeAction. --- .../Ambiguity/AbstractAmbiguousTypeCodeFixProvider.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/Features/Core/Portable/Ambiguity/AbstractAmbiguousTypeCodeFixProvider.cs b/src/Features/Core/Portable/Ambiguity/AbstractAmbiguousTypeCodeFixProvider.cs index 18467e97d1e08..a9d56215f9d3c 100644 --- a/src/Features/Core/Portable/Ambiguity/AbstractAmbiguousTypeCodeFixProvider.cs +++ b/src/Features/Core/Portable/Ambiguity/AbstractAmbiguousTypeCodeFixProvider.cs @@ -9,6 +9,7 @@ using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeFixes; using Microsoft.CodeAnalysis.Editing; +using Microsoft.CodeAnalysis.Formatting; using Microsoft.CodeAnalysis.LanguageServices; using Microsoft.CodeAnalysis.Shared.Extensions; using static Microsoft.CodeAnalysis.CodeActions.CodeAction; @@ -42,7 +43,8 @@ public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context) { var aliasDirective = GetAliasDirective(typeName, symbol); var newRoot = addImportService.AddImport(semanticModel.Compilation, root, diagnosticNode, aliasDirective, placeSystemNamespaceFirst); - codeActionsBuilder.Add(new MyCodeAction( symbol.ContainingNamespace.Name, + var codeActionPreviewText = GetTextPreviewOfChange(aliasDirective, document.Project.Solution.Workspace); + codeActionsBuilder.Add(new MyCodeAction(codeActionPreviewText, c => Task.FromResult(document.WithSyntaxRoot(newRoot)))); } var groupedCodeAction = new GroupingCodeAction("Test", codeActionsBuilder.ToImmutable()); @@ -50,6 +52,9 @@ public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context) } } + private static string GetTextPreviewOfChange(SyntaxNode newNode, Workspace workspace) + => Formatter.Format(newNode, workspace).ToFullString(); + private static string GetAliasFromDiagnsoticNode(ISyntaxFactsService syntaxFacts, SyntaxNode diagnosticNode) { // The content of the node is a good candidate for the alias From 19001be93af8a5e6422899c0b3e0c736fde67482 Mon Sep 17 00:00:00 2001 From: Martin Strecker Date: Mon, 1 Jan 2018 22:50:52 +0100 Subject: [PATCH 09/26] Add FeatureRessources text template. --- .../AbstractAmbiguousTypeCodeFixProvider.cs | 8 ++++---- .../Core/Portable/FeaturesResources.Designer.cs | 13 ++++++++++++- src/Features/Core/Portable/FeaturesResources.resx | 5 ++++- .../Core/Portable/xlf/FeaturesResources.cs.xlf | 5 +++++ .../Core/Portable/xlf/FeaturesResources.de.xlf | 5 +++++ .../Core/Portable/xlf/FeaturesResources.es.xlf | 5 +++++ .../Core/Portable/xlf/FeaturesResources.fr.xlf | 5 +++++ .../Core/Portable/xlf/FeaturesResources.it.xlf | 5 +++++ .../Core/Portable/xlf/FeaturesResources.ja.xlf | 5 +++++ .../Core/Portable/xlf/FeaturesResources.ko.xlf | 5 +++++ .../Core/Portable/xlf/FeaturesResources.pl.xlf | 5 +++++ .../Core/Portable/xlf/FeaturesResources.pt-BR.xlf | 5 +++++ .../Core/Portable/xlf/FeaturesResources.ru.xlf | 5 +++++ .../Core/Portable/xlf/FeaturesResources.tr.xlf | 5 +++++ .../Core/Portable/xlf/FeaturesResources.zh-Hans.xlf | 5 +++++ .../Core/Portable/xlf/FeaturesResources.zh-Hant.xlf | 5 +++++ 16 files changed, 85 insertions(+), 6 deletions(-) diff --git a/src/Features/Core/Portable/Ambiguity/AbstractAmbiguousTypeCodeFixProvider.cs b/src/Features/Core/Portable/Ambiguity/AbstractAmbiguousTypeCodeFixProvider.cs index a9d56215f9d3c..8ac92ac0e2fc9 100644 --- a/src/Features/Core/Portable/Ambiguity/AbstractAmbiguousTypeCodeFixProvider.cs +++ b/src/Features/Core/Portable/Ambiguity/AbstractAmbiguousTypeCodeFixProvider.cs @@ -24,15 +24,14 @@ public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context) { var cancellationToken = context.CancellationToken; var document = context.Document; - var span = context.Span; - var diagnostic = context.Diagnostics.First(); var syntaxFacts = document.GetLanguageService(); var addImportService = document.GetLanguageService(); + var diagnostic = context.Diagnostics.First(); var optionSet = await document.GetOptionsAsync(cancellationToken).ConfigureAwait(false); var placeSystemNamespaceFirst = optionSet.GetOption(GenerationOptions.PlaceSystemNamespaceFirst, document.Project.Language); var root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false); - var diagnosticNode = root.FindNode(span); + var diagnosticNode = root.FindNode(context.Span); var semanticModel = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false); var symbolInfo = semanticModel.GetSymbolInfo(diagnosticNode, cancellationToken); if (SymbolInfoContainesSupportedSymbols(symbolInfo)) @@ -47,7 +46,8 @@ public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context) codeActionsBuilder.Add(new MyCodeAction(codeActionPreviewText, c => Task.FromResult(document.WithSyntaxRoot(newRoot)))); } - var groupedCodeAction = new GroupingCodeAction("Test", codeActionsBuilder.ToImmutable()); + var groupedTitle = string.Format(FeaturesResources.Alias_ambiguous_type_0, typeName); + var groupedCodeAction = new GroupingCodeAction(groupedTitle, codeActionsBuilder.ToImmutable()); context.RegisterCodeFix(groupedCodeAction, diagnostic); } } diff --git a/src/Features/Core/Portable/FeaturesResources.Designer.cs b/src/Features/Core/Portable/FeaturesResources.Designer.cs index d9ef20dfe9613..a9dfa13555d32 100644 --- a/src/Features/Core/Portable/FeaturesResources.Designer.cs +++ b/src/Features/Core/Portable/FeaturesResources.Designer.cs @@ -427,7 +427,18 @@ internal static string Adding_an_imported_method_will_prevent_the_debug_session_ return ResourceManager.GetString("Adding_an_imported_method_will_prevent_the_debug_session_from_continuing", resourceCulture); } } - + + /// + /// Looks up a localized string similar to Alias ambiguous type '{0}'. + /// + internal static string Alias_ambiguous_type_0 + { + get + { + return ResourceManager.GetString("Alias_ambiguous_type_0", resourceCulture); + } + } + /// /// Looks up a localized string similar to All lowercase. /// diff --git a/src/Features/Core/Portable/FeaturesResources.resx b/src/Features/Core/Portable/FeaturesResources.resx index fad430187ade4..e148e16dd490b 100644 --- a/src/Features/Core/Portable/FeaturesResources.resx +++ b/src/Features/Core/Portable/FeaturesResources.resx @@ -1334,4 +1334,7 @@ This version used in: {2} indexer - + + Alias ambiguous type '{0}' + + \ No newline at end of file diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.cs.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.cs.xlf index ce692b345bb3d..e4f0368fe4208 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.cs.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.cs.xlf @@ -1985,6 +1985,11 @@ Tato verze se používá zde: {2}. indexer + + Alias ambiguous type '{0}' + Alias ambiguous type '{0}' + + \ No newline at end of file diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.de.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.de.xlf index a41c19128859a..b0a13cc7e17c3 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.de.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.de.xlf @@ -1985,6 +1985,11 @@ Diese Version wird verwendet in: {2} Indexer + + Alias ambiguous type '{0}' + Alias ambiguous type '{0}' + + \ No newline at end of file diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.es.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.es.xlf index bf0c74f4c7f08..53ec539543884 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.es.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.es.xlf @@ -1985,6 +1985,11 @@ Esta versión se utiliza en: {2} indizador + + Alias ambiguous type '{0}' + Alias ambiguous type '{0}' + + \ No newline at end of file diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.fr.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.fr.xlf index 6f66751514f7d..fb2d4683760be 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.fr.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.fr.xlf @@ -1985,6 +1985,11 @@ Version utilisée dans : {2} indexeur + + Alias ambiguous type '{0}' + Alias ambiguous type '{0}' + + \ No newline at end of file diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.it.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.it.xlf index 896660b25cfdc..aef086e36e5db 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.it.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.it.xlf @@ -1985,6 +1985,11 @@ Questa versione è usata {2} indicizzatore + + Alias ambiguous type '{0}' + Alias ambiguous type '{0}' + + \ No newline at end of file diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.ja.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.ja.xlf index de47819d3ac2d..ff90d419baedd 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.ja.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.ja.xlf @@ -1985,6 +1985,11 @@ This version used in: {2} インデクサー + + Alias ambiguous type '{0}' + Alias ambiguous type '{0}' + + \ No newline at end of file diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.ko.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.ko.xlf index 3700ea5337381..40db56ff6abe3 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.ko.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.ko.xlf @@ -1985,6 +1985,11 @@ This version used in: {2} 인덱서 + + Alias ambiguous type '{0}' + Alias ambiguous type '{0}' + + \ No newline at end of file diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.pl.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.pl.xlf index 3b335ae2259a2..2ebc7240b1f17 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.pl.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.pl.xlf @@ -1985,6 +1985,11 @@ Ta wersja jest używana wersja: {2} indeksator + + Alias ambiguous type '{0}' + Alias ambiguous type '{0}' + + \ No newline at end of file diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.pt-BR.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.pt-BR.xlf index fb1cc03aa5068..ea5a29f1b2fdc 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.pt-BR.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.pt-BR.xlf @@ -1985,6 +1985,11 @@ Essa versão é usada no: {2} indexador + + Alias ambiguous type '{0}' + Alias ambiguous type '{0}' + + \ No newline at end of file diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.ru.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.ru.xlf index 88dda948826e2..c5a7810fbd763 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.ru.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.ru.xlf @@ -1985,6 +1985,11 @@ This version used in: {2} индексатор + + Alias ambiguous type '{0}' + Alias ambiguous type '{0}' + + \ No newline at end of file diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.tr.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.tr.xlf index 5405336f7ab0e..f53efeed7f71a 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.tr.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.tr.xlf @@ -1985,6 +1985,11 @@ Bu sürüm şurada kullanılır: {2} dizin oluşturucu + + Alias ambiguous type '{0}' + Alias ambiguous type '{0}' + + \ No newline at end of file diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.zh-Hans.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.zh-Hans.xlf index d19acae16896a..cd3c016bbea88 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.zh-Hans.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.zh-Hans.xlf @@ -1985,6 +1985,11 @@ This version used in: {2} 索引器 + + Alias ambiguous type '{0}' + Alias ambiguous type '{0}' + + \ No newline at end of file diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.zh-Hant.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.zh-Hant.xlf index a1cb5a656e69f..84a91253099af 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.zh-Hant.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.zh-Hant.xlf @@ -1985,6 +1985,11 @@ This version used in: {2} 索引子 + + Alias ambiguous type '{0}' + Alias ambiguous type '{0}' + + \ No newline at end of file From 63d1741c5466907a975a2c5bae4f964e4dfba3a0 Mon Sep 17 00:00:00 2001 From: Martin Strecker Date: Tue, 2 Jan 2018 21:53:45 +0100 Subject: [PATCH 10/26] Reverted unwanted launchSettings.json commit. --- .../Test/Semantic/Properties/launchSettings.json | 7 +------ .../CSharpTest/Properties/launchSettings.json | 7 +------ .../Test2/My Project/launchSettings.json | 7 +------ .../VisualBasicTest/My Project/launchSettings.json | 7 +------ .../AbstractAmbiguousTypeCodeFixProvider.cs | 0 .../VisualBasicAmbiguousTypeCodeFixProvider.vb | 12 ++++-------- .../CSharpTest/Properties/launchSettings.json | 7 +------ 7 files changed, 9 insertions(+), 38 deletions(-) rename src/Features/Core/Portable/{Ambiguity => AmbiguityCodeFixProvider}/AbstractAmbiguousTypeCodeFixProvider.cs (100%) diff --git a/src/Compilers/CSharp/Test/Semantic/Properties/launchSettings.json b/src/Compilers/CSharp/Test/Semantic/Properties/launchSettings.json index 7ae7003d107af..f84436625a97d 100644 --- a/src/Compilers/CSharp/Test/Semantic/Properties/launchSettings.json +++ b/src/Compilers/CSharp/Test/Semantic/Properties/launchSettings.json @@ -7,11 +7,6 @@ "xUnit.net WPF Runner": { "executablePath": "$(XUnitWpfPath)", "commandLineArgs": "$(XUnitWpfArguments)" - }, - "Run Method": { - "commandName": "Executable", - "executablePath": "C:\\Users\\MartinStrecker\\.nuget\\packages\\xunit.runner.console\\2.2.0-beta4-build3444\\tools\\xunit.console.x86.exe", - "commandLineArgs": "\"C:\\IFP\\OS\\roslyn\\Binaries\\Debug\\UnitTests\\CSharpCompilerSemanticTest\\UnitTests\\CSharpCompilerSemanticTest\\Roslyn.Compilers.CSharp.Semantic.UnitTests.dll\" -wait -method Microsoft.CodeAnalysis.CSharp.UnitTests.QueryTests.FromOrderBySelectQueryTranslation" } } -} \ No newline at end of file +} diff --git a/src/EditorFeatures/CSharpTest/Properties/launchSettings.json b/src/EditorFeatures/CSharpTest/Properties/launchSettings.json index 52e3082a55b25..f84436625a97d 100644 --- a/src/EditorFeatures/CSharpTest/Properties/launchSettings.json +++ b/src/EditorFeatures/CSharpTest/Properties/launchSettings.json @@ -7,11 +7,6 @@ "xUnit.net WPF Runner": { "executablePath": "$(XUnitWpfPath)", "commandLineArgs": "$(XUnitWpfArguments)" - }, - "RunTestMethod": { - "commandName": "Executable", - "executablePath": "C:\\Users\\MartinStrecker\\.nuget\\packages\\xunit.runner.console\\2.2.0-beta4-build3444\\tools\\xunit.console.x86.exe", - "commandLineArgs": "C:\\IFP\\OS\\roslyn\\Binaries\\Debug\\UnitTests\\CSharpEditorServicesTest\\Roslyn.Services.Editor.CSharp.UnitTests.dll -wait -trait \"Feature=CodeActions.AliasType\"" } } -} \ No newline at end of file +} diff --git a/src/EditorFeatures/Test2/My Project/launchSettings.json b/src/EditorFeatures/Test2/My Project/launchSettings.json index 0fed0ab96fb50..f84436625a97d 100644 --- a/src/EditorFeatures/Test2/My Project/launchSettings.json +++ b/src/EditorFeatures/Test2/My Project/launchSettings.json @@ -7,11 +7,6 @@ "xUnit.net WPF Runner": { "executablePath": "$(XUnitWpfPath)", "commandLineArgs": "$(XUnitWpfArguments)" - }, - "TestMethod": { - "commandName": "Executable", - "executablePath": "C:\\Users\\MartinStrecker\\.nuget\\packages\\xunit.runner.console\\2.2.0\\tools\\xunit.console.x86.exe", - "commandLineArgs": "C:\\IFP\\OS\\roslyn\\Binaries\\Debug\\UnitTests\\EditorServicesTest2\\Roslyn.Services.Editor2.UnitTests.dll -wait -trait Feature=GoToDefinition" } } -} \ No newline at end of file +} diff --git a/src/EditorFeatures/VisualBasicTest/My Project/launchSettings.json b/src/EditorFeatures/VisualBasicTest/My Project/launchSettings.json index 9dc9c8a8b4708..f84436625a97d 100644 --- a/src/EditorFeatures/VisualBasicTest/My Project/launchSettings.json +++ b/src/EditorFeatures/VisualBasicTest/My Project/launchSettings.json @@ -7,11 +7,6 @@ "xUnit.net WPF Runner": { "executablePath": "$(XUnitWpfPath)", "commandLineArgs": "$(XUnitWpfArguments)" - }, - "RunVBtest": { - "commandName": "Executable", - "executablePath": "C:\\Users\\MartinStrecker\\.nuget\\packages\\xunit.runner.console\\2.2.0-beta4-build3444\\tools\\xunit.console.x86.exe", - "commandLineArgs": "C:\\IFP\\OS\\roslyn\\Binaries\\Debug\\UnitTests\\BasicEditorServicesTest\\Roslyn.Services.Editor.VisualBasic.UnitTests.dll -wait -trait \"Feature=CodeActions.AliasType1\"" } } -} \ No newline at end of file +} diff --git a/src/Features/Core/Portable/Ambiguity/AbstractAmbiguousTypeCodeFixProvider.cs b/src/Features/Core/Portable/AmbiguityCodeFixProvider/AbstractAmbiguousTypeCodeFixProvider.cs similarity index 100% rename from src/Features/Core/Portable/Ambiguity/AbstractAmbiguousTypeCodeFixProvider.cs rename to src/Features/Core/Portable/AmbiguityCodeFixProvider/AbstractAmbiguousTypeCodeFixProvider.cs diff --git a/src/Features/VisualBasic/Portable/Ambiguity/VisualBasicAmbiguousTypeCodeFixProvider.vb b/src/Features/VisualBasic/Portable/Ambiguity/VisualBasicAmbiguousTypeCodeFixProvider.vb index 0530576c00d11..6a8f2f2e4910b 100644 --- a/src/Features/VisualBasic/Portable/Ambiguity/VisualBasicAmbiguousTypeCodeFixProvider.vb +++ b/src/Features/VisualBasic/Portable/Ambiguity/VisualBasicAmbiguousTypeCodeFixProvider.vb @@ -22,14 +22,10 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Ambiguity End Property Protected Overrides Function GetAliasDirective(ByVal typeName As String, ByVal symbol As ISymbol) As SyntaxNode - Return SyntaxFactory.ImportsStatement( - SyntaxFactory.SeparatedList(Of ImportsClauseSyntax).Add( - SyntaxFactory.SimpleImportsClause( - SyntaxFactory.ImportAliasClause(typeName), - SyntaxFactory.IdentifierName(symbol.ToNameDisplayString()) - ) - ) - ) + Return SyntaxFactory.ImportsStatement(SyntaxFactory.SeparatedList(Of ImportsClauseSyntax).Add( + SyntaxFactory.SimpleImportsClause( + SyntaxFactory.ImportAliasClause(typeName), + SyntaxFactory.IdentifierName(symbol.ToNameDisplayString())))) End Function End Class End Namespace diff --git a/src/Workspaces/CSharpTest/Properties/launchSettings.json b/src/Workspaces/CSharpTest/Properties/launchSettings.json index 676038c8a5733..f84436625a97d 100644 --- a/src/Workspaces/CSharpTest/Properties/launchSettings.json +++ b/src/Workspaces/CSharpTest/Properties/launchSettings.json @@ -7,11 +7,6 @@ "xUnit.net WPF Runner": { "executablePath": "$(XUnitWpfPath)", "commandLineArgs": "$(XUnitWpfArguments)" - }, - "Test": { - "commandName": "Executable", - "executablePath": "C:\\Users\\MartinStrecker\\.nuget\\packages\\xunit.runner.console\\2.2.0-beta4-build3444\\tools\\xunit.console.x86.exe", - "commandLineArgs": "C:\\IFP\\OS\\roslyn\\Binaries\\Debug\\UnitTests\\CSharpServicesTest\\Roslyn.Services.CSharp.UnitTests.dll -wait -class Microsoft.CodeAnalysis.CSharp.UnitTests.Formatting.FormattingEngineTests" } } -} \ No newline at end of file +} From b7d2de0d0a398ce41018ece366ec76ea0a1281bd Mon Sep 17 00:00:00 2001 From: Martin Strecker Date: Wed, 3 Jan 2018 00:42:32 +0100 Subject: [PATCH 11/26] Delayed some initializations. --- .../AbstractAmbiguousTypeCodeFixProvider.cs | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/src/Features/Core/Portable/AmbiguityCodeFixProvider/AbstractAmbiguousTypeCodeFixProvider.cs b/src/Features/Core/Portable/AmbiguityCodeFixProvider/AbstractAmbiguousTypeCodeFixProvider.cs index 8ac92ac0e2fc9..e632b1517382c 100644 --- a/src/Features/Core/Portable/AmbiguityCodeFixProvider/AbstractAmbiguousTypeCodeFixProvider.cs +++ b/src/Features/Core/Portable/AmbiguityCodeFixProvider/AbstractAmbiguousTypeCodeFixProvider.cs @@ -24,11 +24,6 @@ public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context) { var cancellationToken = context.CancellationToken; var document = context.Document; - var syntaxFacts = document.GetLanguageService(); - var addImportService = document.GetLanguageService(); - var diagnostic = context.Diagnostics.First(); - var optionSet = await document.GetOptionsAsync(cancellationToken).ConfigureAwait(false); - var placeSystemNamespaceFirst = optionSet.GetOption(GenerationOptions.PlaceSystemNamespaceFirst, document.Project.Language); var root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false); var diagnosticNode = root.FindNode(context.Span); @@ -36,6 +31,9 @@ public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context) var symbolInfo = semanticModel.GetSymbolInfo(diagnosticNode, cancellationToken); if (SymbolInfoContainesSupportedSymbols(symbolInfo)) { + var syntaxFacts = document.GetLanguageService(); + var addImportService = document.GetLanguageService(); + var placeSystemNamespaceFirst = await GetPlaceSystemNamespaceFirstOptionAsync(document, cancellationToken).ConfigureAwait(false); var codeActionsBuilder = ImmutableArray.CreateBuilder(symbolInfo.CandidateSymbols.Length); var typeName = GetAliasFromDiagnsoticNode(syntaxFacts, diagnosticNode); foreach (var symbol in symbolInfo.CandidateSymbols) @@ -48,10 +46,18 @@ public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context) } var groupedTitle = string.Format(FeaturesResources.Alias_ambiguous_type_0, typeName); var groupedCodeAction = new GroupingCodeAction(groupedTitle, codeActionsBuilder.ToImmutable()); + var diagnostic = context.Diagnostics.First(); context.RegisterCodeFix(groupedCodeAction, diagnostic); } } + private static async Task GetPlaceSystemNamespaceFirstOptionAsync(Document document, CancellationToken cancellationToken) + { + var optionSet = await document.GetOptionsAsync(cancellationToken).ConfigureAwait(false); + var placeSystemNamespaceFirst = optionSet.GetOption(GenerationOptions.PlaceSystemNamespaceFirst, document.Project.Language); + return placeSystemNamespaceFirst; + } + private static string GetTextPreviewOfChange(SyntaxNode newNode, Workspace workspace) => Formatter.Format(newNode, workspace).ToFullString(); From b9a1c961e3180ddbeca66fd366f9566be5081081 Mon Sep 17 00:00:00 2001 From: Martin Strecker Date: Wed, 3 Jan 2018 20:10:02 +0100 Subject: [PATCH 12/26] Some re-factorings and more C# tests. --- .../Ambiguity/AmbiguousTypeTests.cs | 275 +++++++++++++++++- .../CSharpAmbiguousTypeCodeFixProvider.cs | 2 +- .../AbstractAmbiguousTypeCodeFixProvider.cs | 65 ++--- 3 files changed, 305 insertions(+), 37 deletions(-) rename src/Features/CSharp/Portable/{AmbiguityCodeFixProvider => Ambiguity}/CSharpAmbiguousTypeCodeFixProvider.cs (95%) rename src/Features/Core/Portable/{AmbiguityCodeFixProvider => Ambiguity}/AbstractAmbiguousTypeCodeFixProvider.cs (56%) diff --git a/src/EditorFeatures/CSharpTest/Ambiguity/AmbiguousTypeTests.cs b/src/EditorFeatures/CSharpTest/Ambiguity/AmbiguousTypeTests.cs index e32f9ec2aa9a9..27b84df13b517 100644 --- a/src/EditorFeatures/CSharpTest/Ambiguity/AmbiguousTypeTests.cs +++ b/src/EditorFeatures/CSharpTest/Ambiguity/AmbiguousTypeTests.cs @@ -2,7 +2,7 @@ using System.Threading.Tasks; using Microsoft.CodeAnalysis.CodeFixes; -using Microsoft.CodeAnalysis.CSharp.AmbiguityCodeFixProvider; +using Microsoft.CodeAnalysis.CSharp.Ambiguity; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.Diagnostics; using Roslyn.Test.Utilities; @@ -148,7 +148,8 @@ class C public async Task TestNamespaceAndTypenameIdenticalOffersNoDiagnostics() { // This gives CS0433: The type 'Ambiguous' exists in both 'Assembly1' and 'Assembly2' - // Couldn't get a CS0104 in this situation. Keep the test anyway if someone finds a way to force CS0104 here. + // Couldn't get a CS0104 in this situation. Keep the test anyway if someone finds a way to force CS0104 here + // or CS0433 is added as a supported diagnostic for this fix. await TestMissingAsync(@" @@ -250,5 +251,275 @@ static void Main(string[] args) await TestInRegularAndScriptAsync(initialMarkup, expectedMarkup.Replace("#", "using Nested = Static.Nested;"), 0); await TestInRegularAndScriptAsync(initialMarkup, expectedMarkup.Replace("#", "using Nested = Static.Nested;"), 1); } + + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsAliasType + "1")] + public async Task TestAmbiguousClassDiagnosedAtBaseList() + { + var classDef = GetAmbiguousDefinition(@"public class AmbiguousClass { }"); + var initialMarkup = @" +using N1; +using N2; +" + classDef + @" +namespace NTest +{ + public class Test : [|AmbiguousClass|] { } +} +"; + var expectedMarkup = @" +using N1; +using N2; +using AmbiguousClass = N1.AmbiguousClass; +" + classDef + @" +namespace NTest +{ + public class Test : AmbiguousClass { } +} +"; + await TestInRegularAndScriptAsync(initialMarkup, expectedMarkup); + } + + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsAliasType + "1")] + public async Task TestAmbiguousClassDiagnosedAtTypeConstraint() + { + var classDef = GetAmbiguousDefinition(@"public class AmbiguousClass { }"); + var initialMarkup = @" +using N1; +using N2; +" + classDef + @" +namespace NTest +{ + public class Test where T : [|AmbiguousClass|] { } +} +"; + var expectedMarkup = @" +using N1; +using N2; +using AmbiguousClass = N1.AmbiguousClass; +" + classDef + @" +namespace NTest +{ + public class Test where T : AmbiguousClass { } +} +"; + await TestInRegularAndScriptAsync(initialMarkup, expectedMarkup); + } + + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsAliasType + "1")] + public async Task TestAmbiguousEnumDiagnosedAtFieldDeclaration() + { + var enumDef = GetAmbiguousDefinition(@"public enum AmbiguousEnum { }"); + var initialMarkup = @" +using N1; +using N2; +" + enumDef + @" +namespace NTest +{ + public class Test + { + private [|AmbiguousEnum|] _AmbiguousEnum; + } +} +"; + var expectedMarkup = @" +using N1; +using N2; +using AmbiguousEnum = N1.AmbiguousEnum; +" + enumDef + @" +namespace NTest +{ + public class Test + { + private AmbiguousEnum _AmbiguousEnum; + } +} +"; + await TestInRegularAndScriptAsync(initialMarkup, expectedMarkup); + } + + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsAliasType + "1")] + public async Task TestAmbiguousStructDiagnosedAtPropertyDeclaration() + { + var strcutDef = GetAmbiguousDefinition(@"public struct AmbiguousStruct { }"); + var initialMarkup = @" +using N1; +using N2; +" + strcutDef + @" +namespace NTest +{ + public class Test + { + public [|AmbiguousStruct|] AmbiguousStruct { get; } + } +} +"; + var expectedMarkup = @" +using N1; +using N2; +using AmbiguousStruct = N1.AmbiguousStruct; +" + strcutDef + @" +namespace NTest +{ + public class Test + { + public AmbiguousStruct AmbiguousStruct { get; } + } +} +"; + await TestInRegularAndScriptAsync(initialMarkup, expectedMarkup); + } + + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsAliasType + "1")] + public async Task TestAmbiguousClassDiagnosedAtTypeArgument() + { + var classDef = GetAmbiguousDefinition(@"public class AmbiguousClass { }"); + var initialMarkup = @" +using N1; +using N2; +" + classDef + @" +namespace NTest +{ + public class Test + { + public void M() + { + var list = new System.Collections.Generic.List<[|AmbiguousClass|]> { new AmbiguousClass() }; + } + } +} +"; + var expectedMarkup = @" +using N1; +using N2; +using AmbiguousClass = N1.AmbiguousClass; +" + classDef + @" +namespace NTest +{ + public class Test + { + public void M() + { + var list = new System.Collections.Generic.List { new AmbiguousClass() }; + } + } +} +"; + await TestInRegularAndScriptAsync(initialMarkup, expectedMarkup); + } + + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsAliasType + "1")] + public async Task TestAmbiguousClassDiagnosedAtIdentifierOfIncompleteExpression() + { + var classDef = GetAmbiguousDefinition(@"public class AmbiguousClass { }"); + var initialMarkup = @" +using N1; +using N2; +" + classDef + @" +namespace NTest +{ + public class Test + { + public void M() + { + [|AmbiguousClass|] + } + } +} +"; + var expectedMarkup = @" +using N1; +using N2; +using AmbiguousClass = N1.AmbiguousClass; +" + classDef + @" +namespace NTest +{ + public class Test + { + public void M() + { + AmbiguousClass + } + } +} +"; + await TestInRegularAndScriptAsync(initialMarkup, expectedMarkup); + } + + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsAliasType + "1")] + public async Task TestAmbiguousClassDiagnosedAtMethodParameter() + { + var classDef = GetAmbiguousDefinition(@"public class AmbiguousClass { }"); + var initialMarkup = @" +using N1; +using N2; +" + classDef + @" +namespace NTest +{ + public class Test + { + public void M([|AmbiguousClass|] a) + { + } + } +} +"; + var expectedMarkup = @" +using N1; +using N2; +using AmbiguousClass = N1.AmbiguousClass; +" + classDef + @" +namespace NTest +{ + public class Test + { + public void M(AmbiguousClass a) + { + } + } +} +"; + await TestInRegularAndScriptAsync(initialMarkup, expectedMarkup); + } + + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsAliasType + "1")] + public async Task TestAmbiguousClassDiagnosedAtFromClauseTypeIdentifier() + { + var classDef = GetAmbiguousDefinition(@"public class AmbiguousClass { }"); + var initialMarkup = @" +using N1; +using N2; +using System.Linq; +" + classDef + @" +namespace NTest +{ + public class Test + { + public void M() + { + var qry = from [|AmbiguousClass|] a in new object[] { } + select a; + } + } +} +"; + var expectedMarkup = @" +using N1; +using N2; +using System.Linq; +using AmbiguousClass = N1.AmbiguousClass; +" + classDef + @" +namespace NTest +{ + public class Test + { + public void M() + { + var qry = from AmbiguousClass a in new object[] { } + select a; + } + } +} +"; + await TestInRegularAndScriptAsync(initialMarkup, expectedMarkup); + } } } diff --git a/src/Features/CSharp/Portable/AmbiguityCodeFixProvider/CSharpAmbiguousTypeCodeFixProvider.cs b/src/Features/CSharp/Portable/Ambiguity/CSharpAmbiguousTypeCodeFixProvider.cs similarity index 95% rename from src/Features/CSharp/Portable/AmbiguityCodeFixProvider/CSharpAmbiguousTypeCodeFixProvider.cs rename to src/Features/CSharp/Portable/Ambiguity/CSharpAmbiguousTypeCodeFixProvider.cs index 7b1a812a94f33..fdd4067c75917 100644 --- a/src/Features/CSharp/Portable/AmbiguityCodeFixProvider/CSharpAmbiguousTypeCodeFixProvider.cs +++ b/src/Features/CSharp/Portable/Ambiguity/CSharpAmbiguousTypeCodeFixProvider.cs @@ -11,7 +11,7 @@ using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Shared.Extensions; -namespace Microsoft.CodeAnalysis.CSharp.AmbiguityCodeFixProvider +namespace Microsoft.CodeAnalysis.CSharp.Ambiguity { [ExportCodeFixProvider(LanguageNames.CSharp, Name = PredefinedCodeFixProviderNames.AliasType), Shared] [ExtensionOrder(After = PredefinedCodeFixProviderNames.FullyQualify)] diff --git a/src/Features/Core/Portable/AmbiguityCodeFixProvider/AbstractAmbiguousTypeCodeFixProvider.cs b/src/Features/Core/Portable/Ambiguity/AbstractAmbiguousTypeCodeFixProvider.cs similarity index 56% rename from src/Features/Core/Portable/AmbiguityCodeFixProvider/AbstractAmbiguousTypeCodeFixProvider.cs rename to src/Features/Core/Portable/Ambiguity/AbstractAmbiguousTypeCodeFixProvider.cs index e632b1517382c..9f67621527571 100644 --- a/src/Features/Core/Portable/AmbiguityCodeFixProvider/AbstractAmbiguousTypeCodeFixProvider.cs +++ b/src/Features/Core/Portable/Ambiguity/AbstractAmbiguousTypeCodeFixProvider.cs @@ -11,6 +11,7 @@ using Microsoft.CodeAnalysis.Editing; using Microsoft.CodeAnalysis.Formatting; using Microsoft.CodeAnalysis.LanguageServices; +using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.Shared.Extensions; using static Microsoft.CodeAnalysis.CodeActions.CodeAction; @@ -24,49 +25,53 @@ public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context) { var cancellationToken = context.CancellationToken; var document = context.Document; - + var syntaxFacts = document.GetLanguageService(); var root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false); - var diagnosticNode = root.FindNode(context.Span); + + // Innermost: We are looking for an IdentifierName. IdentifierName is sometimes at the same span as its parent (e.g. SimpleBaseTypeSyntax). + var diagnosticNode = root.FindNode(context.Span, getInnermostNodeForTie: true); + if (!syntaxFacts.IsIdentifierName(diagnosticNode)) + { + return; + } + var semanticModel = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false); var symbolInfo = semanticModel.GetSymbolInfo(diagnosticNode, cancellationToken); - if (SymbolInfoContainesSupportedSymbols(symbolInfo)) + if (SymbolCandidatesContainsSupportedSymbols(symbolInfo)) { - var syntaxFacts = document.GetLanguageService(); var addImportService = document.GetLanguageService(); - var placeSystemNamespaceFirst = await GetPlaceSystemNamespaceFirstOptionAsync(document, cancellationToken).ConfigureAwait(false); - var codeActionsBuilder = ImmutableArray.CreateBuilder(symbolInfo.CandidateSymbols.Length); - var typeName = GetAliasFromDiagnsoticNode(syntaxFacts, diagnosticNode); + var diagnostic = context.Diagnostics.First(); + var compilation = semanticModel.Compilation; + var optionSet = await document.GetOptionsAsync(cancellationToken).ConfigureAwait(false); + var placeSystemNamespaceFirst = optionSet.GetOption(GenerationOptions.PlaceSystemNamespaceFirst, document.Project.Language); + var typeName = GetAliasFromDiagnosticNode(syntaxFacts, diagnosticNode); foreach (var symbol in symbolInfo.CandidateSymbols) { var aliasDirective = GetAliasDirective(typeName, symbol); - var newRoot = addImportService.AddImport(semanticModel.Compilation, root, diagnosticNode, aliasDirective, placeSystemNamespaceFirst); - var codeActionPreviewText = GetTextPreviewOfChange(aliasDirective, document.Project.Solution.Workspace); - codeActionsBuilder.Add(new MyCodeAction(codeActionPreviewText, - c => Task.FromResult(document.WithSyntaxRoot(newRoot)))); + var codeActionPreviewText = await GetTextPreviewOfChangeAsync(aliasDirective, + document.Project.Solution.Workspace, + optionSet, + cancellationToken).ConfigureAwait(false); + var newRoot = addImportService.AddImport(compilation, root, diagnosticNode, aliasDirective, placeSystemNamespaceFirst); + var codeAction = new MyCodeAction(codeActionPreviewText, c => Task.FromResult(document.WithSyntaxRoot(newRoot))); + context.RegisterCodeFix(codeAction, context.Diagnostics.First()); } - var groupedTitle = string.Format(FeaturesResources.Alias_ambiguous_type_0, typeName); - var groupedCodeAction = new GroupingCodeAction(groupedTitle, codeActionsBuilder.ToImmutable()); - var diagnostic = context.Diagnostics.First(); - context.RegisterCodeFix(groupedCodeAction, diagnostic); } } - private static async Task GetPlaceSystemNamespaceFirstOptionAsync(Document document, CancellationToken cancellationToken) + private static async Task GetTextPreviewOfChangeAsync(SyntaxNode newNode, Workspace workspace, OptionSet optionSet, CancellationToken cancellationToken) { - var optionSet = await document.GetOptionsAsync(cancellationToken).ConfigureAwait(false); - var placeSystemNamespaceFirst = optionSet.GetOption(GenerationOptions.PlaceSystemNamespaceFirst, document.Project.Language); - return placeSystemNamespaceFirst; + var formatedNode = await Formatter.FormatAsync(newNode, workspace, optionSet, cancellationToken).ConfigureAwait(false); + var formatedText = formatedNode.ToFullString(); + return string.Format(FeaturesResources.Alias_ambiguous_type_0, formatedText); } - private static string GetTextPreviewOfChange(SyntaxNode newNode, Workspace workspace) - => Formatter.Format(newNode, workspace).ToFullString(); - - private static string GetAliasFromDiagnsoticNode(ISyntaxFactsService syntaxFacts, SyntaxNode diagnosticNode) + private static string GetAliasFromDiagnosticNode(ISyntaxFactsService syntaxFacts, SyntaxNode diagnosticNode) { // The content of the node is a good candidate for the alias // For attributes VB requires that the alias ends with 'Attribute' while C# is fine with or without the suffix. var nodeText = diagnosticNode.ToString(); - if (syntaxFacts.IsAttribute(diagnosticNode) || syntaxFacts.IsAttribute(diagnosticNode.Parent)) + if (syntaxFacts.IsAttribute(diagnosticNode.Parent)) { if (!nodeText.EndsWith("Attribute")) { @@ -77,9 +82,9 @@ private static string GetAliasFromDiagnsoticNode(ISyntaxFactsService syntaxFacts return nodeText; } - private bool SymbolInfoContainesSupportedSymbols(SymbolInfo symbolInfo) + private static bool SymbolCandidatesContainsSupportedSymbols(SymbolInfo symbolInfo) => symbolInfo.CandidateReason == CandidateReason.Ambiguous && - // Arity: Aliases can not name unbound generic types. Only closed constructed types can be aliased. + // Arity: Aliases can only name closed constructed types. // Aliasing as a closed constructed type is possible but would require to remove the type arguments from the diagnosed node. // It is unlikely that the user wants that and so generic types are not supported. // SymbolKind.NamedType: only types can be aliased by this fix. @@ -93,13 +98,5 @@ public MyCodeAction(string title, Func> create { } } - - private class GroupingCodeAction : CodeActionWithNestedActions - { - public GroupingCodeAction(string title, ImmutableArray nestedActions) - : base(title, nestedActions, isInlinable: true) - { - } - } } } From f5b3377fabf9d4ea78824ffa78d22cfb07aa10c6 Mon Sep 17 00:00:00 2001 From: Martin Strecker Date: Wed, 3 Jan 2018 20:20:36 +0100 Subject: [PATCH 13/26] Spelling corrections and improved naming clarity. --- .../Ambiguity/AmbiguousTypeTests.cs | 30 +++++++++---------- src/EditorFeatures/TestUtilities/Traits.cs | 2 +- .../Ambiguity/AmbiguousTypeTests.vb | 10 +++---- .../CSharpAmbiguousTypeCodeFixProvider.cs | 2 +- .../AbstractAmbiguousTypeCodeFixProvider.cs | 6 ++-- .../PredefinedCodeFixProviderNames.cs | 2 +- ...VisualBasicAmbiguousTypeCodeFixProvider.vb | 2 +- 7 files changed, 27 insertions(+), 27 deletions(-) diff --git a/src/EditorFeatures/CSharpTest/Ambiguity/AmbiguousTypeTests.cs b/src/EditorFeatures/CSharpTest/Ambiguity/AmbiguousTypeTests.cs index 27b84df13b517..5f5fb8517408f 100644 --- a/src/EditorFeatures/CSharpTest/Ambiguity/AmbiguousTypeTests.cs +++ b/src/EditorFeatures/CSharpTest/Ambiguity/AmbiguousTypeTests.cs @@ -26,7 +26,7 @@ namespace N2 { typeDefinion } }}"; - [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsAliasType)] + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsAliasAmbiguousType)] public async Task TestAmbiguousClassObjectCreationUsingsInNamespace() { var classDef = GetAmbiguousDefinition("public class Ambiguous { }"); @@ -62,7 +62,7 @@ void M() await TestInRegularAndScriptAsync(initialMarkup, expectedMarkupTemplate.Replace("#", "using Ambiguous = N2.Ambiguous;"), 1); } - [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsAliasType)] + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsAliasAmbiguousType)] public async Task TestAmbiguousClassObjectCreationUsingsInCompilationUnit() { var classDef = GetAmbiguousDefinition("public class Ambiguous { }"); @@ -96,7 +96,7 @@ void M() }"); } - [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsAliasType)] + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsAliasAmbiguousType)] public async Task TestAmbiguousClassObjectCreationGenericsDontOfferDiagnostic() { var genericAmbiguousClassDefinition = GetAmbiguousDefinition("public class Ambiguous { }"); @@ -116,7 +116,7 @@ void M() }"); } - [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsAliasType)] + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsAliasAmbiguousType)] public async Task TestAmbiguousAttribute() { var classDef = GetAmbiguousDefinition("public class AmbiguousAttribute: System.Attribute { }"); @@ -144,7 +144,7 @@ class C }"); } - [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsAliasType)] + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsAliasAmbiguousType)] public async Task TestNamespaceAndTypenameIdenticalOffersNoDiagnostics() { // This gives CS0433: The type 'Ambiguous' exists in both 'Assembly1' and 'Assembly2' @@ -192,7 +192,7 @@ void M() "); } - [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsAliasType)] + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsAliasAmbiguousType)] public async Task TestAmbiguousAliasNoDiagnostics() { await TestMissingAsync(@" @@ -204,7 +204,7 @@ class myClass : [|alias|]::Uri "); } - [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsAliasType)] + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsAliasAmbiguousType)] public async Task TestAmbiguousNestedClass() { var initialMarkup = @" @@ -252,7 +252,7 @@ static void Main(string[] args) await TestInRegularAndScriptAsync(initialMarkup, expectedMarkup.Replace("#", "using Nested = Static.Nested;"), 1); } - [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsAliasType + "1")] + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsAliasAmbiguousType + "1")] public async Task TestAmbiguousClassDiagnosedAtBaseList() { var classDef = GetAmbiguousDefinition(@"public class AmbiguousClass { }"); @@ -278,7 +278,7 @@ public class Test : AmbiguousClass { } await TestInRegularAndScriptAsync(initialMarkup, expectedMarkup); } - [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsAliasType + "1")] + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsAliasAmbiguousType + "1")] public async Task TestAmbiguousClassDiagnosedAtTypeConstraint() { var classDef = GetAmbiguousDefinition(@"public class AmbiguousClass { }"); @@ -304,7 +304,7 @@ public class Test where T : AmbiguousClass { } await TestInRegularAndScriptAsync(initialMarkup, expectedMarkup); } - [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsAliasType + "1")] + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsAliasAmbiguousType + "1")] public async Task TestAmbiguousEnumDiagnosedAtFieldDeclaration() { var enumDef = GetAmbiguousDefinition(@"public enum AmbiguousEnum { }"); @@ -336,7 +336,7 @@ public class Test await TestInRegularAndScriptAsync(initialMarkup, expectedMarkup); } - [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsAliasType + "1")] + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsAliasAmbiguousType + "1")] public async Task TestAmbiguousStructDiagnosedAtPropertyDeclaration() { var strcutDef = GetAmbiguousDefinition(@"public struct AmbiguousStruct { }"); @@ -368,7 +368,7 @@ public class Test await TestInRegularAndScriptAsync(initialMarkup, expectedMarkup); } - [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsAliasType + "1")] + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsAliasAmbiguousType + "1")] public async Task TestAmbiguousClassDiagnosedAtTypeArgument() { var classDef = GetAmbiguousDefinition(@"public class AmbiguousClass { }"); @@ -406,7 +406,7 @@ public void M() await TestInRegularAndScriptAsync(initialMarkup, expectedMarkup); } - [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsAliasType + "1")] + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsAliasAmbiguousType + "1")] public async Task TestAmbiguousClassDiagnosedAtIdentifierOfIncompleteExpression() { var classDef = GetAmbiguousDefinition(@"public class AmbiguousClass { }"); @@ -444,7 +444,7 @@ public void M() await TestInRegularAndScriptAsync(initialMarkup, expectedMarkup); } - [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsAliasType + "1")] + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsAliasAmbiguousType + "1")] public async Task TestAmbiguousClassDiagnosedAtMethodParameter() { var classDef = GetAmbiguousDefinition(@"public class AmbiguousClass { }"); @@ -480,7 +480,7 @@ public void M(AmbiguousClass a) await TestInRegularAndScriptAsync(initialMarkup, expectedMarkup); } - [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsAliasType + "1")] + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsAliasAmbiguousType + "1")] public async Task TestAmbiguousClassDiagnosedAtFromClauseTypeIdentifier() { var classDef = GetAmbiguousDefinition(@"public class AmbiguousClass { }"); diff --git a/src/EditorFeatures/TestUtilities/Traits.cs b/src/EditorFeatures/TestUtilities/Traits.cs index e8b780c60dd0a..ff0a224dac19b 100644 --- a/src/EditorFeatures/TestUtilities/Traits.cs +++ b/src/EditorFeatures/TestUtilities/Traits.cs @@ -38,7 +38,7 @@ public static class Features public const string CodeActionsAddImport = "CodeActions.AddImport"; public const string CodeActionsAddMissingReference = "CodeActions.AddMissingReference"; public const string CodeActionsAddParameter = "CodeActions.AddParameter"; - public const string CodeActionsAliasType = "CodeActions.AliasType"; + public const string CodeActionsAliasAmbiguousType = "CodeActions.AliasAmbiguousType"; public const string CodeActionsChangeToAsync = "CodeActions.ChangeToAsync"; public const string CodeActionsChangeToIEnumerable = "CodeActions.ChangeToIEnumerable"; public const string CodeActionsChangeToYield = "CodeActions.ChangeToYield"; diff --git a/src/EditorFeatures/VisualBasicTest/Diagnostics/Ambiguity/AmbiguousTypeTests.vb b/src/EditorFeatures/VisualBasicTest/Diagnostics/Ambiguity/AmbiguousTypeTests.vb index 9b9570d9b0c92..e578aca2347e2 100644 --- a/src/EditorFeatures/VisualBasicTest/Diagnostics/Ambiguity/AmbiguousTypeTests.vb +++ b/src/EditorFeatures/VisualBasicTest/Diagnostics/Ambiguity/AmbiguousTypeTests.vb @@ -22,7 +22,7 @@ Namespace N2 End Namespace" End Function - + Public Async Function TestAmbiguousClassObjectCreationGlobalImports() As Task Dim classDef = GetAmbiguousDefinition(" Public Class Ambiguous @@ -56,7 +56,7 @@ End Namespace" Await TestInRegularAndScriptAsync(initialMarkup, expectedMarkupTemplate.Replace("#", "Imports Ambiguous = N2.Ambiguous"), 1) End Function - + Public Async Function TestAmbiguousAttribute() As Task Dim classDef = GetAmbiguousDefinition(" Class AAttribute @@ -83,7 +83,7 @@ End Class" Await TestInRegularAndScriptAsync(initialMarkup, expectedMarkupTemplate) End Function - + Public Async Function TestAmbiguousBug4817() As Task Dim initialMarkup = " Imports A @@ -106,7 +106,7 @@ End Module Await TestMissingAsync(initialMarkup) End Function - + Public Async Function TestAmbiguousClassInModule() As Task Dim initialMarkup = " Imports N1, N2 @@ -149,7 +149,7 @@ End Class Await TestInRegularAndScriptAsync(initialMarkup, expectedMarkup) End Function - + Public Async Function TestAmbiguousInterfaceNameReferencedInSmallCaps() As Task Dim initialMarkup = " Imports N1, N2 diff --git a/src/Features/CSharp/Portable/Ambiguity/CSharpAmbiguousTypeCodeFixProvider.cs b/src/Features/CSharp/Portable/Ambiguity/CSharpAmbiguousTypeCodeFixProvider.cs index fdd4067c75917..0ebaaed8f7954 100644 --- a/src/Features/CSharp/Portable/Ambiguity/CSharpAmbiguousTypeCodeFixProvider.cs +++ b/src/Features/CSharp/Portable/Ambiguity/CSharpAmbiguousTypeCodeFixProvider.cs @@ -13,7 +13,7 @@ namespace Microsoft.CodeAnalysis.CSharp.Ambiguity { - [ExportCodeFixProvider(LanguageNames.CSharp, Name = PredefinedCodeFixProviderNames.AliasType), Shared] + [ExportCodeFixProvider(LanguageNames.CSharp, Name = PredefinedCodeFixProviderNames.AliasAmbiguousType), Shared] [ExtensionOrder(After = PredefinedCodeFixProviderNames.FullyQualify)] internal class CSharpAmbiguousTypeCodeFixProvider : AbstractAmbiguousTypeCodeFixProvider { diff --git a/src/Features/Core/Portable/Ambiguity/AbstractAmbiguousTypeCodeFixProvider.cs b/src/Features/Core/Portable/Ambiguity/AbstractAmbiguousTypeCodeFixProvider.cs index 9f67621527571..320a3b55f0ddb 100644 --- a/src/Features/Core/Portable/Ambiguity/AbstractAmbiguousTypeCodeFixProvider.cs +++ b/src/Features/Core/Portable/Ambiguity/AbstractAmbiguousTypeCodeFixProvider.cs @@ -61,9 +61,9 @@ public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context) private static async Task GetTextPreviewOfChangeAsync(SyntaxNode newNode, Workspace workspace, OptionSet optionSet, CancellationToken cancellationToken) { - var formatedNode = await Formatter.FormatAsync(newNode, workspace, optionSet, cancellationToken).ConfigureAwait(false); - var formatedText = formatedNode.ToFullString(); - return string.Format(FeaturesResources.Alias_ambiguous_type_0, formatedText); + var formattedNode = await Formatter.FormatAsync(newNode, workspace, optionSet, cancellationToken).ConfigureAwait(false); + var formattedText = formattedNode.ToFullString(); + return string.Format(FeaturesResources.Alias_ambiguous_type_0, formattedText); } private static string GetAliasFromDiagnosticNode(ISyntaxFactsService syntaxFacts, SyntaxNode diagnosticNode) diff --git a/src/Features/Core/Portable/CodeFixes/PredefinedCodeFixProviderNames.cs b/src/Features/Core/Portable/CodeFixes/PredefinedCodeFixProviderNames.cs index 94923a909dd80..5d2f2e4ee5a57 100644 --- a/src/Features/Core/Portable/CodeFixes/PredefinedCodeFixProviderNames.cs +++ b/src/Features/Core/Portable/CodeFixes/PredefinedCodeFixProviderNames.cs @@ -8,7 +8,7 @@ internal static class PredefinedCodeFixProviderNames public const string AddAwait = nameof(AddAwait); public const string AddAsync = nameof(AddAsync); public const string AddParameter = nameof(AddParameter); - public const string AliasType = nameof(AliasType); + public const string AliasAmbiguousType = nameof(AliasAmbiguousType); public const string ApplyNamingStyle = nameof(ApplyNamingStyle); public const string AddBraces = nameof(AddBraces); public const string ChangeReturnType = nameof(ChangeReturnType); diff --git a/src/Features/VisualBasic/Portable/Ambiguity/VisualBasicAmbiguousTypeCodeFixProvider.vb b/src/Features/VisualBasic/Portable/Ambiguity/VisualBasicAmbiguousTypeCodeFixProvider.vb index 6a8f2f2e4910b..b085a00fb5c84 100644 --- a/src/Features/VisualBasic/Portable/Ambiguity/VisualBasicAmbiguousTypeCodeFixProvider.vb +++ b/src/Features/VisualBasic/Portable/Ambiguity/VisualBasicAmbiguousTypeCodeFixProvider.vb @@ -7,7 +7,7 @@ Imports Microsoft.CodeAnalysis.CodeFixes Imports Microsoft.CodeAnalysis.VisualBasic.Syntax Namespace Microsoft.CodeAnalysis.VisualBasic.Ambiguity - + Friend Class VisualBasicAmbiguousTypeCodeFixProvider Inherits AbstractAmbiguousTypeCodeFixProvider From 737143e8f049133d68a8a94667671884a66d36b3 Mon Sep 17 00:00:00 2001 From: Martin Strecker Date: Wed, 3 Jan 2018 20:52:12 +0100 Subject: [PATCH 14/26] Removed useless comment. --- .../Portable/Ambiguity/AbstractAmbiguousTypeCodeFixProvider.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Features/Core/Portable/Ambiguity/AbstractAmbiguousTypeCodeFixProvider.cs b/src/Features/Core/Portable/Ambiguity/AbstractAmbiguousTypeCodeFixProvider.cs index 320a3b55f0ddb..4aa5bc1b168e5 100644 --- a/src/Features/Core/Portable/Ambiguity/AbstractAmbiguousTypeCodeFixProvider.cs +++ b/src/Features/Core/Portable/Ambiguity/AbstractAmbiguousTypeCodeFixProvider.cs @@ -87,7 +87,6 @@ private static bool SymbolCandidatesContainsSupportedSymbols(SymbolInfo symbolIn // Arity: Aliases can only name closed constructed types. // Aliasing as a closed constructed type is possible but would require to remove the type arguments from the diagnosed node. // It is unlikely that the user wants that and so generic types are not supported. - // SymbolKind.NamedType: only types can be aliased by this fix. symbolInfo.CandidateSymbols.All(symbol => symbol.IsKind(SymbolKind.NamedType) && symbol.GetArity() == 0); From c79b26cd5b7dd4761fd33b840277d0060a513fd6 Mon Sep 17 00:00:00 2001 From: Martin Strecker Date: Wed, 3 Jan 2018 22:32:47 +0100 Subject: [PATCH 15/26] Disable FixAll and RS1016. --- .../Portable/Ambiguity/AbstractAmbiguousTypeCodeFixProvider.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Features/Core/Portable/Ambiguity/AbstractAmbiguousTypeCodeFixProvider.cs b/src/Features/Core/Portable/Ambiguity/AbstractAmbiguousTypeCodeFixProvider.cs index 4aa5bc1b168e5..973ebc2a764e9 100644 --- a/src/Features/Core/Portable/Ambiguity/AbstractAmbiguousTypeCodeFixProvider.cs +++ b/src/Features/Core/Portable/Ambiguity/AbstractAmbiguousTypeCodeFixProvider.cs @@ -21,6 +21,8 @@ internal abstract class AbstractAmbiguousTypeCodeFixProvider : CodeFixProvider { protected abstract SyntaxNode GetAliasDirective(string typeName, ISymbol symbol); + public override FixAllProvider GetFixAllProvider() => null; + public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context) { var cancellationToken = context.CancellationToken; From 527d2deec1a23f1150cb64bba63b6ba60d3529d5 Mon Sep 17 00:00:00 2001 From: Martin Strecker Date: Thu, 4 Jan 2018 21:41:25 +0100 Subject: [PATCH 16/26] Renamed some files/classes to be in line with the naming conventions for Codefixer. Changed how Attribute suffix is handled. --- ...ypeTests.cs => AliasAmbiguousTypeTests.cs} | 20 +++++------ ...ypeTests.vb => AliasAmbiguousTypeTests.vb} | 8 ++--- ...SharpAliasAmbiguousTypeCodeFixProvider.cs} | 4 +-- ...tractAliasAmbiguousTypeCodeFixProvider.cs} | 34 ++++--------------- ...BasicAliasAmbiguousTypeCodeFixProvider.vb} | 7 ++-- 5 files changed, 27 insertions(+), 46 deletions(-) rename src/EditorFeatures/CSharpTest/Ambiguity/{AmbiguousTypeTests.cs => AliasAmbiguousTypeTests.cs} (97%) rename src/EditorFeatures/VisualBasicTest/Diagnostics/Ambiguity/{AmbiguousTypeTests.vb => AliasAmbiguousTypeTests.vb} (96%) rename src/Features/CSharp/Portable/Ambiguity/{CSharpAmbiguousTypeCodeFixProvider.cs => CSharpAliasAmbiguousTypeCodeFixProvider.cs} (89%) rename src/Features/Core/Portable/Ambiguity/{AbstractAmbiguousTypeCodeFixProvider.cs => AbstractAliasAmbiguousTypeCodeFixProvider.cs} (70%) rename src/Features/VisualBasic/Portable/Ambiguity/{VisualBasicAmbiguousTypeCodeFixProvider.vb => VisualBasicAliasAmbiguousTypeCodeFixProvider.vb} (89%) diff --git a/src/EditorFeatures/CSharpTest/Ambiguity/AmbiguousTypeTests.cs b/src/EditorFeatures/CSharpTest/Ambiguity/AliasAmbiguousTypeTests.cs similarity index 97% rename from src/EditorFeatures/CSharpTest/Ambiguity/AmbiguousTypeTests.cs rename to src/EditorFeatures/CSharpTest/Ambiguity/AliasAmbiguousTypeTests.cs index 5f5fb8517408f..33318ffe4cf72 100644 --- a/src/EditorFeatures/CSharpTest/Ambiguity/AmbiguousTypeTests.cs +++ b/src/EditorFeatures/CSharpTest/Ambiguity/AliasAmbiguousTypeTests.cs @@ -10,10 +10,10 @@ namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.Ambiguity { - public class AmbiguousTypeTests : AbstractCSharpDiagnosticProviderBasedUserDiagnosticTest + public class AliasAmbiguousTypeTests : AbstractCSharpDiagnosticProviderBasedUserDiagnosticTest { internal override (DiagnosticAnalyzer, CodeFixProvider) CreateDiagnosticProviderAndFixer(Workspace workspace) - => (null, new CSharpAmbiguousTypeCodeFixProvider()); + => (null, new CSharpAliasAmbiguousTypeCodeFixProvider()); private string GetAmbiguousDefinition(string typeDefinion) => $@" @@ -252,7 +252,7 @@ static void Main(string[] args) await TestInRegularAndScriptAsync(initialMarkup, expectedMarkup.Replace("#", "using Nested = Static.Nested;"), 1); } - [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsAliasAmbiguousType + "1")] + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsAliasAmbiguousType)] public async Task TestAmbiguousClassDiagnosedAtBaseList() { var classDef = GetAmbiguousDefinition(@"public class AmbiguousClass { }"); @@ -278,7 +278,7 @@ public class Test : AmbiguousClass { } await TestInRegularAndScriptAsync(initialMarkup, expectedMarkup); } - [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsAliasAmbiguousType + "1")] + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsAliasAmbiguousType)] public async Task TestAmbiguousClassDiagnosedAtTypeConstraint() { var classDef = GetAmbiguousDefinition(@"public class AmbiguousClass { }"); @@ -304,7 +304,7 @@ public class Test where T : AmbiguousClass { } await TestInRegularAndScriptAsync(initialMarkup, expectedMarkup); } - [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsAliasAmbiguousType + "1")] + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsAliasAmbiguousType)] public async Task TestAmbiguousEnumDiagnosedAtFieldDeclaration() { var enumDef = GetAmbiguousDefinition(@"public enum AmbiguousEnum { }"); @@ -336,7 +336,7 @@ public class Test await TestInRegularAndScriptAsync(initialMarkup, expectedMarkup); } - [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsAliasAmbiguousType + "1")] + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsAliasAmbiguousType)] public async Task TestAmbiguousStructDiagnosedAtPropertyDeclaration() { var strcutDef = GetAmbiguousDefinition(@"public struct AmbiguousStruct { }"); @@ -368,7 +368,7 @@ public class Test await TestInRegularAndScriptAsync(initialMarkup, expectedMarkup); } - [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsAliasAmbiguousType + "1")] + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsAliasAmbiguousType)] public async Task TestAmbiguousClassDiagnosedAtTypeArgument() { var classDef = GetAmbiguousDefinition(@"public class AmbiguousClass { }"); @@ -406,7 +406,7 @@ public void M() await TestInRegularAndScriptAsync(initialMarkup, expectedMarkup); } - [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsAliasAmbiguousType + "1")] + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsAliasAmbiguousType)] public async Task TestAmbiguousClassDiagnosedAtIdentifierOfIncompleteExpression() { var classDef = GetAmbiguousDefinition(@"public class AmbiguousClass { }"); @@ -444,7 +444,7 @@ public void M() await TestInRegularAndScriptAsync(initialMarkup, expectedMarkup); } - [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsAliasAmbiguousType + "1")] + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsAliasAmbiguousType)] public async Task TestAmbiguousClassDiagnosedAtMethodParameter() { var classDef = GetAmbiguousDefinition(@"public class AmbiguousClass { }"); @@ -480,7 +480,7 @@ public void M(AmbiguousClass a) await TestInRegularAndScriptAsync(initialMarkup, expectedMarkup); } - [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsAliasAmbiguousType + "1")] + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsAliasAmbiguousType)] public async Task TestAmbiguousClassDiagnosedAtFromClauseTypeIdentifier() { var classDef = GetAmbiguousDefinition(@"public class AmbiguousClass { }"); diff --git a/src/EditorFeatures/VisualBasicTest/Diagnostics/Ambiguity/AmbiguousTypeTests.vb b/src/EditorFeatures/VisualBasicTest/Diagnostics/Ambiguity/AliasAmbiguousTypeTests.vb similarity index 96% rename from src/EditorFeatures/VisualBasicTest/Diagnostics/Ambiguity/AmbiguousTypeTests.vb rename to src/EditorFeatures/VisualBasicTest/Diagnostics/Ambiguity/AliasAmbiguousTypeTests.vb index e578aca2347e2..7bb8161ee4bb8 100644 --- a/src/EditorFeatures/VisualBasicTest/Diagnostics/Ambiguity/AmbiguousTypeTests.vb +++ b/src/EditorFeatures/VisualBasicTest/Diagnostics/Ambiguity/AliasAmbiguousTypeTests.vb @@ -5,11 +5,11 @@ Imports Microsoft.CodeAnalysis.Diagnostics Imports Microsoft.CodeAnalysis.VisualBasic.Ambiguity Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.UnitTests.Diagnostics.Ambiguity - Public Class AmbiguousTypeTests + Public Class AliasAmbiguousTypeTests Inherits AbstractVisualBasicDiagnosticProviderBasedUserDiagnosticTest Friend Overrides Function CreateDiagnosticProviderAndFixer(workspace As Workspace) As (DiagnosticAnalyzer, CodeFixProvider) - Return (Nothing, New VisualBasicAmbiguousTypeCodeFixProvider()) + Return (Nothing, New VisualBasicAliasAmbiguousTypeCodeFixProvider()) End Function Private Function GetAmbiguousDefinition(ByVal typeDefinion As String) As String @@ -149,7 +149,7 @@ End Class Await TestInRegularAndScriptAsync(initialMarkup, expectedMarkup) End Function - + Public Async Function TestAmbiguousInterfaceNameReferencedInSmallCaps() As Task Dim initialMarkup = " Imports N1, N2 @@ -167,7 +167,7 @@ End Class " Dim expectedMarkup = " Imports N1, N2 -Imports i1 = N1.I1 +Imports I1 = N1.I1 Namespace N1 Interface I1 diff --git a/src/Features/CSharp/Portable/Ambiguity/CSharpAmbiguousTypeCodeFixProvider.cs b/src/Features/CSharp/Portable/Ambiguity/CSharpAliasAmbiguousTypeCodeFixProvider.cs similarity index 89% rename from src/Features/CSharp/Portable/Ambiguity/CSharpAmbiguousTypeCodeFixProvider.cs rename to src/Features/CSharp/Portable/Ambiguity/CSharpAliasAmbiguousTypeCodeFixProvider.cs index 0ebaaed8f7954..403080b110af6 100644 --- a/src/Features/CSharp/Portable/Ambiguity/CSharpAmbiguousTypeCodeFixProvider.cs +++ b/src/Features/CSharp/Portable/Ambiguity/CSharpAliasAmbiguousTypeCodeFixProvider.cs @@ -6,7 +6,7 @@ using System.Diagnostics; using System.Threading; using System.Threading.Tasks; -using Microsoft.CodeAnalysis.AmbiguityCodeFixProvider; +using Microsoft.CodeAnalysis.Ambiguity; using Microsoft.CodeAnalysis.CodeFixes; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Shared.Extensions; @@ -15,7 +15,7 @@ namespace Microsoft.CodeAnalysis.CSharp.Ambiguity { [ExportCodeFixProvider(LanguageNames.CSharp, Name = PredefinedCodeFixProviderNames.AliasAmbiguousType), Shared] [ExtensionOrder(After = PredefinedCodeFixProviderNames.FullyQualify)] - internal class CSharpAmbiguousTypeCodeFixProvider : AbstractAmbiguousTypeCodeFixProvider + internal class CSharpAliasAmbiguousTypeCodeFixProvider : AbstractAliasAmbiguousTypeCodeFixProvider { /// /// 'reference' is an ambiguous reference between 'identifier' and 'identifier' diff --git a/src/Features/Core/Portable/Ambiguity/AbstractAmbiguousTypeCodeFixProvider.cs b/src/Features/Core/Portable/Ambiguity/AbstractAliasAmbiguousTypeCodeFixProvider.cs similarity index 70% rename from src/Features/Core/Portable/Ambiguity/AbstractAmbiguousTypeCodeFixProvider.cs rename to src/Features/Core/Portable/Ambiguity/AbstractAliasAmbiguousTypeCodeFixProvider.cs index 973ebc2a764e9..e841246d1e562 100644 --- a/src/Features/Core/Portable/Ambiguity/AbstractAmbiguousTypeCodeFixProvider.cs +++ b/src/Features/Core/Portable/Ambiguity/AbstractAliasAmbiguousTypeCodeFixProvider.cs @@ -15,9 +15,9 @@ using Microsoft.CodeAnalysis.Shared.Extensions; using static Microsoft.CodeAnalysis.CodeActions.CodeAction; -namespace Microsoft.CodeAnalysis.AmbiguityCodeFixProvider +namespace Microsoft.CodeAnalysis.Ambiguity { - internal abstract class AbstractAmbiguousTypeCodeFixProvider : CodeFixProvider + internal abstract class AbstractAliasAmbiguousTypeCodeFixProvider : CodeFixProvider { protected abstract SyntaxNode GetAliasDirective(string typeName, ISymbol symbol); @@ -46,14 +46,11 @@ public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context) var compilation = semanticModel.Compilation; var optionSet = await document.GetOptionsAsync(cancellationToken).ConfigureAwait(false); var placeSystemNamespaceFirst = optionSet.GetOption(GenerationOptions.PlaceSystemNamespaceFirst, document.Project.Language); - var typeName = GetAliasFromDiagnosticNode(syntaxFacts, diagnosticNode); foreach (var symbol in symbolInfo.CandidateSymbols) { + var typeName = symbol.Name; var aliasDirective = GetAliasDirective(typeName, symbol); - var codeActionPreviewText = await GetTextPreviewOfChangeAsync(aliasDirective, - document.Project.Solution.Workspace, - optionSet, - cancellationToken).ConfigureAwait(false); + var codeActionPreviewText = GetTextPreviewOfChange(aliasDirective); var newRoot = addImportService.AddImport(compilation, root, diagnosticNode, aliasDirective, placeSystemNamespaceFirst); var codeAction = new MyCodeAction(codeActionPreviewText, c => Task.FromResult(document.WithSyntaxRoot(newRoot))); context.RegisterCodeFix(codeAction, context.Diagnostics.First()); @@ -61,27 +58,10 @@ public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context) } } - private static async Task GetTextPreviewOfChangeAsync(SyntaxNode newNode, Workspace workspace, OptionSet optionSet, CancellationToken cancellationToken) + private static string GetTextPreviewOfChange(SyntaxNode newNode) { - var formattedNode = await Formatter.FormatAsync(newNode, workspace, optionSet, cancellationToken).ConfigureAwait(false); - var formattedText = formattedNode.ToFullString(); - return string.Format(FeaturesResources.Alias_ambiguous_type_0, formattedText); - } - - private static string GetAliasFromDiagnosticNode(ISyntaxFactsService syntaxFacts, SyntaxNode diagnosticNode) - { - // The content of the node is a good candidate for the alias - // For attributes VB requires that the alias ends with 'Attribute' while C# is fine with or without the suffix. - var nodeText = diagnosticNode.ToString(); - if (syntaxFacts.IsAttribute(diagnosticNode.Parent)) - { - if (!nodeText.EndsWith("Attribute")) - { - nodeText += "Attribute"; - } - } - - return nodeText; + var normalizedNodeText = newNode.NormalizeWhitespace().ToFullString(); + return string.Format(FeaturesResources.Alias_ambiguous_type_0, normalizedNodeText); } private static bool SymbolCandidatesContainsSupportedSymbols(SymbolInfo symbolInfo) diff --git a/src/Features/VisualBasic/Portable/Ambiguity/VisualBasicAmbiguousTypeCodeFixProvider.vb b/src/Features/VisualBasic/Portable/Ambiguity/VisualBasicAliasAmbiguousTypeCodeFixProvider.vb similarity index 89% rename from src/Features/VisualBasic/Portable/Ambiguity/VisualBasicAmbiguousTypeCodeFixProvider.vb rename to src/Features/VisualBasic/Portable/Ambiguity/VisualBasicAliasAmbiguousTypeCodeFixProvider.vb index b085a00fb5c84..6b14c925164e2 100644 --- a/src/Features/VisualBasic/Portable/Ambiguity/VisualBasicAmbiguousTypeCodeFixProvider.vb +++ b/src/Features/VisualBasic/Portable/Ambiguity/VisualBasicAliasAmbiguousTypeCodeFixProvider.vb @@ -2,15 +2,16 @@ Imports System.Collections.Immutable Imports System.Composition -Imports Microsoft.CodeAnalysis.AmbiguityCodeFixProvider +Imports Microsoft.CodeAnalysis.Ambiguity Imports Microsoft.CodeAnalysis.CodeFixes Imports Microsoft.CodeAnalysis.VisualBasic.Syntax Namespace Microsoft.CodeAnalysis.VisualBasic.Ambiguity + - Friend Class VisualBasicAmbiguousTypeCodeFixProvider - Inherits AbstractAmbiguousTypeCodeFixProvider + Friend Class VisualBasicAliasAmbiguousTypeCodeFixProvider + Inherits AbstractAliasAmbiguousTypeCodeFixProvider 'BC30561: '' is ambiguous, imported from the namespaces or types '' Private Const BC30561 As String = NameOf(BC30561) From 2d0a0aa986263146a536d4b016fd90a65e71be7f Mon Sep 17 00:00:00 2001 From: Martin Strecker Date: Thu, 4 Jan 2018 23:04:20 +0100 Subject: [PATCH 17/26] Added AliasImportDeclaration to SyntaxGenerator. --- .../Ambiguity/AliasAmbiguousTypeTests.vb | 2 +- .../CSharpAliasAmbiguousTypeCodeFixProvider.cs | 10 ---------- .../AbstractAliasAmbiguousTypeCodeFixProvider.cs | 8 ++------ ...sualBasicAliasAmbiguousTypeCodeFixProvider.vb | 13 +------------ .../CodeGeneration/CSharpSyntaxGenerator.cs | 16 +++++++++++----- .../Core/Portable/Editing/SyntaxGenerator.cs | 7 +++++++ .../Core/Portable/PublicAPI.Unshipped.txt | 1 + .../CodeGeneration/VisualBasicSyntaxGenerator.vb | 14 +++++++++++++- 8 files changed, 36 insertions(+), 35 deletions(-) diff --git a/src/EditorFeatures/VisualBasicTest/Diagnostics/Ambiguity/AliasAmbiguousTypeTests.vb b/src/EditorFeatures/VisualBasicTest/Diagnostics/Ambiguity/AliasAmbiguousTypeTests.vb index 7bb8161ee4bb8..116bfe2e17191 100644 --- a/src/EditorFeatures/VisualBasicTest/Diagnostics/Ambiguity/AliasAmbiguousTypeTests.vb +++ b/src/EditorFeatures/VisualBasicTest/Diagnostics/Ambiguity/AliasAmbiguousTypeTests.vb @@ -128,7 +128,7 @@ End Class " Dim expectedMarkup = " Imports N1, N2 -Imports Goo = N1.K.Goo +Imports Goo = N1.Goo Namespace N1 Module K diff --git a/src/Features/CSharp/Portable/Ambiguity/CSharpAliasAmbiguousTypeCodeFixProvider.cs b/src/Features/CSharp/Portable/Ambiguity/CSharpAliasAmbiguousTypeCodeFixProvider.cs index 403080b110af6..ea7fcc30b72b5 100644 --- a/src/Features/CSharp/Portable/Ambiguity/CSharpAliasAmbiguousTypeCodeFixProvider.cs +++ b/src/Features/CSharp/Portable/Ambiguity/CSharpAliasAmbiguousTypeCodeFixProvider.cs @@ -1,15 +1,9 @@ // 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.Immutable; using System.Composition; -using System.Diagnostics; -using System.Threading; -using System.Threading.Tasks; using Microsoft.CodeAnalysis.Ambiguity; using Microsoft.CodeAnalysis.CodeFixes; -using Microsoft.CodeAnalysis.CSharp.Syntax; -using Microsoft.CodeAnalysis.Shared.Extensions; namespace Microsoft.CodeAnalysis.CSharp.Ambiguity { @@ -24,9 +18,5 @@ internal class CSharpAliasAmbiguousTypeCodeFixProvider : AbstractAliasAmbiguousT public override ImmutableArray FixableDiagnosticIds => ImmutableArray.Create(CS0104); - - protected override SyntaxNode GetAliasDirective(string typeName, ISymbol symbol) - => SyntaxFactory.UsingDirective(SyntaxFactory.NameEquals(typeName), - SyntaxFactory.IdentifierName(symbol.ToNameDisplayString())); } } diff --git a/src/Features/Core/Portable/Ambiguity/AbstractAliasAmbiguousTypeCodeFixProvider.cs b/src/Features/Core/Portable/Ambiguity/AbstractAliasAmbiguousTypeCodeFixProvider.cs index e841246d1e562..64a764bb31e98 100644 --- a/src/Features/Core/Portable/Ambiguity/AbstractAliasAmbiguousTypeCodeFixProvider.cs +++ b/src/Features/Core/Portable/Ambiguity/AbstractAliasAmbiguousTypeCodeFixProvider.cs @@ -1,17 +1,13 @@ // 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.Immutable; using System.Linq; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.AddImports; -using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeFixes; using Microsoft.CodeAnalysis.Editing; -using Microsoft.CodeAnalysis.Formatting; using Microsoft.CodeAnalysis.LanguageServices; -using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.Shared.Extensions; using static Microsoft.CodeAnalysis.CodeActions.CodeAction; @@ -19,7 +15,6 @@ namespace Microsoft.CodeAnalysis.Ambiguity { internal abstract class AbstractAliasAmbiguousTypeCodeFixProvider : CodeFixProvider { - protected abstract SyntaxNode GetAliasDirective(string typeName, ISymbol symbol); public override FixAllProvider GetFixAllProvider() => null; @@ -42,6 +37,7 @@ public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context) if (SymbolCandidatesContainsSupportedSymbols(symbolInfo)) { var addImportService = document.GetLanguageService(); + var syntaxGenerator = document.GetLanguageService(); var diagnostic = context.Diagnostics.First(); var compilation = semanticModel.Compilation; var optionSet = await document.GetOptionsAsync(cancellationToken).ConfigureAwait(false); @@ -49,7 +45,7 @@ public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context) foreach (var symbol in symbolInfo.CandidateSymbols) { var typeName = symbol.Name; - var aliasDirective = GetAliasDirective(typeName, symbol); + var aliasDirective = syntaxGenerator.AliasImportDeclaration(typeName, (ITypeSymbol)symbol); var codeActionPreviewText = GetTextPreviewOfChange(aliasDirective); var newRoot = addImportService.AddImport(compilation, root, diagnosticNode, aliasDirective, placeSystemNamespaceFirst); var codeAction = new MyCodeAction(codeActionPreviewText, c => Task.FromResult(document.WithSyntaxRoot(newRoot))); diff --git a/src/Features/VisualBasic/Portable/Ambiguity/VisualBasicAliasAmbiguousTypeCodeFixProvider.vb b/src/Features/VisualBasic/Portable/Ambiguity/VisualBasicAliasAmbiguousTypeCodeFixProvider.vb index 6b14c925164e2..4e42e323eb9c5 100644 --- a/src/Features/VisualBasic/Portable/Ambiguity/VisualBasicAliasAmbiguousTypeCodeFixProvider.vb +++ b/src/Features/VisualBasic/Portable/Ambiguity/VisualBasicAliasAmbiguousTypeCodeFixProvider.vb @@ -4,7 +4,6 @@ Imports System.Collections.Immutable Imports System.Composition Imports Microsoft.CodeAnalysis.Ambiguity Imports Microsoft.CodeAnalysis.CodeFixes -Imports Microsoft.CodeAnalysis.VisualBasic.Syntax Namespace Microsoft.CodeAnalysis.VisualBasic.Ambiguity @@ -16,17 +15,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Ambiguity 'BC30561: '' is ambiguous, imported from the namespaces or types '' Private Const BC30561 As String = NameOf(BC30561) - Public Overrides ReadOnly Property FixableDiagnosticIds As ImmutableArray(Of String) - Get - Return ImmutableArray.Create(BC30561) - End Get - End Property + Public Overrides ReadOnly Property FixableDiagnosticIds As ImmutableArray(Of String) = ImmutableArray.Create(BC30561) - Protected Overrides Function GetAliasDirective(ByVal typeName As String, ByVal symbol As ISymbol) As SyntaxNode - Return SyntaxFactory.ImportsStatement(SyntaxFactory.SeparatedList(Of ImportsClauseSyntax).Add( - SyntaxFactory.SimpleImportsClause( - SyntaxFactory.ImportAliasClause(typeName), - SyntaxFactory.IdentifierName(symbol.ToNameDisplayString())))) - End Function End Class End Namespace diff --git a/src/Workspaces/CSharp/Portable/CodeGeneration/CSharpSyntaxGenerator.cs b/src/Workspaces/CSharp/Portable/CodeGeneration/CSharpSyntaxGenerator.cs index 98399b69302e7..b254250d30142 100644 --- a/src/Workspaces/CSharp/Portable/CodeGeneration/CSharpSyntaxGenerator.cs +++ b/src/Workspaces/CSharp/Portable/CodeGeneration/CSharpSyntaxGenerator.cs @@ -82,6 +82,12 @@ public override SyntaxNode NamespaceImportDeclaration(SyntaxNode name) return SyntaxFactory.UsingDirective((NameSyntax)name); } + public override SyntaxNode AliasImportDeclaration(string aliasIdentifierName, INamespaceOrTypeSymbol symbol) + { + var nameSyntax = symbol.GenerateNameSyntax(); + return SyntaxFactory.UsingDirective(SyntaxFactory.NameEquals(aliasIdentifierName), nameSyntax); + } + public override SyntaxNode NamespaceDeclaration(SyntaxNode name, IEnumerable declarations) { return SyntaxFactory.NamespaceDeclaration( @@ -3186,9 +3192,9 @@ private static SyntaxNode WithBaseList(SyntaxNode declaration, BaseListSyntax ba } } -#endregion + #endregion -#region Remove, Replace, Insert + #region Remove, Replace, Insert public override SyntaxNode ReplaceNode(SyntaxNode root, SyntaxNode declaration, SyntaxNode newDeclaration) { @@ -3525,9 +3531,9 @@ private static SyntaxNode ShiftTrivia(SyntaxNode root, SyntaxNode node) internal override bool IsRegularOrDocComment(SyntaxTrivia trivia) => trivia.IsRegularOrDocComment(); -#endregion + #endregion -#region Statements and Expressions + #region Statements and Expressions public override SyntaxNode AddEventHandler(SyntaxNode @event, SyntaxNode handler) { @@ -4214,6 +4220,6 @@ internal override SyntaxNode RefExpression(SyntaxNode expression) public override SyntaxNode TupleExpression(IEnumerable arguments) => SyntaxFactory.TupleExpression(SyntaxFactory.SeparatedList(arguments.Select(AsArgument))); -#endregion + #endregion } } diff --git a/src/Workspaces/Core/Portable/Editing/SyntaxGenerator.cs b/src/Workspaces/Core/Portable/Editing/SyntaxGenerator.cs index d57bd1a9eda6d..34863b14b810f 100644 --- a/src/Workspaces/Core/Portable/Editing/SyntaxGenerator.cs +++ b/src/Workspaces/Core/Portable/Editing/SyntaxGenerator.cs @@ -787,6 +787,13 @@ public SyntaxNode NamespaceImportDeclaration(string name) return NamespaceImportDeclaration(DottedName(name)); } + /// + /// Creates a alias import declaration. + /// + /// The name of the alias. + /// The namespace or type to be aliased. + public abstract SyntaxNode AliasImportDeclaration(string aliasIdentifierName, INamespaceOrTypeSymbol symbol); + /// /// Creates an attribute. /// diff --git a/src/Workspaces/Core/Portable/PublicAPI.Unshipped.txt b/src/Workspaces/Core/Portable/PublicAPI.Unshipped.txt index 14602a54c9a68..1d296abf18c52 100644 --- a/src/Workspaces/Core/Portable/PublicAPI.Unshipped.txt +++ b/src/Workspaces/Core/Portable/PublicAPI.Unshipped.txt @@ -4,6 +4,7 @@ Microsoft.CodeAnalysis.Editing.SyntaxGenerator.TupleTypeExpression(System.Collec Microsoft.CodeAnalysis.Editing.SyntaxGenerator.TupleTypeExpression(System.Collections.Generic.IEnumerable elements) -> Microsoft.CodeAnalysis.SyntaxNode Microsoft.CodeAnalysis.Editing.SyntaxGenerator.TupleTypeExpression(params Microsoft.CodeAnalysis.SyntaxNode[] elements) -> Microsoft.CodeAnalysis.SyntaxNode Microsoft.CodeAnalysis.Editing.SyntaxGenerator.WithAccessorDeclarations(Microsoft.CodeAnalysis.SyntaxNode declaration, params Microsoft.CodeAnalysis.SyntaxNode[] accessorDeclarations) -> Microsoft.CodeAnalysis.SyntaxNode +abstract Microsoft.CodeAnalysis.Editing.SyntaxGenerator.AliasImportDeclaration(string aliasIdentifierName, Microsoft.CodeAnalysis.INamespaceOrTypeSymbol symbol) -> Microsoft.CodeAnalysis.SyntaxNode abstract Microsoft.CodeAnalysis.Editing.SyntaxGenerator.GetAccessorDeclaration(Microsoft.CodeAnalysis.Accessibility accessibility = Microsoft.CodeAnalysis.Accessibility.NotApplicable, System.Collections.Generic.IEnumerable statements = null) -> Microsoft.CodeAnalysis.SyntaxNode abstract Microsoft.CodeAnalysis.Editing.SyntaxGenerator.SetAccessorDeclaration(Microsoft.CodeAnalysis.Accessibility accessibility = Microsoft.CodeAnalysis.Accessibility.NotApplicable, System.Collections.Generic.IEnumerable statements = null) -> Microsoft.CodeAnalysis.SyntaxNode abstract Microsoft.CodeAnalysis.Editing.SyntaxGenerator.TupleElementExpression(Microsoft.CodeAnalysis.SyntaxNode type, string name = null) -> Microsoft.CodeAnalysis.SyntaxNode diff --git a/src/Workspaces/VisualBasic/Portable/CodeGeneration/VisualBasicSyntaxGenerator.vb b/src/Workspaces/VisualBasic/Portable/CodeGeneration/VisualBasicSyntaxGenerator.vb index 484b5d59eba45..5b25ea656da55 100644 --- a/src/Workspaces/VisualBasic/Portable/CodeGeneration/VisualBasicSyntaxGenerator.vb +++ b/src/Workspaces/VisualBasic/Portable/CodeGeneration/VisualBasicSyntaxGenerator.vb @@ -1500,6 +1500,18 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.CodeGeneration Return SyntaxFactory.ImportsStatement(SyntaxFactory.SingletonSeparatedList(Of ImportsClauseSyntax)(SyntaxFactory.SimpleImportsClause(DirectCast(name, NameSyntax)))) End Function + Public Overrides Function AliasImportDeclaration(aliasIdentifierName As String, symbol As INamespaceOrTypeSymbol) As SyntaxNode + Dim typeSyntax = symbol.GenerateTypeSyntax() + If TypeOf typeSyntax Is NameSyntax Then + Return SyntaxFactory.ImportsStatement(SyntaxFactory.SeparatedList(Of ImportsClauseSyntax).Add( + SyntaxFactory.SimpleImportsClause( + SyntaxFactory.ImportAliasClause(aliasIdentifierName), + CType(typeSyntax, NameSyntax)))) + + End If + Throw New ArgumentException("Symbol can not be named.", NameOf(symbol)) + End Function + Public Overrides Function NamespaceDeclaration(name As SyntaxNode, nestedDeclarations As IEnumerable(Of SyntaxNode)) As SyntaxNode Dim imps As IEnumerable(Of StatementSyntax) = AsImports(nestedDeclarations) Dim members As IEnumerable(Of StatementSyntax) = AsNamespaceMembers(nestedDeclarations) @@ -2886,7 +2898,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.CodeGeneration Case SyntaxKind.ProtectedKeyword If accessibility = Accessibility.Friend Then accessibility = Accessibility.ProtectedOrFriend - ElseIf accessibility = Accessibility.Private + ElseIf accessibility = Accessibility.Private Then accessibility = Accessibility.ProtectedAndFriend Else accessibility = Accessibility.Protected From b0ebe21cab0ed255c792c231346fbeaad2264bd8 Mon Sep 17 00:00:00 2001 From: Martin Strecker Date: Thu, 4 Jan 2018 23:17:04 +0100 Subject: [PATCH 18/26] Delayed call to addImportService to CodeAction. --- .../CSharpTest/Ambiguity/AliasAmbiguousTypeTests.cs | 12 ++++++------ .../Diagnostics/Ambiguity/AliasAmbiguousTypeTests.vb | 6 +++--- .../AbstractAliasAmbiguousTypeCodeFixProvider.cs | 8 ++++++-- 3 files changed, 15 insertions(+), 11 deletions(-) diff --git a/src/EditorFeatures/CSharpTest/Ambiguity/AliasAmbiguousTypeTests.cs b/src/EditorFeatures/CSharpTest/Ambiguity/AliasAmbiguousTypeTests.cs index 33318ffe4cf72..25cda5c36939e 100644 --- a/src/EditorFeatures/CSharpTest/Ambiguity/AliasAmbiguousTypeTests.cs +++ b/src/EditorFeatures/CSharpTest/Ambiguity/AliasAmbiguousTypeTests.cs @@ -48,7 +48,7 @@ namespace Test { using N1; using N2; - # + {0} class C { @@ -58,8 +58,8 @@ void M() } } }"; - await TestInRegularAndScriptAsync(initialMarkup, expectedMarkupTemplate.Replace("#", "using Ambiguous = N1.Ambiguous;"), 0); - await TestInRegularAndScriptAsync(initialMarkup, expectedMarkupTemplate.Replace("#", "using Ambiguous = N2.Ambiguous;"), 1); + await TestInRegularAndScriptAsync(initialMarkup, string.Format(expectedMarkupTemplate, "using Ambiguous = N1.Ambiguous;"), index: 0); + await TestInRegularAndScriptAsync(initialMarkup, string.Format(expectedMarkupTemplate, "using Ambiguous = N2.Ambiguous;"), index: 1); } [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsAliasAmbiguousType)] @@ -230,7 +230,7 @@ static void Main(string[] args) var expectedMarkup = @" using static Static; using static Static; -# +{0} public static class Static { @@ -248,8 +248,8 @@ static void Main(string[] args) c.M(); } }"; - await TestInRegularAndScriptAsync(initialMarkup, expectedMarkup.Replace("#", "using Nested = Static.Nested;"), 0); - await TestInRegularAndScriptAsync(initialMarkup, expectedMarkup.Replace("#", "using Nested = Static.Nested;"), 1); + await TestInRegularAndScriptAsync(initialMarkup, string.Format(expectedMarkup, "using Nested = Static.Nested;"), index: 0); + await TestInRegularAndScriptAsync(initialMarkup, string.Format(expectedMarkup, "using Nested = Static.Nested;"), index: 1); } [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsAliasAmbiguousType)] diff --git a/src/EditorFeatures/VisualBasicTest/Diagnostics/Ambiguity/AliasAmbiguousTypeTests.vb b/src/EditorFeatures/VisualBasicTest/Diagnostics/Ambiguity/AliasAmbiguousTypeTests.vb index 116bfe2e17191..011325b49b39f 100644 --- a/src/EditorFeatures/VisualBasicTest/Diagnostics/Ambiguity/AliasAmbiguousTypeTests.vb +++ b/src/EditorFeatures/VisualBasicTest/Diagnostics/Ambiguity/AliasAmbiguousTypeTests.vb @@ -42,7 +42,7 @@ End Namespace" Dim expectedMarkupTemplate = " Imports N1 Imports N2 -# +{0} " & classDef & " Namespace N3 @@ -52,8 +52,8 @@ Namespace N3 End Sub End Class End Namespace" - Await TestInRegularAndScriptAsync(initialMarkup, expectedMarkupTemplate.Replace("#", "Imports Ambiguous = N1.Ambiguous"), 0) - Await TestInRegularAndScriptAsync(initialMarkup, expectedMarkupTemplate.Replace("#", "Imports Ambiguous = N2.Ambiguous"), 1) + Await TestInRegularAndScriptAsync(initialMarkup, String.Format(expectedMarkupTemplate, "Imports Ambiguous = N1.Ambiguous"), index:=0) + Await TestInRegularAndScriptAsync(initialMarkup, String.Format(expectedMarkupTemplate, "Imports Ambiguous = N2.Ambiguous"), index:=1) End Function diff --git a/src/Features/Core/Portable/Ambiguity/AbstractAliasAmbiguousTypeCodeFixProvider.cs b/src/Features/Core/Portable/Ambiguity/AbstractAliasAmbiguousTypeCodeFixProvider.cs index 64a764bb31e98..7d90e67f56572 100644 --- a/src/Features/Core/Portable/Ambiguity/AbstractAliasAmbiguousTypeCodeFixProvider.cs +++ b/src/Features/Core/Portable/Ambiguity/AbstractAliasAmbiguousTypeCodeFixProvider.cs @@ -47,8 +47,12 @@ public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context) var typeName = symbol.Name; var aliasDirective = syntaxGenerator.AliasImportDeclaration(typeName, (ITypeSymbol)symbol); var codeActionPreviewText = GetTextPreviewOfChange(aliasDirective); - var newRoot = addImportService.AddImport(compilation, root, diagnosticNode, aliasDirective, placeSystemNamespaceFirst); - var codeAction = new MyCodeAction(codeActionPreviewText, c => Task.FromResult(document.WithSyntaxRoot(newRoot))); + Task CreateChangedDocument(CancellationToken c) + { + var newRoot = addImportService.AddImport(compilation, root, diagnosticNode, aliasDirective, placeSystemNamespaceFirst); + return Task.FromResult(document.WithSyntaxRoot(newRoot)); + }; + var codeAction = new MyCodeAction(codeActionPreviewText, CreateChangedDocument); context.RegisterCodeFix(codeAction, context.Diagnostics.First()); } } From ef756c898e67974763813d2604fc84adb1f57474 Mon Sep 17 00:00:00 2001 From: Martin Strecker Date: Thu, 4 Jan 2018 23:38:31 +0100 Subject: [PATCH 19/26] Reverted changes to use GroupingCodeAction again. --- .../Ambiguity/AliasAmbiguousTypeTests.cs | 57 ++++++++++++++++--- .../Ambiguity/AliasAmbiguousTypeTests.vb | 6 ++ ...stractAliasAmbiguousTypeCodeFixProvider.cs | 22 ++++--- 3 files changed, 70 insertions(+), 15 deletions(-) diff --git a/src/EditorFeatures/CSharpTest/Ambiguity/AliasAmbiguousTypeTests.cs b/src/EditorFeatures/CSharpTest/Ambiguity/AliasAmbiguousTypeTests.cs index 25cda5c36939e..7e77e0336be01 100644 --- a/src/EditorFeatures/CSharpTest/Ambiguity/AliasAmbiguousTypeTests.cs +++ b/src/EditorFeatures/CSharpTest/Ambiguity/AliasAmbiguousTypeTests.cs @@ -1,6 +1,8 @@ // 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.Immutable; using System.Threading.Tasks; +using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeFixes; using Microsoft.CodeAnalysis.CSharp.Ambiguity; using Microsoft.CodeAnalysis.Diagnostics; @@ -15,6 +17,9 @@ public class AliasAmbiguousTypeTests : AbstractCSharpDiagnosticProviderBasedUser internal override (DiagnosticAnalyzer, CodeFixProvider) CreateDiagnosticProviderAndFixer(Workspace workspace) => (null, new CSharpAliasAmbiguousTypeCodeFixProvider()); + protected override ImmutableArray MassageActions(ImmutableArray actions) + => FlattenActions(actions); + private string GetAmbiguousDefinition(string typeDefinion) => $@" namespace N1 @@ -43,12 +48,12 @@ void M() } } }"; - var expectedMarkupTemplate = classDef + @" + var expectedMarkup0 = classDef + @" namespace Test { using N1; using N2; - {0} + using Ambiguous = N1.Ambiguous; class C { @@ -58,8 +63,23 @@ void M() } } }"; - await TestInRegularAndScriptAsync(initialMarkup, string.Format(expectedMarkupTemplate, "using Ambiguous = N1.Ambiguous;"), index: 0); - await TestInRegularAndScriptAsync(initialMarkup, string.Format(expectedMarkupTemplate, "using Ambiguous = N2.Ambiguous;"), index: 1); + var expectedMarkup1 = classDef + @" +namespace Test +{ + using N1; + using N2; + using Ambiguous = N2.Ambiguous; + + class C + { + void M() + { + var a = new Ambiguous(); + } + } +}"; + await TestInRegularAndScriptAsync(initialMarkup, expectedMarkup0, index: 0); + await TestInRegularAndScriptAsync(initialMarkup, expectedMarkup1, index: 1); } [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsAliasAmbiguousType)] @@ -227,10 +247,31 @@ static void Main(string[] args) c.M(); } }"; - var expectedMarkup = @" + var expectedMarkup0 = @" +using static Static; +using static Static; +using Nested = Static.Nested; + +public static class Static +{ + public class Nested + { + public void M() { } + } +} + +class D +{ + static void Main(string[] args) + { + var c = new Nested(); + c.M(); + } +}"; + var expectedMarkup1 = @" using static Static; using static Static; -{0} +using Nested = Static.Nested; public static class Static { @@ -248,8 +289,8 @@ static void Main(string[] args) c.M(); } }"; - await TestInRegularAndScriptAsync(initialMarkup, string.Format(expectedMarkup, "using Nested = Static.Nested;"), index: 0); - await TestInRegularAndScriptAsync(initialMarkup, string.Format(expectedMarkup, "using Nested = Static.Nested;"), index: 1); + await TestInRegularAndScriptAsync(initialMarkup, expectedMarkup0, index: 0); + await TestInRegularAndScriptAsync(initialMarkup, expectedMarkup1, index: 1); } [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsAliasAmbiguousType)] diff --git a/src/EditorFeatures/VisualBasicTest/Diagnostics/Ambiguity/AliasAmbiguousTypeTests.vb b/src/EditorFeatures/VisualBasicTest/Diagnostics/Ambiguity/AliasAmbiguousTypeTests.vb index 011325b49b39f..83e2509e981cc 100644 --- a/src/EditorFeatures/VisualBasicTest/Diagnostics/Ambiguity/AliasAmbiguousTypeTests.vb +++ b/src/EditorFeatures/VisualBasicTest/Diagnostics/Ambiguity/AliasAmbiguousTypeTests.vb @@ -1,5 +1,7 @@ ' Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +Imports System.Collections.Immutable +Imports Microsoft.CodeAnalysis.CodeActions Imports Microsoft.CodeAnalysis.CodeFixes Imports Microsoft.CodeAnalysis.Diagnostics Imports Microsoft.CodeAnalysis.VisualBasic.Ambiguity @@ -12,6 +14,10 @@ Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.UnitTests.Diagnostics.Ambigu Return (Nothing, New VisualBasicAliasAmbiguousTypeCodeFixProvider()) End Function + Protected Overrides Function MassageActions(actions As ImmutableArray(Of CodeAction)) As ImmutableArray(Of CodeAction) + Return FlattenActions(actions) + End Function + Private Function GetAmbiguousDefinition(ByVal typeDefinion As String) As String Return $" Namespace N1 diff --git a/src/Features/Core/Portable/Ambiguity/AbstractAliasAmbiguousTypeCodeFixProvider.cs b/src/Features/Core/Portable/Ambiguity/AbstractAliasAmbiguousTypeCodeFixProvider.cs index 7d90e67f56572..2b7ac62573189 100644 --- a/src/Features/Core/Portable/Ambiguity/AbstractAliasAmbiguousTypeCodeFixProvider.cs +++ b/src/Features/Core/Portable/Ambiguity/AbstractAliasAmbiguousTypeCodeFixProvider.cs @@ -1,10 +1,12 @@ // 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.Immutable; using System.Linq; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.AddImports; +using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeFixes; using Microsoft.CodeAnalysis.Editing; using Microsoft.CodeAnalysis.LanguageServices; @@ -42,6 +44,7 @@ public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context) var compilation = semanticModel.Compilation; var optionSet = await document.GetOptionsAsync(cancellationToken).ConfigureAwait(false); var placeSystemNamespaceFirst = optionSet.GetOption(GenerationOptions.PlaceSystemNamespaceFirst, document.Project.Language); + var codeActionsBuilder = ImmutableArray.CreateBuilder(symbolInfo.CandidateSymbols.Length); foreach (var symbol in symbolInfo.CandidateSymbols) { var typeName = symbol.Name; @@ -52,17 +55,15 @@ Task CreateChangedDocument(CancellationToken c) var newRoot = addImportService.AddImport(compilation, root, diagnosticNode, aliasDirective, placeSystemNamespaceFirst); return Task.FromResult(document.WithSyntaxRoot(newRoot)); }; - var codeAction = new MyCodeAction(codeActionPreviewText, CreateChangedDocument); - context.RegisterCodeFix(codeAction, context.Diagnostics.First()); + codeActionsBuilder.Add(new MyCodeAction(codeActionPreviewText, CreateChangedDocument)); } + var groupedTitle = string.Format(FeaturesResources.Alias_ambiguous_type_0, diagnosticNode.ToString()); + var groupedCodeAction = new GroupingCodeAction(groupedTitle, codeActionsBuilder.ToImmutable()); + context.RegisterCodeFix(groupedCodeAction, context.Diagnostics.First()); } } - private static string GetTextPreviewOfChange(SyntaxNode newNode) - { - var normalizedNodeText = newNode.NormalizeWhitespace().ToFullString(); - return string.Format(FeaturesResources.Alias_ambiguous_type_0, normalizedNodeText); - } + private static string GetTextPreviewOfChange(SyntaxNode newNode) => newNode.NormalizeWhitespace().ToFullString(); private static bool SymbolCandidatesContainsSupportedSymbols(SymbolInfo symbolInfo) => symbolInfo.CandidateReason == CandidateReason.Ambiguous && @@ -79,5 +80,12 @@ public MyCodeAction(string title, Func> create { } } + private class GroupingCodeAction : CodeActionWithNestedActions + { + public GroupingCodeAction(string title, ImmutableArray nestedActions) + : base(title, nestedActions, isInlinable: true) + { + } + } } } From b9e5918dbc36e5641b5cfcb5327d09ddbb67d711 Mon Sep 17 00:00:00 2001 From: Martin Strecker Date: Fri, 5 Jan 2018 13:17:58 +0100 Subject: [PATCH 20/26] Changed folders and namespaces to match naming convention. --- .../AliasAmbiguousTypeTests.cs | 4 ++-- .../AliasAmbiguousTypeTests.vb | 4 ++-- .../CSharpAliasAmbiguousTypeCodeFixProvider.cs | 4 ++-- .../AbstractAliasAmbiguousTypeCodeFixProvider.cs | 2 +- .../VisualBasicAliasAmbiguousTypeCodeFixProvider.vb | 4 ++-- 5 files changed, 9 insertions(+), 9 deletions(-) rename src/EditorFeatures/CSharpTest/{Ambiguity => AliasAmbiguousType}/AliasAmbiguousTypeTests.cs (99%) rename src/EditorFeatures/VisualBasicTest/Diagnostics/{Ambiguity => AliasAmbiguousType}/AliasAmbiguousTypeTests.vb (98%) rename src/Features/CSharp/Portable/{Ambiguity => AliasAmbiguousType}/CSharpAliasAmbiguousTypeCodeFixProvider.cs (89%) rename src/Features/Core/Portable/{Ambiguity => AliasAmbiguousType}/AbstractAliasAmbiguousTypeCodeFixProvider.cs (98%) rename src/Features/VisualBasic/Portable/{Ambiguity => AliasAmbiguousType}/VisualBasicAliasAmbiguousTypeCodeFixProvider.vb (88%) diff --git a/src/EditorFeatures/CSharpTest/Ambiguity/AliasAmbiguousTypeTests.cs b/src/EditorFeatures/CSharpTest/AliasAmbiguousType/AliasAmbiguousTypeTests.cs similarity index 99% rename from src/EditorFeatures/CSharpTest/Ambiguity/AliasAmbiguousTypeTests.cs rename to src/EditorFeatures/CSharpTest/AliasAmbiguousType/AliasAmbiguousTypeTests.cs index 7e77e0336be01..c0198215cda7c 100644 --- a/src/EditorFeatures/CSharpTest/Ambiguity/AliasAmbiguousTypeTests.cs +++ b/src/EditorFeatures/CSharpTest/AliasAmbiguousType/AliasAmbiguousTypeTests.cs @@ -4,13 +4,13 @@ using System.Threading.Tasks; using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeFixes; -using Microsoft.CodeAnalysis.CSharp.Ambiguity; +using Microsoft.CodeAnalysis.CSharp.AliasAmbiguousType; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.Diagnostics; using Roslyn.Test.Utilities; using Xunit; -namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.Ambiguity +namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.AliasAmbiguousType { public class AliasAmbiguousTypeTests : AbstractCSharpDiagnosticProviderBasedUserDiagnosticTest { diff --git a/src/EditorFeatures/VisualBasicTest/Diagnostics/Ambiguity/AliasAmbiguousTypeTests.vb b/src/EditorFeatures/VisualBasicTest/Diagnostics/AliasAmbiguousType/AliasAmbiguousTypeTests.vb similarity index 98% rename from src/EditorFeatures/VisualBasicTest/Diagnostics/Ambiguity/AliasAmbiguousTypeTests.vb rename to src/EditorFeatures/VisualBasicTest/Diagnostics/AliasAmbiguousType/AliasAmbiguousTypeTests.vb index 83e2509e981cc..fa20fb8fbaed9 100644 --- a/src/EditorFeatures/VisualBasicTest/Diagnostics/Ambiguity/AliasAmbiguousTypeTests.vb +++ b/src/EditorFeatures/VisualBasicTest/Diagnostics/AliasAmbiguousType/AliasAmbiguousTypeTests.vb @@ -4,9 +4,9 @@ Imports System.Collections.Immutable Imports Microsoft.CodeAnalysis.CodeActions Imports Microsoft.CodeAnalysis.CodeFixes Imports Microsoft.CodeAnalysis.Diagnostics -Imports Microsoft.CodeAnalysis.VisualBasic.Ambiguity +Imports Microsoft.CodeAnalysis.VisualBasic.AliasAmbiguousType -Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.UnitTests.Diagnostics.Ambiguity +Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.UnitTests.Diagnostics.AliasAmbiguousType Public Class AliasAmbiguousTypeTests Inherits AbstractVisualBasicDiagnosticProviderBasedUserDiagnosticTest diff --git a/src/Features/CSharp/Portable/Ambiguity/CSharpAliasAmbiguousTypeCodeFixProvider.cs b/src/Features/CSharp/Portable/AliasAmbiguousType/CSharpAliasAmbiguousTypeCodeFixProvider.cs similarity index 89% rename from src/Features/CSharp/Portable/Ambiguity/CSharpAliasAmbiguousTypeCodeFixProvider.cs rename to src/Features/CSharp/Portable/AliasAmbiguousType/CSharpAliasAmbiguousTypeCodeFixProvider.cs index ea7fcc30b72b5..a12ef32b7e639 100644 --- a/src/Features/CSharp/Portable/Ambiguity/CSharpAliasAmbiguousTypeCodeFixProvider.cs +++ b/src/Features/CSharp/Portable/AliasAmbiguousType/CSharpAliasAmbiguousTypeCodeFixProvider.cs @@ -2,10 +2,10 @@ using System.Collections.Immutable; using System.Composition; -using Microsoft.CodeAnalysis.Ambiguity; +using Microsoft.CodeAnalysis.AliasAmbiguousType; using Microsoft.CodeAnalysis.CodeFixes; -namespace Microsoft.CodeAnalysis.CSharp.Ambiguity +namespace Microsoft.CodeAnalysis.CSharp.AliasAmbiguousType { [ExportCodeFixProvider(LanguageNames.CSharp, Name = PredefinedCodeFixProviderNames.AliasAmbiguousType), Shared] [ExtensionOrder(After = PredefinedCodeFixProviderNames.FullyQualify)] diff --git a/src/Features/Core/Portable/Ambiguity/AbstractAliasAmbiguousTypeCodeFixProvider.cs b/src/Features/Core/Portable/AliasAmbiguousType/AbstractAliasAmbiguousTypeCodeFixProvider.cs similarity index 98% rename from src/Features/Core/Portable/Ambiguity/AbstractAliasAmbiguousTypeCodeFixProvider.cs rename to src/Features/Core/Portable/AliasAmbiguousType/AbstractAliasAmbiguousTypeCodeFixProvider.cs index 2b7ac62573189..59b0ed76db6e4 100644 --- a/src/Features/Core/Portable/Ambiguity/AbstractAliasAmbiguousTypeCodeFixProvider.cs +++ b/src/Features/Core/Portable/AliasAmbiguousType/AbstractAliasAmbiguousTypeCodeFixProvider.cs @@ -13,7 +13,7 @@ using Microsoft.CodeAnalysis.Shared.Extensions; using static Microsoft.CodeAnalysis.CodeActions.CodeAction; -namespace Microsoft.CodeAnalysis.Ambiguity +namespace Microsoft.CodeAnalysis.AliasAmbiguousType { internal abstract class AbstractAliasAmbiguousTypeCodeFixProvider : CodeFixProvider { diff --git a/src/Features/VisualBasic/Portable/Ambiguity/VisualBasicAliasAmbiguousTypeCodeFixProvider.vb b/src/Features/VisualBasic/Portable/AliasAmbiguousType/VisualBasicAliasAmbiguousTypeCodeFixProvider.vb similarity index 88% rename from src/Features/VisualBasic/Portable/Ambiguity/VisualBasicAliasAmbiguousTypeCodeFixProvider.vb rename to src/Features/VisualBasic/Portable/AliasAmbiguousType/VisualBasicAliasAmbiguousTypeCodeFixProvider.vb index 4e42e323eb9c5..bd5a83654b439 100644 --- a/src/Features/VisualBasic/Portable/Ambiguity/VisualBasicAliasAmbiguousTypeCodeFixProvider.vb +++ b/src/Features/VisualBasic/Portable/AliasAmbiguousType/VisualBasicAliasAmbiguousTypeCodeFixProvider.vb @@ -2,10 +2,10 @@ Imports System.Collections.Immutable Imports System.Composition -Imports Microsoft.CodeAnalysis.Ambiguity +Imports Microsoft.CodeAnalysis.AliasAmbiguousType Imports Microsoft.CodeAnalysis.CodeFixes -Namespace Microsoft.CodeAnalysis.VisualBasic.Ambiguity +Namespace Microsoft.CodeAnalysis.VisualBasic.AliasAmbiguousType From cc862d0770df18ca8561c67d0760d1fc5b1d9197 Mon Sep 17 00:00:00 2001 From: Martin Strecker Date: Sat, 6 Jan 2018 21:58:22 +0100 Subject: [PATCH 21/26] Naming corrections. --- .../AbstractAliasAmbiguousTypeCodeFixProvider.cs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/Features/Core/Portable/AliasAmbiguousType/AbstractAliasAmbiguousTypeCodeFixProvider.cs b/src/Features/Core/Portable/AliasAmbiguousType/AbstractAliasAmbiguousTypeCodeFixProvider.cs index 59b0ed76db6e4..be68b769f37b2 100644 --- a/src/Features/Core/Portable/AliasAmbiguousType/AbstractAliasAmbiguousTypeCodeFixProvider.cs +++ b/src/Features/Core/Portable/AliasAmbiguousType/AbstractAliasAmbiguousTypeCodeFixProvider.cs @@ -57,9 +57,9 @@ Task CreateChangedDocument(CancellationToken c) }; codeActionsBuilder.Add(new MyCodeAction(codeActionPreviewText, CreateChangedDocument)); } - var groupedTitle = string.Format(FeaturesResources.Alias_ambiguous_type_0, diagnosticNode.ToString()); - var groupedCodeAction = new GroupingCodeAction(groupedTitle, codeActionsBuilder.ToImmutable()); - context.RegisterCodeFix(groupedCodeAction, context.Diagnostics.First()); + var groupingTitle = string.Format(FeaturesResources.Alias_ambiguous_type_0, diagnosticNode.ToString()); + var groupingCodeAction = new GroupingCodeAction(groupingTitle, codeActionsBuilder.ToImmutable()); + context.RegisterCodeFix(groupingCodeAction, context.Diagnostics.First()); } } @@ -80,6 +80,7 @@ public MyCodeAction(string title, Func> create { } } + private class GroupingCodeAction : CodeActionWithNestedActions { public GroupingCodeAction(string title, ImmutableArray nestedActions) From ae7bdbfe6197e34e4a8134fe46693533a536b24d Mon Sep 17 00:00:00 2001 From: Martin Strecker Date: Sun, 7 Jan 2018 15:39:07 +0100 Subject: [PATCH 22/26] Title for the context menu is predicted by simplifying the generated alias node. --- ...CSharpAliasAmbiguousTypeCodeFixProvider.cs | 40 +++++++++++++++++++ ...stractAliasAmbiguousTypeCodeFixProvider.cs | 4 +- ...lBasicAliasAmbiguousTypeCodeFixProvider.vb | 36 +++++++++++++++++ 3 files changed, 78 insertions(+), 2 deletions(-) diff --git a/src/Features/CSharp/Portable/AliasAmbiguousType/CSharpAliasAmbiguousTypeCodeFixProvider.cs b/src/Features/CSharp/Portable/AliasAmbiguousType/CSharpAliasAmbiguousTypeCodeFixProvider.cs index a12ef32b7e639..1cb2c7d248551 100644 --- a/src/Features/CSharp/Portable/AliasAmbiguousType/CSharpAliasAmbiguousTypeCodeFixProvider.cs +++ b/src/Features/CSharp/Portable/AliasAmbiguousType/CSharpAliasAmbiguousTypeCodeFixProvider.cs @@ -2,8 +2,10 @@ using System.Collections.Immutable; using System.Composition; +using System.Diagnostics; using Microsoft.CodeAnalysis.AliasAmbiguousType; using Microsoft.CodeAnalysis.CodeFixes; +using Microsoft.CodeAnalysis.CSharp.Syntax; namespace Microsoft.CodeAnalysis.CSharp.AliasAmbiguousType { @@ -18,5 +20,43 @@ internal class CSharpAliasAmbiguousTypeCodeFixProvider : AbstractAliasAmbiguousT public override ImmutableArray FixableDiagnosticIds => ImmutableArray.Create(CS0104); + + protected override string GetTextPreviewOfChange(SyntaxNode aliasNode) + { + Debug.Assert(aliasNode is UsingDirectiveSyntax); + // A poor man's name simplifier. For the preview of the context menu text the likely change is predicted by + // removing the global:: namespace alias if present. For the majority of cases this should be the same result + // as what the real Simplifier produces in the preview pane and when the fix is applied. + aliasNode = RemoveGlobalNamespaceAliasIfPresent(aliasNode); + return aliasNode.NormalizeWhitespace().ToFullString(); + } + + NameSyntax GetLeftmostQualifiedName(NameSyntax nameSyntax) + { + while (nameSyntax is QualifiedNameSyntax qualifiedNameSyntax) + { + nameSyntax = qualifiedNameSyntax.Left; + } + + return nameSyntax; + } + + private SyntaxNode RemoveGlobalNamespaceAliasIfPresent(SyntaxNode aliasNode) + { + var usingDirective = (UsingDirectiveSyntax)aliasNode; + var nameSyntax = usingDirective.Name; + var leftmostName = GetLeftmostQualifiedName(nameSyntax); + if (leftmostName is AliasQualifiedNameSyntax aliasQualifiedName && + aliasQualifiedName.Alias.Identifier.IsKind(SyntaxKind.GlobalKeyword)) + { + if (aliasQualifiedName.Parent is QualifiedNameSyntax parentOfGlobalAlias) + { + var replacement = parentOfGlobalAlias.WithLeft(SyntaxFactory.IdentifierName(aliasQualifiedName.Name.Identifier)); + usingDirective = usingDirective.ReplaceNode(parentOfGlobalAlias, replacement); + } + } + + return usingDirective; + } } } diff --git a/src/Features/Core/Portable/AliasAmbiguousType/AbstractAliasAmbiguousTypeCodeFixProvider.cs b/src/Features/Core/Portable/AliasAmbiguousType/AbstractAliasAmbiguousTypeCodeFixProvider.cs index be68b769f37b2..e793f06e7a00f 100644 --- a/src/Features/Core/Portable/AliasAmbiguousType/AbstractAliasAmbiguousTypeCodeFixProvider.cs +++ b/src/Features/Core/Portable/AliasAmbiguousType/AbstractAliasAmbiguousTypeCodeFixProvider.cs @@ -18,6 +18,8 @@ namespace Microsoft.CodeAnalysis.AliasAmbiguousType internal abstract class AbstractAliasAmbiguousTypeCodeFixProvider : CodeFixProvider { + protected abstract string GetTextPreviewOfChange(SyntaxNode aliasNode); + public override FixAllProvider GetFixAllProvider() => null; public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context) @@ -63,8 +65,6 @@ Task CreateChangedDocument(CancellationToken c) } } - private static string GetTextPreviewOfChange(SyntaxNode newNode) => newNode.NormalizeWhitespace().ToFullString(); - private static bool SymbolCandidatesContainsSupportedSymbols(SymbolInfo symbolInfo) => symbolInfo.CandidateReason == CandidateReason.Ambiguous && // Arity: Aliases can only name closed constructed types. diff --git a/src/Features/VisualBasic/Portable/AliasAmbiguousType/VisualBasicAliasAmbiguousTypeCodeFixProvider.vb b/src/Features/VisualBasic/Portable/AliasAmbiguousType/VisualBasicAliasAmbiguousTypeCodeFixProvider.vb index bd5a83654b439..6c10ea6a76783 100644 --- a/src/Features/VisualBasic/Portable/AliasAmbiguousType/VisualBasicAliasAmbiguousTypeCodeFixProvider.vb +++ b/src/Features/VisualBasic/Portable/AliasAmbiguousType/VisualBasicAliasAmbiguousTypeCodeFixProvider.vb @@ -4,6 +4,7 @@ Imports System.Collections.Immutable Imports System.Composition Imports Microsoft.CodeAnalysis.AliasAmbiguousType Imports Microsoft.CodeAnalysis.CodeFixes +Imports Microsoft.CodeAnalysis.VisualBasic.Syntax Namespace Microsoft.CodeAnalysis.VisualBasic.AliasAmbiguousType @@ -17,5 +18,40 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.AliasAmbiguousType Public Overrides ReadOnly Property FixableDiagnosticIds As ImmutableArray(Of String) = ImmutableArray.Create(BC30561) + Protected Overrides Function GetTextPreviewOfChange(aliasNode As SyntaxNode) As String + Debug.Assert(TypeOf aliasNode Is ImportsStatementSyntax) + ' A poor man's name simplifier. For the preview of the context menu text the likely change is predicted by + ' removing the global. namespace alias if present. For the majority Of cases this should be the same result + ' as what the real Simplifier produces in the preview pane And when the fix is applied. + ' The real Simplifier also removes Module names from the import which is not supported here. + aliasNode = RemoveGlobalNamespaceAliasIfPresent(aliasNode) + Return aliasNode.NormalizeWhitespace().ToFullString() + End Function + + Private Function RemoveGlobalNamespaceAliasIfPresent(aliasNode As SyntaxNode) As SyntaxNode + Dim importsStatement = CType(aliasNode, ImportsStatementSyntax) + If importsStatement.ImportsClauses.Count = 1 AndAlso + TypeOf importsStatement.ImportsClauses(0) Is SimpleImportsClauseSyntax Then + Dim importsName = CType(importsStatement.ImportsClauses(0), SimpleImportsClauseSyntax).Name + Dim leftMostNameSyntax = GetLeftmostName(importsName) + If TypeOf leftMostNameSyntax Is GlobalNameSyntax Then + If TypeOf leftMostNameSyntax.Parent Is QualifiedNameSyntax Then + Dim parentOfGlobal = CType(leftMostNameSyntax.Parent, QualifiedNameSyntax) + Dim replacement = SyntaxFactory.IdentifierName(parentOfGlobal.Right.Identifier) + importsStatement = importsStatement.ReplaceNode(parentOfGlobal, replacement) + End If + End If + End If + + Return importsStatement + End Function + + Private Function GetLeftmostName(name As NameSyntax) As NameSyntax + While TypeOf name Is QualifiedNameSyntax + name = CType(name, QualifiedNameSyntax).Left + End While + + Return name + End Function End Class End Namespace From b752f84ddebcabb0767f0790a10e48fb00e76e41 Mon Sep 17 00:00:00 2001 From: Martin Strecker Date: Mon, 8 Jan 2018 06:13:28 +0100 Subject: [PATCH 23/26] Simplified generation of CodeAction title. --- ...CSharpAliasAmbiguousTypeCodeFixProvider.cs | 42 ++----------------- ...stractAliasAmbiguousTypeCodeFixProvider.cs | 9 ++-- ...lBasicAliasAmbiguousTypeCodeFixProvider.vb | 36 +--------------- 3 files changed, 9 insertions(+), 78 deletions(-) diff --git a/src/Features/CSharp/Portable/AliasAmbiguousType/CSharpAliasAmbiguousTypeCodeFixProvider.cs b/src/Features/CSharp/Portable/AliasAmbiguousType/CSharpAliasAmbiguousTypeCodeFixProvider.cs index 1cb2c7d248551..208d113e56f97 100644 --- a/src/Features/CSharp/Portable/AliasAmbiguousType/CSharpAliasAmbiguousTypeCodeFixProvider.cs +++ b/src/Features/CSharp/Portable/AliasAmbiguousType/CSharpAliasAmbiguousTypeCodeFixProvider.cs @@ -2,10 +2,9 @@ using System.Collections.Immutable; using System.Composition; -using System.Diagnostics; using Microsoft.CodeAnalysis.AliasAmbiguousType; using Microsoft.CodeAnalysis.CodeFixes; -using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Shared.Extensions; namespace Microsoft.CodeAnalysis.CSharp.AliasAmbiguousType { @@ -21,42 +20,7 @@ internal class CSharpAliasAmbiguousTypeCodeFixProvider : AbstractAliasAmbiguousT public override ImmutableArray FixableDiagnosticIds => ImmutableArray.Create(CS0104); - protected override string GetTextPreviewOfChange(SyntaxNode aliasNode) - { - Debug.Assert(aliasNode is UsingDirectiveSyntax); - // A poor man's name simplifier. For the preview of the context menu text the likely change is predicted by - // removing the global:: namespace alias if present. For the majority of cases this should be the same result - // as what the real Simplifier produces in the preview pane and when the fix is applied. - aliasNode = RemoveGlobalNamespaceAliasIfPresent(aliasNode); - return aliasNode.NormalizeWhitespace().ToFullString(); - } - - NameSyntax GetLeftmostQualifiedName(NameSyntax nameSyntax) - { - while (nameSyntax is QualifiedNameSyntax qualifiedNameSyntax) - { - nameSyntax = qualifiedNameSyntax.Left; - } - - return nameSyntax; - } - - private SyntaxNode RemoveGlobalNamespaceAliasIfPresent(SyntaxNode aliasNode) - { - var usingDirective = (UsingDirectiveSyntax)aliasNode; - var nameSyntax = usingDirective.Name; - var leftmostName = GetLeftmostQualifiedName(nameSyntax); - if (leftmostName is AliasQualifiedNameSyntax aliasQualifiedName && - aliasQualifiedName.Alias.Identifier.IsKind(SyntaxKind.GlobalKeyword)) - { - if (aliasQualifiedName.Parent is QualifiedNameSyntax parentOfGlobalAlias) - { - var replacement = parentOfGlobalAlias.WithLeft(SyntaxFactory.IdentifierName(aliasQualifiedName.Name.Identifier)); - usingDirective = usingDirective.ReplaceNode(parentOfGlobalAlias, replacement); - } - } - - return usingDirective; - } + protected override string GetTextPreviewOfChange(string alias, ITypeSymbol typeSymbol) + => $"using { alias } = { typeSymbol.ToNameDisplayString() };"; } } diff --git a/src/Features/Core/Portable/AliasAmbiguousType/AbstractAliasAmbiguousTypeCodeFixProvider.cs b/src/Features/Core/Portable/AliasAmbiguousType/AbstractAliasAmbiguousTypeCodeFixProvider.cs index e793f06e7a00f..bd28196edfc20 100644 --- a/src/Features/Core/Portable/AliasAmbiguousType/AbstractAliasAmbiguousTypeCodeFixProvider.cs +++ b/src/Features/Core/Portable/AliasAmbiguousType/AbstractAliasAmbiguousTypeCodeFixProvider.cs @@ -18,7 +18,7 @@ namespace Microsoft.CodeAnalysis.AliasAmbiguousType internal abstract class AbstractAliasAmbiguousTypeCodeFixProvider : CodeFixProvider { - protected abstract string GetTextPreviewOfChange(SyntaxNode aliasNode); + protected abstract string GetTextPreviewOfChange(string aliasName, ITypeSymbol typeSymbol); public override FixAllProvider GetFixAllProvider() => null; @@ -42,18 +42,17 @@ public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context) { var addImportService = document.GetLanguageService(); var syntaxGenerator = document.GetLanguageService(); - var diagnostic = context.Diagnostics.First(); var compilation = semanticModel.Compilation; var optionSet = await document.GetOptionsAsync(cancellationToken).ConfigureAwait(false); var placeSystemNamespaceFirst = optionSet.GetOption(GenerationOptions.PlaceSystemNamespaceFirst, document.Project.Language); var codeActionsBuilder = ImmutableArray.CreateBuilder(symbolInfo.CandidateSymbols.Length); - foreach (var symbol in symbolInfo.CandidateSymbols) + foreach (var symbol in symbolInfo.CandidateSymbols.Cast()) { var typeName = symbol.Name; - var aliasDirective = syntaxGenerator.AliasImportDeclaration(typeName, (ITypeSymbol)symbol); - var codeActionPreviewText = GetTextPreviewOfChange(aliasDirective); + var codeActionPreviewText = GetTextPreviewOfChange(typeName, symbol); Task CreateChangedDocument(CancellationToken c) { + var aliasDirective = syntaxGenerator.AliasImportDeclaration(typeName, symbol); var newRoot = addImportService.AddImport(compilation, root, diagnosticNode, aliasDirective, placeSystemNamespaceFirst); return Task.FromResult(document.WithSyntaxRoot(newRoot)); }; diff --git a/src/Features/VisualBasic/Portable/AliasAmbiguousType/VisualBasicAliasAmbiguousTypeCodeFixProvider.vb b/src/Features/VisualBasic/Portable/AliasAmbiguousType/VisualBasicAliasAmbiguousTypeCodeFixProvider.vb index 6c10ea6a76783..fb033c7c4c38a 100644 --- a/src/Features/VisualBasic/Portable/AliasAmbiguousType/VisualBasicAliasAmbiguousTypeCodeFixProvider.vb +++ b/src/Features/VisualBasic/Portable/AliasAmbiguousType/VisualBasicAliasAmbiguousTypeCodeFixProvider.vb @@ -4,7 +4,6 @@ Imports System.Collections.Immutable Imports System.Composition Imports Microsoft.CodeAnalysis.AliasAmbiguousType Imports Microsoft.CodeAnalysis.CodeFixes -Imports Microsoft.CodeAnalysis.VisualBasic.Syntax Namespace Microsoft.CodeAnalysis.VisualBasic.AliasAmbiguousType @@ -18,40 +17,9 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.AliasAmbiguousType Public Overrides ReadOnly Property FixableDiagnosticIds As ImmutableArray(Of String) = ImmutableArray.Create(BC30561) - Protected Overrides Function GetTextPreviewOfChange(aliasNode As SyntaxNode) As String - Debug.Assert(TypeOf aliasNode Is ImportsStatementSyntax) - ' A poor man's name simplifier. For the preview of the context menu text the likely change is predicted by - ' removing the global. namespace alias if present. For the majority Of cases this should be the same result - ' as what the real Simplifier produces in the preview pane And when the fix is applied. - ' The real Simplifier also removes Module names from the import which is not supported here. - aliasNode = RemoveGlobalNamespaceAliasIfPresent(aliasNode) - Return aliasNode.NormalizeWhitespace().ToFullString() + Protected Overrides Function GetTextPreviewOfChange(aliasName As String, typeSymbol As ITypeSymbol) As String + Return $"Imports { aliasName } = { typeSymbol.ToNameDisplayString() }" End Function - Private Function RemoveGlobalNamespaceAliasIfPresent(aliasNode As SyntaxNode) As SyntaxNode - Dim importsStatement = CType(aliasNode, ImportsStatementSyntax) - If importsStatement.ImportsClauses.Count = 1 AndAlso - TypeOf importsStatement.ImportsClauses(0) Is SimpleImportsClauseSyntax Then - Dim importsName = CType(importsStatement.ImportsClauses(0), SimpleImportsClauseSyntax).Name - Dim leftMostNameSyntax = GetLeftmostName(importsName) - If TypeOf leftMostNameSyntax Is GlobalNameSyntax Then - If TypeOf leftMostNameSyntax.Parent Is QualifiedNameSyntax Then - Dim parentOfGlobal = CType(leftMostNameSyntax.Parent, QualifiedNameSyntax) - Dim replacement = SyntaxFactory.IdentifierName(parentOfGlobal.Right.Identifier) - importsStatement = importsStatement.ReplaceNode(parentOfGlobal, replacement) - End If - End If - End If - - Return importsStatement - End Function - - Private Function GetLeftmostName(name As NameSyntax) As NameSyntax - While TypeOf name Is QualifiedNameSyntax - name = CType(name, QualifiedNameSyntax).Left - End While - - Return name - End Function End Class End Namespace From b631e9151db735ce6ad853e7d52b11a40a607eca Mon Sep 17 00:00:00 2001 From: Martin Strecker Date: Mon, 8 Jan 2018 08:08:39 +0100 Subject: [PATCH 24/26] Added test for code fix title. --- .../CSharpTest/AliasAmbiguousType/AliasAmbiguousTypeTests.cs | 2 ++ .../Diagnostics/AliasAmbiguousType/AliasAmbiguousTypeTests.vb | 1 + .../AbstractAliasAmbiguousTypeCodeFixProvider.cs | 2 +- 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/EditorFeatures/CSharpTest/AliasAmbiguousType/AliasAmbiguousTypeTests.cs b/src/EditorFeatures/CSharpTest/AliasAmbiguousType/AliasAmbiguousTypeTests.cs index c0198215cda7c..28a5728fa032f 100644 --- a/src/EditorFeatures/CSharpTest/AliasAmbiguousType/AliasAmbiguousTypeTests.cs +++ b/src/EditorFeatures/CSharpTest/AliasAmbiguousType/AliasAmbiguousTypeTests.cs @@ -80,6 +80,7 @@ void M() }"; await TestInRegularAndScriptAsync(initialMarkup, expectedMarkup0, index: 0); await TestInRegularAndScriptAsync(initialMarkup, expectedMarkup1, index: 1); + await TestSmartTagTextAsync(initialMarkup, "using Ambiguous = N1.Ambiguous;", index: 0); } [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsAliasAmbiguousType)] @@ -291,6 +292,7 @@ static void Main(string[] args) }"; await TestInRegularAndScriptAsync(initialMarkup, expectedMarkup0, index: 0); await TestInRegularAndScriptAsync(initialMarkup, expectedMarkup1, index: 1); + await TestSmartTagTextAsync(initialMarkup, "using Nested = Static.Nested;", index: 0); } [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsAliasAmbiguousType)] diff --git a/src/EditorFeatures/VisualBasicTest/Diagnostics/AliasAmbiguousType/AliasAmbiguousTypeTests.vb b/src/EditorFeatures/VisualBasicTest/Diagnostics/AliasAmbiguousType/AliasAmbiguousTypeTests.vb index fa20fb8fbaed9..0713cf13e7bca 100644 --- a/src/EditorFeatures/VisualBasicTest/Diagnostics/AliasAmbiguousType/AliasAmbiguousTypeTests.vb +++ b/src/EditorFeatures/VisualBasicTest/Diagnostics/AliasAmbiguousType/AliasAmbiguousTypeTests.vb @@ -60,6 +60,7 @@ Namespace N3 End Namespace" Await TestInRegularAndScriptAsync(initialMarkup, String.Format(expectedMarkupTemplate, "Imports Ambiguous = N1.Ambiguous"), index:=0) Await TestInRegularAndScriptAsync(initialMarkup, String.Format(expectedMarkupTemplate, "Imports Ambiguous = N2.Ambiguous"), index:=1) + Await TestSmartTagTextAsync(initialMarkup, "Imports Ambiguous = N1.Ambiguous", index:=0) End Function diff --git a/src/Features/Core/Portable/AliasAmbiguousType/AbstractAliasAmbiguousTypeCodeFixProvider.cs b/src/Features/Core/Portable/AliasAmbiguousType/AbstractAliasAmbiguousTypeCodeFixProvider.cs index bd28196edfc20..0b5b91d0f4e97 100644 --- a/src/Features/Core/Portable/AliasAmbiguousType/AbstractAliasAmbiguousTypeCodeFixProvider.cs +++ b/src/Features/Core/Portable/AliasAmbiguousType/AbstractAliasAmbiguousTypeCodeFixProvider.cs @@ -66,7 +66,7 @@ Task CreateChangedDocument(CancellationToken c) private static bool SymbolCandidatesContainsSupportedSymbols(SymbolInfo symbolInfo) => symbolInfo.CandidateReason == CandidateReason.Ambiguous && - // Arity: Aliases can only name closed constructed types. + // Arity: Aliases can only name closed constructed types. (See also proposal https://github.com/dotnet/csharplang/issues/1239) // Aliasing as a closed constructed type is possible but would require to remove the type arguments from the diagnosed node. // It is unlikely that the user wants that and so generic types are not supported. symbolInfo.CandidateSymbols.All(symbol => symbol.IsKind(SymbolKind.NamedType) && From 5577f1a249f3f2f14bf73a0d82f192d5e190be1d Mon Sep 17 00:00:00 2001 From: Martin Strecker Date: Wed, 17 Jan 2018 13:43:51 +0100 Subject: [PATCH 25/26] Formatting and small re-factorings (PR feedback). --- ...stractAliasAmbiguousTypeCodeFixProvider.cs | 24 ++++++------------- ...lBasicAliasAmbiguousTypeCodeFixProvider.vb | 2 -- 2 files changed, 7 insertions(+), 19 deletions(-) diff --git a/src/Features/Core/Portable/AliasAmbiguousType/AbstractAliasAmbiguousTypeCodeFixProvider.cs b/src/Features/Core/Portable/AliasAmbiguousType/AbstractAliasAmbiguousTypeCodeFixProvider.cs index 0b5b91d0f4e97..40a66612167a3 100644 --- a/src/Features/Core/Portable/AliasAmbiguousType/AbstractAliasAmbiguousTypeCodeFixProvider.cs +++ b/src/Features/Core/Portable/AliasAmbiguousType/AbstractAliasAmbiguousTypeCodeFixProvider.cs @@ -17,7 +17,6 @@ namespace Microsoft.CodeAnalysis.AliasAmbiguousType { internal abstract class AbstractAliasAmbiguousTypeCodeFixProvider : CodeFixProvider { - protected abstract string GetTextPreviewOfChange(string aliasName, ITypeSymbol typeSymbol); public override FixAllProvider GetFixAllProvider() => null; @@ -50,16 +49,15 @@ public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context) { var typeName = symbol.Name; var codeActionPreviewText = GetTextPreviewOfChange(typeName, symbol); - Task CreateChangedDocument(CancellationToken c) - { - var aliasDirective = syntaxGenerator.AliasImportDeclaration(typeName, symbol); - var newRoot = addImportService.AddImport(compilation, root, diagnosticNode, aliasDirective, placeSystemNamespaceFirst); - return Task.FromResult(document.WithSyntaxRoot(newRoot)); - }; - codeActionsBuilder.Add(new MyCodeAction(codeActionPreviewText, CreateChangedDocument)); + codeActionsBuilder.Add(new MyCodeAction(codeActionPreviewText, c => + { + var aliasDirective = syntaxGenerator.AliasImportDeclaration(typeName, symbol); + var newRoot = addImportService.AddImport(compilation, root, diagnosticNode, aliasDirective, placeSystemNamespaceFirst); + return Task.FromResult(document.WithSyntaxRoot(newRoot)); + })); } var groupingTitle = string.Format(FeaturesResources.Alias_ambiguous_type_0, diagnosticNode.ToString()); - var groupingCodeAction = new GroupingCodeAction(groupingTitle, codeActionsBuilder.ToImmutable()); + var groupingCodeAction = new CodeActionWithNestedActions(groupingTitle, codeActionsBuilder.ToImmutable(), isInlinable: true); context.RegisterCodeFix(groupingCodeAction, context.Diagnostics.First()); } } @@ -79,13 +77,5 @@ public MyCodeAction(string title, Func> create { } } - - private class GroupingCodeAction : CodeActionWithNestedActions - { - public GroupingCodeAction(string title, ImmutableArray nestedActions) - : base(title, nestedActions, isInlinable: true) - { - } - } } } diff --git a/src/Features/VisualBasic/Portable/AliasAmbiguousType/VisualBasicAliasAmbiguousTypeCodeFixProvider.vb b/src/Features/VisualBasic/Portable/AliasAmbiguousType/VisualBasicAliasAmbiguousTypeCodeFixProvider.vb index fb033c7c4c38a..7f57e68a61973 100644 --- a/src/Features/VisualBasic/Portable/AliasAmbiguousType/VisualBasicAliasAmbiguousTypeCodeFixProvider.vb +++ b/src/Features/VisualBasic/Portable/AliasAmbiguousType/VisualBasicAliasAmbiguousTypeCodeFixProvider.vb @@ -6,7 +6,6 @@ Imports Microsoft.CodeAnalysis.AliasAmbiguousType Imports Microsoft.CodeAnalysis.CodeFixes Namespace Microsoft.CodeAnalysis.VisualBasic.AliasAmbiguousType - Friend Class VisualBasicAliasAmbiguousTypeCodeFixProvider @@ -20,6 +19,5 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.AliasAmbiguousType Protected Overrides Function GetTextPreviewOfChange(aliasName As String, typeSymbol As ITypeSymbol) As String Return $"Imports { aliasName } = { typeSymbol.ToNameDisplayString() }" End Function - End Class End Namespace From a470e466eb1cbba76659f064a2f66984b8cdff81 Mon Sep 17 00:00:00 2001 From: Martin Strecker Date: Thu, 1 Mar 2018 11:02:12 +0100 Subject: [PATCH 26/26] Added SyntaxGenerator.AliasImportDeclaration overload that takes a SyntaxNode for the name to create an alias for. --- .../CodeGeneration/CSharpSyntaxGenerator.cs | 10 +++++----- .../Core/Portable/Editing/SyntaxGenerator.cs | 19 +++++++++++++++++-- .../Core/Portable/PublicAPI.Unshipped.txt | 4 +++- .../VisualBasicSyntaxGenerator.vb | 13 ++++++++----- 4 files changed, 33 insertions(+), 13 deletions(-) diff --git a/src/Workspaces/CSharp/Portable/CodeGeneration/CSharpSyntaxGenerator.cs b/src/Workspaces/CSharp/Portable/CodeGeneration/CSharpSyntaxGenerator.cs index b254250d30142..cde3ee831d91b 100644 --- a/src/Workspaces/CSharp/Portable/CodeGeneration/CSharpSyntaxGenerator.cs +++ b/src/Workspaces/CSharp/Portable/CodeGeneration/CSharpSyntaxGenerator.cs @@ -82,11 +82,8 @@ public override SyntaxNode NamespaceImportDeclaration(SyntaxNode name) return SyntaxFactory.UsingDirective((NameSyntax)name); } - public override SyntaxNode AliasImportDeclaration(string aliasIdentifierName, INamespaceOrTypeSymbol symbol) - { - var nameSyntax = symbol.GenerateNameSyntax(); - return SyntaxFactory.UsingDirective(SyntaxFactory.NameEquals(aliasIdentifierName), nameSyntax); - } + public override SyntaxNode AliasImportDeclaration(string aliasIdentifierName, SyntaxNode name) + => SyntaxFactory.UsingDirective(SyntaxFactory.NameEquals(aliasIdentifierName), (NameSyntax)name); public override SyntaxNode NamespaceDeclaration(SyntaxNode name, IEnumerable declarations) { @@ -3968,6 +3965,9 @@ public override SyntaxNode QualifiedName(SyntaxNode left, SyntaxNode right) return SyntaxFactory.QualifiedName((NameSyntax)left, (SimpleNameSyntax)right).WithAdditionalAnnotations(Simplifier.Annotation); } + public override SyntaxNode NameExpression(INamespaceOrTypeSymbol namespaceOrTypeSymbol) + => namespaceOrTypeSymbol.GenerateNameSyntax(); + public override SyntaxNode TypeExpression(ITypeSymbol typeSymbol) { return typeSymbol.GenerateTypeSyntax(); diff --git a/src/Workspaces/Core/Portable/Editing/SyntaxGenerator.cs b/src/Workspaces/Core/Portable/Editing/SyntaxGenerator.cs index 34863b14b810f..6708a81b4689c 100644 --- a/src/Workspaces/Core/Portable/Editing/SyntaxGenerator.cs +++ b/src/Workspaces/Core/Portable/Editing/SyntaxGenerator.cs @@ -788,11 +788,19 @@ public SyntaxNode NamespaceImportDeclaration(string name) } /// - /// Creates a alias import declaration. + /// Creates an alias import declaration. /// /// The name of the alias. /// The namespace or type to be aliased. - public abstract SyntaxNode AliasImportDeclaration(string aliasIdentifierName, INamespaceOrTypeSymbol symbol); + public SyntaxNode AliasImportDeclaration(string aliasIdentifierName, INamespaceOrTypeSymbol symbol) + => AliasImportDeclaration(aliasIdentifierName, NameExpression(symbol)); + + /// + /// Creates an alias import declaration. + /// + /// The name of the alias. + /// The namespace or type to be aliased. + public abstract SyntaxNode AliasImportDeclaration(string aliasIdentifierName, SyntaxNode name); /// /// Creates an attribute. @@ -1696,6 +1704,13 @@ public SyntaxNode DottedName(string dottedName) private static readonly char[] s_dotSeparator = new char[] { '.' }; + /// + /// Creates a name that denotes a type or namespace. + /// + /// The symbol to create a name for. + /// + public abstract SyntaxNode NameExpression(INamespaceOrTypeSymbol namespaceOrTypeSymbol); + /// /// Creates an expression that denotes a type. /// diff --git a/src/Workspaces/Core/Portable/PublicAPI.Unshipped.txt b/src/Workspaces/Core/Portable/PublicAPI.Unshipped.txt index 1d296abf18c52..7259513ebd7a2 100644 --- a/src/Workspaces/Core/Portable/PublicAPI.Unshipped.txt +++ b/src/Workspaces/Core/Portable/PublicAPI.Unshipped.txt @@ -1,11 +1,13 @@ Microsoft.CodeAnalysis.Editing.DeclarationModifiers.IsRef.get -> bool +Microsoft.CodeAnalysis.Editing.SyntaxGenerator.AliasImportDeclaration(string aliasIdentifierName, Microsoft.CodeAnalysis.INamespaceOrTypeSymbol symbol) -> Microsoft.CodeAnalysis.SyntaxNode Microsoft.CodeAnalysis.Editing.SyntaxGenerator.TupleElementExpression(Microsoft.CodeAnalysis.ITypeSymbol type, string name = null) -> Microsoft.CodeAnalysis.SyntaxNode Microsoft.CodeAnalysis.Editing.SyntaxGenerator.TupleTypeExpression(System.Collections.Generic.IEnumerable elementTypes, System.Collections.Generic.IEnumerable elementNames = null) -> Microsoft.CodeAnalysis.SyntaxNode Microsoft.CodeAnalysis.Editing.SyntaxGenerator.TupleTypeExpression(System.Collections.Generic.IEnumerable elements) -> Microsoft.CodeAnalysis.SyntaxNode Microsoft.CodeAnalysis.Editing.SyntaxGenerator.TupleTypeExpression(params Microsoft.CodeAnalysis.SyntaxNode[] elements) -> Microsoft.CodeAnalysis.SyntaxNode Microsoft.CodeAnalysis.Editing.SyntaxGenerator.WithAccessorDeclarations(Microsoft.CodeAnalysis.SyntaxNode declaration, params Microsoft.CodeAnalysis.SyntaxNode[] accessorDeclarations) -> Microsoft.CodeAnalysis.SyntaxNode -abstract Microsoft.CodeAnalysis.Editing.SyntaxGenerator.AliasImportDeclaration(string aliasIdentifierName, Microsoft.CodeAnalysis.INamespaceOrTypeSymbol symbol) -> Microsoft.CodeAnalysis.SyntaxNode +abstract Microsoft.CodeAnalysis.Editing.SyntaxGenerator.AliasImportDeclaration(string aliasIdentifierName, Microsoft.CodeAnalysis.SyntaxNode name) -> Microsoft.CodeAnalysis.SyntaxNode abstract Microsoft.CodeAnalysis.Editing.SyntaxGenerator.GetAccessorDeclaration(Microsoft.CodeAnalysis.Accessibility accessibility = Microsoft.CodeAnalysis.Accessibility.NotApplicable, System.Collections.Generic.IEnumerable statements = null) -> Microsoft.CodeAnalysis.SyntaxNode +abstract Microsoft.CodeAnalysis.Editing.SyntaxGenerator.NameExpression(Microsoft.CodeAnalysis.INamespaceOrTypeSymbol namespaceOrTypeSymbol) -> Microsoft.CodeAnalysis.SyntaxNode abstract Microsoft.CodeAnalysis.Editing.SyntaxGenerator.SetAccessorDeclaration(Microsoft.CodeAnalysis.Accessibility accessibility = Microsoft.CodeAnalysis.Accessibility.NotApplicable, System.Collections.Generic.IEnumerable statements = null) -> Microsoft.CodeAnalysis.SyntaxNode abstract Microsoft.CodeAnalysis.Editing.SyntaxGenerator.TupleElementExpression(Microsoft.CodeAnalysis.SyntaxNode type, string name = null) -> Microsoft.CodeAnalysis.SyntaxNode abstract Microsoft.CodeAnalysis.Editing.SyntaxGenerator.TupleExpression(System.Collections.Generic.IEnumerable arguments) -> Microsoft.CodeAnalysis.SyntaxNode diff --git a/src/Workspaces/VisualBasic/Portable/CodeGeneration/VisualBasicSyntaxGenerator.vb b/src/Workspaces/VisualBasic/Portable/CodeGeneration/VisualBasicSyntaxGenerator.vb index 5b25ea656da55..d5f0883a33c0c 100644 --- a/src/Workspaces/VisualBasic/Portable/CodeGeneration/VisualBasicSyntaxGenerator.vb +++ b/src/Workspaces/VisualBasic/Portable/CodeGeneration/VisualBasicSyntaxGenerator.vb @@ -341,6 +341,10 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.CodeGeneration Throw New NotSupportedException("ThrowExpressions are not supported in Visual Basic") End Function + Public Overrides Function NameExpression(namespaceOrTypeSymbol As INamespaceOrTypeSymbol) As SyntaxNode + Return namespaceOrTypeSymbol.GenerateTypeSyntax() + End Function + Public Overrides Function TypeExpression(typeSymbol As ITypeSymbol) As SyntaxNode Return typeSymbol.GenerateTypeSyntax() End Function @@ -1500,16 +1504,15 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.CodeGeneration Return SyntaxFactory.ImportsStatement(SyntaxFactory.SingletonSeparatedList(Of ImportsClauseSyntax)(SyntaxFactory.SimpleImportsClause(DirectCast(name, NameSyntax)))) End Function - Public Overrides Function AliasImportDeclaration(aliasIdentifierName As String, symbol As INamespaceOrTypeSymbol) As SyntaxNode - Dim typeSyntax = symbol.GenerateTypeSyntax() - If TypeOf typeSyntax Is NameSyntax Then + Public Overrides Function AliasImportDeclaration(aliasIdentifierName As String, name As SyntaxNode) As SyntaxNode + If TypeOf name Is NameSyntax Then Return SyntaxFactory.ImportsStatement(SyntaxFactory.SeparatedList(Of ImportsClauseSyntax).Add( SyntaxFactory.SimpleImportsClause( SyntaxFactory.ImportAliasClause(aliasIdentifierName), - CType(typeSyntax, NameSyntax)))) + CType(name, NameSyntax)))) End If - Throw New ArgumentException("Symbol can not be named.", NameOf(symbol)) + Throw New ArgumentException("name is not a NameSyntax.", NameOf(name)) End Function Public Overrides Function NamespaceDeclaration(name As SyntaxNode, nestedDeclarations As IEnumerable(Of SyntaxNode)) As SyntaxNode