| File: UseNamedArguments\CSharpUseNamedArgumentsCodeRefactoringProvider.cs | Web Access |
| Project: src\roslyn\src\Features\CSharp\Portable\Microsoft.CodeAnalysis.CSharp.Features.csproj (Microsoft.CodeAnalysis.CSharp.Features) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. using System.Collections.Generic; using System.Collections.Immutable; using System.Composition; using System.Diagnostics.CodeAnalysis; using System.Linq; using Microsoft.CodeAnalysis.CodeRefactorings; using Microsoft.CodeAnalysis.CSharp.Extensions; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.UseNamedArguments; namespace Microsoft.CodeAnalysis.CSharp.UseNamedArguments; using static SyntaxFactory; [ExtensionOrder(After = PredefinedCodeRefactoringProviderNames.IntroduceVariable)] [ExportCodeRefactoringProvider(LanguageNames.CSharp, Name = PredefinedCodeRefactoringProviderNames.UseNamedArguments), Shared] internal sealed class CSharpUseNamedArgumentsCodeRefactoringProvider : AbstractUseNamedArgumentsCodeRefactoringProvider { private abstract class BaseAnalyzer<TSyntax, TSyntaxList> : Analyzer<TSyntax, TSyntax, TSyntaxList> where TSyntax : SyntaxNode where TSyntaxList : SyntaxNode { protected abstract ExpressionSyntax GetArgumentExpression(TSyntax argumentSyntax); protected sealed override SyntaxNode? GetReceiver(SyntaxNode argument) => argument.Parent?.Parent; protected sealed override bool IsLegalToAddNamedArguments(ImmutableArray<IParameterSymbol> parameters, int argumentCount) => !parameters.Last().IsParams || parameters.Length >= argumentCount; protected override bool SupportsNonTrailingNamedArguments(ParseOptions options) => options.LanguageVersion() >= LanguageVersion.CSharp7_2; protected override bool IsImplicitIndexOrRangeIndexer(ImmutableArray<IParameterSymbol> parameters, TSyntax argument, SemanticModel semanticModel) { // There is no direct way to tell if an implicit range or index indexer was used. // The heuristic we use here is to check if the parameter doesn't fit the method it's being used with. // The easiest way to check that is to see if the argType only has at most an explicit conversion // to the indexers parameter types. var argType = semanticModel.GetTypeInfo(GetArgumentExpression(argument)).Type; if (argType?.ContainingNamespace is { Name: nameof(System), ContainingNamespace.IsGlobalNamespace: true } && (argType.Name == "Range" || argType.Name == "Index")) { var conversion = semanticModel.Compilation.ClassifyConversion(argType, parameters[0].Type); if (!conversion.Exists || conversion.IsExplicit) { return true; } } return false; } } private sealed class ArgumentAnalyzer : BaseAnalyzer<ArgumentSyntax, BaseArgumentListSyntax> { protected override bool IsPositionalArgument(ArgumentSyntax node) => node.NameColon == null; protected override SeparatedSyntaxList<ArgumentSyntax> GetArguments(BaseArgumentListSyntax argumentList) => argumentList.Arguments; protected override BaseArgumentListSyntax WithArguments( BaseArgumentListSyntax argumentList, IEnumerable<ArgumentSyntax> namedArguments, IEnumerable<SyntaxToken> separators) => argumentList.WithArguments(SeparatedList(namedArguments, separators)); protected override ArgumentSyntax WithName(ArgumentSyntax argument, string name) => argument.WithNameColon(NameColon(name.ToIdentifierName())); protected override ExpressionSyntax GetArgumentExpression(ArgumentSyntax argumentSyntax) => argumentSyntax.Expression; } private sealed class AttributeArgumentAnalyzer : BaseAnalyzer<AttributeArgumentSyntax, AttributeArgumentListSyntax> { protected override bool IsPositionalArgument(AttributeArgumentSyntax argument) => argument is { NameColon: null, NameEquals: null }; protected override SeparatedSyntaxList<AttributeArgumentSyntax> GetArguments(AttributeArgumentListSyntax argumentList) => argumentList.Arguments; protected override AttributeArgumentListSyntax WithArguments( AttributeArgumentListSyntax argumentList, IEnumerable<AttributeArgumentSyntax> namedArguments, IEnumerable<SyntaxToken> separators) => argumentList.WithArguments(SeparatedList(namedArguments, separators)); protected override AttributeArgumentSyntax WithName(AttributeArgumentSyntax argument, string name) => argument.WithNameColon(NameColon(name.ToIdentifierName())); protected override ExpressionSyntax GetArgumentExpression(AttributeArgumentSyntax argumentSyntax) => argumentSyntax.Expression; } [ImportingConstructor] [SuppressMessage("RoslynDiagnosticsReliability", "RS0033:Importing constructor should be [Obsolete]", Justification = "Used in test code: https://github.com/dotnet/roslyn/issues/42814")] public CSharpUseNamedArgumentsCodeRefactoringProvider() : base(new ArgumentAnalyzer(), new AttributeArgumentAnalyzer()) { } }