File: CodeModel\FileCodeModel_CodeGen.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.
 
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Threading;
using Microsoft.CodeAnalysis;
using Microsoft.VisualStudio.LanguageServices.Implementation.CodeModel.InternalElements;
using Microsoft.VisualStudio.LanguageServices.Implementation.CodeModel.Interop;
using Microsoft.VisualStudio.LanguageServices.Implementation.Interop;
using Microsoft.VisualStudio.LanguageServices.Implementation.Utilities;
 
namespace Microsoft.VisualStudio.LanguageServices.Implementation.CodeModel;
 
/// <summary>
/// Implementations of EnvDTE.FileCodeModel for both languages.
/// </summary>
public sealed partial class FileCodeModel
{
    private SyntaxNode InsertAttribute(SyntaxNode containerNode, SyntaxNode attributeNode, int insertionIndex)
    {
        return PerformEdit(document =>
        {
            var resultNode = CodeModelService.InsertAttribute(
                document, IsBatchOpen, insertionIndex, containerNode, attributeNode, CancellationToken.None, out var newDocument);
 
            return Tuple.Create(resultNode, newDocument);
        });
    }
 
    private SyntaxNode InsertAttributeArgument(SyntaxNode containerNode, SyntaxNode attributeArgumentNode, int insertionIndex)
    {
        return PerformEdit(document =>
        {
            var resultNode = CodeModelService.InsertAttributeArgument(
                document, IsBatchOpen, insertionIndex, containerNode, attributeArgumentNode, CancellationToken.None, out var newDocument);
 
            return Tuple.Create(resultNode, newDocument);
        });
    }
 
    private SyntaxNode InsertImport(SyntaxNode containerNode, SyntaxNode importNode, int insertionIndex)
    {
        return PerformEdit(document =>
        {
            var resultNode = CodeModelService.InsertImport(
                document, IsBatchOpen, insertionIndex, containerNode, importNode, CancellationToken.None, out var newDocument);
 
            return Tuple.Create(resultNode, newDocument);
        });
    }
 
    private SyntaxNode InsertMember(SyntaxNode containerNode, SyntaxNode memberNode, int insertionIndex)
    {
        return PerformEdit(document =>
        {
            var resultNode = CodeModelService.InsertMember(
                document, IsBatchOpen, insertionIndex, containerNode, memberNode, CancellationToken.None, out var newDocument);
 
            return Tuple.Create(resultNode, newDocument);
        });
    }
 
    private SyntaxNode InsertParameter(SyntaxNode containerNode, SyntaxNode parameterNode, int insertionIndex)
    {
        return PerformEdit(document =>
        {
            var resultNode = CodeModelService.InsertParameter(
                document, IsBatchOpen, insertionIndex, containerNode, parameterNode, CancellationToken.None, out var newDocument);
 
            return Tuple.Create(resultNode, newDocument);
        });
    }
 
    private EnvDTE.CodeElement CreateInternalCodeMember(CodeModelState state, FileCodeModel fileCodeModel, SyntaxNode node)
    {
        var element = CodeModelService.CreateInternalCodeElement(state, fileCodeModel, node);
        if (IsBatchOpen)
        {
            var codeElement = ComAggregate.TryGetManagedObject<AbstractKeyedCodeElement>(element);
            if (codeElement != null)
            {
                _batchElements.Add(codeElement);
            }
        }
 
        return element;
    }
 
    private void UpdateNode(SyntaxNode node, SyntaxNode updatedNode)
    {
        PerformEdit(document =>
        {
            return CodeModelService.UpdateNode(document, node, updatedNode, CancellationToken.None);
        });
    }
 
    private static object[] GetValidArray(object itemOrArray, bool allowMultipleElements)
    {
        // TODO(DustinCa): Check VB's behavior when a bad array is passed.
        var result = new List<object>();
 
