// 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 Microsoft.CodeAnalysis.Shared.Extensions; namespace Microsoft.CodeAnalysis.LanguageService; internal abstract partial class AbstractStructuralTypeDisplayService { private sealed class StructuralTypeCollectorVisitor(Dictionary<INamedTypeSymbol, (int order, int count)> namedTypes) : SymbolVisitor { private readonly ISet<INamedTypeSymbol> _seenTypes = new HashSet<INamedTypeSymbol>(); private readonly Dictionary<INamedTypeSymbol, (int order, int count)> _namedTypes = namedTypes; public override void DefaultVisit(ISymbol node) => throw new NotImplementedException(); public override void VisitAlias(IAliasSymbol symbol) { // TODO(cyrusn): I don't think we need to inspect the target of an alias. } public override void VisitArrayType(IArrayTypeSymbol symbol) => symbol.ElementType.Accept(this); public override void VisitAssembly(IAssemblySymbol symbol) { } public override void VisitDynamicType(IDynamicTypeSymbol symbol) { } public override void VisitField(IFieldSymbol symbol) => symbol.Type.Accept(this); public override void VisitLabel(ILabelSymbol symbol) { } public override void VisitLocal(ILocalSymbol symbol) => symbol.Type.Accept(this); public override void VisitMethod(IMethodSymbol symbol) { // Visit the type arguments first. That way we'll see things in the proper order. // i.e. if we have: anon Select<anon, anon>(anon a) it will come out as: // // 'b Select<'a, 'b>('a a); foreach (var typeArgument in symbol.TypeArguments) typeArgument.Accept(this); foreach (var parameter in symbol.Parameters) parameter.Accept(this); symbol.ReturnType.Accept(this); } public override void VisitModule(IModuleSymbol symbol) { } public override void VisitNamedType(INamedTypeSymbol symbol) { // If we're hitting an anonymous/tuple type another time, then up the count we have for it. // that way we can tell how often this type appears in the final signature. if (_namedTypes.TryGetValue(symbol, out var orderAndCount)) { orderAndCount.count++; _namedTypes[symbol] = orderAndCount; return; } if (_seenTypes.Add(symbol)) { if (symbol.IsAnonymousType()) { _namedTypes.Add(symbol, (order: _namedTypes.Count, count: 1)); if (symbol.IsDelegateType()) { symbol.DelegateInvokeMethod?.Accept(this); } else { foreach (var property in symbol.GetValidAnonymousTypeProperties()) property.Accept(this); } } else if (symbol.IsTupleType) { _namedTypes.Add(symbol, (order: _namedTypes.Count, count: 1)); foreach (var field in symbol.TupleElements) field.Accept(this); } else { foreach (var typeArgument in symbol.GetAllTypeArguments()) typeArgument.Accept(this); } } } public override void VisitNamespace(INamespaceSymbol symbol) { } public override void VisitParameter(IParameterSymbol symbol) => symbol.Type.Accept(this); public override void VisitPointerType(IPointerTypeSymbol symbol) => symbol.PointedAtType.Accept(this); public override void VisitProperty(IPropertySymbol symbol) { symbol.Type.Accept(this); foreach (var parameter in symbol.Parameters) parameter.Accept(this); } public override void VisitEvent(IEventSymbol symbol) => symbol.Type.Accept(this); public override void VisitTypeParameter(ITypeParameterSymbol symbol) { foreach (var constraint in symbol.ConstraintTypes) constraint.Accept(this); } public override void VisitRangeVariable(IRangeVariableSymbol symbol) { } } } |