File: src\Workspaces\SharedUtilitiesAndExtensions\Compiler\CSharp\Extensions\ArgumentSyntaxExtensions.cs
Web Access
Project: src\src\Workspaces\CSharp\Portable\Microsoft.CodeAnalysis.CSharp.Workspaces.csproj (Microsoft.CodeAnalysis.CSharp.Workspaces)
// 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.Linq;
using System.Text.RegularExpressions;
using System.Threading;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Shared.Extensions;
 
namespace Microsoft.CodeAnalysis.CSharp.Extensions;
 
internal static class ArgumentSyntaxExtensions
{
    public static SyntaxTokenList GenerateParameterModifiers(this ArgumentSyntax argument)
    {
        if (argument.RefKindKeyword != default)
        {
            return [SyntaxFactory.Token(argument.RefKindKeyword.Kind())];
        }
 
        return default;
    }
 
    public static RefKind GetRefKind(this ArgumentSyntax? argument)
        => argument?.RefKindKeyword.Kind() switch
        {
            SyntaxKind.RefKeyword => RefKind.Ref,
            SyntaxKind.OutKeyword => RefKind.Out,
            SyntaxKind.InKeyword => RefKind.In,
            _ => RefKind.None,
        };
 
    /// <summary>
    /// Returns the parameter to which this argument is passed. If <paramref name="allowParams"/>
    /// is true, the last parameter will be returned if it is params parameter and the index of
    /// the specified argument is greater than the number of parameters.
    /// </summary>
    public static IParameterSymbol? DetermineParameter(
        this ArgumentSyntax argument,
        SemanticModel semanticModel,
        bool allowUncertainCandidates = false,
        bool allowParams = false,
        CancellationToken cancellationToken = default)
    {
        if (argument.Parent is not BaseArgumentListSyntax argumentList ||
            argumentList.Parent is null)
        {
            return null;
        }
 
        // Get the symbol as long if it's not null or if there is only one candidate symbol
        var symbolInfo = semanticModel.GetSymbolInfo(argumentList.Parent, cancellationToken);
        var symbols = symbolInfo.GetBestOrAllSymbols();
 
        if (symbols.Length >= 2 && !allowUncertainCandidates)
            return null;
 
        foreach (var symbol in symbols)
        {
            var parameters = symbol.GetParameters();
 
            // Handle named argument
            if (argument.NameColon != null && !argument.NameColon.IsMissing)
            {
                var name = argument.NameColon.Name.Identifier.ValueText;
                var parameter = parameters.FirstOrDefault(p => p.Name == name);
                if (parameter != null)
                    return parameter;
 
                continue;
            }
 
            // Handle positional argument
            var index = argumentList.Arguments.IndexOf(argument);
            if (index < 0)
                continue;
 
            if (index < parameters.Length)
            {
                var parameter = parameters[index];
                if (argument.RefOrOutKeyword.Kind() == SyntaxKind.OutKeyword &&
                    parameter.RefKind != RefKind.Out)
                {
                    continue;
                }
 
                if (argument.RefOrOutKeyword.Kind() == SyntaxKind.RefKeyword &&
                    parameter.RefKind != RefKind.Ref)
                {
                    continue;
                }
 
                return parameter;
            }
 
            if (allowParams)
            {
                var lastParameter = parameters.LastOrDefault();
                if (lastParameter == null)
                    continue;
 
                if (lastParameter.IsParams)
                    return lastParameter;
            }
        }
 
        return null;
    }
}