        if (itemOrArray != null && itemOrArray != DBNull.Value && itemOrArray != Type.Missing)
        {
            if (itemOrArray is Array realArray)
            {
                if (realArray.Rank != 1)
                {
                    throw Exceptions.ThrowEInvalidArg();
                }
 
                // TODO(DustinCa): This is duplicating the C# source. Is it true that non-zero based arrays are supported?
                var lowerBound = realArray.GetLowerBound(0);
                var upperBound = realArray.GetUpperBound(0);
 
                if (!allowMultipleElements && upperBound > lowerBound)
                {
                    throw Exceptions.ThrowEInvalidArg();
                }
 
                for (var i = lowerBound; i <= upperBound; i++)
                {
                    var item = realArray.GetValue(i);
 
                    if (item != null && item != DBNull.Value)
                    {
                        result.Add(item);
                    }
                }
            }
            else
            {
                result.Add(itemOrArray);
            }
        }
 
        return [.. result];
    }
 
    internal EnvDTE80.CodeAttributeArgument AddAttributeArgument(SyntaxNode containerNode, string name, string value, object position)
    {
        var attributeArgumentNode = CodeModelService.CreateAttributeArgumentNode(CodeModelService.GetUnescapedName(name), value);
        var insertionIndex = CodeModelService.PositionVariantToAttributeArgumentInsertionIndex(position, containerNode, fileCodeModel: this);
 
        var newNode = InsertAttributeArgument(containerNode, attributeArgumentNode, insertionIndex);
 
        return (EnvDTE80.CodeAttributeArgument)CodeModelService.CreateInternalCodeElement(this.State, fileCodeModel: this, node: newNode);
    }
 
    internal EnvDTE.CodeAttribute AddAttribute(SyntaxNode containerNode, string name, string value, object position, string? target = null)
    {
        containerNode = CodeModelService.GetNodeWithAttributes(containerNode);
        var attributeNode = CodeModelService.CreateAttributeNode(CodeModelService.GetUnescapedName(name), value, target);
        var insertionIndex = CodeModelService.PositionVariantToAttributeInsertionIndex(position, containerNode, fileCodeModel: this);
 
        var newNode = InsertAttribute(containerNode, attributeNode, insertionIndex);
 
        return (EnvDTE.CodeAttribute)CodeModelService.CreateInternalCodeElement(this.State, fileCodeModel: this, node: newNode);
    }
 
    internal EnvDTE.CodeParameter AddParameter(EnvDTE.CodeElement parent, SyntaxNode containerNode, string name, object type, object position)
    {
        var typeSymbol = CodeModelService.GetTypeSymbol(type, this.GetSemanticModel(), containerNode.SpanStart);
        var typeName = typeSymbol.GetEscapedFullName();
 
        var parameterNode = CodeModelService.CreateParameterNode(CodeModelService.GetUnescapedName(name), typeName);
        var insertionIndex = CodeModelService.PositionVariantToParameterInsertionIndex(position, containerNode, fileCodeModel: this);
 
        var newNode = InsertParameter(containerNode, parameterNode, insertionIndex);
 
        // Since parameters form part of the NodeKey for functions, delegates, and indexers,
        // creating a CodeParameter hooked up to the correct parent is a little tricky. After
        // the call to InsertParameter, the syntax tree has been updated, but not the NodeKey
        // map or the NodeKey in the parent CodeParameter. If we delegate the creation of the
        // CodeParameter to CodeModelService.CreateInternalCodeElement, it will attempt to
        // look up an element in the NodeKey map based on the new syntax tree. This will fail,
        // causing it to create a new, duplicate element for the parent. Later, when we
        // reacquire the NodeKeys, the original element will get the proper NodeKey, while the
        // duplicate will be updated to a meaningless NodeKey. Since the duplicate is the one
        // being used by the CodeParameter, most operations on it will then fail.
        // Instead, we need to have the parent passed in to us.
        var parentObj = ComAggregate.GetManagedObject<AbstractCodeMember>(parent);
        return CodeParameter.Create(this.State, parentObj, CodeModelService.GetParameterName(newNode));
    }
 
