File: CodeModel\InternalElements\CodeParameter.cs
Web Access
Project: src\src\VisualStudio\Core\Impl\Microsoft.VisualStudio.LanguageServices.Implementation.csproj (Microsoft.VisualStudio.LanguageServices.Implementation)
// 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.
 
#nullable disable
 
using System;
using System.Runtime.InteropServices;
using System.Threading;
using Microsoft.CodeAnalysis;
using Microsoft.VisualStudio.LanguageServices.Implementation.CodeModel.Collections;
using Microsoft.VisualStudio.LanguageServices.Implementation.CodeModel.Interop;
using Microsoft.VisualStudio.LanguageServices.Implementation.Interop;
using Microsoft.VisualStudio.LanguageServices.Implementation.Utilities;
using Roslyn.Utilities;
 
namespace Microsoft.VisualStudio.LanguageServices.Implementation.CodeModel.InternalElements;
 
[ComVisible(true)]
[ComDefaultInterface(typeof(EnvDTE80.CodeParameter2))]
public sealed class CodeParameter : AbstractCodeElement, EnvDTE.CodeParameter, EnvDTE80.CodeParameter2, IParameterKind
{
    internal static EnvDTE.CodeParameter Create(
        CodeModelState state,
        AbstractCodeMember parent,
        string name)
    {
        var element = new CodeParameter(state, parent, name);
        return (EnvDTE.CodeParameter)ComAggregate.CreateAggregatedObject(element);
    }
 
    private readonly ParentHandle<AbstractCodeMember> _parentHandle;
    private readonly string _name;
 
    private CodeParameter(
        CodeModelState state,
        AbstractCodeMember parent,
        string name)
        : base(state, parent.FileCodeModel)
    {
        _parentHandle = new ParentHandle<AbstractCodeMember>(parent);
        _name = name;
    }
 
    private IParameterSymbol ParameterSymbol
    {
        get { return (IParameterSymbol)LookupSymbol(); }
    }
 
    private void UpdateNodeAndReacquireParentNodeKey<T>(Action<SyntaxNode, T> parameterUpdater, T value)
    {
        void updater(SyntaxNode n, T v)
        {
            var parentNode = _parentHandle.Value.LookupNode();
            var parentNodePath = new SyntaxPath(parentNode);
 
            parameterUpdater(n, v);
 
            _parentHandle.Value.ReacquireNodeKey(parentNodePath, CancellationToken.None);
        }
 
        UpdateNode(updater, value);
    }
 
    protected override EnvDTE.CodeElements GetCollection()
        => GetCollection<CodeParameter>(Parent);
 
    protected override string GetName()
        => _name;
 
    protected override string GetFullName()
    {
        var node = LookupNode();
        if (node == null)
        {
            return string.Empty;
        }
 
        return CodeModelService.GetParameterFullName(node);
    }
 
    internal override bool TryLookupNode(out SyntaxNode node)
    {
        node = null;
 
        var parentNode = _parentHandle.Value.LookupNode();
        if (parentNode == null)
        {
            return false;
        }
 
        if (!CodeModelService.TryGetParameterNode(parentNode, _name, out var parameterNode))
        {
            return false;
        }
 
        node = parameterNode;
        return node != null;
    }
 
    public override EnvDTE.vsCMElement Kind
    {
        get { return EnvDTE.vsCMElement.vsCMElementParameter; }
    }
 
    public override object Parent
    {
        get { return _parentHandle.Value; }
    }
 
    EnvDTE.CodeElement EnvDTE.CodeParameter.Parent
    {
        get { return (EnvDTE.CodeElement)Parent; }
    }
 
    EnvDTE.CodeElement EnvDTE80.CodeParameter2.Parent
    {
        get { return (EnvDTE.CodeElement)Parent; }
    }
 
    public override EnvDTE.CodeElements Children
    {
        get { return this.Attributes; }
    }
 
    public EnvDTE.CodeElements Attributes
    {
        get { return AttributeCollection.Create(this.State, this); }
    }
 
    public string DocComment
    {
        get
        {
            return string.Empty;
        }
 
        set
        {
            throw Exceptions.ThrowENotImpl();
        }
    }
 
    public EnvDTE.CodeTypeRef Type
    {
        get
        {
            return CodeTypeRef.Create(this.State, this, GetProjectId(), ParameterSymbol.Type);
        }
 
        set
        {
            UpdateNodeAndReacquireParentNodeKey(FileCodeModel.UpdateType, value);
        }
    }
 
    public EnvDTE80.vsCMParameterKind ParameterKind
    {
        get
        {
            return CodeModelService.GetParameterKind(LookupNode());
        }
 
        set
        {
            UpdateNodeAndReacquireParentNodeKey(FileCodeModel.UpdateParameterKind, value);
        }
    }
 
    public string DefaultValue
    {
        get
        {
            return CodeModelService.GetInitExpression(LookupNode());
        }
 
        set
        {
            UpdateNode(FileCodeModel.UpdateInitExpression, value);
        }
    }
 
    public EnvDTE.CodeAttribute AddAttribute(string name, string value, object position)
    {
        return FileCodeModel.EnsureEditor(() =>
        {
            return FileCodeModel.AddAttribute(LookupNode(), name, value, position);
        });
    }
 
    void IParameterKind.SetParameterPassingMode(PARAMETER_PASSING_MODE passingMode)
        => this.ParameterKind = this.CodeModelService.UpdateParameterKind(ParameterKind, passingMode);
 
    void IParameterKind.SetParameterArrayDimensions(int dimensions)
    {
        var type = this.ParameterSymbol.Type;
        var compilation = this.FileCodeModel.GetCompilation();
 
        var elementType = type is IArrayTypeSymbol
            ? ((IArrayTypeSymbol)type).ElementType
            : type;
 
        // The original C# implementation had a weird behavior where it wold allow setting array dimensions
        // to 0 to create an array with a single rank.
        var rank = Math.Max(dimensions, 1);
        var newType = compilation.CreateArrayTypeSymbol(elementType, rank);
 
        this.Type = CodeTypeRef.Create(this.State, this, GetProjectId(), newType);
    }
 
    int IParameterKind.GetParameterArrayCount()
    {
        var arrayType = this.ParameterSymbol.Type as IArrayTypeSymbol;
        var count = 0;
 
        while (arrayType != null)
        {
            count++;
            arrayType = arrayType.ElementType as IArrayTypeSymbol;
        }
 
        return count;
    }
 
    int IParameterKind.GetParameterArrayDimensions(int index)
    {
        if (index < 0)
        {
            throw Exceptions.ThrowEInvalidArg();
        }
 
        var arrayType = this.ParameterSymbol.Type as IArrayTypeSymbol;
        var count = 0;
 
        while (count < index && arrayType != null)
        {
            count++;
            arrayType = arrayType.ElementType as IArrayTypeSymbol;
        }
 
        if (arrayType == null)
        {
            throw Exceptions.ThrowEInvalidArg();
        }
 
        return arrayType.Rank;
    }
 
    PARAMETER_PASSING_MODE IParameterKind.GetParameterPassingMode()
    {
        var parameterKind = this.ParameterKind;
 
        if ((parameterKind & EnvDTE80.vsCMParameterKind.vsCMParameterKindRef) != 0)
        {
            return PARAMETER_PASSING_MODE.cmParameterTypeInOut;
        }
        else if ((parameterKind & EnvDTE80.vsCMParameterKind.vsCMParameterKindOut) != 0)
        {
            return PARAMETER_PASSING_MODE.cmParameterTypeOut;
        }
        else
        {
            return PARAMETER_PASSING_MODE.cmParameterTypeIn;
        }
    }
}