File: CodeModel\InternalElements\AbstractCodeType.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.Linq;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Microsoft.VisualStudio.LanguageServices.Implementation.CodeModel.Collections;
using Microsoft.VisualStudio.LanguageServices.Implementation.CodeModel.Interop;
using Microsoft.VisualStudio.LanguageServices.Implementation.Interop;
 
namespace Microsoft.VisualStudio.LanguageServices.Implementation.CodeModel.InternalElements;
 
public abstract class AbstractCodeType : AbstractCodeMember, EnvDTE.CodeType
{
    internal AbstractCodeType(
        CodeModelState state,
        FileCodeModel fileCodeModel,
        SyntaxNodeKey nodeKey,
        int? nodeKind)
        : base(state, fileCodeModel, nodeKey, nodeKind)
    {
    }
 
    internal AbstractCodeType(
        CodeModelState state,
        FileCodeModel fileCodeModel,
        int nodeKind,
        string name)
        : base(state, fileCodeModel, nodeKind, name)
    {
    }
 
    private SyntaxNode GetNamespaceOrTypeNode()
    {
        return LookupNode().Ancestors()
            .Where(n => CodeModelService.IsNamespace(n) || CodeModelService.IsType(n))
            .FirstOrDefault();
    }
 
    private SyntaxNode GetNamespaceNode()
    {
        return LookupNode().Ancestors()
            .Where(n => CodeModelService.IsNamespace(n))
            .FirstOrDefault();
    }
 
    internal INamedTypeSymbol LookupTypeSymbol()
        => (INamedTypeSymbol)LookupSymbol();
 
    protected override object GetExtenderNames()
        => CodeModelService.GetTypeExtenderNames();
 
    protected override object GetExtender(string name)
        => CodeModelService.GetTypeExtender(name, this);
 
    public override object Parent
    {
        get
        {
            var containingNamespaceOrType = GetNamespaceOrTypeNode();
 
            return containingNamespaceOrType != null
                ? FileCodeModel.GetOrCreateCodeElement<EnvDTE.CodeElement>(containingNamespaceOrType)
                : this.FileCodeModel;
        }
    }
 
    public override EnvDTE.CodeElements Children
    {
        get
        {
            return UnionCollection.Create(this.State, this,
                (ICodeElements)this.Attributes,
                (ICodeElements)InheritsImplementsCollection.Create(this.State, this, this.FileCodeModel, this.NodeKey),
                (ICodeElements)this.Members);
        }
    }
 
    public EnvDTE.CodeElements Bases
    {
        get
        {
            return BasesCollection.Create(this.State, this, this.FileCodeModel, this.NodeKey, interfaces: false);
        }
    }
 
    public EnvDTE80.vsCMDataTypeKind DataTypeKind
    {
        get
        {
            return CodeModelService.GetDataTypeKind(LookupNode(), (INamedTypeSymbol)LookupSymbol());
        }
 
        set
        {
            UpdateNode(FileCodeModel.UpdateDataTypeKind, value);
        }
    }
 
    public EnvDTE.CodeElements DerivedTypes
    {
        get { throw new NotImplementedException(); }
    }
 
    public EnvDTE.CodeElements ImplementedInterfaces
    {
        get
        {
            return BasesCollection.Create(this.State, this, this.FileCodeModel, this.NodeKey, interfaces: true);
        }
    }
 
    public EnvDTE.CodeElements Members
    {
        get
        {
            return TypeCollection.Create(this.State, this, this.FileCodeModel, this.NodeKey);
        }
    }
 
    public EnvDTE.CodeNamespace Namespace
    {
        get
        {
            var namespaceNode = GetNamespaceNode();
 
            return namespaceNode != null
                ? FileCodeModel.GetOrCreateCodeElement<EnvDTE.CodeNamespace>(namespaceNode)
                : null;
        }
    }
 
    /// <returns>True if the current type inherits from or equals the type described by the
    /// given full name.</returns>
    /// <remarks>Equality is included in the check as per Dev10 Bug #725630</remarks>
    public bool get_IsDerivedFrom(string fullName)
    {
        var currentType = LookupTypeSymbol();
        if (currentType == null)
        {
            return false;
        }
 
        var baseType = GetSemanticModel().Compilation.GetTypeByMetadataName(fullName);
        if (baseType == null)
        {
            return false;
        }
 
        return currentType.InheritsFromOrEquals(baseType);
    }
 
    public override bool IsCodeType
    {
        get { return true; }
    }
 
    public void RemoveMember(object element)
    {
        // Is this an EnvDTE.CodeElement that we created? If so, try to get the underlying code element object.
        var abstractCodeElement = ComAggregate.TryGetManagedObject<AbstractCodeElement>(element);
 
        if (abstractCodeElement == null)
        {
            if (element is EnvDTE.CodeElement codeElement)
            {
                // Is at least an EnvDTE.CodeElement? If so, try to retrieve it from the Members collection by name.
                // Note: This might throw an ArgumentException if the name isn't found in the collection.
 
                abstractCodeElement = ComAggregate.TryGetManagedObject<AbstractCodeElement>(this.Members.Item(codeElement.Name));
            }
            else if (element is string or int)
            {
                // Is this a string or int? If so, try to retrieve it from the Members collection. Again, this will
                // throw an ArgumentException if the name or index isn't found in the collection.
 
                abstractCodeElement = ComAggregate.TryGetManagedObject<AbstractCodeElement>(this.Members.Item(element));
            }
        }
 
        if (abstractCodeElement == null)
        {
            throw new ArgumentException(ServicesVSResources.Element_is_not_valid, nameof(element));
        }
 
        abstractCodeElement.Delete();
    }
 
    public EnvDTE.CodeElement AddBase(object @base, object position)
    {
        return FileCodeModel.EnsureEditor(() =>
        {
            FileCodeModel.AddBase(LookupNode(), @base, position);
 
            var codeElements = this.Bases as ICodeElements;
            var hr = codeElements.Item(1, out var element);
 
            if (ErrorHandler.Succeeded(hr))
            {
                return element;
            }
 
            return null;
        });
    }
 
    public EnvDTE.CodeInterface AddImplementedInterface(object @base, object position)
    {
        return FileCodeModel.EnsureEditor(() =>
        {
            var name = FileCodeModel.AddImplementedInterface(LookupNode(), @base, position);
 
            var codeElements = this.ImplementedInterfaces as ICodeElements;
            var hr = codeElements.Item(name, out var element);
 
            if (ErrorHandler.Succeeded(hr))
            {
                return element as EnvDTE.CodeInterface;
            }
 
            return null;
        });
    }
 
    public void RemoveBase(object element)
    {
        FileCodeModel.EnsureEditor(() =>
        {
            FileCodeModel.RemoveBase(LookupNode(), element);
        });
    }
 
    public void RemoveInterface(object element)
    {
        FileCodeModel.EnsureEditor(() =>
        {
            FileCodeModel.RemoveImplementedInterface(LookupNode(), element);
        });
    }
}