    internal EnvDTE.CodeClass AddClass(SyntaxNode containerNode, string name, object position, object bases, object implementedInterfaces, EnvDTE.vsCMAccess access)
    {
        var containerNodePosition = containerNode.SpanStart;
        var semanticModel = GetSemanticModel();
        var options = GetDocumentOptions();
 
        var baseArray = GetValidArray(bases, allowMultipleElements: false);
        Debug.Assert(baseArray.Length is 0 or 1);
 
        var baseTypeSymbol = baseArray.Length == 1
            ? (INamedTypeSymbol?)CodeModelService.GetTypeSymbol(baseArray[0], semanticModel, containerNodePosition)
            : null;
 
        var implementedInterfaceArray = GetValidArray(implementedInterfaces, allowMultipleElements: true);
 
        var implementedInterfaceSymbols = Array.ConvertAll(implementedInterfaceArray,
            i => (INamedTypeSymbol)CodeModelService.GetTypeSymbol(i, semanticModel, containerNodePosition));
 
        var newType = CreateTypeDeclaration(
            containerNode,
            TypeKind.Class,
            CodeModelService.GetUnescapedName(name),
            access,
            options,
            baseType: baseTypeSymbol,
            implementedInterfaces: [.. implementedInterfaceSymbols]);
 
        var insertionIndex = CodeModelService.PositionVariantToMemberInsertionIndex(position, containerNode, fileCodeModel: this);
 
        newType = InsertMember(containerNode, newType, insertionIndex);
 
        return (EnvDTE.CodeClass)CreateInternalCodeMember(this.State, fileCodeModel: this, node: newType);
    }
 
    internal EnvDTE.CodeDelegate AddDelegate(SyntaxNode containerNode, string name, object type, object position, EnvDTE.vsCMAccess access)
    {
        var containerNodePosition = containerNode.SpanStart;
        var semanticModel = GetSemanticModel();
        var options = GetDocumentOptions();
 
        var returnType = (INamedTypeSymbol)CodeModelService.GetTypeSymbol(type, semanticModel, containerNodePosition);
 
        var newType = CreateDelegateTypeDeclaration(containerNode, CodeModelService.GetUnescapedName(name), access, returnType, options);
        var insertionIndex = CodeModelService.PositionVariantToMemberInsertionIndex(position, containerNode, fileCodeModel: this);
 
        newType = InsertMember(containerNode, newType, insertionIndex);
 
        return (EnvDTE.CodeDelegate)CreateInternalCodeMember(this.State, fileCodeModel: this, node: newType);
    }
 
#pragma warning disable IDE0060 // Remove unused parameter - // TODO(DustinCa): "bases" is ignored in C# code model. Need to check VB.
    internal EnvDTE.CodeEnum AddEnum(SyntaxNode containerNode, string name, object position, object bases, EnvDTE.vsCMAccess access)
#pragma warning restore IDE0060 // Remove unused parameter
    {
        var options = GetDocumentOptions();
 
        var newType = CreateTypeDeclaration(containerNode, TypeKind.Enum, name, access, options);
        var insertionIndex = CodeModelService.PositionVariantToMemberInsertionIndex(position, containerNode, fileCodeModel: this);
 
        newType = InsertMember(containerNode, newType, insertionIndex);
 
        return (EnvDTE.CodeEnum)CreateInternalCodeMember(this.State, fileCodeModel: this, node: newType);
    }
 
