|
// 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.Diagnostics.CodeAnalysis;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Xml.Linq;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CodeGeneration;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.CodeGeneration;
using Microsoft.CodeAnalysis.CSharp.Extensions;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Editor;
using Microsoft.CodeAnalysis.Editor.Shared.Utilities;
using Microsoft.CodeAnalysis.Host;
using Microsoft.CodeAnalysis.Options;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Microsoft.CodeAnalysis.Text;
using Microsoft.VisualStudio.LanguageServices.CSharp.CodeModel.Extenders;
using Microsoft.VisualStudio.LanguageServices.CSharp.CodeModel.MethodXml;
using Microsoft.VisualStudio.LanguageServices.CSharp.Utilities;
using Microsoft.VisualStudio.LanguageServices.Implementation.CodeModel;
using Microsoft.VisualStudio.LanguageServices.Implementation.CodeModel.InternalElements;
using Microsoft.VisualStudio.LanguageServices.Implementation.CodeModel.Interop;
using Microsoft.VisualStudio.LanguageServices.Implementation.Utilities;
using Roslyn.Utilities;
namespace Microsoft.VisualStudio.LanguageServices.CSharp.CodeModel
{
using static CSharpSyntaxTokens;
internal partial class CSharpCodeModelService : AbstractCodeModelService
{
private static readonly SyntaxTree s_emptyTree = SyntaxFactory.ParseSyntaxTree(SourceText.From("", encoding: null, SourceHashAlgorithms.Default));
internal CSharpCodeModelService(
HostLanguageServices languageServiceProvider,
EditorOptionsService editorOptionsService,
IEnumerable<IRefactorNotifyService> refactorNotifyServices,
IThreadingContext threadingContext)
: base(languageServiceProvider,
editorOptionsService,
refactorNotifyServices,
BlankLineInGeneratedMethodFormattingRule.Instance,
EndRegionFormattingRule.Instance,
threadingContext)
{
}
private static readonly SymbolDisplayFormat s_codeTypeRefAsFullNameFormat =
new(
typeQualificationStyle: SymbolDisplayTypeQualificationStyle.NameAndContainingTypesAndNamespaces,
genericsOptions: SymbolDisplayGenericsOptions.IncludeTypeParameters,
miscellaneousOptions: SymbolDisplayMiscellaneousOptions.EscapeKeywordIdentifiers | SymbolDisplayMiscellaneousOptions.ExpandNullable);
private static readonly SymbolDisplayFormat s_codeTypeRefAsStringFormat =
new(
typeQualificationStyle: SymbolDisplayTypeQualificationStyle.NameAndContainingTypesAndNamespaces,
genericsOptions: SymbolDisplayGenericsOptions.IncludeTypeParameters,
miscellaneousOptions: SymbolDisplayMiscellaneousOptions.EscapeKeywordIdentifiers | SymbolDisplayMiscellaneousOptions.UseSpecialTypes);
private static readonly SymbolDisplayFormat s_externalNameFormat =
new(
miscellaneousOptions: SymbolDisplayMiscellaneousOptions.EscapeKeywordIdentifiers,
parameterOptions: SymbolDisplayParameterOptions.IncludeName);
private static readonly SymbolDisplayFormat s_externalFullNameFormat =
new(
typeQualificationStyle: SymbolDisplayTypeQualificationStyle.NameAndContainingTypesAndNamespaces,
memberOptions: SymbolDisplayMemberOptions.IncludeContainingType | SymbolDisplayMemberOptions.IncludeExplicitInterface,
genericsOptions: SymbolDisplayGenericsOptions.IncludeTypeParameters,
miscellaneousOptions: SymbolDisplayMiscellaneousOptions.EscapeKeywordIdentifiers,
parameterOptions: SymbolDisplayParameterOptions.IncludeName);
private static readonly SymbolDisplayFormat s_setTypeFormat =
new(
globalNamespaceStyle: SymbolDisplayGlobalNamespaceStyle.Omitted,
typeQualificationStyle: SymbolDisplayTypeQualificationStyle.NameAndContainingTypesAndNamespaces,
genericsOptions: SymbolDisplayGenericsOptions.IncludeTypeParameters,
miscellaneousOptions: SymbolDisplayMiscellaneousOptions.EscapeKeywordIdentifiers | SymbolDisplayMiscellaneousOptions.UseSpecialTypes);
private static bool IsNameableNode(SyntaxNode node)
{
switch (node.Kind())
{
case SyntaxKind.ClassDeclaration:
case SyntaxKind.RecordDeclaration:
case SyntaxKind.ConstructorDeclaration:
case SyntaxKind.ConversionOperatorDeclaration:
case SyntaxKind.DelegateDeclaration:
case SyntaxKind.DestructorDeclaration:
case SyntaxKind.EnumDeclaration:
case SyntaxKind.EnumMemberDeclaration:
case SyntaxKind.EventDeclaration:
case SyntaxKind.IndexerDeclaration:
case SyntaxKind.InterfaceDeclaration:
case SyntaxKind.MethodDeclaration:
case SyntaxKind.NamespaceDeclaration:
case SyntaxKind.FileScopedNamespaceDeclaration:
case SyntaxKind.OperatorDeclaration:
case SyntaxKind.PropertyDeclaration:
case SyntaxKind.StructDeclaration:
case SyntaxKind.RecordStructDeclaration:
return true;
case SyntaxKind.VariableDeclarator:
// Could be a regular field or an event field.
return node.FirstAncestorOrSelf<BaseFieldDeclarationSyntax>() != null;
default:
return false;
}
}
public override EnvDTE.vsCMElement GetElementKind(SyntaxNode node)
{
switch (node.Kind())
{
case SyntaxKind.ClassDeclaration:
return EnvDTE.vsCMElement.vsCMElementClass;
default:
Debug.Fail("Unsupported element kind: " + node.Kind());
throw Exceptions.ThrowEInvalidArg();
}
}
public override bool MatchesScope(SyntaxNode node, EnvDTE.vsCMElement scope)
{
switch (node.Kind())
{
case SyntaxKind.NamespaceDeclaration:
case SyntaxKind.FileScopedNamespaceDeclaration:
if (scope == EnvDTE.vsCMElement.vsCMElementNamespace &&
node.Parent != null)
{
return true;
}
break;
case SyntaxKind.ClassDeclaration:
if (scope == EnvDTE.vsCMElement.vsCMElementClass)
{
return true;
}
break;
case SyntaxKind.ConstructorDeclaration:
case SyntaxKind.DestructorDeclaration:
case SyntaxKind.MethodDeclaration:
case SyntaxKind.OperatorDeclaration:
case SyntaxKind.ConversionOperatorDeclaration:
case SyntaxKind.GetAccessorDeclaration:
case SyntaxKind.SetAccessorDeclaration:
case SyntaxKind.AddAccessorDeclaration:
case SyntaxKind.RemoveAccessorDeclaration:
if (scope == EnvDTE.vsCMElement.vsCMElementFunction)
{
return true;
}
break;
case SyntaxKind.EnumMemberDeclaration:
if (scope == EnvDTE.vsCMElement.vsCMElementVariable)
{
return true;
}
break;
case SyntaxKind.FieldDeclaration:
if (scope == EnvDTE.vsCMElement.vsCMElementVariable)
{
return true;
}
break;
case SyntaxKind.EventDeclaration:
case SyntaxKind.EventFieldDeclaration:
if (scope == EnvDTE.vsCMElement.vsCMElementEvent)
{
return true;
}
break;
case SyntaxKind.PropertyDeclaration:
case SyntaxKind.IndexerDeclaration:
if (scope == EnvDTE.vsCMElement.vsCMElementProperty)
{
return true;
}
break;
case SyntaxKind.Attribute:
if (scope == EnvDTE.vsCMElement.vsCMElementAttribute)
{
return true;
}
break;
case SyntaxKind.InterfaceDeclaration:
if (scope == EnvDTE.vsCMElement.vsCMElementInterface)
{
return true;
}
break;
case SyntaxKind.DelegateDeclaration:
if (scope == EnvDTE.vsCMElement.vsCMElementDelegate)
{
return true;
}
break;
case SyntaxKind.EnumDeclaration:
if (scope == EnvDTE.vsCMElement.vsCMElementEnum)
{
return true;
}
break;
case SyntaxKind.StructDeclaration:
if (scope == EnvDTE.vsCMElement.vsCMElementStruct)
{
return true;
}
break;
case SyntaxKind.UsingDirective:
if (scope == EnvDTE.vsCMElement.vsCMElementImportStmt &&
((UsingDirectiveSyntax)node).Name != null)
{
return true;
}
break;
case SyntaxKind.VariableDeclaration:
case SyntaxKind.VariableDeclarator:
// The parent of a VariableDeclarator might be an event or
// a field declaration. If the parent matches the desired
// scope, then this node matches the scope as well.
return MatchesScope(node.Parent!, scope);
case SyntaxKind.Parameter:
if (scope == EnvDTE.vsCMElement.vsCMElementParameter)
{
return true;
}
break;
default:
return false;
}
return false;
}
public override IEnumerable<SyntaxNode> GetOptionNodes(SyntaxNode parent)
// Only VB has Option statements
=> [];
public override IEnumerable<SyntaxNode> GetImportNodes(SyntaxNode parent)
=> parent switch
{
CompilationUnitSyntax compilationUnit => compilationUnit.Usings,
BaseNamespaceDeclarationSyntax baseNamespace => baseNamespace.Usings,
_ => [],
};
private static IEnumerable<SyntaxNode> GetAttributeNodes(SyntaxList<AttributeListSyntax> attributeDeclarationList)
{
foreach (var attributeDeclaration in attributeDeclarationList)
{
foreach (var attribute in attributeDeclaration.Attributes)
{
yield return attribute;
}
}
}
public override IEnumerable<SyntaxNode> GetAttributeNodes(SyntaxNode parent)
{
if (parent is CompilationUnitSyntax compilationUnit)
{
return GetAttributeNodes(compilationUnit.AttributeLists);
}
else if (parent is BaseTypeDeclarationSyntax baseType)
{
return GetAttributeNodes(baseType.AttributeLists);
}
else if (parent is BaseMethodDeclarationSyntax baseMethod)
{
return GetAttributeNodes(baseMethod.AttributeLists);
}
else if (parent is BasePropertyDeclarationSyntax baseProperty)
{
return GetAttributeNodes(baseProperty.AttributeLists);
}
else if (parent is BaseFieldDeclarationSyntax baseField)
{
return GetAttributeNodes(baseField.AttributeLists);
}
else if (parent is DelegateDeclarationSyntax delegateDecl)
{
return GetAttributeNodes(delegateDecl.AttributeLists);
}
else if (parent is EnumMemberDeclarationSyntax enumMember)
{
return GetAttributeNodes(enumMember.AttributeLists);
}
else if (parent is ParameterSyntax parameter)
{
return GetAttributeNodes(parameter.AttributeLists);
}
else if (parent is VariableDeclaratorSyntax or
VariableDeclarationSyntax)
{
return GetAttributeNodes(parent.Parent!);
}
else if (parent is AccessorDeclarationSyntax accessor)
{
return GetAttributeNodes(accessor.AttributeLists);
}
return [];
}
public override IEnumerable<SyntaxNode> GetAttributeArgumentNodes(SyntaxNode parent)
{
if (parent is AttributeSyntax attribute)
{
if (attribute.ArgumentList == null)
return [];
return attribute.ArgumentList.Arguments;
}
return [];
}
public override IEnumerable<SyntaxNode> GetInheritsNodes(SyntaxNode parent)
{
// Only VB has Inherits statements
return [];
}
public override IEnumerable<SyntaxNode> GetImplementsNodes(SyntaxNode parent)
{
// Only VB has Implements statements
return [];
}
private static bool IsContainerNode(SyntaxNode container)
=> container is CompilationUnitSyntax or
BaseNamespaceDeclarationSyntax or
BaseTypeDeclarationSyntax;
private static bool IsNamespaceOrTypeDeclaration(SyntaxNode node)
=> node is BaseNamespaceDeclarationSyntax or
BaseTypeDeclarationSyntax or
DelegateDeclarationSyntax;
private static IEnumerable<MemberDeclarationSyntax> GetChildMemberNodes(SyntaxNode container)
{
if (container is CompilationUnitSyntax compilationUnit)
{
foreach (var member in compilationUnit.Members)
{
if (IsNamespaceOrTypeDeclaration(member))
{
yield return member;
}
}
}
else if (container is BaseNamespaceDeclarationSyntax namespaceDecl)
{
foreach (var member in namespaceDecl.Members)
{
if (IsNamespaceOrTypeDeclaration(member))
{
yield return member;
}
}
}
else if (container is TypeDeclarationSyntax typeDecl)
{
foreach (var member in typeDecl.Members)
{
if (member.Kind() != SyntaxKind.NamespaceDeclaration)
{
yield return member;
}
}
}
else if (container is EnumDeclarationSyntax enumDecl)
{
foreach (var member in enumDecl.Members)
{
yield return member;
}
}
}
private static bool NodeIsSupported(bool test, SyntaxNode node)
=> !test || IsNameableNode(node);
/// <summary>
/// Retrieves the members of a specified <paramref name="container"/> node. The members that are
/// returned can be controlled by passing various parameters.
/// </summary>
/// <param name="container">The <see cref="SyntaxNode"/> from which to retrieve members.</param>
/// <param name="includeSelf">If true, the container is returned as well.</param>
/// <param name="recursive">If true, members are recursed to return descendant members as well
/// as immediate children. For example, a namespace would return the namespaces and types within.
/// However, if <paramref name="recursive"/> is true, members with the namespaces and types would
/// also be returned.</param>
/// <param name="logicalFields">If true, field declarations are broken into their respective declarators.
/// For example, the field "int x, y" would return two declarators, one for x and one for y in place
/// of the field.</param>
/// <param name="onlySupportedNodes">If true, only members supported by Code Model are returned.</param>
public override IEnumerable<SyntaxNode> GetMemberNodes(SyntaxNode container, bool includeSelf, bool recursive, bool logicalFields, bool onlySupportedNodes)
{
// Filter out all records from code model, they are not supported at all.
return GetMemberNodesWorker(container, includeSelf, recursive, logicalFields, onlySupportedNodes).Where(t => t is not RecordDeclarationSyntax);
}
private IEnumerable<SyntaxNode> GetMemberNodesWorker(SyntaxNode container, bool includeSelf, bool recursive, bool logicalFields, bool onlySupportedNodes)
{
if (!IsContainerNode(container))
{
yield break;
}
if (includeSelf && NodeIsSupported(onlySupportedNodes, container))
{
yield return container;
}
foreach (var member in GetChildMemberNodes(container))
{
if (member is BaseFieldDeclarationSyntax baseField)
{
// For fields, the 'logical' and 'supported' flags are intrinsically tied.
// * If 'logical' is true, only declarators should be returned, regardless of the value of 'supported'.
// * If 'logical' is false, the field should only be returned if 'supported' is also false.
if (logicalFields)
{
foreach (var declarator in baseField.Declaration.Variables)
{
// We know that variable declarators are supported, so there's no need to check them here.
yield return declarator;
}
}
else if (!onlySupportedNodes)
{
// Only return field declarations if the supported flag is false.
yield return member;
}
}
else if (NodeIsSupported(onlySupportedNodes, member))
{
yield return member;
}
if (recursive && IsContainerNode(member))
{
foreach (var innerMember in GetMemberNodes(member, includeSelf: false, recursive: true, logicalFields: logicalFields, onlySupportedNodes: onlySupportedNodes))
{
yield return innerMember;
}
}
}
}
public override string Language
{
get { return EnvDTE.CodeModelLanguageConstants.vsCMLanguageCSharp; }
}
public override string AssemblyAttributeString
{
get
{
return "assembly";
}
}
/// <summary>
/// Do not use this method directly! Instead, go through <see cref="FileCodeModel.GetOrCreateCodeElement{T}(SyntaxNode)"/>
/// </summary>
public override EnvDTE.CodeElement CreateInternalCodeElement(
CodeModelState state,
FileCodeModel fileCodeModel,
SyntaxNode node)
{
// Attributes, attribute arguments, parameters, imports directives, and
// accessor functions do not have their own node keys. Rather, they are
// based off their parents node keys and other data.
switch (node.Kind())
{
case SyntaxKind.Attribute:
return (EnvDTE.CodeElement)CreateInternalCodeAttribute(state, fileCodeModel, node);
case SyntaxKind.AttributeArgument:
return (EnvDTE.CodeElement)CreateInternalCodeAttributeArgument(state, fileCodeModel, (AttributeArgumentSyntax)node);
case SyntaxKind.Parameter:
return (EnvDTE.CodeElement)CreateInternalCodeParameter(state, fileCodeModel, (ParameterSyntax)node);
case SyntaxKind.UsingDirective:
return CreateInternalCodeImport(state, fileCodeModel, (UsingDirectiveSyntax)node);
}
if (IsAccessorNode(node))
{
return (EnvDTE.CodeElement)CreateInternalCodeAccessorFunction(state, fileCodeModel, node);
}
var nodeKey = GetNodeKey(node);
switch (node.Kind())
{
case SyntaxKind.ClassDeclaration:
return (EnvDTE.CodeElement)CodeClass.Create(state, fileCodeModel, nodeKey, (int)node.Kind());
case SyntaxKind.InterfaceDeclaration:
return (EnvDTE.CodeElement)CodeInterface.Create(state, fileCodeModel, nodeKey, (int)node.Kind());
case SyntaxKind.StructDeclaration:
return (EnvDTE.CodeElement)CodeStruct.Create(state, fileCodeModel, nodeKey, (int)node.Kind());
case SyntaxKind.EnumDeclaration:
return (EnvDTE.CodeElement)CodeEnum.Create(state, fileCodeModel, nodeKey, (int)node.Kind());
case SyntaxKind.EnumMemberDeclaration:
return (EnvDTE.CodeElement)CodeVariable.Create(state, fileCodeModel, nodeKey, (int)node.Kind());
case SyntaxKind.DelegateDeclaration:
return (EnvDTE.CodeElement)CodeDelegate.Create(state, fileCodeModel, nodeKey, (int)node.Kind());
case SyntaxKind.MethodDeclaration:
case SyntaxKind.ConstructorDeclaration:
case SyntaxKind.DestructorDeclaration:
case SyntaxKind.OperatorDeclaration:
case SyntaxKind.ConversionOperatorDeclaration:
return (EnvDTE.CodeElement)CodeFunction.Create(state, fileCodeModel, nodeKey, (int)node.Kind());
case SyntaxKind.NamespaceDeclaration:
case SyntaxKind.FileScopedNamespaceDeclaration:
return (EnvDTE.CodeElement)CodeNamespace.Create(state, fileCodeModel, nodeKey, (int)node.Kind());
case SyntaxKind.PropertyDeclaration:
case SyntaxKind.IndexerDeclaration:
return (EnvDTE.CodeElement)CodeProperty.Create(state, fileCodeModel, nodeKey, (int)node.Kind());
case SyntaxKind.EventDeclaration:
return (EnvDTE.CodeElement)CodeEvent.Create(state, fileCodeModel, nodeKey, (int)node.Kind());
case SyntaxKind.VariableDeclarator:
var baseFieldDeclaration = node.FirstAncestorOrSelf<BaseFieldDeclarationSyntax>();
if (baseFieldDeclaration != null)
{
if (baseFieldDeclaration.Kind() == SyntaxKind.FieldDeclaration)
{
return (EnvDTE.CodeElement)CodeVariable.Create(state, fileCodeModel, nodeKey, (int)node.Kind());
}
else if (baseFieldDeclaration.Kind() == SyntaxKind.EventFieldDeclaration)
{
return (EnvDTE.CodeElement)CodeEvent.Create(state, fileCodeModel, nodeKey, (int)node.Kind());
}
}
throw Exceptions.ThrowEUnexpected();
default:
throw new InvalidOperationException();
}
}
public override EnvDTE.CodeElement CreateUnknownCodeElement(CodeModelState state, FileCodeModel fileCodeModel, SyntaxNode node)
{
switch (node.Kind())
{
case SyntaxKind.NamespaceDeclaration:
case SyntaxKind.FileScopedNamespaceDeclaration:
return (EnvDTE.CodeElement)CodeNamespace.CreateUnknown(state, fileCodeModel, node.RawKind, GetName(node));
case SyntaxKind.ClassDeclaration:
return (EnvDTE.CodeElement)CodeClass.CreateUnknown(state, fileCodeModel, node.RawKind, GetName(node));
case SyntaxKind.InterfaceDeclaration:
return (EnvDTE.CodeElement)CodeInterface.CreateUnknown(state, fileCodeModel, node.RawKind, GetName(node));
case SyntaxKind.StructDeclaration:
return (EnvDTE.CodeElement)CodeStruct.CreateUnknown(state, fileCodeModel, node.RawKind, GetName(node));
case SyntaxKind.EnumDeclaration:
return (EnvDTE.CodeElement)CodeEnum.CreateUnknown(state, fileCodeModel, node.RawKind, GetName(node));
case SyntaxKind.DelegateDeclaration:
return (EnvDTE.CodeElement)CodeDelegate.CreateUnknown(state, fileCodeModel, node.RawKind, GetName(node));
case SyntaxKind.MethodDeclaration:
case SyntaxKind.ConstructorDeclaration:
case SyntaxKind.DestructorDeclaration:
case SyntaxKind.OperatorDeclaration:
case SyntaxKind.ConversionOperatorDeclaration:
return (EnvDTE.CodeElement)CodeFunction.CreateUnknown(state, fileCodeModel, node.RawKind, GetName(node));
case SyntaxKind.PropertyDeclaration:
case SyntaxKind.IndexerDeclaration:
return (EnvDTE.CodeElement)CodeProperty.CreateUnknown(state, fileCodeModel, node.RawKind, GetName(node));
case SyntaxKind.EventDeclaration:
return (EnvDTE.CodeElement)CodeEvent.CreateUnknown(state, fileCodeModel, node.RawKind, GetName(node));
case SyntaxKind.VariableDeclarator:
var eventFieldDeclaration = node.FirstAncestorOrSelf<EventFieldDeclarationSyntax>();
if (eventFieldDeclaration != null)
{
return (EnvDTE.CodeElement)CodeEvent.CreateUnknown(state, fileCodeModel, node.RawKind, GetName(node));
}
goto case SyntaxKind.EnumMemberDeclaration;
case SyntaxKind.EnumMemberDeclaration:
return (EnvDTE.CodeElement)CodeVariable.CreateUnknown(state, fileCodeModel, node.RawKind, GetName(node));
default:
throw Exceptions.ThrowEUnexpected();
}
}
public override EnvDTE.CodeElement CreateUnknownRootNamespaceCodeElement(CodeModelState state, FileCodeModel fileCodeModel)
=> (EnvDTE.CodeElement)CodeNamespace.CreateUnknown(state, fileCodeModel, (int)SyntaxKind.NamespaceDeclaration, string.Empty);
public override EnvDTE.CodeTypeRef CreateCodeTypeRef(CodeModelState state, ProjectId projectId, object type)
{
var project = state.Workspace.CurrentSolution.GetProject(projectId);
if (project == null)
{
throw Exceptions.ThrowEFail();
}
var compilation = project.GetRequiredCompilationAsync(CancellationToken.None).Result;
if (type is EnvDTE.vsCMTypeRef or int)
{
var specialType = GetSpecialType((EnvDTE.vsCMTypeRef)type);
return CodeTypeRef.Create(state, null, projectId, compilation.GetSpecialType(specialType));
}
string typeName;
object? parent = null;
if (type is EnvDTE.CodeType codeType)
{
typeName = codeType.FullName;
parent = codeType;
}
else if (type is string str)
{
typeName = str;
}
else
{
throw Exceptions.ThrowEInvalidArg();
}
var typeSymbol = GetTypeSymbolFromFullName(typeName, compilation);
if (typeSymbol == null)
{
throw Exceptions.ThrowEFail();
}
if (typeSymbol.TypeKind == TypeKind.Unknown)
{
if (SyntaxFactsService.IsValidIdentifier(typeSymbol.Name))
{
throw Exceptions.ThrowEFail();
}
}
return CodeTypeRef.Create(state, parent, projectId, typeSymbol);
}
public override EnvDTE.vsCMTypeRef GetTypeKindForCodeTypeRef(ITypeSymbol typeSymbol)
{
if (typeSymbol.TypeKind == TypeKind.Array)
{
return EnvDTE.vsCMTypeRef.vsCMTypeRefArray;
}
else if (typeSymbol.TypeKind == TypeKind.Pointer)
{
return EnvDTE.vsCMTypeRef.vsCMTypeRefPointer;
}
else if (
typeSymbol.TypeKind is TypeKind.Dynamic or
TypeKind.Unknown)
{
return EnvDTE.vsCMTypeRef.vsCMTypeRefOther;
}
else
{
switch (typeSymbol.SpecialType)
{
case SpecialType.System_Void:
return EnvDTE.vsCMTypeRef.vsCMTypeRefVoid;
case SpecialType.System_String:
return EnvDTE.vsCMTypeRef.vsCMTypeRefString;
case SpecialType.System_Object:
return EnvDTE.vsCMTypeRef.vsCMTypeRefObject;
case SpecialType.System_Char:
return EnvDTE.vsCMTypeRef.vsCMTypeRefChar;
case SpecialType.System_Byte:
return EnvDTE.vsCMTypeRef.vsCMTypeRefByte;
case SpecialType.System_Boolean:
return EnvDTE.vsCMTypeRef.vsCMTypeRefBool;
case SpecialType.System_Int16:
return EnvDTE.vsCMTypeRef.vsCMTypeRefShort;
case SpecialType.System_Int32:
return EnvDTE.vsCMTypeRef.vsCMTypeRefInt;
case SpecialType.System_Int64:
return EnvDTE.vsCMTypeRef.vsCMTypeRefLong;
case SpecialType.System_Single:
return EnvDTE.vsCMTypeRef.vsCMTypeRefFloat;
case SpecialType.System_Double:
return EnvDTE.vsCMTypeRef.vsCMTypeRefDouble;
case SpecialType.System_Decimal:
return EnvDTE.vsCMTypeRef.vsCMTypeRefDecimal;
case SpecialType.System_UInt16:
return (EnvDTE.vsCMTypeRef)EnvDTE80.vsCMTypeRef2.vsCMTypeRefUnsignedShort;
case SpecialType.System_UInt32:
return (EnvDTE.vsCMTypeRef)EnvDTE80.vsCMTypeRef2.vsCMTypeRefUnsignedInt;
case SpecialType.System_UInt64:
return (EnvDTE.vsCMTypeRef)EnvDTE80.vsCMTypeRef2.vsCMTypeRefUnsignedLong;
case SpecialType.System_SByte:
return (EnvDTE.vsCMTypeRef)EnvDTE80.vsCMTypeRef2.vsCMTypeRefSByte;
}
// Comment below is from native code
// The following are not supported
// vsCMTypeRefUnsignedChar - PT_UCHAR not in C# ??
// vsCMTypeRefReference - C++ specific
// vsCMTypeRefMCBoxedReference - C++ specific
}
if (typeSymbol.TypeKind is TypeKind.Class or
TypeKind.Interface or
TypeKind.Enum or
TypeKind.Struct or
TypeKind.Delegate)
{
return EnvDTE.vsCMTypeRef.vsCMTypeRefCodeType;
}
return EnvDTE.vsCMTypeRef.vsCMTypeRefOther;
}
public override string GetAsFullNameForCodeTypeRef(ITypeSymbol typeSymbol)
=> typeSymbol.ToDisplayString(s_codeTypeRefAsFullNameFormat);
public override string GetAsStringForCodeTypeRef(ITypeSymbol typeSymbol)
=> typeSymbol.ToDisplayString(s_codeTypeRefAsStringFormat);
public override bool IsParameterNode(SyntaxNode node)
=> node is ParameterSyntax;
public override bool IsAttributeNode(SyntaxNode node)
=> node is AttributeSyntax;
public override bool IsAttributeArgumentNode(SyntaxNode node)
=> node is AttributeArgumentSyntax;
public override bool IsOptionNode(SyntaxNode node)
{
// Only VB implementation has Option statements
return false;
}
public override bool IsImportNode(SyntaxNode node)
=> node is UsingDirectiveSyntax;
[return: NotNullIfNotNull(nameof(name))]
public override string? GetUnescapedName(string? name)
=> name is ['@', .. var rest] ? rest : name;
public override string GetName(SyntaxNode node)
{
if (node == null)
{
throw new ArgumentNullException(nameof(node));
}
switch (node.Kind())
{
case SyntaxKind.ClassDeclaration:
case SyntaxKind.InterfaceDeclaration:
case SyntaxKind.StructDeclaration:
case SyntaxKind.EnumDeclaration:
return ((BaseTypeDeclarationSyntax)node).Identifier.ToString();
case SyntaxKind.DelegateDeclaration:
return ((DelegateDeclarationSyntax)node).Identifier.ToString();
case SyntaxKind.MethodDeclaration:
return ((MethodDeclarationSyntax)node).ExplicitInterfaceSpecifier?.ToString() +
((MethodDeclarationSyntax)node).Identifier.ToString();
case SyntaxKind.ConstructorDeclaration:
return ((ConstructorDeclarationSyntax)node).Identifier.ToString();
case SyntaxKind.DestructorDeclaration:
return "~" + ((DestructorDeclarationSyntax)node).Identifier.ToString();
case SyntaxKind.PropertyDeclaration:
return ((PropertyDeclarationSyntax)node).ExplicitInterfaceSpecifier?.ToString() +
((PropertyDeclarationSyntax)node).Identifier.ToString();
case SyntaxKind.IndexerDeclaration:
return ((IndexerDeclarationSyntax)node).ExplicitInterfaceSpecifier?.ToString() +
((IndexerDeclarationSyntax)node).ThisKeyword.ToString();
case SyntaxKind.EventDeclaration:
return ((EventDeclarationSyntax)node).ExplicitInterfaceSpecifier?.ToString() +
((EventDeclarationSyntax)node).Identifier.ToString();
case SyntaxKind.Parameter:
return GetParameterName(node);
case SyntaxKind.NamespaceDeclaration:
case SyntaxKind.FileScopedNamespaceDeclaration:
return ((BaseNamespaceDeclarationSyntax)node).Name.ToString();
case SyntaxKind.OperatorDeclaration:
return "operator " + ((OperatorDeclarationSyntax)node).OperatorToken.ToString();
case SyntaxKind.ConversionOperatorDeclaration:
var conversionOperator = (ConversionOperatorDeclarationSyntax)node;
return (conversionOperator.ImplicitOrExplicitKeyword.Kind() == SyntaxKind.ImplicitKeyword ? "implicit " : "explicit ")
+ "operator "
+ conversionOperator.Type.ToString();
case SyntaxKind.EnumMemberDeclaration:
return ((EnumMemberDeclarationSyntax)node).Identifier.ToString();
case SyntaxKind.VariableDeclarator:
return ((VariableDeclaratorSyntax)node).Identifier.ToString();
case SyntaxKind.Attribute:
return ((AttributeSyntax)node).Name.ToString();
case SyntaxKind.AttributeArgument:
var attributeArgument = (AttributeArgumentSyntax)node;
return attributeArgument.NameEquals != null
? attributeArgument.NameEquals.Name.ToString()
: string.Empty;
case SyntaxKind.UsingDirective:
throw Exceptions.ThrowEFail();
default:
Debug.Fail("Invalid node kind: " + node.Kind());
throw new ArgumentException();
}
}
public override SyntaxNode GetNodeWithName(SyntaxNode node)
{
var kind = node.Kind();
if (kind is SyntaxKind.OperatorDeclaration or
SyntaxKind.ConversionOperatorDeclaration)
{
throw Exceptions.ThrowEFail();
}
return node;
}
public override SyntaxNode SetName(SyntaxNode node, string name)
{
if (node == null)
{
throw new ArgumentNullException(nameof(node));
}
// In all cases, the resulting syntax for the new name has elastic trivia attached,
// whether via this call to SyntaxFactory.Identifier or via explicitly added elastic
// markers.
var newIdentifier = SyntaxFactory.Identifier(name);
switch (node.Kind())
{
case SyntaxKind.ClassDeclaration:
case SyntaxKind.StructDeclaration:
return SetNameOfClassOrStruct(node, newIdentifier);
case SyntaxKind.InterfaceDeclaration:
return ((InterfaceDeclarationSyntax)node).WithIdentifier(newIdentifier);
case SyntaxKind.EnumDeclaration:
return ((EnumDeclarationSyntax)node).WithIdentifier(newIdentifier);
case SyntaxKind.DelegateDeclaration:
return ((DelegateDeclarationSyntax)node).WithIdentifier(newIdentifier);
case SyntaxKind.MethodDeclaration:
return ((MethodDeclarationSyntax)node).WithIdentifier(newIdentifier);
case SyntaxKind.ConstructorDeclaration:
return ((ConstructorDeclarationSyntax)node).WithIdentifier(newIdentifier);
case SyntaxKind.DestructorDeclaration:
return ((DestructorDeclarationSyntax)node).WithIdentifier(newIdentifier);
case SyntaxKind.PropertyDeclaration:
return ((PropertyDeclarationSyntax)node).WithIdentifier(newIdentifier);
case SyntaxKind.EventDeclaration:
return ((EventDeclarationSyntax)node).WithIdentifier(newIdentifier);
case SyntaxKind.Parameter:
return ((ParameterSyntax)node).WithIdentifier(newIdentifier);
case SyntaxKind.NamespaceDeclaration:
case SyntaxKind.FileScopedNamespaceDeclaration:
return ((BaseNamespaceDeclarationSyntax)node).WithName(
SyntaxFactory.ParseName(name)
.WithLeadingTrivia(SyntaxFactory.TriviaList(SyntaxFactory.ElasticMarker))
.WithTrailingTrivia(SyntaxFactory.TriviaList(SyntaxFactory.ElasticMarker)));
case SyntaxKind.EnumMemberDeclaration:
return ((EnumMemberDeclarationSyntax)node).WithIdentifier(newIdentifier);
case SyntaxKind.VariableDeclarator:
return ((VariableDeclaratorSyntax)node).WithIdentifier(newIdentifier);
case SyntaxKind.Attribute:
return ((AttributeSyntax)node).WithName(
SyntaxFactory.ParseName(name)
.WithLeadingTrivia(SyntaxFactory.TriviaList(SyntaxFactory.ElasticMarker))
.WithTrailingTrivia(SyntaxFactory.TriviaList(SyntaxFactory.ElasticMarker)));
case SyntaxKind.AttributeArgument:
return ((AttributeArgumentSyntax)node).WithNameEquals(SyntaxFactory.NameEquals(SyntaxFactory.IdentifierName(name)));
default:
Debug.Fail("Invalid node kind: " + node.Kind());
throw new ArgumentException();
}
}
private static SyntaxNode SetNameOfClassOrStruct(SyntaxNode node, SyntaxToken newIdentifier)
{
var typeNode = (TypeDeclarationSyntax)node;
foreach (var member in typeNode.Members)
{
if (member.Kind() == SyntaxKind.ConstructorDeclaration)
{
var constructor = ((ConstructorDeclarationSyntax)member).WithIdentifier(newIdentifier);
typeNode = typeNode.ReplaceNode(member, constructor);
}
else if (member.Kind() == SyntaxKind.DestructorDeclaration)
{
var destructor = ((DestructorDeclarationSyntax)member).WithIdentifier(newIdentifier);
typeNode = typeNode.ReplaceNode(member, destructor);
}
}
if (typeNode.Kind() == SyntaxKind.StructDeclaration)
{
return ((StructDeclarationSyntax)typeNode).WithIdentifier(newIdentifier);
}
else
{
return ((ClassDeclarationSyntax)typeNode).WithIdentifier(newIdentifier);
}
}
public override string GetFullName(SyntaxNode node, SemanticModel semanticModel)
{
if (node.Kind() == SyntaxKind.UsingDirective)
{
throw Exceptions.ThrowEFail();
}
var symbol = node is AttributeSyntax
? semanticModel.GetTypeInfo(node).Type
: semanticModel.GetDeclaredSymbol(node);
Contract.ThrowIfNull(symbol);
return GetExternalSymbolFullName(symbol);
}
public override string GetFullyQualifiedName(string name, int position, SemanticModel semanticModel)
{
var typeName = SyntaxFactory.ParseTypeName(name);
if (typeName is PredefinedTypeSyntax predefinedTypeNode)
{
if (SyntaxFactsService.TryGetPredefinedType(predefinedTypeNode.Keyword, out var predefinedType))
{
var specialType = predefinedType.ToSpecialType();
return semanticModel.Compilation.GetSpecialType(specialType).GetEscapedFullName();
}
}
else
{
var symbols = semanticModel.LookupNamespacesAndTypes(position, name: name);
if (symbols.Length > 0)
{
return symbols[0].GetEscapedFullName();
}
}
return name;
}
public override bool IsValidExternalSymbol(ISymbol symbol)
{
if (symbol is IMethodSymbol methodSymbol)
{
if (methodSymbol.MethodKind is MethodKind.PropertyGet or
MethodKind.PropertySet or
MethodKind.EventAdd or
MethodKind.EventRemove or
MethodKind.EventRaise)
{
return false;
}
}
return symbol.DeclaredAccessibility is Accessibility.Public
or Accessibility.Protected
or Accessibility.ProtectedOrInternal;
}
public override string GetExternalSymbolName(ISymbol symbol)
{
if (symbol == null)
{
throw Exceptions.ThrowEFail();
}
return symbol.ToDisplayString(s_externalNameFormat);
}
public override string GetExternalSymbolFullName(ISymbol symbol)
{
if (symbol == null)
{
throw Exceptions.ThrowEFail();
}
return symbol.ToDisplayString(s_externalFullNameFormat);
}
public override EnvDTE.vsCMAccess GetAccess(ISymbol symbol)
=> symbol.DeclaredAccessibility switch
{
Accessibility.Public => EnvDTE.vsCMAccess.vsCMAccessPublic,
Accessibility.Private => EnvDTE.vsCMAccess.vsCMAccessPrivate,
Accessibility.Internal => EnvDTE.vsCMAccess.vsCMAccessProject,
Accessibility.Protected => EnvDTE.vsCMAccess.vsCMAccessProtected,
Accessibility.ProtectedOrInternal => EnvDTE.vsCMAccess.vsCMAccessProjectOrProtected,
Accessibility.ProtectedAndInternal =>
// there is no appropriate mapping for private protected in EnvDTE.vsCMAccess
// See https://github.com/dotnet/roslyn/issues/22406
EnvDTE.vsCMAccess.vsCMAccessProtected,
_ => throw Exceptions.ThrowEFail(),
};
public override EnvDTE.vsCMAccess GetAccess(SyntaxNode node)
{
var member = GetNodeWithModifiers(node);
if (member == null)
{
throw Exceptions.ThrowEFail();
}
var modifiers = member.GetModifiers();
if (modifiers.Any(SyntaxKind.PublicKeyword))
{
return EnvDTE.vsCMAccess.vsCMAccessPublic;
}
else if (modifiers.Any(SyntaxKind.ProtectedKeyword) && modifiers.Any(SyntaxKind.InternalKeyword))
{
return EnvDTE.vsCMAccess.vsCMAccessProjectOrProtected;
}
else if (modifiers.Any(SyntaxKind.InternalKeyword))
{
return EnvDTE.vsCMAccess.vsCMAccessProject;
}
else if (modifiers.Any(SyntaxKind.ProtectedKeyword))
{
return EnvDTE.vsCMAccess.vsCMAccessProtected;
}
else if (modifiers.Any(SyntaxKind.PrivateKeyword))
{
return EnvDTE.vsCMAccess.vsCMAccessPrivate;
}
else
{
// The code does not specify the accessibility, so we need to
// determine the default accessibility.
return GetDefaultAccessibility(member);
}
}
#nullable disable
public override SyntaxNode GetNodeWithModifiers(SyntaxNode node)
{
return node is VariableDeclaratorSyntax
? node.GetAncestor<MemberDeclarationSyntax>()
: node;
}
public override SyntaxNode GetNodeWithType(SyntaxNode node)
{
return node is VariableDeclaratorSyntax
? node.GetAncestor<MemberDeclarationSyntax>()
: node;
}
#nullable restore
public override SyntaxNode GetNodeWithInitializer(SyntaxNode node)
=> node;
private EnvDTE.vsCMAccess GetDefaultAccessibility(SyntaxNode node)
{
if (node is EnumMemberDeclarationSyntax)
{
return EnvDTE.vsCMAccess.vsCMAccessPublic;
}
if (node is BaseFieldDeclarationSyntax or
BaseMethodDeclarationSyntax or
BasePropertyDeclarationSyntax)
{
// Members of interfaces and enums are public, while all other
// members are private.
if (node.HasAncestor<InterfaceDeclarationSyntax>() ||
node.HasAncestor<EnumDeclarationSyntax>())
{
return EnvDTE.vsCMAccess.vsCMAccessPublic;
}
else
{
return EnvDTE.vsCMAccess.vsCMAccessPrivate;
}
}
if (node is BaseTypeDeclarationSyntax or
DelegateDeclarationSyntax)
{
// Types declared within types are private by default,
// otherwise internal.
if (node.HasAncestor<BaseTypeDeclarationSyntax>())
{
return EnvDTE.vsCMAccess.vsCMAccessPrivate;
}
else
{
return EnvDTE.vsCMAccess.vsCMAccessProject;
}
}
if (node is AccessorDeclarationSyntax or
ArrowExpressionClauseSyntax)
{
return GetAccess(node.GetAncestors<BasePropertyDeclarationSyntax>().First());
}
throw Exceptions.ThrowEFail();
}
public override SyntaxNode SetAccess(SyntaxNode node, EnvDTE.vsCMAccess newAccess)
{
if (node is not MemberDeclarationSyntax member)
{
throw Exceptions.ThrowEFail();
}
if (member.Parent is (kind: SyntaxKind.InterfaceDeclaration or SyntaxKind.EnumDeclaration))
{
if (newAccess is EnvDTE.vsCMAccess.vsCMAccessDefault or
EnvDTE.vsCMAccess.vsCMAccessPublic)
{
return member;
}
else
{
throw Exceptions.ThrowEInvalidArg();
}
}
if (member is BaseTypeDeclarationSyntax or
EnumDeclarationSyntax)
{
if (!(member.Parent is BaseTypeDeclarationSyntax) &&
(newAccess == EnvDTE.vsCMAccess.vsCMAccessPrivate ||
newAccess == EnvDTE.vsCMAccess.vsCMAccessProtected ||
newAccess == EnvDTE.vsCMAccess.vsCMAccessProjectOrProtected))
{
throw Exceptions.ThrowEInvalidArg();
}
}
var modifierFlags = member.GetModifierFlags() & ~ModifierFlags.AccessModifierMask;
switch (newAccess)
{
case EnvDTE.vsCMAccess.vsCMAccessPrivate:
modifierFlags |= ModifierFlags.Private;
break;
case EnvDTE.vsCMAccess.vsCMAccessProtected:
modifierFlags |= ModifierFlags.Protected;
break;
case EnvDTE.vsCMAccess.vsCMAccessPublic:
modifierFlags |= ModifierFlags.Public;
break;
case EnvDTE.vsCMAccess.vsCMAccessProject:
modifierFlags |= ModifierFlags.Internal;
break;
case EnvDTE.vsCMAccess.vsCMAccessProjectOrProtected:
modifierFlags |= ModifierFlags.Protected | ModifierFlags.Internal;
break;
case EnvDTE.vsCMAccess.vsCMAccessDefault:
break; // No change
default:
throw Exceptions.ThrowEUnexpected();
}
return member.UpdateModifiers(modifierFlags);
}
private static IList<SyntaxTrivia> CollectComments(IList<SyntaxTrivia> triviaList)
{
var commentList = new List<SyntaxTrivia>();
for (var i = triviaList.Count - 1; i >= 0; i--)
{
var trivia = triviaList[i];
if (trivia.IsRegularComment())
{
commentList.Add(trivia);
}
else if (trivia.Kind() is not SyntaxKind.WhitespaceTrivia and
not SyntaxKind.EndOfLineTrivia)
{
break;
}
}
commentList.Reverse();
return commentList;
}
public override string GetComment(SyntaxNode node)
{
var firstToken = node.GetFirstToken();
var commentList = CollectComments(firstToken.LeadingTrivia.ToArray());
if (commentList.Count == 0)
{
return string.Empty;
}
var textBuilder = new StringBuilder();
foreach (var trivia in commentList)
{
if (trivia.IsRegularComment())
{
textBuilder.AppendLine(trivia.GetCommentText());
}
else
{
throw Exceptions.ThrowEFail();
}
}
return textBuilder.ToString();
}
public override SyntaxNode SetComment(SyntaxNode node, string value)
{
Debug.Assert(node is MemberDeclarationSyntax);
var memberDeclaration = (MemberDeclarationSyntax)node;
var text = memberDeclaration.SyntaxTree.GetText(CancellationToken.None);
var newLine = GetNewLineCharacter(text);
var commentText = string.Empty;
if (value != null)
{
var builder = new StringBuilder();
foreach (var line in value.Split(new[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries))
{
builder.Append("// ");
builder.Append(line);
builder.Append(newLine);
}
commentText = builder.ToString();
}
var newTriviaList = SyntaxFactory.ParseLeadingTrivia(commentText);
var leadingTriviaList = memberDeclaration.GetLeadingTrivia().ToList();
var commentList = CollectComments(leadingTriviaList);
if (commentList.Count > 0)
{
// In this case, we're going to replace the existing comment.
var firstIndex = leadingTriviaList.FindIndex(t => t == commentList[0]);
var lastIndex = leadingTriviaList.FindIndex(t => t == commentList[commentList.Count - 1]);
var count = lastIndex - firstIndex + 1;
leadingTriviaList.RemoveRange(firstIndex, count);
// Note: single line comments have a trailing new-line but that won't be
// returned by CollectComments. So, we may need to remove an additional new line below.
if (firstIndex < leadingTriviaList.Count &&
leadingTriviaList[firstIndex].Kind() == SyntaxKind.EndOfLineTrivia)
{
leadingTriviaList.RemoveAt(firstIndex);
}
foreach (var trivia in newTriviaList.Reverse())
{
leadingTriviaList.Insert(firstIndex, trivia);
}
}
else
{
// Otherwise, just add the comment to the end of the leading trivia.
leadingTriviaList.AddRange(newTriviaList);
}
return memberDeclaration.WithLeadingTrivia(leadingTriviaList);
}
private static DocumentationCommentTriviaSyntax? GetDocCommentNode(MemberDeclarationSyntax memberDeclaration)
{
var docCommentTrivia = memberDeclaration
.GetLeadingTrivia()
.Reverse()
.FirstOrDefault(t => t.IsDocComment());
if (!docCommentTrivia.IsDocComment())
{
return null;
}
return (DocumentationCommentTriviaSyntax?)docCommentTrivia.GetStructure();
}
public override string GetDocComment(SyntaxNode node)
{
Debug.Assert(node is MemberDeclarationSyntax);
var memberDeclaration = (MemberDeclarationSyntax)node;
var documentationComment = GetDocCommentNode(memberDeclaration);
if (documentationComment == null)
{
return string.Empty;
}
var text = memberDeclaration.SyntaxTree.GetText(CancellationToken.None);
var newLine = GetNewLineCharacter(text);
var lines = documentationComment.ToString().Split(new[] { newLine }, StringSplitOptions.None);
// trim off leading whitespace and exterior trivia.
var lengthToStrip = lines[0].GetLeadingWhitespace().Length;
for (var i = 1; i < lines.Length; i++)
{
var line = lines[i].TrimStart();
if (line.StartsWith("///", StringComparison.Ordinal))
{
line = line[3..];
}
if (line.Length > 0)
{
lengthToStrip = Math.Min(lengthToStrip, line.GetLeadingWhitespace().Length);
}
lines[i] = line;
}
for (var i = 0; i < lines.Length; i++)
{
var line = lines[i];
if (line.Length > lengthToStrip)
{
lines[i] = line[lengthToStrip..];
}
}
return "<doc>\r\n" + lines.Join(newLine) + "</doc>";
}
public override SyntaxNode SetDocComment(SyntaxNode node, string value)
{
Debug.Assert(node is MemberDeclarationSyntax);
XDocument xmlDocument;
try
{
using var reader = new StringReader(value);
xmlDocument = XDocument.Load(reader);
}
catch
{
throw Exceptions.ThrowEInvalidArg();
}
if (xmlDocument.FirstNode is not XElement docElement ||
docElement.Name.ToString().ToLower() != "doc")
{
throw Exceptions.ThrowEInvalidArg();
}
var memberDeclaration = (MemberDeclarationSyntax)node;
var text = memberDeclaration.SyntaxTree.GetText(CancellationToken.None);
var newLine = GetNewLineCharacter(text);
var builder = new StringBuilder();
foreach (var child in docElement.Elements())
{
foreach (var line in child.ToString().Split(new[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries))
{
builder.Append("/// ");
builder.Append(line);
builder.Append(newLine);
}
}
var newTriviaList = SyntaxFactory.ParseLeadingTrivia(builder.ToString());
var leadingTriviaList = memberDeclaration.GetLeadingTrivia().ToList();
var documentationComment = GetDocCommentNode(memberDeclaration);
if (documentationComment != null)
{
// In this case, we're going to replace the existing XML doc comment.
var index = leadingTriviaList.FindIndex(t => t == documentationComment.ParentTrivia);
leadingTriviaList.RemoveAt(index);
foreach (var triviaElement in newTriviaList.Reverse())
{
leadingTriviaList.Insert(index, triviaElement);
}
}
else
{
// Otherwise, just add the XML doc comment to the end of the leading trivia.
leadingTriviaList.AddRange(newTriviaList);
}
return memberDeclaration.WithLeadingTrivia(leadingTriviaList);
}
public override IEnumerable<SyntaxNode> GetParameterNodes(SyntaxNode parentNode)
{
if (parentNode is BaseMethodDeclarationSyntax baseMethod)
{
return baseMethod.ParameterList.Parameters;
}
else if (parentNode is IndexerDeclarationSyntax indexer)
{
return indexer.ParameterList.Parameters;
}
else if (parentNode is DelegateDeclarationSyntax delegateDecl)
{
return delegateDecl.ParameterList.Parameters;
}
return [];
}
public override bool IsExpressionBodiedProperty(SyntaxNode node)
=> (node as PropertyDeclarationSyntax)?.ExpressionBody != null;
public override bool TryGetAutoPropertyExpressionBody(SyntaxNode parentNode, [NotNullWhen(true)] out SyntaxNode? accessorNode)
{
accessorNode = (parentNode as PropertyDeclarationSyntax)?.ExpressionBody;
return accessorNode != null;
}
public override bool IsAccessorNode(SyntaxNode node)
{
switch (node.Kind())
{
case SyntaxKind.GetAccessorDeclaration:
case SyntaxKind.SetAccessorDeclaration:
case SyntaxKind.AddAccessorDeclaration:
case SyntaxKind.RemoveAccessorDeclaration:
return true;
}
return false;
}
public override MethodKind GetAccessorKind(SyntaxNode node)
=> node.Kind() switch
{
SyntaxKind.GetAccessorDeclaration => MethodKind.PropertyGet,
SyntaxKind.SetAccessorDeclaration => MethodKind.PropertySet,
SyntaxKind.AddAccessorDeclaration => MethodKind.EventAdd,
SyntaxKind.RemoveAccessorDeclaration => MethodKind.EventRemove,
_ => throw Exceptions.ThrowEUnexpected(),
};
private static SyntaxKind GetAccessorSyntaxKind(MethodKind methodKind)
=> methodKind switch
{
MethodKind.PropertyGet => SyntaxKind.GetAccessorDeclaration,
MethodKind.PropertySet => SyntaxKind.SetAccessorDeclaration,
MethodKind.EventAdd => SyntaxKind.AddAccessorDeclaration,
MethodKind.EventRemove => SyntaxKind.RemoveAccessorDeclaration,
_ => throw Exceptions.ThrowEUnexpected(),
};
public override bool TryGetAccessorNode(SyntaxNode parentNode, MethodKind kind, [NotNullWhen(true)] out SyntaxNode? accessorNode)
{
Debug.Assert(parentNode is BasePropertyDeclarationSyntax);
var basePropertyDeclaration = (BasePropertyDeclarationSyntax)parentNode;
var accessorKind = GetAccessorSyntaxKind(kind);
if (basePropertyDeclaration.AccessorList != null)
{
foreach (var accessor in basePropertyDeclaration.AccessorList.Accessors)
{
if (accessor.Kind() == accessorKind)
{
accessorNode = accessor;
return true;
}
}
}
accessorNode = null;
return false;
}
public override bool TryGetParameterNode(SyntaxNode parentNode, string name, [NotNullWhen(true)] out SyntaxNode? parameterNode)
{
foreach (ParameterSyntax parameter in GetParameterNodes(parentNode))
{
if (parameter.Identifier.ToString() == name)
{
parameterNode = parameter;
return true;
}
}
parameterNode = null;
return false;
}
public override bool TryGetImportNode(SyntaxNode parentNode, string dottedName, [NotNullWhen(true)] out SyntaxNode? importNode)
{
foreach (UsingDirectiveSyntax usingDirective in GetImportNodes(parentNode))
{
if (usingDirective.Name?.ToString() == dottedName)
{
importNode = usingDirective;
return true;
}
}
importNode = null;
return false;
}
public override bool TryGetOptionNode(SyntaxNode parentNode, string name, int ordinal, out SyntaxNode optionNode)
{
// Only VB has Option statements
throw new NotSupportedException();
}
public override bool TryGetInheritsNode(SyntaxNode parentNode, string name, int ordinal, out SyntaxNode inheritsNode)
{
// Only VB has Inherits statements
throw new NotSupportedException();
}
public override bool TryGetImplementsNode(SyntaxNode parentNode, string name, int ordinal, out SyntaxNode implementsNode)
{
// Only VB has Implements statements
throw new NotSupportedException();
}
public override bool TryGetAttributeNode(SyntaxNode parentNode, string name, int ordinal, [NotNullWhen(true)] out SyntaxNode? attributeNode)
{
var count = -1;
foreach (AttributeSyntax attribute in GetAttributeNodes(parentNode))
{
if (attribute.Name.ToString() == name)
{
count++;
if (count == ordinal)
{
attributeNode = attribute;
return true;
}
}
}
attributeNode = null;
return false;
}
public override bool TryGetAttributeArgumentNode(SyntaxNode attributeNode, int index, [NotNullWhen(true)] out SyntaxNode? attributeArgumentNode)
{
Debug.Assert(attributeNode is AttributeSyntax);
var attribute = (AttributeSyntax)attributeNode;
if (attribute.ArgumentList != null &&
attribute.ArgumentList.Arguments.Count > index)
{
attributeArgumentNode = attribute.ArgumentList.Arguments[index];
return true;
}
attributeArgumentNode = null;
return false;
}
public override void GetOptionNameAndOrdinal(SyntaxNode parentNode, SyntaxNode optionNode, out string name, out int ordinal)
{
// Only VB supports Option statements
throw new NotSupportedException();
}
public override void GetInheritsNamespaceAndOrdinal(SyntaxNode parentNode, SyntaxNode inheritsNode, out string namespaceName, out int ordinal)
{
// Only VB supports Inherits statements
throw new NotSupportedException();
}
public override void GetImplementsNamespaceAndOrdinal(SyntaxNode parentNode, SyntaxNode implementsNode, out string namespaceName, out int ordinal)
{
// Only VB supports Implements statements
throw new NotSupportedException();
}
public override void GetAttributeNameAndOrdinal(SyntaxNode parentNode, SyntaxNode attributeNode, out string name, out int ordinal)
{
Debug.Assert(attributeNode is AttributeSyntax);
name = ((AttributeSyntax)attributeNode).Name.ToString();
ordinal = -1;
foreach (AttributeSyntax attribute in GetAttributeNodes(parentNode))
{
if (attribute.Name.ToString() == name)
{
ordinal++;
}
if (attribute == attributeNode)
{
break;
}
}
}
public override void GetAttributeArgumentParentAndIndex(SyntaxNode attributeArgumentNode, out SyntaxNode attributeNode, out int index)
{
Debug.Assert(attributeArgumentNode is AttributeArgumentSyntax);
var argument = (AttributeArgumentSyntax)attributeArgumentNode;
var attribute = (AttributeSyntax)argument.Ancestors().First(n => n.Kind() == SyntaxKind.Attribute);
attributeNode = attribute;
index = attribute.ArgumentList!.Arguments.IndexOf((AttributeArgumentSyntax)attributeArgumentNode);
}
public override SyntaxNode GetAttributeTargetNode(SyntaxNode attributeNode)
{
Debug.Assert(attributeNode is AttributeSyntax);
RoslynDebug.Assert(attributeNode.Parent is AttributeListSyntax);
return (AttributeListSyntax)attributeNode.Parent;
}
public override string GetAttributeTarget(SyntaxNode attributeNode)
{
Debug.Assert(attributeNode is AttributeSyntax);
RoslynDebug.Assert(attributeNode.Parent is AttributeListSyntax);
var attributeList = (AttributeListSyntax)attributeNode.Parent;
if (attributeList.Target != null)
{
return attributeList.Target.Identifier.ToString();
}
return string.Empty;
}
public override SyntaxNode SetAttributeTarget(SyntaxNode attributeNode, string target)
{
Debug.Assert(attributeNode is AttributeListSyntax);
var attributeList = (AttributeListSyntax)attributeNode;
if (string.IsNullOrEmpty(target))
{
return attributeList.WithTarget(null);
}
else
{
return attributeList.WithTarget(
SyntaxFactory.AttributeTargetSpecifier(SyntaxFactory.Identifier(target)));
}
}
public override string GetAttributeValue(SyntaxNode attributeNode)
{
Debug.Assert(attributeNode is AttributeSyntax);
var attribute = (AttributeSyntax)attributeNode;
var argumentList = attribute.ArgumentList;
if (argumentList != null)
{
return argumentList.Arguments.ToString();
}
return string.Empty;
}
public override SyntaxNode SetAttributeValue(SyntaxNode attributeNode, string value)
{
Debug.Assert(attributeNode is AttributeSyntax);
var attribute = (AttributeSyntax)attributeNode;
var argumentList = attribute.ArgumentList;
var parsedArgumentList = SyntaxFactory.ParseAttributeArgumentList("(" + value + ")");
// Parsing here will always succeed since it only returns null when the text doesn't start with a ( and we
// enforce above that it has such a character.
Contract.ThrowIfNull(parsedArgumentList);
var newArgumentList = argumentList != null
? argumentList.WithArguments(parsedArgumentList.Arguments)
: parsedArgumentList;
return attribute.WithArgumentList(newArgumentList);
}
public override SyntaxNode GetNodeWithAttributes(SyntaxNode node)
{
return node is VariableDeclaratorSyntax
? node.GetAncestors<MemberDeclarationSyntax>().First()
: node;
}
public override SyntaxNode GetEffectiveParentForAttribute(SyntaxNode node)
{
if (node.GetAncestor<BaseFieldDeclarationSyntax>() is BaseFieldDeclarationSyntax fieldDeclaration)
{
return fieldDeclaration.Declaration.Variables.First();
}
else if (node.GetAncestor<ParameterSyntax>() is ParameterSyntax parameterSyntax)
{
return parameterSyntax;
}
else
{
Contract.ThrowIfNull(node.Parent);
return node.Parent;
}
}
public override SyntaxNode CreateAttributeNode(string name, string value, string? target = null)
{
var specifier = target != null
? SyntaxFactory.AttributeTargetSpecifier(SyntaxFactory.Identifier(target),
SyntaxFactory.Token([SyntaxFactory.ElasticMarker], SyntaxKind.ColonToken, [SyntaxFactory.Space]))
: null;
return SyntaxFactory.AttributeList(
target: specifier,
attributes: [SyntaxFactory.Attribute(
name: SyntaxFactory.ParseName(name),
argumentList: SyntaxFactory.ParseAttributeArgumentList("(" + value + ")"))]);
}
public override SyntaxNode CreateAttributeArgumentNode(string name, string value)
{
if (!string.IsNullOrEmpty(name))
{
return SyntaxFactory.AttributeArgument(
nameEquals: SyntaxFactory.NameEquals(name),
nameColon: null,
expression: SyntaxFactory.ParseExpression(value));
}
else
{
return SyntaxFactory.AttributeArgument(SyntaxFactory.ParseExpression(value));
}
}
public override SyntaxNode CreateImportNode(string name, string? alias = null)
{
var nameSyntax = SyntaxFactory.ParseName(name);
if (!RoslynString.IsNullOrEmpty(alias))
{
var aliasSyntax = SyntaxFactory.NameEquals(alias);
return SyntaxFactory.UsingDirective(aliasSyntax, nameSyntax);
}
else
{
return SyntaxFactory.UsingDirective(nameSyntax);
}
}
public override SyntaxNode CreateParameterNode(string name, string type)
=> SyntaxFactory.Parameter(SyntaxFactory.Identifier(name)).WithType(SyntaxFactory.ParseTypeName(type));
public override string GetAttributeArgumentValue(SyntaxNode attributeArgumentNode)
{
Debug.Assert(attributeArgumentNode is AttributeArgumentSyntax);
return ((AttributeArgumentSyntax)attributeArgumentNode).Expression.ToString();
}
public override string GetImportAlias(SyntaxNode importNode)
{
if (importNode is UsingDirectiveSyntax usingDirective)
{
return usingDirective.Alias != null
? usingDirective.Alias.Name.ToString()
: string.Empty;
}
throw new InvalidOperationException();
}
public override string GetImportNamespaceOrType(SyntaxNode importNode)
{
if (importNode is UsingDirectiveSyntax usingDirective)
{
return usingDirective.NamespaceOrType.ToString();
}
throw new InvalidOperationException();
}
public override void GetImportParentAndName(SyntaxNode importNode, out SyntaxNode? namespaceNode, out string name)
{
if (importNode is UsingDirectiveSyntax usingDirective)
{
namespaceNode = usingDirective.IsParentKind(SyntaxKind.CompilationUnit)
? null
: usingDirective.Parent;
name = usingDirective.NamespaceOrType.ToString();
return;
}
throw new InvalidOperationException();
}
public override string GetParameterName(SyntaxNode node)
{
if (node is ParameterSyntax parameter)
{
return parameter.Identifier.ToString();
}
throw new InvalidOperationException();
}
public override EnvDTE80.vsCMParameterKind GetParameterKind(SyntaxNode node)
{
if (node is ParameterSyntax parameter)
{
var kind = EnvDTE80.vsCMParameterKind.vsCMParameterKindNone;
var modifiers = parameter.Modifiers;
if (modifiers.Any(SyntaxKind.RefKeyword))
{
kind = EnvDTE80.vsCMParameterKind.vsCMParameterKindRef;
}
else if (modifiers.Any(SyntaxKind.OutKeyword))
{
kind = EnvDTE80.vsCMParameterKind.vsCMParameterKindOut;
}
else if (modifiers.Any(SyntaxKind.ParamsKeyword))
{
kind = EnvDTE80.vsCMParameterKind.vsCMParameterKindParamArray;
}
// Note: this is not an "else if" since it might be both
// optional and "ref".
if (parameter.Default != null)
{
kind |= EnvDTE80.vsCMParameterKind.vsCMParameterKindOptional;
}
return kind;
}
throw new InvalidOperationException();
}
public override SyntaxNode SetParameterKind(SyntaxNode node, EnvDTE80.vsCMParameterKind kind)
{
if (node is not ParameterSyntax parameter)
{
throw Exceptions.ThrowEFail();
}
// We can't do anything with "Optional", so just strip it out and ignore it.
if ((kind & EnvDTE80.vsCMParameterKind.vsCMParameterKindOptional) != 0)
{
kind -= EnvDTE80.vsCMParameterKind.vsCMParameterKindOptional;
}
SyntaxTokenList newModifiers;
switch (kind)
{
case EnvDTE80.vsCMParameterKind.vsCMParameterKindOut:
newModifiers = [OutKeyword];
break;
case EnvDTE80.vsCMParameterKind.vsCMParameterKindRef:
newModifiers = [RefKeyword];
break;
case EnvDTE80.vsCMParameterKind.vsCMParameterKindIn:
case EnvDTE80.vsCMParameterKind.vsCMParameterKindNone:
newModifiers = [];
break;
case EnvDTE80.vsCMParameterKind.vsCMParameterKindParamArray:
{
var parameterList = (ParameterListSyntax)parameter.Parent!;
if (parameterList.Parameters.LastOrDefault() == parameter &&
parameter.Type is ArrayTypeSyntax)
{
newModifiers = [ParamsKeyword];
break;
}
throw Exceptions.ThrowEInvalidArg();
}
default:
throw Exceptions.ThrowEInvalidArg();
}
return parameter.WithModifiers(newModifiers);
}
public override EnvDTE80.vsCMParameterKind UpdateParameterKind(EnvDTE80.vsCMParameterKind parameterKind, PARAMETER_PASSING_MODE passingMode)
{
var updatedParameterKind = parameterKind;
switch (passingMode)
{
case PARAMETER_PASSING_MODE.cmParameterTypeIn:
updatedParameterKind |= EnvDTE80.vsCMParameterKind.vsCMParameterKindNone;
updatedParameterKind &= ~EnvDTE80.vsCMParameterKind.vsCMParameterKindRef;
updatedParameterKind &= ~EnvDTE80.vsCMParameterKind.vsCMParameterKindOut;
break;
case PARAMETER_PASSING_MODE.cmParameterTypeInOut:
updatedParameterKind &= ~EnvDTE80.vsCMParameterKind.vsCMParameterKindNone;
updatedParameterKind |= EnvDTE80.vsCMParameterKind.vsCMParameterKindRef;
updatedParameterKind &= ~EnvDTE80.vsCMParameterKind.vsCMParameterKindOut;
break;
case PARAMETER_PASSING_MODE.cmParameterTypeOut:
updatedParameterKind &= ~EnvDTE80.vsCMParameterKind.vsCMParameterKindNone;
updatedParameterKind &= ~EnvDTE80.vsCMParameterKind.vsCMParameterKindRef;
updatedParameterKind |= EnvDTE80.vsCMParameterKind.vsCMParameterKindOut;
break;
}
return updatedParameterKind;
}
public override EnvDTE.vsCMFunction ValidateFunctionKind(SyntaxNode containerNode, EnvDTE.vsCMFunction kind, string name)
{
if (kind == EnvDTE.vsCMFunction.vsCMFunctionSub)
{
return EnvDTE.vsCMFunction.vsCMFunctionFunction;
}
if (kind == EnvDTE.vsCMFunction.vsCMFunctionFunction)
{
return kind;
}
if (kind is EnvDTE.vsCMFunction.vsCMFunctionConstructor or
EnvDTE.vsCMFunction.vsCMFunctionDestructor)
{
if (containerNode is InterfaceDeclarationSyntax)
{
throw Exceptions.ThrowEFail();
}
return kind;
}
throw Exceptions.ThrowENotImpl();
}
public override bool SupportsEventThrower
{
get { return false; }
}
public override bool GetCanOverride(SyntaxNode memberNode)
{
Debug.Assert(memberNode is MemberDeclarationSyntax);
if (memberNode is not MemberDeclarationSyntax member)
{
throw Exceptions.ThrowEFail();
}
if (member.Parent is InterfaceDeclarationSyntax)
{
return true;
}
var flags = member.GetModifierFlags();
return (flags & (ModifierFlags.Abstract | ModifierFlags.Virtual)) != 0;
}
public override SyntaxNode SetCanOverride(SyntaxNode memberNode, bool value)
{
Debug.Assert(memberNode is MemberDeclarationSyntax);
if (memberNode is not MemberDeclarationSyntax member)
{
throw Exceptions.ThrowEFail();
}
if (member.Parent is InterfaceDeclarationSyntax)
{
if (!value)
{
throw Exceptions.ThrowEInvalidArg();
}
return memberNode;
}
var flags = member.GetModifierFlags();
if (value)
{
flags |= ModifierFlags.Virtual;
}
else
{
flags &= ~ModifierFlags.Virtual;
}
return member.UpdateModifiers(flags);
}
public override EnvDTE80.vsCMClassKind GetClassKind(SyntaxNode typeNode, INamedTypeSymbol typeSymbol)
{
Debug.Assert(typeNode is ClassDeclarationSyntax);
var type = (ClassDeclarationSyntax)typeNode;
var flags = type.GetModifierFlags();
return (flags & ModifierFlags.Partial) != 0
? EnvDTE80.vsCMClassKind.vsCMClassKindPartialClass
: EnvDTE80.vsCMClassKind.vsCMClassKindMainClass;
}
public override SyntaxNode SetClassKind(SyntaxNode typeNode, EnvDTE80.vsCMClassKind kind)
{
Debug.Assert(typeNode is ClassDeclarationSyntax);
var type = (ClassDeclarationSyntax)typeNode;
var flags = type.GetModifierFlags();
if (kind == EnvDTE80.vsCMClassKind.vsCMClassKindPartialClass)
{
flags |= ModifierFlags.Partial;
}
else if (kind == EnvDTE80.vsCMClassKind.vsCMClassKindMainClass)
{
flags &= ~ModifierFlags.Partial;
}
return type.UpdateModifiers(flags);
}
public override EnvDTE80.vsCMConstKind GetConstKind(SyntaxNode variableNode)
{
if (variableNode is EnumMemberDeclarationSyntax)
{
return EnvDTE80.vsCMConstKind.vsCMConstKindConst;
}
if (GetNodeWithModifiers(variableNode) is not MemberDeclarationSyntax member)
{
throw Exceptions.ThrowEFail();
}
var flags = member.GetModifierFlags();
var result = EnvDTE80.vsCMConstKind.vsCMConstKindNone;
if ((flags & ModifierFlags.ReadOnly) != 0)
{
result |= EnvDTE80.vsCMConstKind.vsCMConstKindReadOnly;
}
if ((flags & ModifierFlags.Const) != 0)
{
result |= EnvDTE80.vsCMConstKind.vsCMConstKindConst;
}
// Note: It's possible that we could return vsCMConstKindCont | vsCMConstKindReadOnly if the
// user had incorrectly specified both const and readonly in their code. That's OK since
// Code Model represents the source.
return result;
}
public override SyntaxNode SetConstKind(SyntaxNode variableNode, EnvDTE80.vsCMConstKind kind)
{
Debug.Assert(variableNode is FieldDeclarationSyntax or
EnumMemberDeclarationSyntax);
if (variableNode is EnumMemberDeclarationSyntax)
{
if (kind is not EnvDTE80.vsCMConstKind.vsCMConstKindConst and
not EnvDTE80.vsCMConstKind.vsCMConstKindReadOnly)
{
throw Exceptions.ThrowEInvalidArg();
}
return variableNode;
}
var member = (MemberDeclarationSyntax)variableNode;
var flags = member.GetModifierFlags();
flags &= ~(ModifierFlags.Const | ModifierFlags.ReadOnly);
switch (kind)
{
case EnvDTE80.vsCMConstKind.vsCMConstKindConst:
flags |= ModifierFlags.Const;
break;
case EnvDTE80.vsCMConstKind.vsCMConstKindReadOnly:
flags |= ModifierFlags.ReadOnly;
break;
}
return member.UpdateModifiers(flags);
}
public override EnvDTE80.vsCMDataTypeKind GetDataTypeKind(SyntaxNode typeNode, INamedTypeSymbol symbol)
{
Debug.Assert(typeNode is BaseTypeDeclarationSyntax);
var type = (BaseTypeDeclarationSyntax)typeNode;
var flags = type.GetModifierFlags();
return (flags & ModifierFlags.Partial) != 0
? EnvDTE80.vsCMDataTypeKind.vsCMDataTypeKindPartial
: EnvDTE80.vsCMDataTypeKind.vsCMDataTypeKindMain;
}
public override SyntaxNode SetDataTypeKind(SyntaxNode typeNode, EnvDTE80.vsCMDataTypeKind kind)
{
Debug.Assert(typeNode is BaseTypeDeclarationSyntax);
var type = (BaseTypeDeclarationSyntax)typeNode;
var flags = type.GetModifierFlags();
if (kind == EnvDTE80.vsCMDataTypeKind.vsCMDataTypeKindPartial)
{
flags |= ModifierFlags.Partial;
}
else if (kind == EnvDTE80.vsCMDataTypeKind.vsCMDataTypeKindMain)
{
flags &= ~ModifierFlags.Partial;
}
return type.UpdateModifiers(flags);
}
public override EnvDTE.vsCMFunction GetFunctionKind(IMethodSymbol symbol)
{
switch (symbol.MethodKind)
{
case MethodKind.Ordinary:
case MethodKind.ExplicitInterfaceImplementation:
return EnvDTE.vsCMFunction.vsCMFunctionFunction;
case MethodKind.Constructor:
case MethodKind.StaticConstructor:
return EnvDTE.vsCMFunction.vsCMFunctionConstructor;
case MethodKind.Destructor:
return EnvDTE.vsCMFunction.vsCMFunctionDestructor;
case MethodKind.UserDefinedOperator:
case MethodKind.Conversion:
return EnvDTE.vsCMFunction.vsCMFunctionOperator;
case MethodKind.PropertyGet:
case MethodKind.EventRemove:
return EnvDTE.vsCMFunction.vsCMFunctionPropertyGet;
case MethodKind.PropertySet:
case MethodKind.EventAdd:
return EnvDTE.vsCMFunction.vsCMFunctionPropertySet;
default:
throw Exceptions.ThrowEUnexpected();
}
}
public override EnvDTE80.vsCMInheritanceKind GetInheritanceKind(SyntaxNode typeNode, INamedTypeSymbol typeSymbol)
{
Debug.Assert(typeNode is ClassDeclarationSyntax);
var type = (ClassDeclarationSyntax)typeNode;
var flags = type.GetModifierFlags();
var result = EnvDTE80.vsCMInheritanceKind.vsCMInheritanceKindNone;
if ((flags & ModifierFlags.Abstract) != 0)
{
result |= EnvDTE80.vsCMInheritanceKind.vsCMInheritanceKindAbstract;
}
if ((flags & ModifierFlags.New) != 0)
{
result |= EnvDTE80.vsCMInheritanceKind.vsCMInheritanceKindNew;
}
if ((flags & ModifierFlags.Sealed) != 0)
{
result |= EnvDTE80.vsCMInheritanceKind.vsCMInheritanceKindSealed;
}
return result;
}
public override bool GetMustImplement(SyntaxNode memberNode)
{
Debug.Assert(memberNode is MemberDeclarationSyntax);
if (memberNode is not MemberDeclarationSyntax member)
{
throw Exceptions.ThrowEFail();
}
if (member.Parent is InterfaceDeclarationSyntax)
{
return true;
}
var flags = member.GetModifierFlags();
return (flags & ModifierFlags.Abstract) != 0;
}
public override SyntaxNode SetMustImplement(SyntaxNode memberNode, bool value)
{
Debug.Assert(memberNode is MethodDeclarationSyntax or
BasePropertyDeclarationSyntax or
EventFieldDeclarationSyntax);
if (memberNode is not MemberDeclarationSyntax member)
{
throw Exceptions.ThrowEFail();
}
if (member.Parent is InterfaceDeclarationSyntax)
{
if (!value)
{
throw Exceptions.ThrowEInvalidArg();
}
return memberNode;
}
// If this is a class member and the class is not abstract, we throw.
if (member.Parent is ClassDeclarationSyntax)
{
var parentFlags = ((ClassDeclarationSyntax)member.Parent).GetModifierFlags();
if (value && (parentFlags & ModifierFlags.Abstract) == 0)
{
throw Exceptions.ThrowEInvalidArg();
}
}
var flags = member.GetModifierFlags();
if (value)
{
flags |= ModifierFlags.Abstract;
// If this is a method, remove the body if it is empty.
if (member is MethodDeclarationSyntax method)
{
if (method.Body != null && method.Body.Statements.Count == 0)
{
member = method.WithBody(null).WithSemicolonToken(SyntaxFactory.Token([SyntaxFactory.ElasticMarker], SyntaxKind.SemicolonToken, method.Body.CloseBraceToken.TrailingTrivia));
}
}
else
{
// If this is a property, remove the bodies of the accessors if they are empty.
// Note that "empty" means that the bodies contain no statements or just a single return statement.
if (member is BasePropertyDeclarationSyntax property && property.AccessorList != null)
{
var updatedAccessors = new List<AccessorDeclarationSyntax>();
foreach (var accessor in property.AccessorList.Accessors)
{
if (accessor.Body == null ||
accessor.Body.Statements.Count > 1 ||
(accessor.Body.Statements.Count == 1 && !accessor.Body.Statements[0].IsKind(SyntaxKind.ReturnStatement)))
{
// Leave this accessor as is
updatedAccessors.Add(accessor);
continue;
}
var updatedAccessor = accessor.WithBody(null).WithSemicolonToken(SyntaxFactory.Token([SyntaxFactory.ElasticMarker], SyntaxKind.SemicolonToken, accessor.Body.CloseBraceToken.TrailingTrivia));
updatedAccessors.Add(updatedAccessor);
}
var updatedAccessorList = property.AccessorList.WithAccessors([.. updatedAccessors]);
member = property.ReplaceNode(property.AccessorList, updatedAccessorList);
}
}
}
else
{
flags &= ~ModifierFlags.Abstract;
// If this is a method, add a body.
if (member is MethodDeclarationSyntax method)
{
if (method.Body == null)
{
var newBody = SyntaxFactory.Block();
newBody = newBody.WithCloseBraceToken(newBody.CloseBraceToken.WithTrailingTrivia(method.SemicolonToken.TrailingTrivia));
member = method.WithSemicolonToken(default).WithBody(newBody);
}
}
else
{
// If this is a property, add bodies to the accessors if they don't have them.
if (member is BasePropertyDeclarationSyntax property && property.AccessorList != null)
{
var updatedAccessors = new List<AccessorDeclarationSyntax>();
foreach (var accessor in property.AccessorList.Accessors)
{
if (accessor.Body != null)
{
// Leave this accessor as is
updatedAccessors.Add(accessor);
continue;
}
var newBody = SyntaxFactory.Block();
newBody = newBody.WithCloseBraceToken(newBody.CloseBraceToken.WithTrailingTrivia(accessor.SemicolonToken.TrailingTrivia));
var updatedAccessor = accessor.WithSemicolonToken(default).WithBody(newBody);
updatedAccessors.Add(updatedAccessor);
}
var updatedAccessorList = property.AccessorList.WithAccessors([.. updatedAccessors]);
member = property.ReplaceNode(property.AccessorList, updatedAccessorList);
}
}
}
return member.UpdateModifiers(flags);
}
public override SyntaxNode SetInheritanceKind(SyntaxNode typeNode, EnvDTE80.vsCMInheritanceKind kind)
{
Debug.Assert(typeNode is ClassDeclarationSyntax);
if (typeNode is not MemberDeclarationSyntax member)
{
throw Exceptions.ThrowEFail();
}
var flags = member.GetModifierFlags();
flags &= ~(ModifierFlags.Abstract | ModifierFlags.New | ModifierFlags.Sealed);
if (kind != EnvDTE80.vsCMInheritanceKind.vsCMInheritanceKindNone)
{
if ((kind & EnvDTE80.vsCMInheritanceKind.vsCMInheritanceKindAbstract) != 0)
{
flags |= ModifierFlags.Abstract;
}
else if ((kind & EnvDTE80.vsCMInheritanceKind.vsCMInheritanceKindSealed) != 0)
{
flags |= ModifierFlags.Sealed;
}
// Can have new in combination with the above flags
if ((kind & EnvDTE80.vsCMInheritanceKind.vsCMInheritanceKindNew) != 0)
{
flags |= ModifierFlags.New;
}
}
return member.UpdateModifiers(flags);
}
public override EnvDTE80.vsCMOverrideKind GetOverrideKind(SyntaxNode memberNode)
{
Debug.Assert(memberNode is BaseMethodDeclarationSyntax or
BasePropertyDeclarationSyntax or
EventFieldDeclarationSyntax);
var member = (MemberDeclarationSyntax)memberNode;
var flags = member.GetModifierFlags();
var containingType = member.FirstAncestorOrSelf<TypeDeclarationSyntax>();
var result = EnvDTE80.vsCMOverrideKind.vsCMOverrideKindNone;
if ((flags & ModifierFlags.Abstract) != 0 || containingType?.Kind() == SyntaxKind.InterfaceDeclaration)
{
result |= EnvDTE80.vsCMOverrideKind.vsCMOverrideKindAbstract;
}
if ((flags & ModifierFlags.Virtual) != 0 || containingType?.Kind() == SyntaxKind.InterfaceDeclaration)
{
result |= EnvDTE80.vsCMOverrideKind.vsCMOverrideKindVirtual;
}
if ((flags & ModifierFlags.Override) != 0)
{
result |= EnvDTE80.vsCMOverrideKind.vsCMOverrideKindOverride;
}
if ((flags & ModifierFlags.New) != 0)
{
result |= EnvDTE80.vsCMOverrideKind.vsCMOverrideKindNew;
}
if ((flags & ModifierFlags.Sealed) != 0)
{
result |= EnvDTE80.vsCMOverrideKind.vsCMOverrideKindSealed;
}
return result;
}
public override SyntaxNode SetOverrideKind(SyntaxNode memberNode, EnvDTE80.vsCMOverrideKind kind)
{
Debug.Assert(memberNode is BaseMethodDeclarationSyntax or
BasePropertyDeclarationSyntax or
EventFieldDeclarationSyntax);
// The legacy C# code model sets the MustImplement property here depending on whether the Abstract kind is set
// TODO(DustinCa): VB implements MustImplement in terms of OverrideKind, should we do the same?
memberNode = SetMustImplement(memberNode, (kind & EnvDTE80.vsCMOverrideKind.vsCMOverrideKindAbstract) != 0);
var member = (MemberDeclarationSyntax)memberNode;
var flags = member.GetModifierFlags();
flags &= ~(ModifierFlags.Abstract | ModifierFlags.Virtual | ModifierFlags.Override | ModifierFlags.New | ModifierFlags.Sealed);
if (member.IsParentKind(SyntaxKind.InterfaceDeclaration))
{
if ((kind & (EnvDTE80.vsCMOverrideKind.vsCMOverrideKindOverride | EnvDTE80.vsCMOverrideKind.vsCMOverrideKindSealed)) != 0)
{
throw Exceptions.ThrowEInvalidArg();
}
else if ((kind & (EnvDTE80.vsCMOverrideKind.vsCMOverrideKindAbstract | EnvDTE80.vsCMOverrideKind.vsCMOverrideKindVirtual)) != 0)
{
// Switch these flags off
kind &= ~(EnvDTE80.vsCMOverrideKind.vsCMOverrideKindAbstract | EnvDTE80.vsCMOverrideKind.vsCMOverrideKindVirtual);
}
}
if (kind != EnvDTE80.vsCMOverrideKind.vsCMOverrideKindNone)
{
if ((kind & EnvDTE80.vsCMOverrideKind.vsCMOverrideKindAbstract) != 0)
{
flags |= ModifierFlags.Abstract;
}
if ((kind & EnvDTE80.vsCMOverrideKind.vsCMOverrideKindVirtual) != 0)
{
flags |= ModifierFlags.Virtual;
}
if ((kind & EnvDTE80.vsCMOverrideKind.vsCMOverrideKindOverride) != 0)
{
flags |= ModifierFlags.Override;
}
if ((kind & EnvDTE80.vsCMOverrideKind.vsCMOverrideKindSealed) != 0)
{
flags |= ModifierFlags.Sealed;
}
if ((kind & EnvDTE80.vsCMOverrideKind.vsCMOverrideKindNew) != 0)
{
flags |= ModifierFlags.New;
}
}
return member.UpdateModifiers(flags);
}
public override bool GetIsAbstract(SyntaxNode memberNode, ISymbol symbol)
{
Debug.Assert(memberNode is MemberDeclarationSyntax);
var member = (MemberDeclarationSyntax)memberNode;
return (member.GetModifierFlags() & ModifierFlags.Abstract) != 0;
}
public override SyntaxNode SetIsAbstract(SyntaxNode memberNode, bool value)
{
Debug.Assert(memberNode is MemberDeclarationSyntax);
if (memberNode is not MemberDeclarationSyntax member)
{
throw Exceptions.ThrowEFail();
}
var flags = member.GetModifierFlags();
if (value)
{
flags |= ModifierFlags.Abstract;
}
else
{
flags &= ~ModifierFlags.Abstract;
}
return member.UpdateModifiers(flags);
}
public override bool GetIsConstant(SyntaxNode variableNode)
{
Debug.Assert(variableNode is MemberDeclarationSyntax or
VariableDeclaratorSyntax);
if (variableNode is EnumMemberDeclarationSyntax)
{
return true;
}
if (GetNodeWithModifiers(variableNode) is not MemberDeclarationSyntax member)
{
throw Exceptions.ThrowEFail();
}
// C# legacy Code Model returns true for readonly fields as well.
return (member.GetModifierFlags() & (ModifierFlags.Const | ModifierFlags.ReadOnly)) != 0;
}
public override SyntaxNode SetIsConstant(SyntaxNode variableNode, bool value)
{
Debug.Assert(variableNode is MemberDeclarationSyntax);
if (variableNode is EnumMemberDeclarationSyntax)
{
if (!value)
{
throw Exceptions.ThrowEFail();
}
return variableNode;
}
if (variableNode is not MemberDeclarationSyntax member)
{
throw Exceptions.ThrowEFail();
}
if (GetIsConstant(member) == value)
{
return member;
}
var flags = member.GetModifierFlags();
if (value)
{
flags |= ModifierFlags.Const;
}
else
{
flags &= ~(ModifierFlags.Const | ModifierFlags.ReadOnly);
}
return member.UpdateModifiers(flags);
}
public override bool GetIsDefault(SyntaxNode propertyNode)
{
Debug.Assert(propertyNode is BasePropertyDeclarationSyntax);
if (propertyNode is not BasePropertyDeclarationSyntax property)
{
throw Exceptions.ThrowEFail();
}
return property.IsKind(SyntaxKind.IndexerDeclaration);
}
public override SyntaxNode SetIsDefault(SyntaxNode propertyNode, bool value)
{
// The C# legacy Code Model throws this specific exception rather than a COM exception.
throw new InvalidOperationException();
}
public override bool GetIsGeneric(SyntaxNode memberNode)
{
Debug.Assert(memberNode is MemberDeclarationSyntax);
if (GetNodeWithModifiers(memberNode) is not MemberDeclarationSyntax member)
{
throw Exceptions.ThrowEFail();
}
return member.GetArity() > 0;
}
public override bool GetIsPropertyStyleEvent(SyntaxNode eventNode)
{
Debug.Assert(eventNode is EventFieldDeclarationSyntax or
EventDeclarationSyntax);
return eventNode is EventDeclarationSyntax;
}
public override bool GetIsShared(SyntaxNode memberNode, ISymbol symbol)
{
Debug.Assert(memberNode is MemberDeclarationSyntax or
VariableDeclaratorSyntax);
if (GetNodeWithModifiers(memberNode) is not MemberDeclarationSyntax member)
{
throw Exceptions.ThrowEFail();
}
return (member.GetModifierFlags() & ModifierFlags.Static) != 0;
}
public override SyntaxNode SetIsShared(SyntaxNode memberNode, bool value)
{
Debug.Assert(memberNode is MemberDeclarationSyntax);
if (memberNode is not MemberDeclarationSyntax member)
{
throw Exceptions.ThrowEFail();
}
var flags = member.GetModifierFlags();
if (value)
{
flags |= ModifierFlags.Static;
}
else
{
flags &= ~ModifierFlags.Static;
}
return member.UpdateModifiers(flags);
}
public override EnvDTE80.vsCMPropertyKind GetReadWrite(SyntaxNode memberNode)
{
Debug.Assert(memberNode is BasePropertyDeclarationSyntax);
if (memberNode is not BasePropertyDeclarationSyntax property)
{
throw Exceptions.ThrowEFail();
}
var hasGetter = property.AccessorList != null && property.AccessorList.Accessors.Any(SyntaxKind.GetAccessorDeclaration);
var hasSetter = property.AccessorList != null && property.AccessorList.Accessors.Any(SyntaxKind.SetAccessorDeclaration);
if (!hasGetter && !hasSetter)
{
var expressionBody = property.GetExpressionBody();
if (expressionBody != null)
{
hasGetter = true;
}
}
if (hasGetter && hasSetter)
{
return EnvDTE80.vsCMPropertyKind.vsCMPropertyKindReadWrite;
}
else if (hasSetter)
{
return EnvDTE80.vsCMPropertyKind.vsCMPropertyKindWriteOnly;
}
else if (hasGetter)
{
return EnvDTE80.vsCMPropertyKind.vsCMPropertyKindReadOnly;
}
else
{
return EnvDTE80.vsCMPropertyKind.vsCMPropertyKindReadWrite;
}
}
public override SyntaxNode SetType(SyntaxNode node, ITypeSymbol? typeSymbol)
{
// The VB implementation of this supports typeSymbol being "null" to mean converting a Function to a Sub. The C# implementation
// however doesn't support this being null. This was noticed during null annotation of the service itself.
Contract.ThrowIfNull(typeSymbol);
Debug.Assert(node is MemberDeclarationSyntax or
ParameterSyntax);
TypeSyntax? oldType;
if (node is MemberDeclarationSyntax memberDeclaration)
{
oldType = memberDeclaration.GetMemberType();
}
else if (node is ParameterSyntax parameter)
{
oldType = parameter.Type;
}
else
{
throw Exceptions.ThrowEFail();
}
if (oldType == null)
{
throw Exceptions.ThrowEFail();
}
var typeName = typeSymbol.ToDisplayString(s_setTypeFormat);
var newType = SyntaxFactory.ParseTypeName(typeName);
return node.ReplaceNode(oldType, newType);
}
private Document Delete(Document document, VariableDeclaratorSyntax node)
{
var fieldDeclaration = node.FirstAncestorOrSelf<BaseFieldDeclarationSyntax>();
Contract.ThrowIfNull(fieldDeclaration);
// If we won't have anything left, then just delete the whole declaration
if (fieldDeclaration.Declaration.Variables.Count == 1)
{
return Delete(document, fieldDeclaration);
}
else
{
var newFieldDeclaration = fieldDeclaration.RemoveNode(node, SyntaxRemoveOptions.KeepNoTrivia);
Contract.ThrowIfNull(newFieldDeclaration);
return document.ReplaceNodeSynchronously(fieldDeclaration, newFieldDeclaration, CancellationToken.None);
}
}
private static Document Delete(Document document, EnumMemberDeclarationSyntax node)
{
var enumDeclaration = (EnumDeclarationSyntax)node.Parent!;
var members = enumDeclaration.Members;
var newMembers = members.Remove(node);
var newEnumDeclaration = enumDeclaration.WithMembers(newMembers);
// If we're removing the last enum member, we may need to move any trailing trivia
// to the enum member that comes before it.
var memberIndex = members.IndexOf(node);
if (memberIndex == members.Count - 1 && newMembers.Count > 0)
{
var trailingTrivia = node.GetTrailingTrivia();
var lastMember = newEnumDeclaration.Members.Last();
newEnumDeclaration = newEnumDeclaration.ReplaceNode(lastMember, lastMember.WithTrailingTrivia(trailingTrivia));
}
return document.ReplaceNodeSynchronously(enumDeclaration, newEnumDeclaration, CancellationToken.None);
}
private static Document Delete(Document document, AttributeSyntax node)
{
var attributeList = node.FirstAncestorOrSelf<AttributeListSyntax>();
Contract.ThrowIfNull(attributeList);
// If we don't have anything left, then just delete the whole attribute list.
if (attributeList.Attributes.Count == 1)
{
var text = document.GetTextSynchronously(CancellationToken.None);
// Note that we want to keep all leading trivia and delete all trailing trivia.
var deletionStart = attributeList.SpanStart;
var deletionEnd = attributeList.FullSpan.End;
text = text.Replace(TextSpan.FromBounds(deletionStart, deletionEnd), string.Empty);
return document.WithText(text);
}
else
{
var newAttributeList = attributeList.RemoveNode(node, SyntaxRemoveOptions.KeepNoTrivia);
Contract.ThrowIfNull(newAttributeList);
return document.ReplaceNodeSynchronously(attributeList, newAttributeList, CancellationToken.None);
}
}
private static Document Delete(Document document, AttributeArgumentSyntax node)
{
var argumentList = node.FirstAncestorOrSelf<AttributeArgumentListSyntax>();
Contract.ThrowIfNull(argumentList);
var newArgumentList = argumentList.RemoveNode(node, SyntaxRemoveOptions.KeepNoTrivia);
Contract.ThrowIfNull(newArgumentList);
return document.ReplaceNodeSynchronously(argumentList, newArgumentList, CancellationToken.None);
}
private static Document Delete(Document document, ParameterSyntax node)
{
var parameterList = node.FirstAncestorOrSelf<ParameterListSyntax>();
Contract.ThrowIfNull(parameterList);
var newParameterList = parameterList.RemoveNode(node, SyntaxRemoveOptions.KeepNoTrivia);
Contract.ThrowIfNull(newParameterList);
return document.ReplaceNodeSynchronously(parameterList, newParameterList, CancellationToken.None);
}
private static Document DeleteMember(Document document, SyntaxNode node)
{
var text = document.GetTextSynchronously(CancellationToken.None);
// We want to delete all the leading trivia from the node back to,
// but not including:
// * the first preprocessor directive
// - or -
// * the first comment after a white-space only line
// We also want to delete all the trailing trivia
var deletionEnd = node.FullSpan.End;
var deletionStart = node.SpanStart;
var contiguousEndOfLines = 0;
foreach (var trivia in node.GetLeadingTrivia().Reverse())
{
if (trivia.IsDirective)
{
break;
}
if (trivia.Kind() == SyntaxKind.EndOfLineTrivia)
{
if (contiguousEndOfLines > 0)
{
break;
}
else
{
contiguousEndOfLines++;
}
}
else if (trivia.Kind() != SyntaxKind.WhitespaceTrivia)
{
contiguousEndOfLines = 0;
}
deletionStart = trivia.FullSpan.Start;
}
text = text.Replace(TextSpan.FromBounds(deletionStart, deletionEnd), string.Empty);
return document.WithText(text);
}
public override Document Delete(Document document, SyntaxNode node)
=> node.Kind() switch
{
SyntaxKind.VariableDeclarator => Delete(document, (VariableDeclaratorSyntax)node),
SyntaxKind.EnumMemberDeclaration => Delete(document, (EnumMemberDeclarationSyntax)node),
SyntaxKind.Attribute => Delete(document, (AttributeSyntax)node),
SyntaxKind.AttributeArgument => Delete(document, (AttributeArgumentSyntax)node),
SyntaxKind.Parameter => Delete(document, (ParameterSyntax)node),
_ => DeleteMember(document, node),
};
public override string GetMethodXml(SyntaxNode node, SemanticModel semanticModel)
{
if (node is not MethodDeclarationSyntax methodDeclaration)
{
throw Exceptions.ThrowEUnexpected();
}
return MethodXmlBuilder.Generate(methodDeclaration, semanticModel);
}
public override string? GetInitExpression(SyntaxNode node)
{
switch (node.Kind())
{
case SyntaxKind.EnumMemberDeclaration:
var enumMemberDeclaration = (EnumMemberDeclarationSyntax)node;
return enumMemberDeclaration.EqualsValue?.Value.ToString();
case SyntaxKind.VariableDeclarator:
var variableDeclarator = (VariableDeclaratorSyntax)node;
return variableDeclarator.Initializer?.Value.ToString();
case SyntaxKind.Parameter:
var parameter = (ParameterSyntax)node;
return parameter.Default?.Value.ToString();
default:
throw Exceptions.ThrowEFail();
}
}
public override SyntaxNode AddInitExpression(SyntaxNode node, string value)
{
switch (node.Kind())
{
case SyntaxKind.EnumMemberDeclaration:
{
var enumMemberDeclaration = (EnumMemberDeclarationSyntax)node;
if (string.IsNullOrWhiteSpace(value))
{
return enumMemberDeclaration.WithEqualsValue(null);
}
var expression = SyntaxFactory.ParseExpression(value);
var equalsValueClause = enumMemberDeclaration.EqualsValue != null
? enumMemberDeclaration.EqualsValue.WithValue(expression)
: SyntaxFactory.EqualsValueClause(expression);
return enumMemberDeclaration.WithEqualsValue(equalsValueClause);
}
case SyntaxKind.VariableDeclarator:
{
var variableDeclarator = (VariableDeclaratorSyntax)node;
if (string.IsNullOrWhiteSpace(value))
{
return variableDeclarator.WithInitializer(null);
}
var expression = SyntaxFactory.ParseExpression(value);
var equalsValueClause = variableDeclarator.Initializer != null
? variableDeclarator.Initializer.WithValue(expression)
: SyntaxFactory.EqualsValueClause(expression);
return variableDeclarator.WithInitializer(equalsValueClause);
}
case SyntaxKind.Parameter:
{
var parameter = (ParameterSyntax)node;
if (string.IsNullOrWhiteSpace(value))
{
return parameter.WithDefault(null);
}
var expression = SyntaxFactory.ParseExpression(value);
var equalsValueClause = parameter.Default != null
? parameter.Default.WithValue(expression)
: SyntaxFactory.EqualsValueClause(expression);
return parameter.WithDefault(equalsValueClause);
}
default:
throw Exceptions.ThrowEFail();
}
}
public override CodeGenerationDestination GetDestination(SyntaxNode node)
=> CSharpCodeGenerationHelpers.GetDestination(node);
protected override Accessibility GetDefaultAccessibility(SymbolKind targetSymbolKind, CodeGenerationDestination destination)
{
switch (targetSymbolKind)
{
case SymbolKind.Field:
case SymbolKind.Method:
case SymbolKind.Property:
case SymbolKind.Event:
return Accessibility.Private;
case SymbolKind.NamedType:
switch (destination)
{
case CodeGenerationDestination.ClassType:
case CodeGenerationDestination.EnumType:
case CodeGenerationDestination.InterfaceType:
case CodeGenerationDestination.StructType:
return Accessibility.Private;
default:
return Accessibility.Internal;
}
default:
Debug.Fail("Invalid symbol kind: " + targetSymbolKind);
throw Exceptions.ThrowEFail();
}
}
protected override ITypeSymbol? GetTypeSymbolFromPartialName(string partialName, SemanticModel semanticModel, int position)
{
var parsedTypeName = SyntaxFactory.ParseTypeName(partialName);
return semanticModel.GetSpeculativeTypeInfo(position, parsedTypeName, SpeculativeBindingOption.BindAsTypeOrNamespace).Type;
}
public override ITypeSymbol? GetTypeSymbolFromFullName(string fullName, Compilation compilation)
{
ITypeSymbol? typeSymbol = compilation.GetTypeByMetadataName(fullName);
if (typeSymbol == null)
{
var parsedTypeName = SyntaxFactory.ParseTypeName(fullName);
// Check to see if the name we parsed has any skipped text. If it does, don't bother trying to
// speculatively bind it because we'll likely just get the wrong thing since we found a bunch
// of non-sensical tokens.
if (parsedTypeName.ContainsSkippedText)
{
return null;
}
// If we couldn't get the name, we just grab the first tree in the compilation to
// speculatively bind at position zero. However, if there *aren't* any trees, we fork the
// compilation with an empty tree for the purposes of speculative binding.
//
// I'm a bad person.
var tree = compilation.SyntaxTrees.FirstOrDefault();
if (tree == null)
{
tree = s_emptyTree;
compilation = compilation.AddSyntaxTrees(tree);
}
var semanticModel = compilation.GetSemanticModel(tree);
typeSymbol = semanticModel.GetSpeculativeTypeInfo(0, parsedTypeName, SpeculativeBindingOption.BindAsTypeOrNamespace).Type;
}
if (typeSymbol == null)
{
Debug.Fail("Could not find type: " + fullName);
throw new ArgumentException();
}
return typeSymbol;
}
public override SyntaxNode CreateReturnDefaultValueStatement(ITypeSymbol type)
{
return SyntaxFactory.ReturnStatement(
SyntaxFactory.DefaultExpression(
SyntaxFactory.ParseTypeName(type.ToDisplayString())));
}
protected override int GetAttributeIndexInContainer(SyntaxNode containerNode, Func<SyntaxNode, bool> predicate)
{
var attributes = GetAttributeNodes(containerNode).ToArray();
var index = 0;
while (index < attributes.Length)
{
var attribute = (AttributeSyntax)attributes[index];
if (predicate(attribute))
{
var attributeDeclaration = (AttributeListSyntax)attribute.Parent!;
// If this attribute is part of a declaration with multiple attributes,
// make sure to return the index of the last attribute in the declaration.
if (attributeDeclaration.Attributes.Count > 1)
{
var indexOfAttributeInDeclaration = attributeDeclaration.Attributes.IndexOf(attribute);
return index + (attributeDeclaration.Attributes.Count - indexOfAttributeInDeclaration);
}
return index + 1;
}
index++;
}
return -1;
}
protected override int GetAttributeArgumentIndexInContainer(SyntaxNode containerNode, Func<SyntaxNode, bool> predicate)
{
var attributeArguments = GetAttributeArgumentNodes(containerNode).ToArray();
for (var index = 0; index < attributeArguments.Length; index++)
{
if (predicate(attributeArguments[index]))
{
return index + 1;
}
}
return -1;
}
protected override int GetImportIndexInContainer(SyntaxNode containerNode, Func<SyntaxNode, bool> predicate)
{
var imports = GetImportNodes(containerNode).ToArray();
for (var index = 0; index < imports.Length; index++)
{
if (predicate(imports[index]))
{
return index + 1;
}
}
return -1;
}
protected override int GetParameterIndexInContainer(SyntaxNode containerNode, Func<SyntaxNode, bool> predicate)
{
var parameters = GetParameterNodes(containerNode).ToArray();
for (var index = 0; index < parameters.Length; index++)
{
if (predicate(parameters[index]))
{
return index + 1;
}
}
return -1;
}
protected override int GetMemberIndexInContainer(SyntaxNode containerNode, Func<SyntaxNode, bool> predicate)
{
var members = GetLogicalMemberNodes(containerNode).ToArray();
var index = 0;
while (index < members.Length)
{
var member = members[index];
if (predicate(member))
{
// If a variable declarator was specified, make sure we return
// the index of the last variable declarator in the parenting field declaration.
if (member is VariableDeclaratorSyntax variableDeclarator)
{
var variableDeclaration = (VariableDeclarationSyntax)member.Parent!;
var indexOfDeclaratorInField = variableDeclaration.Variables.IndexOf(variableDeclarator);
return index + (variableDeclaration.Variables.Count - indexOfDeclaratorInField);
}
// Note: we always return the item *after* this index.
return index + 1;
}
index++;
}
return -1;
}
protected override SyntaxNode GetFieldFromVariableNode(SyntaxNode node)
{
return node.Kind() == SyntaxKind.VariableDeclarator
? node.GetAncestors<BaseFieldDeclarationSyntax>().First()
: node;
}
protected override SyntaxNode GetVariableFromFieldNode(SyntaxNode finalNode)
{
// Work around the fact that code model really deals in terms of variable declarators
return finalNode is BaseFieldDeclarationSyntax
? ((BaseFieldDeclarationSyntax)finalNode).Declaration.Variables.Single()
: finalNode;
}
protected override SyntaxNode GetAttributeFromAttributeDeclarationNode(SyntaxNode node)
{
return node is AttributeListSyntax
? ((AttributeListSyntax)node).Attributes.First()
: node;
}
protected override TextSpan GetSpanToFormat(SyntaxNode root, TextSpan span)
{
var startToken = root.FindToken(span.Start).GetPreviousToken();
if (startToken.Kind() == SyntaxKind.OpenBraceToken)
{
startToken = startToken.GetPreviousToken();
}
var endToken = root.FindToken(span.End).GetNextToken();
if (endToken.Kind() == SyntaxKind.CloseBraceToken)
{
endToken = endToken.GetPreviousToken();
}
startToken = GetTokenWithoutAnnotation(startToken, t => t.GetPreviousToken());
endToken = GetTokenWithoutAnnotation(endToken, t => t.GetNextToken());
return GetEncompassingSpan(root, startToken, endToken);
}
protected SyntaxNode InsertMemberNodeIntoContainerCore(int index, SyntaxNode member, SyntaxNode container)
{
if (container is CompilationUnitSyntax compilationUnit)
{
var newMembers = compilationUnit.Members.Insert(index, (MemberDeclarationSyntax)member);
return compilationUnit.WithMembers(newMembers);
}
else if (container is BaseNamespaceDeclarationSyntax namespaceDeclaration)
{
var newMembers = namespaceDeclaration.Members.Insert(index, (MemberDeclarationSyntax)member);
return namespaceDeclaration.WithMembers(newMembers);
}
else if (container is TypeDeclarationSyntax typeDeclaration)
{
var newMembers = typeDeclaration.Members.Insert(index, (MemberDeclarationSyntax)member);
return typeDeclaration.WithMembers(newMembers);
}
else if (container is EnumDeclarationSyntax enumDeclaration)
{
// If we're inserting at the end of the list of enum members, we may need to strip the trailing
// line from the last enum member and add it to the separator that comes after it.
if (index > 0 && index == enumDeclaration.Members.Count)
{
var lastMember = enumDeclaration.Members[index - 1];
var trailingTrivia = lastMember.GetTrailingTrivia();
enumDeclaration = enumDeclaration.ReplaceNode(lastMember, lastMember.WithTrailingTrivia(SyntaxTriviaList.Empty));
var newMembers = enumDeclaration.Members.Insert(index, (EnumMemberDeclarationSyntax)member);
enumDeclaration = enumDeclaration.WithMembers(newMembers);
var separator = enumDeclaration.Members.GetSeparator(index - 1);
return enumDeclaration.ReplaceToken(separator, separator.WithTrailingTrivia(trailingTrivia));
}
else
{
var newMembers = enumDeclaration.Members.Insert(index, (EnumMemberDeclarationSyntax)member);
return enumDeclaration.WithMembers(newMembers);
}
}
throw Exceptions.ThrowEFail();
}
private static MemberDeclarationSyntax GetMember(SyntaxNode container, int index)
{
if (container is CompilationUnitSyntax compilationUnit)
{
return compilationUnit.Members[index];
}
else if (container is BaseNamespaceDeclarationSyntax namespaceDeclaration)
{
return namespaceDeclaration.Members[index];
}
else if (container is TypeDeclarationSyntax typeDeclaration)
{
return typeDeclaration.Members[index];
}
else if (container is EnumDeclarationSyntax enumDeclaration)
{
return enumDeclaration.Members[index];
}
throw Exceptions.ThrowEFail();
}
private static SyntaxNode EnsureAfterEndRegion(int index, SyntaxNode container)
{
// If the next token after our member has only whitespace and #endregion as leading
// trivia, we'll move that to be leading trivia of our member.
var newContainer = container;
var newMember = GetMember(newContainer, index);
var lastToken = newMember.GetLastToken();
var nextToken = lastToken.GetNextToken();
var triviaList = nextToken.LeadingTrivia;
var lastNonWhitespaceTrivia = triviaList.LastOrDefault(trivia => !trivia.IsWhitespaceOrEndOfLine());
if (lastNonWhitespaceTrivia.Kind() == SyntaxKind.EndRegionDirectiveTrivia)
{
newContainer = newContainer
.ReplaceToken(nextToken, nextToken.WithLeadingTrivia(SyntaxTriviaList.Empty));
newMember = GetMember(newContainer, index);
var firstToken = newMember.GetFirstToken();
newContainer = newContainer
.ReplaceToken(firstToken, firstToken.WithLeadingTrivia(triviaList));
}
return newContainer;
}
protected override SyntaxNode InsertMemberNodeIntoContainer(int index, SyntaxNode member, SyntaxNode container)
{
var newContainer = InsertMemberNodeIntoContainerCore(index, member, container);
newContainer = EnsureAfterEndRegion(index, newContainer);
return newContainer;
}
protected override SyntaxNode InsertAttributeArgumentIntoContainer(int index, SyntaxNode attributeArgument, SyntaxNode container)
{
if (container is AttributeSyntax attribute)
{
var argumentList = attribute.ArgumentList;
AttributeArgumentListSyntax newArgumentList;
if (argumentList == null)
{
newArgumentList = SyntaxFactory.AttributeArgumentList([(AttributeArgumentSyntax)attributeArgument]);
}
else
{
var newArguments = argumentList.Arguments.Insert(index, (AttributeArgumentSyntax)attributeArgument);
newArgumentList = argumentList.WithArguments(newArguments);
}
return attribute.WithArgumentList(newArgumentList);
}
throw Exceptions.ThrowEFail();
}
protected override SyntaxNode InsertAttributeListIntoContainer(int index, SyntaxNode list, SyntaxNode container)
{
// If the attribute list is being inserted at the first index and the container is not the compilation unit, copy leading trivia
// to the list that is being inserted.
if (index == 0 && !(container is CompilationUnitSyntax))
{
var firstToken = container.GetFirstToken();
if (firstToken.HasLeadingTrivia)
{
var trivia = firstToken.LeadingTrivia;
container = container.ReplaceToken(firstToken, firstToken.WithLeadingTrivia(SyntaxTriviaList.Empty));
list = list.WithLeadingTrivia(trivia);
}
}
if (container is CompilationUnitSyntax compilationUnit)
{
var newAttributeLists = compilationUnit.AttributeLists.Insert(index, (AttributeListSyntax)list);
return compilationUnit.WithAttributeLists(newAttributeLists);
}
else if (container is EnumDeclarationSyntax enumDeclaration)
{
var newAttributeLists = enumDeclaration.AttributeLists.Insert(index, (AttributeListSyntax)list);
return enumDeclaration.WithAttributeLists(newAttributeLists);
}
else if (container is ClassDeclarationSyntax classDeclaration)
{
var newAttributeLists = classDeclaration.AttributeLists.Insert(index, (AttributeListSyntax)list);
return classDeclaration.WithAttributeLists(newAttributeLists);
}
else if (container is StructDeclarationSyntax structDeclaration)
{
var newAttributeLists = structDeclaration.AttributeLists.Insert(index, (AttributeListSyntax)list);
return structDeclaration.WithAttributeLists(newAttributeLists);
}
else if (container is InterfaceDeclarationSyntax interfaceDeclaration)
{
var newAttributeLists = interfaceDeclaration.AttributeLists.Insert(index, (AttributeListSyntax)list);
return interfaceDeclaration.WithAttributeLists(newAttributeLists);
}
else if (container is MethodDeclarationSyntax method)
{
var newAttributeLists = method.AttributeLists.Insert(index, (AttributeListSyntax)list);
return method.WithAttributeLists(newAttributeLists);
}
else if (container is OperatorDeclarationSyntax operationDeclaration)
{
var newAttributeLists = operationDeclaration.AttributeLists.Insert(index, (AttributeListSyntax)list);
return operationDeclaration.WithAttributeLists(newAttributeLists);
}
else if (container is ConversionOperatorDeclarationSyntax conversion)
{
var newAttributeLists = conversion.AttributeLists.Insert(index, (AttributeListSyntax)list);
return conversion.WithAttributeLists(newAttributeLists);
}
else if (container is ConstructorDeclarationSyntax constructor)
{
var newAttributeLists = constructor.AttributeLists.Insert(index, (AttributeListSyntax)list);
return constructor.WithAttributeLists(newAttributeLists);
}
else if (container is DestructorDeclarationSyntax destructor)
{
var newAttributeLists = destructor.AttributeLists.Insert(index, (AttributeListSyntax)list);
return destructor.WithAttributeLists(newAttributeLists);
}
else if (container is PropertyDeclarationSyntax property)
{
var newAttributeLists = property.AttributeLists.Insert(index, (AttributeListSyntax)list);
return property.WithAttributeLists(newAttributeLists);
}
else if (container is EventDeclarationSyntax eventDeclaration)
{
var newAttributeLists = eventDeclaration.AttributeLists.Insert(index, (AttributeListSyntax)list);
return eventDeclaration.WithAttributeLists(newAttributeLists);
}
else if (container is IndexerDeclarationSyntax indexer)
{
var newAttributeLists = indexer.AttributeLists.Insert(index, (AttributeListSyntax)list);
return indexer.WithAttributeLists(newAttributeLists);
}
else if (container is FieldDeclarationSyntax field)
{
var newAttributeLists = field.AttributeLists.Insert(index, (AttributeListSyntax)list);
return field.WithAttributeLists(newAttributeLists);
}
else if (container is EventFieldDeclarationSyntax eventFieldDeclaration)
{
var newAttributeLists = eventFieldDeclaration.AttributeLists.Insert(index, (AttributeListSyntax)list);
return eventFieldDeclaration.WithAttributeLists(newAttributeLists);
}
else if (container is DelegateDeclarationSyntax delegateDeclaration)
{
var newAttributeLists = delegateDeclaration.AttributeLists.Insert(index, (AttributeListSyntax)list);
return delegateDeclaration.WithAttributeLists(newAttributeLists);
}
else if (container is EnumMemberDeclarationSyntax member)
{
var newAttributeLists = member.AttributeLists.Insert(index, (AttributeListSyntax)list);
return member.WithAttributeLists(newAttributeLists);
}
else if (container is ParameterSyntax parameter)
{
var newAttributeLists = parameter.AttributeLists.Insert(index, (AttributeListSyntax)list);
return parameter.WithAttributeLists(newAttributeLists);
}
else if (container is VariableDeclaratorSyntax or VariableDeclarationSyntax)
{
return InsertAttributeListIntoContainer(index, list, container.Parent!);
}
throw Exceptions.ThrowEUnexpected();
}
protected override SyntaxNode InsertImportIntoContainer(int index, SyntaxNode importNode, SyntaxNode container)
{
var import = (UsingDirectiveSyntax)importNode;
if (container is CompilationUnitSyntax compilationUnit)
{
var usingsList = compilationUnit.Usings.Insert(index, import);
return compilationUnit.WithUsings(usingsList);
}
throw Exceptions.ThrowEUnexpected();
}
protected override SyntaxNode InsertParameterIntoContainer(int index, SyntaxNode parameter, SyntaxNode container)
{
if (container is BaseMethodDeclarationSyntax method)
{
var parameterList = method.ParameterList.Parameters.Insert(index, (ParameterSyntax)parameter);
return method.WithParameterList(method.ParameterList.WithParameters(parameterList));
}
else if (container is IndexerDeclarationSyntax indexer)
{
var parameterList = indexer.ParameterList.Parameters.Insert(index, (ParameterSyntax)parameter);
return indexer.WithParameterList(indexer.ParameterList.WithParameters(parameterList));
}
else if (container is DelegateDeclarationSyntax delegateDeclaration)
{
var parameterList = delegateDeclaration.ParameterList.Parameters.Insert(index, (ParameterSyntax)parameter);
return delegateDeclaration.WithParameterList(delegateDeclaration.ParameterList.WithParameters(parameterList));
}
throw Exceptions.ThrowEUnexpected();
}
protected override bool IsCodeModelNode(SyntaxNode node)
{
switch (node.Kind())
{
case SyntaxKind.ClassDeclaration:
case SyntaxKind.CompilationUnit:
case SyntaxKind.ConstructorDeclaration:
case SyntaxKind.ConversionOperatorDeclaration:
case SyntaxKind.DelegateDeclaration:
case SyntaxKind.DestructorDeclaration:
case SyntaxKind.EnumDeclaration:
case SyntaxKind.EnumMemberDeclaration:
case SyntaxKind.FieldDeclaration:
case SyntaxKind.IndexerDeclaration:
case SyntaxKind.InterfaceDeclaration:
case SyntaxKind.MethodDeclaration:
case SyntaxKind.NamespaceDeclaration:
case SyntaxKind.FileScopedNamespaceDeclaration:
case SyntaxKind.OperatorDeclaration:
case SyntaxKind.PropertyDeclaration:
case SyntaxKind.StructDeclaration:
case SyntaxKind.UsingDirective:
return true;
default:
return false;
}
}
public override bool IsNamespace(SyntaxNode node)
=> node is BaseNamespaceDeclarationSyntax;
public override bool IsType(SyntaxNode node)
{
return node?.Kind()
is SyntaxKind.ClassDeclaration
or SyntaxKind.InterfaceDeclaration
or SyntaxKind.StructDeclaration
or SyntaxKind.EnumDeclaration
or SyntaxKind.DelegateDeclaration;
}
private static bool IsAutoImplementedProperty(PropertyDeclarationSyntax propertyDeclaration)
{
if (propertyDeclaration.IsParentKind(SyntaxKind.InterfaceDeclaration))
{
return false;
}
var modifierFlags = propertyDeclaration.GetModifierFlags();
if ((modifierFlags & ModifierFlags.Abstract) != 0 ||
(modifierFlags & ModifierFlags.Extern) != 0)
{
return false;
}
if (propertyDeclaration.AccessorList == null)
{
return false;
}
AccessorDeclarationSyntax? getAccessor = null;
AccessorDeclarationSyntax? setAccessor = null;
foreach (var accessor in propertyDeclaration.AccessorList.Accessors)
{
switch (accessor.Kind())
{
case SyntaxKind.GetAccessorDeclaration:
getAccessor ??= accessor;
break;
case SyntaxKind.SetAccessorDeclaration:
setAccessor ??= accessor;
break;
}
}
if (getAccessor == null || setAccessor == null)
{
return false;
}
return getAccessor.Body == null && setAccessor.Body == null;
}
private static bool IsExtensionMethod(MethodDeclarationSyntax methodDeclaration)
{
if (methodDeclaration?.Parent is not ClassDeclarationSyntax classDecl ||
!classDecl.Modifiers.Any(SyntaxKind.StaticKeyword))
{
return false;
}
if (methodDeclaration.ParameterList == null ||
methodDeclaration.ParameterList.Parameters.Count == 0)
{
return false;
}
return methodDeclaration.ParameterList.Parameters[0].Modifiers.Any(SyntaxKind.ThisKeyword);
}
private static bool IsPartialMethod(MethodDeclarationSyntax methodDeclaration)
=> methodDeclaration.Modifiers.Any(SyntaxKind.PartialKeyword);
public override string[] GetFunctionExtenderNames()
=> [ExtenderNames.ExtensionMethod, ExtenderNames.PartialMethod];
public override object GetFunctionExtender(string name, SyntaxNode node, ISymbol symbol)
{
if (node == null || node.Kind() != SyntaxKind.MethodDeclaration ||
symbol == null || symbol.Kind != SymbolKind.Method)
{
throw Exceptions.ThrowEUnexpected();
}
if (StringComparer.Ordinal.Equals(name, ExtenderNames.PartialMethod))
{
var methodDeclaration = (MethodDeclarationSyntax)node;
var isPartial = IsPartialMethod(methodDeclaration);
var isDeclaration = false;
var hasOtherPart = false;
if (isPartial)
{
var methodSymbol = (IMethodSymbol)symbol;
isDeclaration = methodSymbol.PartialDefinitionPart == null;
hasOtherPart = isDeclaration
? methodSymbol.PartialImplementationPart != null
: methodSymbol.PartialDefinitionPart != null;
}
return PartialMethodExtender.Create(isPartial, isDeclaration, hasOtherPart);
}
else if (StringComparer.Ordinal.Equals(name, ExtenderNames.ExtensionMethod))
{
var methodDeclaration = (MethodDeclarationSyntax)node;
var isExtension = IsExtensionMethod(methodDeclaration);
return ExtensionMethodExtender.Create(isExtension);
}
throw Exceptions.ThrowEFail();
}
public override string[] GetPropertyExtenderNames()
=> [ExtenderNames.AutoImplementedProperty];
public override object GetPropertyExtender(string name, SyntaxNode node, ISymbol symbol)
{
if (node == null || node.Kind() != SyntaxKind.PropertyDeclaration ||
symbol == null || symbol.Kind != SymbolKind.Property)
{
throw Exceptions.ThrowEUnexpected();
}
if (StringComparer.Ordinal.Equals(name, ExtenderNames.AutoImplementedProperty))
{
var propertyDeclaration = (PropertyDeclarationSyntax)node;
var isAutoImplemented = IsAutoImplementedProperty(propertyDeclaration);
return AutoImplementedPropertyExtender.Create(isAutoImplemented);
}
throw Exceptions.ThrowEFail();
}
public override string[] GetExternalTypeExtenderNames()
=> [ExtenderNames.ExternalLocation];
public override object GetExternalTypeExtender(string name, string externalLocation)
{
Debug.Assert(externalLocation != null);
if (StringComparer.Ordinal.Equals(name, ExtenderNames.ExternalLocation))
{
return CodeTypeLocationExtender.Create(externalLocation);
}
throw Exceptions.ThrowEFail();
}
public override string[] GetTypeExtenderNames()
=> Array.Empty<string>();
public override object GetTypeExtender(string name, AbstractCodeType symbol)
=> throw Exceptions.ThrowEFail();
protected override bool AddBlankLineToMethodBody(SyntaxNode node, SyntaxNode newNode)
{
return node is MethodDeclarationSyntax methodDeclaration
&& methodDeclaration.Body == null
&& newNode is MethodDeclarationSyntax newMethodDeclaration
&& newMethodDeclaration.Body != null;
}
private static TypeDeclarationSyntax InsertIntoBaseList(TypeDeclarationSyntax typeDeclaration, ITypeSymbol typeSymbol, SemanticModel semanticModel, int insertionIndex)
{
var position = typeDeclaration.SpanStart;
var identifier = typeDeclaration.Identifier;
if (identifier.HasTrailingTrivia)
{
typeDeclaration = typeDeclaration.WithIdentifier(
identifier.WithTrailingTrivia(identifier.TrailingTrivia.SkipWhile(t => t.IsWhitespaceOrEndOfLine())));
}
var typeName = SyntaxFactory.ParseTypeName(typeSymbol.ToMinimalDisplayString(semanticModel, position));
var baseList = typeDeclaration.BaseList != null
? typeDeclaration.BaseList.WithTypes(typeDeclaration.BaseList.Types.Insert(insertionIndex, SyntaxFactory.SimpleBaseType(typeName)))
: SyntaxFactory.BaseList([SyntaxFactory.SimpleBaseType(typeName)]);
return typeDeclaration.WithBaseList(baseList);
}
public override bool IsValidBaseType(SyntaxNode node, ITypeSymbol typeSymbol)
{
if (node.IsKind(SyntaxKind.ClassDeclaration))
{
return typeSymbol.TypeKind == TypeKind.Class;
}
else if (node.IsKind(SyntaxKind.InterfaceDeclaration))
{
return typeSymbol.TypeKind == TypeKind.Interface;
}
return false;
}
public override SyntaxNode AddBase(SyntaxNode node, ITypeSymbol typeSymbol, SemanticModel semanticModel, int? position)
{
if (node.Kind() is not (SyntaxKind.ClassDeclaration or SyntaxKind.InterfaceDeclaration))
{
throw Exceptions.ThrowEFail();
}
var typeDeclaration = (TypeDeclarationSyntax)node;
var baseCount = typeDeclaration.BaseList != null
? typeDeclaration.BaseList.Types.Count
: 0;
int insertionIndex;
if (typeDeclaration.IsKind(SyntaxKind.ClassDeclaration))
{
insertionIndex = 0;
}
else if (position != null)
{
insertionIndex = position.Value;
if (insertionIndex > baseCount)
{
throw Exceptions.ThrowEInvalidArg();
}
}
else
{
insertionIndex = baseCount;
}
return InsertIntoBaseList(typeDeclaration, typeSymbol, semanticModel, insertionIndex);
}
public override SyntaxNode RemoveBase(SyntaxNode node, ITypeSymbol typeSymbol, SemanticModel semanticModel)
{
if (node.Kind() is not (SyntaxKind.ClassDeclaration or SyntaxKind.InterfaceDeclaration))
{
throw Exceptions.ThrowEFail();
}
var typeDeclaration = (TypeDeclarationSyntax)node;
if (typeDeclaration.BaseList == null ||
typeDeclaration.BaseList.Types.Count == 0)
{
throw Exceptions.ThrowEInvalidArg();
}
var isFirst = true;
BaseTypeSyntax? baseType = null;
foreach (var type in typeDeclaration.BaseList.Types)
{
if (!isFirst && node.IsKind(SyntaxKind.ClassDeclaration))
{
break;
}
var typeInfo = semanticModel.GetTypeInfo(type.Type, CancellationToken.None);
if (typeInfo.Type != null &&
typeInfo.Type.Equals(typeSymbol))
{
baseType = type;
break;
}
isFirst = false;
}
if (baseType == null)
{
throw Exceptions.ThrowEInvalidArg();
}
var newTypes = typeDeclaration.BaseList.Types.Remove(baseType);
var newBaseList = typeDeclaration.BaseList.WithTypes(newTypes);
if (newBaseList.Types.Count == 0)
{
newBaseList = null;
}
return typeDeclaration.WithBaseList(newBaseList);
}
public override bool IsValidInterfaceType(SyntaxNode node, ITypeSymbol typeSymbol)
{
if (node.Kind() is SyntaxKind.ClassDeclaration or SyntaxKind.StructDeclaration)
{
return typeSymbol.TypeKind == TypeKind.Interface;
}
return false;
}
public override SyntaxNode AddImplementedInterface(SyntaxNode node, ITypeSymbol typeSymbol, SemanticModel semanticModel, int? position)
{
if (node.Kind() is not (SyntaxKind.ClassDeclaration or SyntaxKind.StructDeclaration))
{
throw Exceptions.ThrowEFail();
}
if (typeSymbol.Kind != SymbolKind.NamedType ||
typeSymbol.TypeKind != TypeKind.Interface)
{
throw Exceptions.ThrowEInvalidArg();
}
var typeDeclaration = (TypeDeclarationSyntax)node;
var baseCount = typeDeclaration.BaseList != null
? typeDeclaration.BaseList.Types.Count
: 0;
int insertionIndex;
if (position != null)
{
insertionIndex = position.Value;
if (insertionIndex > baseCount)
{
throw Exceptions.ThrowEInvalidArg();
}
}
else
{
insertionIndex = baseCount;
}
return InsertIntoBaseList(typeDeclaration, typeSymbol, semanticModel, insertionIndex);
}
public override SyntaxNode RemoveImplementedInterface(SyntaxNode node, ITypeSymbol typeSymbol, SemanticModel semanticModel)
{
if (node.Kind() is not (SyntaxKind.ClassDeclaration or SyntaxKind.StructDeclaration))
{
throw Exceptions.ThrowEFail();
}
var typeDeclaration = (TypeDeclarationSyntax)node;
if (typeDeclaration.BaseList == null ||
typeDeclaration.BaseList.Types.Count == 0)
{
throw Exceptions.ThrowEInvalidArg();
}
BaseTypeSyntax? baseType = null;
foreach (var type in typeDeclaration.BaseList.Types)
{
var typeInfo = semanticModel.GetTypeInfo(type.Type, CancellationToken.None);
if (typeInfo.Type != null &&
typeInfo.Type.Equals(typeSymbol))
{
baseType = type;
break;
}
}
if (baseType == null)
{
throw Exceptions.ThrowEInvalidArg();
}
var newTypes = typeDeclaration.BaseList.Types.Remove(baseType);
var newBaseList = typeDeclaration.BaseList.WithTypes(newTypes);
if (newBaseList.Types.Count == 0)
{
newBaseList = null;
}
return typeDeclaration.WithBaseList(newBaseList);
}
}
}
|