private static async Task <GraphNodeId> GetPartialForNamedTypeAsync(INamedTypeSymbol namedType, GraphNodeIdName nodeName, Solution solution, CancellationToken cancellationToken, bool isInGenericArguments = false) { // If this is a simple type, then we don't have much to do if (namedType.ContainingType == null && namedType.ConstructedFrom == namedType && namedType.Arity == 0) { return(GraphNodeId.GetPartial(nodeName, namedType.Name)); } else { // For a generic type, we need to populate "type" property with the following form: // // Type = (Name =...GenericParameterCount = GenericArguments =...ParentType =...) // // where "Name" contains a symbol name // and "GenericParameterCount" contains the number of type parameters, // and "GenericArguments" contains its type parameters' node information. // and "ParentType" contains its containing type's node information. var partials = new List <GraphNodeId>(); partials.Add(GraphNodeId.GetPartial(CodeQualifiedName.Name, namedType.Name)); if (namedType.Arity > 0) { partials.Add(GraphNodeId.GetPartial(CodeGraphNodeIdName.GenericParameterCountIdentifier, namedType.Arity.ToString())); } // For the property "GenericArguments", we only populate them // when type parameters are constructed using instance types (i.e., namedType.ConstructedFrom != namedType). // However, there is a case where we need to populate "GenericArguments" even though arguments are not marked as "constructed" // because a symbol is not marked as "constructed" when a type is constructed using its own type parameters. // To distinguish this case, we use "isInGenericArguments" flag which we pass either to populate arguments recursively or to populate "ParentType". bool hasGenericArguments = (namedType.ConstructedFrom != namedType || isInGenericArguments) && namedType.TypeArguments != null && namedType.TypeArguments.Any(); if (hasGenericArguments) { var genericArguments = new List <GraphNodeId>(); foreach (var arg in namedType.TypeArguments) { var nodes = await GetPartialsForNamespaceAndTypeAsync(arg, includeNamespace : true, solution : solution, cancellationToken : cancellationToken, isInGenericArguments : true).ConfigureAwait(false); genericArguments.Add(GraphNodeId.GetNested(nodes.ToArray())); } partials.Add(GraphNodeId.GetArray( CodeGraphNodeIdName.GenericArgumentsIdentifier, genericArguments.ToArray())); } if (namedType.ContainingType != null) { partials.Add(await GetPartialForTypeAsync(namedType.ContainingType, CodeGraphNodeIdName.ParentType, solution, cancellationToken, hasGenericArguments).ConfigureAwait(false)); } return(GraphNodeId.GetPartial(nodeName, MakeCollectionIfNecessary(false, partials.ToArray()))); } }
public static async Task <GraphNodeId> GetIdForMemberAsync(ISymbol member, Solution solution, CancellationToken cancellationToken) { var partials = new List <GraphNodeId>(); partials.AddRange(await GetPartialsForNamespaceAndTypeAsync(member.ContainingType, true, solution, cancellationToken).ConfigureAwait(false)); var parameters = member.GetParameters(); if (parameters.Any() || member.GetArity() > 0) { var memberPartials = new List <GraphNodeId>(); memberPartials.Add(GraphNodeId.GetPartial(CodeQualifiedName.Name, member.MetadataName)); if (member.GetArity() > 0) { memberPartials.Add(GraphNodeId.GetPartial(CodeGraphNodeIdName.GenericParameterCountIdentifier, member.GetArity().ToString())); } if (parameters.Any()) { var parameterTypeIds = new List <GraphNodeId>(); foreach (var p in parameters) { var parameterIds = await GetPartialsForNamespaceAndTypeAsync(p.Type, true, solution, cancellationToken).ConfigureAwait(false); var nodes = parameterIds.ToList(); if (p.IsRefOrOut()) { nodes.Add(GraphNodeId.GetPartial(CodeGraphNodeIdName.ParamKind, ParamKind.Ref)); } parameterTypeIds.Add(GraphNodeId.GetNested(nodes.ToArray())); } if (member is IMethodSymbol methodSymbol && methodSymbol.MethodKind == MethodKind.Conversion) { // For explicit/implicit conversion operators, we need to include the return type in the method Id, // because there can be several conversion operators with same parameters and only differ by return type. // For example, // // public class Class1 // { // public static explicit (explicit) operator int(Class1 c) { ... } // public static explicit (explicit) operator double(Class1 c) { ... } // } var nodes = await GetPartialsForNamespaceAndTypeAsync(methodSymbol.ReturnType, true, solution, cancellationToken).ConfigureAwait(false); List <GraphNodeId> returnTypePartial = nodes.ToList(); returnTypePartial.Add(GraphNodeId.GetPartial(CodeGraphNodeIdName.ParamKind, Microsoft.VisualStudio.GraphModel.CodeSchema.ParamKind.Return)); GraphNodeId returnCollection = GraphNodeId.GetNested(returnTypePartial.ToArray()); parameterTypeIds.Add(returnCollection); } memberPartials.Add(GraphNodeId.GetArray( CodeGraphNodeIdName.OverloadingParameters, parameterTypeIds.ToArray())); } partials.Add(GraphNodeId.GetPartial( CodeGraphNodeIdName.Member, MakeCollectionIfNecessary(false, memberPartials.ToArray()))); } else { partials.Add(GraphNodeId.GetPartial(CodeGraphNodeIdName.Member, member.MetadataName)); } return(GraphNodeId.GetNested(partials.ToArray())); }