    public EnvDTE.CodeVariable AddEnumMember(SyntaxNode containerNode, string name, object value, object position)
    {
        if (value is not null and not string)
        {
            throw Exceptions.ThrowEInvalidArg();
        }
 
        var semanticModel = GetSemanticModel();
        var options = GetDocumentOptions();
 
        var type = (ITypeSymbol?)semanticModel.GetDeclaredSymbol(containerNode);
        if (type == null)
        {
            throw Exceptions.ThrowEInvalidArg();
        }
 
        var newField = CreateFieldDeclaration(containerNode, CodeModelService.GetUnescapedName(name), EnvDTE.vsCMAccess.vsCMAccessPublic, type, options);
        if (value != null)
        {
            newField = CodeModelService.AddInitExpression(newField, (string)value);
        }
 
        var insertionIndex = CodeModelService.PositionVariantToMemberInsertionIndex(position, containerNode, fileCodeModel: this);
 
        newField = InsertMember(containerNode, newField, insertionIndex);
 
        return (EnvDTE.CodeVariable)CreateInternalCodeMember(this.State, fileCodeModel: this, node: newField);
    }
 
    public EnvDTE80.CodeEvent AddEvent(SyntaxNode containerNode, string name, string fullDelegateName, bool createPropertyStyleEvent, object position, EnvDTE.vsCMAccess access)
    {
        var containerNodePosition = containerNode.SpanStart;
        var semanticModel = GetSemanticModel();
        var options = GetDocumentOptions();
 
        var eventType = (INamedTypeSymbol)CodeModelService.GetTypeSymbol(fullDelegateName, semanticModel, containerNodePosition);
 
        var newEvent = CreateEventDeclaration(containerNode, CodeModelService.GetUnescapedName(name), access, eventType, options, createPropertyStyleEvent);
        var insertionIndex = CodeModelService.PositionVariantToMemberInsertionIndex(position, containerNode, fileCodeModel: this);
 
        newEvent = InsertMember(containerNode, newEvent, insertionIndex);
 
        return (EnvDTE80.CodeEvent)CreateInternalCodeMember(this.State, fileCodeModel: this, node: newEvent);
    }
 
    internal EnvDTE.CodeFunction AddFunction(SyntaxNode containerNode, string name, EnvDTE.vsCMFunction kind, object type, object position, EnvDTE.vsCMAccess access)
    {
        var options = GetDocumentOptions();
        kind = CodeModelService.ValidateFunctionKind(containerNode, kind, name);
 
        SyntaxNode newMember;
 
        if (kind is EnvDTE.vsCMFunction.vsCMFunctionSub or
            EnvDTE.vsCMFunction.vsCMFunctionFunction)
        {
            var containerNodePosition = containerNode.SpanStart;
            var semanticModel = GetSemanticModel();
            var returnType = kind == EnvDTE.vsCMFunction.vsCMFunctionFunction
                ? CodeModelService.GetTypeSymbol(type, semanticModel, containerNodePosition)
                : semanticModel.Compilation.GetSpecialType(SpecialType.System_Void);
 
            newMember = CreateMethodDeclaration(containerNode, CodeModelService.GetUnescapedName(name), access, returnType, options);
        }
        else if (kind == EnvDTE.vsCMFunction.vsCMFunctionConstructor)
        {
            newMember = CreateConstructorDeclaration(containerNode, CodeModelService.GetUnescapedName(name), access, options);
        }
        else
        {
            newMember = CreateDestructorDeclaration(containerNode, CodeModelService.GetUnescapedName(name), options);
        }
 
        var insertionIndex = CodeModelService.PositionVariantToMemberInsertionIndex(position, containerNode, fileCodeModel: this);
 
        newMember = InsertMember(containerNode, newMember, insertionIndex);
 
        return (EnvDTE.CodeFunction)CreateInternalCodeMember(this.State, fileCodeModel: this, node: newMember);
    }
 
