diff --git a/src/Features/CSharp/Portable/CodeRefactorings/CSharpRefactoringHelpersService.cs b/src/Features/CSharp/Portable/CodeRefactorings/CSharpRefactoringHelpersService.cs index d906bc1e9bd3facdb0e3dd166395da5b665303e6..992aca3b2bc634092c8bfa4382d5ff1c01b98c11 100644 --- a/src/Features/CSharp/Portable/CodeRefactorings/CSharpRefactoringHelpersService.cs +++ b/src/Features/CSharp/Portable/CodeRefactorings/CSharpRefactoringHelpersService.cs @@ -11,7 +11,7 @@ namespace Microsoft.CodeAnalysis.CSharp.CodeRefactorings { [ExportLanguageService(typeof(IRefactoringHelpersService), LanguageNames.CSharp), Shared] - internal class CSharpRefactoringHelpersService : AbstractRefactoringHelpersService + internal class CSharpRefactoringHelpersService : AbstractRefactoringHelpersService { protected override IEnumerable ExtractNodesSimple(SyntaxNode node, ISyntaxFactsService syntaxFacts) { diff --git a/src/Features/CSharp/Portable/NameTupleElement/CSharpNameTupleElementCodeRefactoringProvider.cs b/src/Features/CSharp/Portable/NameTupleElement/CSharpNameTupleElementCodeRefactoringProvider.cs index 2f80a3fbc44666c886be5c4c357511c312eadcc6..695e7bba652cf46ec846fc866ed5e8f625fe1c49 100644 --- a/src/Features/CSharp/Portable/NameTupleElement/CSharpNameTupleElementCodeRefactoringProvider.cs +++ b/src/Features/CSharp/Portable/NameTupleElement/CSharpNameTupleElementCodeRefactoringProvider.cs @@ -11,7 +11,7 @@ namespace Microsoft.CodeAnalysis.CSharp.NameTupleElement { [ExtensionOrder(After = PredefinedCodeRefactoringProviderNames.IntroduceVariable)] [ExportCodeRefactoringProvider(LanguageNames.CSharp, Name = nameof(CSharpNameTupleElementCodeRefactoringProvider)), Shared] - internal class CSharpNameTupleElementCodeRefactoringProvider : AbstractNameTupleElementCodeRefactoringProvider + internal class CSharpNameTupleElementCodeRefactoringProvider : AbstractNameTupleElementCodeRefactoringProvider { [ImportingConstructor] public CSharpNameTupleElementCodeRefactoringProvider() diff --git a/src/Features/CSharp/Portable/UseNamedArguments/CSharpUseNamedArgumentsCodeRefactoringProvider.cs b/src/Features/CSharp/Portable/UseNamedArguments/CSharpUseNamedArgumentsCodeRefactoringProvider.cs index 719ffe3c0e9d2327df3c19351b59036e207c3e15..66f5ebf60ba1b271cff770f484775abba0ddab46 100644 --- a/src/Features/CSharp/Portable/UseNamedArguments/CSharpUseNamedArgumentsCodeRefactoringProvider.cs +++ b/src/Features/CSharp/Portable/UseNamedArguments/CSharpUseNamedArgumentsCodeRefactoringProvider.cs @@ -15,7 +15,7 @@ namespace Microsoft.CodeAnalysis.CSharp.UseNamedArguments [ExportCodeRefactoringProvider(LanguageNames.CSharp, Name = nameof(CSharpUseNamedArgumentsCodeRefactoringProvider)), Shared] internal class CSharpUseNamedArgumentsCodeRefactoringProvider : AbstractUseNamedArgumentsCodeRefactoringProvider { - private abstract class BaseAnalyzer : Analyzer + private abstract class BaseAnalyzer : Analyzer where TSyntax : SyntaxNode where TSyntaxList : SyntaxNode { diff --git a/src/Features/Core/Portable/CodeRefactorings/AbstractRefactoringHelpersService.cs b/src/Features/Core/Portable/CodeRefactorings/AbstractRefactoringHelpersService.cs index 36514536f1a5c5789bb6df0713c5214c6b97661b..5782a88ca8311a8329779f9cf2ec863ec2d86c9d 100644 --- a/src/Features/Core/Portable/CodeRefactorings/AbstractRefactoringHelpersService.cs +++ b/src/Features/Core/Portable/CodeRefactorings/AbstractRefactoringHelpersService.cs @@ -4,7 +4,6 @@ using System.Collections.Generic; using System.Collections.Immutable; using System.Linq; -using System.Runtime.CompilerServices; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.LanguageServices; @@ -14,8 +13,9 @@ namespace Microsoft.CodeAnalysis.CodeRefactorings { - internal abstract class AbstractRefactoringHelpersService : IRefactoringHelpersService + internal abstract class AbstractRefactoringHelpersService : IRefactoringHelpersService where TExpressionSyntax : SyntaxNode + where TArgumentSyntax : SyntaxNode { public async Task> GetRelevantNodesAsync( Document document, TextSpan selectionRaw, @@ -105,9 +105,10 @@ internal abstract class AbstractRefactoringHelpersService : I AddNodesForTokenToLeft(syntaxFacts, relevantNodesBuilder, location, tokenToLeft, cancellationToken); // If the wanted node is an expression syntax -> traverse upwards even if location is deep within a SyntaxNode. - if (typeof(TSyntaxNode).IsSubclassOf(typeof(TExpressionSyntax)) || typeof(TSyntaxNode) == typeof(TExpressionSyntax)) + // We want to treat more types like expressions, e.g.: ArgumentSyntax should still trigger even if deep-in. + if (IsWantedTypeExpressionLike()) { - await AddNodesDeepInExpression(document, location, relevantNodesBuilder, cancellationToken).ConfigureAwait(false); + await AddNodesDeepIn(document, location, relevantNodesBuilder, cancellationToken).ConfigureAwait(false); } } @@ -119,6 +120,20 @@ internal abstract class AbstractRefactoringHelpersService : I } } + private static bool IsWantedTypeExpressionLike() where TSyntaxNode : SyntaxNode + { + var wantedType = typeof(TSyntaxNode); + var expressionType = typeof(TExpressionSyntax); + var argumentType = typeof(TArgumentSyntax); + + return IsAEqualOrSubclassOfB(wantedType, expressionType) || IsAEqualOrSubclassOfB(wantedType, argumentType); + + static bool IsAEqualOrSubclassOfB(Type a, Type b) + { + return a.IsSubclassOf(b) || a == b; + } + } + private async Task<(SyntaxToken tokenToRightOrIn, SyntaxToken tokenToLeft, int location)> GetTokensToRightOrInToLeftAndUpdatedLocation( Document document, SyntaxNode root, @@ -404,7 +419,7 @@ protected virtual IEnumerable ExtractNodesInHeader(SyntaxNode root, } } - protected virtual async Task AddNodesDeepInExpression( + protected virtual async Task AddNodesDeepIn( Document document, int position, ArrayBuilder relevantNodesBuilder, CancellationToken cancellationToken) where TSyntaxNode : SyntaxNode diff --git a/src/Features/Core/Portable/NameTupleElement/AbstractNameTupleElementCodeRefactoringProvider.cs b/src/Features/Core/Portable/NameTupleElement/AbstractNameTupleElementCodeRefactoringProvider.cs index a19f47e18dc006192f2abfca3d289f1c1b5aea1d..6c6731ceded21f358ed932618c3d9312285eeecb 100644 --- a/src/Features/Core/Portable/NameTupleElement/AbstractNameTupleElementCodeRefactoringProvider.cs +++ b/src/Features/Core/Portable/NameTupleElement/AbstractNameTupleElementCodeRefactoringProvider.cs @@ -12,10 +12,9 @@ namespace Microsoft.CodeAnalysis.NameTupleElement { - abstract class AbstractNameTupleElementCodeRefactoringProvider : CodeRefactoringProvider + abstract class AbstractNameTupleElementCodeRefactoringProvider : CodeRefactoringProvider where TArgumentSyntax : SyntaxNode - where TExpresionSyntax : SyntaxNode - where TTupleExpressionSyntax : TExpresionSyntax + where TTupleExpressionSyntax : SyntaxNode { protected abstract bool IsCloseParenOrComma(SyntaxToken token); protected abstract TArgumentSyntax WithName(TArgumentSyntax argument, string argumentName); @@ -45,9 +44,8 @@ public override async Task ComputeRefactoringsAsync(CodeRefactoringContext conte } var syntaxFacts = document.GetLanguageService(); - var expressions = await document.TryGetRelevantNodesAsync(span, cancellationToken).ConfigureAwait(false); - var argument = expressions.FirstOrDefault( - n => n.Parent is TArgumentSyntax && n.Parent.Parent is TTupleExpressionSyntax)?.Parent as TArgumentSyntax; + var potentialArguments = await document.TryGetRelevantNodesAsync(span, cancellationToken).ConfigureAwait(false); + var argument = potentialArguments.FirstOrDefault(n => n?.Parent is TTupleExpressionSyntax); if (argument == null || !syntaxFacts.IsSimpleArgument(argument)) { return default; @@ -63,7 +61,7 @@ public override async Task ComputeRefactoringsAsync(CodeRefactoringContext conte } syntaxFacts.GetPartsOfTupleExpression(tuple, out _, out var arguments, out _); - var argumentIndex = arguments.IndexOf(argument); + var argumentIndex = potentialArguments.IndexOf(argument); var elements = tupleType.TupleElements; if (elements.IsDefaultOrEmpty || argumentIndex >= elements.Length) { diff --git a/src/Features/Core/Portable/UseNamedArguments/AbstractUseNamedArgumentsCodeRefactoringProvider.cs b/src/Features/Core/Portable/UseNamedArguments/AbstractUseNamedArgumentsCodeRefactoringProvider.cs index 6146bb9dd358beb9d691e0a90c8f28d883a848ff..40124bf32d8c80469220633dee74064d60f52104 100644 --- a/src/Features/Core/Portable/UseNamedArguments/AbstractUseNamedArgumentsCodeRefactoringProvider.cs +++ b/src/Features/Core/Portable/UseNamedArguments/AbstractUseNamedArgumentsCodeRefactoringProvider.cs @@ -20,19 +20,17 @@ protected interface IAnalyzer Task ComputeRefactoringsAsync(CodeRefactoringContext context, SyntaxNode root); } - protected abstract class Analyzer : IAnalyzer + protected abstract class Analyzer : IAnalyzer where TBaseArgumentSyntax : SyntaxNode where TSimpleArgumentSyntax : TBaseArgumentSyntax where TArgumentListSyntax : SyntaxNode - where TExpressionSyntax : SyntaxNode { public async Task ComputeRefactoringsAsync( CodeRefactoringContext context, SyntaxNode root) { var (document, textSpan, cancellationToken) = context; - var expressions = await context.TryGetSelectedNodesAsync().ConfigureAwait(false); - var argument = expressions.FirstOrDefault(n => n.Parent is TSimpleArgumentSyntax)?.Parent as TSimpleArgumentSyntax; + var argument = await context.TryGetSelectedNodeAsync().ConfigureAwait(false) as TSimpleArgumentSyntax; if (argument == null) { return; diff --git a/src/Features/VisualBasic/Portable/CodeRefactorings/VisualBasicRefactoringHelpersService.vb b/src/Features/VisualBasic/Portable/CodeRefactorings/VisualBasicRefactoringHelpersService.vb index f02f9e396c40f0aff9a7f24c4b18f8940f6858ba..e33e0cf1c006dccefb4c3c4b9042592496955a6c 100644 --- a/src/Features/VisualBasic/Portable/CodeRefactorings/VisualBasicRefactoringHelpersService.vb +++ b/src/Features/VisualBasic/Portable/CodeRefactorings/VisualBasicRefactoringHelpersService.vb @@ -9,7 +9,7 @@ Imports Microsoft.CodeAnalysis.LanguageServices Namespace Microsoft.CodeAnalysis.VisualBasic.CodeRefactorings Friend Class VisualBasicRefactoringHelpersService - Inherits AbstractRefactoringHelpersService(Of ExpressionSyntax) + Inherits AbstractRefactoringHelpersService(Of ExpressionSyntax, ArgumentSyntax) Protected Overrides Iterator Function ExtractNodesSimple(node As SyntaxNode, syntaxFacts As ISyntaxFactsService) As IEnumerable(Of SyntaxNode) For Each baseExtraction In MyBase.ExtractNodesSimple(node, syntaxFacts) diff --git a/src/Features/VisualBasic/Portable/NameTupleElement/VisualBasicNameTupleElementCodeRefactoringProvider.vb b/src/Features/VisualBasic/Portable/NameTupleElement/VisualBasicNameTupleElementCodeRefactoringProvider.vb index 08eba57cf88f7f16c0464dd4175c59a7db54137d..e420b534f5ef836de038b89e4af8c7dc7813e08f 100644 --- a/src/Features/VisualBasic/Portable/NameTupleElement/VisualBasicNameTupleElementCodeRefactoringProvider.vb +++ b/src/Features/VisualBasic/Portable/NameTupleElement/VisualBasicNameTupleElementCodeRefactoringProvider.vb @@ -9,7 +9,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.NameTupleElement Friend Class VisualBasicNameTupleElementCodeRefactoringProvider - Inherits AbstractNameTupleElementCodeRefactoringProvider(Of SimpleArgumentSyntax, TupleExpressionSyntax, ExpressionSyntax) + Inherits AbstractNameTupleElementCodeRefactoringProvider(Of SimpleArgumentSyntax, TupleExpressionSyntax) Public Sub New() diff --git a/src/Features/VisualBasic/Portable/UseNamedArguments/VisualBasicUseNamedArgumentsCodeRefactoringProvider.vb b/src/Features/VisualBasic/Portable/UseNamedArguments/VisualBasicUseNamedArgumentsCodeRefactoringProvider.vb index 73d51fd9bae0db92c2902a2e4a625812b35383ac..daf41d5f9a749286b7265c471767c33de0fb955d 100644 --- a/src/Features/VisualBasic/Portable/UseNamedArguments/VisualBasicUseNamedArgumentsCodeRefactoringProvider.vb +++ b/src/Features/VisualBasic/Portable/UseNamedArguments/VisualBasicUseNamedArgumentsCodeRefactoringProvider.vb @@ -14,7 +14,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.UseNamedArguments Inherits AbstractUseNamedArgumentsCodeRefactoringProvider Private Class ArgumentAnalyzer - Inherits Analyzer(Of ArgumentSyntax, SimpleArgumentSyntax, ArgumentListSyntax, ExpressionSyntax) + Inherits Analyzer(Of ArgumentSyntax, SimpleArgumentSyntax, ArgumentListSyntax) Protected Overrides Function IsPositionalArgument(argument As SimpleArgumentSyntax) As Boolean Return argument.NameColonEquals Is Nothing