    internal EnvDTE80.CodeImport AddImport(SyntaxNode containerNode, string name, object position, string alias)
    {
        var importNode = CodeModelService.CreateImportNode(CodeModelService.GetUnescapedName(name), alias);
        var insertionIndex = CodeModelService.PositionVariantToImportInsertionIndex(position, containerNode, fileCodeModel: this);
 
        var newNode = InsertImport(containerNode, importNode, insertionIndex);
 
        return (EnvDTE80.CodeImport)CodeModelService.CreateInternalCodeElement(this.State, fileCodeModel: this, node: newNode);
    }
 
    internal EnvDTE.CodeInterface AddInterface(SyntaxNode containerNode, string name, object position, object bases, EnvDTE.vsCMAccess access)
    {
        var containerNodePosition = containerNode.SpanStart;
        var semanticModel = GetSemanticModel();
        var options = GetDocumentOptions();
 
        var implementedInterfaceArray = GetValidArray(bases, allowMultipleElements: true);
 
        var implementedInterfaceSymbols = Array.ConvertAll(implementedInterfaceArray,
            i => (INamedTypeSymbol)CodeModelService.GetTypeSymbol(i, semanticModel, containerNodePosition));
 
        var newType = CreateTypeDeclaration(
            containerNode,
            TypeKind.Interface,
            CodeModelService.GetUnescapedName(name),
            access,
            options,
            implementedInterfaces: [.. implementedInterfaceSymbols]);
 
        var insertionIndex = CodeModelService.PositionVariantToMemberInsertionIndex(position, containerNode, fileCodeModel: this);
 
        newType = InsertMember(containerNode, newType, insertionIndex);
 
        return (EnvDTE.CodeInterface)CreateInternalCodeMember(this.State, fileCodeModel: this, node: newType);
    }
 
    internal EnvDTE.CodeNamespace AddNamespace(SyntaxNode containerNode, string name, object position)
    {
        var options = GetDocumentOptions();
        var newNamespace = CreateNamespaceDeclaration(containerNode, name, options);
        var insertionIndex = CodeModelService.PositionVariantToMemberInsertionIndex(position, containerNode, fileCodeModel: this);
 
        newNamespace = InsertMember(containerNode, newNamespace, insertionIndex);
 
        return (EnvDTE.CodeNamespace)CreateInternalCodeMember(this.State, fileCodeModel: this, node: newNamespace);
    }
 
    internal EnvDTE.CodeProperty AddProperty(SyntaxNode containerNode, string getterName, string putterName, object type, object position, EnvDTE.vsCMAccess access)
    {
        var isGetterPresent = !string.IsNullOrEmpty(getterName);
        var isPutterPresent = !string.IsNullOrEmpty(putterName);
 
        if ((!isGetterPresent && !isPutterPresent) ||
            (isGetterPresent && isPutterPresent && getterName != putterName))
        {
            throw Exceptions.ThrowEInvalidArg();
        }
 
        var containerNodePosition = containerNode.SpanStart;
        var semanticModel = GetSemanticModel();
        var options = GetDocumentOptions();
 
        var propertyType = CodeModelService.GetTypeSymbol(type, semanticModel, containerNodePosition);
        var newProperty = CreatePropertyDeclaration(
            containerNode,
            CodeModelService.GetUnescapedName(isGetterPresent ? getterName : putterName),
            isGetterPresent,
            isPutterPresent,
            access,
            propertyType,
            options);
        var insertionIndex = CodeModelService.PositionVariantToMemberInsertionIndex(position, containerNode, fileCodeModel: this);
 
        newProperty = InsertMember(containerNode, newProperty, insertionIndex);
 
        return (EnvDTE.CodeProperty)CreateInternalCodeMember(this.State, fileCodeModel: this, node: newProperty);
    }
 
#pragma warning disable IDE0060 // Remove unused parameter - // TODO(DustinCa): Old C# code base doesn't even check bases for validity -- does VB?
    internal EnvDTE.CodeStruct AddStruct(SyntaxNode containerNode, string name, object position, object bases, object implementedInterfaces, EnvDTE.vsCMAccess access)
#pragma warning restore IDE0060 // Remove unused parameter
    {
        var containerNodePosition = containerNode.SpanStart;
        var semanticModel = GetSemanticModel();
        var options = GetDocumentOptions();
 
        var implementedInterfaceArray = GetValidArray(bases, allowMultipleElements: true);
 
        var implementedInterfaceSymbols = Array.ConvertAll(implementedInterfaceArray,
            i => (INamedTypeSymbol)CodeModelService.GetTypeSymbol(i, semanticModel, containerNodePosition));
 
        var newType = CreateTypeDeclaration(
            containerNode,
            TypeKind.Struct,
            CodeModelService.GetUnescapedName(name),
            access,
            options,
            implementedInterfaces: [.. implementedInterfaceSymbols]);
 
        var insertionIndex = CodeModelService.PositionVariantToMemberInsertionIndex(position, containerNode, fileCodeModel: this);
 
        newType = InsertMember(containerNode, newType, insertionIndex);
 
        return (EnvDTE.CodeStruct)CreateInternalCodeMember(this.State, fileCodeModel: this, node: newType);
    }
 
    public EnvDTE.CodeVariable AddVariable(SyntaxNode containerNode, string name, object type, object position, EnvDTE.vsCMAccess access)
    {
        var containerNodePosition = containerNode.SpanStart;
        var semanticModel = GetSemanticModel();
        var options = GetDocumentOptions();
 
        var fieldType = CodeModelService.GetTypeSymbol(type, semanticModel, containerNodePosition);
        var newField = CreateFieldDeclaration(containerNode, CodeModelService.GetUnescapedName(name), access, fieldType, options);
        var insertionIndex = CodeModelService.PositionVariantToMemberInsertionIndex(position, containerNode, fileCodeModel: this);
 
        newField = InsertMember(containerNode, newField, insertionIndex);
 
        return (EnvDTE.CodeVariable)CreateInternalCodeMember(this.State, fileCodeModel: this, node: newField);
    }
 
    internal void UpdateAccess(SyntaxNode node, EnvDTE.vsCMAccess access)
    {
        node = CodeModelService.GetNodeWithModifiers(node);
        var updatedNode = CodeModelService.SetAccess(node, access);
 
        if (node != updatedNode)
        {
            UpdateNode(node, updatedNode);
        }
    }
 
    internal void UpdateAttributeTarget(SyntaxNode node, string value)
    {
        node = CodeModelService.GetAttributeTargetNode(node);
        var updatedNode = CodeModelService.SetAttributeTarget(node, value);
 
        if (node != updatedNode)
        {
            UpdateNode(node, updatedNode);
        }
    }
 
    internal void UpdateAttributeValue(SyntaxNode node, string value)
    {
        var updatedNode = CodeModelService.SetAttributeValue(node, value);
 
        if (node != updatedNode)
        {
            UpdateNode(node, updatedNode);
        }
    }
 
    internal void UpdateCanOverride(SyntaxNode node, bool value)
    {
        node = CodeModelService.GetNodeWithModifiers(node);
        var updatedNode = CodeModelService.SetCanOverride(node, value);
 
        if (node != updatedNode)
        {
            UpdateNode(node, updatedNode);
        }
    }
 
    internal void UpdateClassKind(SyntaxNode node, EnvDTE80.vsCMClassKind kind)
    {
        node = CodeModelService.GetNodeWithModifiers(node);
        var updatedNode = CodeModelService.SetClassKind(node, kind);
 
        if (node != updatedNode)
        {
            UpdateNode(node, updatedNode);
        }
    }
 
    internal void UpdateComment(SyntaxNode node, string value)
    {
        node = CodeModelService.GetNodeWithModifiers(node);
        var updatedNode = CodeModelService.SetComment(node, value);
 
        if (node != updatedNode)
        {
            UpdateNode(node, updatedNode);
        }
    }
 
    internal void UpdateConstKind(SyntaxNode node, EnvDTE80.vsCMConstKind kind)
    {
        node = CodeModelService.GetNodeWithModifiers(node);
        var updatedNode = CodeModelService.SetConstKind(node, kind);
 
        if (node != updatedNode)
        {
            UpdateNode(node, updatedNode);
        }
    }
 
    internal void UpdateDataTypeKind(SyntaxNode node, EnvDTE80.vsCMDataTypeKind kind)
    {
        node = CodeModelService.GetNodeWithModifiers(node);
        var updatedNode = CodeModelService.SetDataTypeKind(node, kind);
 
        if (node != updatedNode)
        {
            UpdateNode(node, updatedNode);
        }
    }
 
    internal void UpdateDocComment(SyntaxNode node, string value)
    {
        node = CodeModelService.GetNodeWithModifiers(node);
        var updatedNode = CodeModelService.SetDocComment(node, value);
 
        if (node != updatedNode)
        {
            UpdateNode(node, updatedNode);
        }
    }
 
    internal void UpdateInheritanceKind(SyntaxNode node, EnvDTE80.vsCMInheritanceKind kind)
    {
        node = CodeModelService.GetNodeWithModifiers(node);
        var updatedNode = CodeModelService.SetInheritanceKind(node, kind);
 
        if (node != updatedNode)
        {
            UpdateNode(node, updatedNode);
        }
    }
 
    internal void UpdateIsConstant(SyntaxNode node, bool value)
    {
        node = CodeModelService.GetNodeWithModifiers(node);
        var updatedNode = CodeModelService.SetIsConstant(node, value);
 
        if (node != updatedNode)
        {
            UpdateNode(node, updatedNode);
        }
    }
 
    internal void UpdateIsDefault(SyntaxNode node, bool value)
    {
        node = CodeModelService.GetNodeWithModifiers(node);
        var updatedNode = CodeModelService.SetIsDefault(node, value);
 
        if (node != updatedNode)
        {
            UpdateNode(node, updatedNode);
        }
    }
 
    internal void UpdateIsShared(SyntaxNode node, bool value)
    {
        node = CodeModelService.GetNodeWithModifiers(node);
        var updatedNode = CodeModelService.SetIsShared(node, value);
 
        if (node != updatedNode)
        {
            UpdateNode(node, updatedNode);
        }
    }
 
    internal void UpdateMustImplement(SyntaxNode node, bool value)
    {
        node = CodeModelService.GetNodeWithModifiers(node);
        var updatedNode = CodeModelService.SetMustImplement(node, value);
 
        if (node != updatedNode)
        {
            UpdateNode(node, updatedNode);
        }
    }
 
    internal void UpdateName(SyntaxNode node, string name)
    {
        node = CodeModelService.GetNodeWithName(node);
        var updatedNode = CodeModelService.SetName(node, name);
 
        if (node != updatedNode)
        {
            UpdateNode(node, updatedNode);
        }
    }
 
    internal void UpdateOverrideKind(SyntaxNode node, EnvDTE80.vsCMOverrideKind kind)
    {
        node = CodeModelService.GetNodeWithModifiers(node);
        var updatedNode = CodeModelService.SetOverrideKind(node, kind);
 
        if (node != updatedNode)
        {
            UpdateNode(node, updatedNode);
        }
    }
 
    internal void UpdateParameterKind(SyntaxNode node, EnvDTE80.vsCMParameterKind kind)
    {
        var updatedNode = CodeModelService.SetParameterKind(node, kind);
 
        if (node != updatedNode)
        {
            UpdateNode(node, updatedNode);
        }
    }
 
    internal void UpdateInitExpression(SyntaxNode node, string initExpression)
    {
        node = CodeModelService.GetNodeWithInitializer(node);
        var updatedNode = CodeModelService.AddInitExpression(node, initExpression);
 
        if (node != updatedNode)
        {
            UpdateNode(node, updatedNode);
        }
    }
 
    internal void UpdateType(SyntaxNode node, EnvDTE.CodeTypeRef codeTypeRef)
    {
        node = CodeModelService.GetNodeWithType(node);
        var typeSymbol = codeTypeRef != null
            ? CodeModelService.GetTypeSymbolFromFullName(codeTypeRef.AsFullName, GetCompilation())
            : null;
 
        var updatedNode = CodeModelService.SetType(node, typeSymbol);
 
        if (node != updatedNode)
        {
            UpdateNode(node, updatedNode);
        }
    }
 
    private static int? GetRealPosition(object? position)
    {
        int? realPosition;
 
        if (position == Type.Missing)
        {
            realPosition = null;
        }
        else if (position == null)
        {
            realPosition = 0;
        }
        else if (position is int i)
        {
            realPosition = i;
 
            // -1 means "add to the end". We'll null for that.
            if (realPosition == -1)
            {
                realPosition = null;
            }
        }
        else
        {
            throw Exceptions.ThrowEInvalidArg();
        }
 
        return realPosition;
    }
 
    internal void AddBase(SyntaxNode node, object @base, object? position = null)
    {
        var semanticModel = GetSemanticModel();
        var typeSymbol = CodeModelService.GetTypeSymbol(@base, semanticModel, node.SpanStart);
 
        // If a CodeType or CodeTypeRef was specified, we need to verify that a
        // valid base type was specified (i.e. class for class, interface for interface).
        if (CodeModelInterop.CanChangedVariantType(@base, VarEnum.VT_UNKNOWN) &&
            !CodeModelService.IsValidBaseType(node, typeSymbol))
        {
            throw Exceptions.ThrowEInvalidArg();
        }
 
        var realPosition = GetRealPosition(position);
 
        var updatedNode = CodeModelService.AddBase(node, typeSymbol, semanticModel, realPosition);
 
        if (node != updatedNode)
        {
            UpdateNode(node, updatedNode);
        }
    }
 
    internal void RemoveBase(SyntaxNode node, object element)
    {
        var semanticModel = GetSemanticModel();
        var typeSymbol = CodeModelService.GetTypeSymbol(element, semanticModel, node.SpanStart);
 
        var updatedNode = CodeModelService.RemoveBase(node, typeSymbol, semanticModel);
 
        if (node != updatedNode)
        {
            UpdateNode(node, updatedNode);
        }
    }
 
    internal string AddImplementedInterface(SyntaxNode node, object @base, object? position = null)
    {
        var semanticModel = GetSemanticModel();
        var typeSymbol = CodeModelService.GetTypeSymbol(@base, semanticModel, node.SpanStart);
 
        // If a CodeType or CodeTypeRef was specified, we need to verify that a
        // valid base type was specified (i.e. an interface for a struct or class).
        if (CodeModelInterop.CanChangedVariantType(@base, VarEnum.VT_UNKNOWN) &&
            !CodeModelService.IsValidInterfaceType(node, typeSymbol))
        {
            throw Exceptions.ThrowEInvalidArg();
        }
 
        var realPosition = GetRealPosition(position);
 
        var updatedNode = CodeModelService.AddImplementedInterface(node, typeSymbol, semanticModel, realPosition);
 
        if (node != updatedNode)
        {
            UpdateNode(node, updatedNode);
        }
 
        return typeSymbol.Name;
    }
 
    internal void RemoveImplementedInterface(SyntaxNode node, object element)
    {
        var semanticModel = GetSemanticModel();
        var typeSymbol = CodeModelService.GetTypeSymbol(element, semanticModel, node.SpanStart);
 
        var updatedNode = CodeModelService.RemoveImplementedInterface(node, typeSymbol, semanticModel);
 
        if (node != updatedNode)
        {
            UpdateNode(node, updatedNode);
        }
    }
}