Files
2026-08-27 10:56:38 -06:00

320 lines
9.3 KiB
C#

using System.Collections.Immutable;
using System.Linq;
using Microsoft.CodeAnalysis.PooledObjects;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.SymbolDisplay;
internal abstract class AbstractSymbolDisplayVisitor : SymbolVisitor
{
protected readonly ArrayBuilder<SymbolDisplayPart> builder;
protected readonly SymbolDisplayFormat format;
protected readonly bool isFirstSymbolVisited;
protected readonly bool inNamespaceOrType;
protected readonly SemanticModel semanticModelOpt;
protected readonly int positionOpt;
private AbstractSymbolDisplayVisitor _lazyNotFirstVisitor;
private AbstractSymbolDisplayVisitor _lazyNotFirstVisitorNamespaceOrType;
protected AbstractSymbolDisplayVisitor NotFirstVisitor
{
get
{
if (_lazyNotFirstVisitor == null)
{
_lazyNotFirstVisitor = MakeNotFirstVisitor();
}
return _lazyNotFirstVisitor;
}
}
protected AbstractSymbolDisplayVisitor NotFirstVisitorNamespaceOrType
{
get
{
if (_lazyNotFirstVisitorNamespaceOrType == null)
{
_lazyNotFirstVisitorNamespaceOrType = MakeNotFirstVisitor(inNamespaceOrType: true);
}
return _lazyNotFirstVisitorNamespaceOrType;
}
}
protected bool IsMinimizing => semanticModelOpt != null;
protected AbstractSymbolDisplayVisitor(ArrayBuilder<SymbolDisplayPart> builder, SymbolDisplayFormat format, bool isFirstSymbolVisited, SemanticModel semanticModelOpt, int positionOpt, bool inNamespaceOrType = false)
{
this.builder = builder;
this.format = format;
this.isFirstSymbolVisited = isFirstSymbolVisited;
this.semanticModelOpt = semanticModelOpt;
this.positionOpt = positionOpt;
this.inNamespaceOrType = inNamespaceOrType;
if (!isFirstSymbolVisited)
{
_lazyNotFirstVisitor = this;
}
}
protected abstract AbstractSymbolDisplayVisitor MakeNotFirstVisitor(bool inNamespaceOrType = false);
protected abstract void AddLiteralValue(SpecialType type, object value);
protected abstract void AddExplicitlyCastedLiteralValue(INamedTypeSymbol namedType, SpecialType type, object value);
protected abstract void AddSpace();
protected abstract void AddBitwiseOr();
protected void AddNonNullConstantValue(ITypeSymbol type, object constantValue, bool preferNumericValueOrExpandedFlagsForEnum = false)
{
if (ITypeSymbolHelpers.IsNullableType(type))
{
type = ITypeSymbolHelpers.GetNullableUnderlyingType(type);
}
if (type.TypeKind == TypeKind.Enum)
{
AddEnumConstantValue((INamedTypeSymbol)type, constantValue, preferNumericValueOrExpandedFlagsForEnum);
}
else
{
AddLiteralValue(type.SpecialType, constantValue);
}
}
private void AddEnumConstantValue(INamedTypeSymbol enumType, object constantValue, bool preferNumericValueOrExpandedFlags)
{
if (IsFlagsEnum(enumType))
{
AddFlagsEnumConstantValue(enumType, constantValue, preferNumericValueOrExpandedFlags);
}
else if (preferNumericValueOrExpandedFlags)
{
AddLiteralValue(enumType.EnumUnderlyingType.SpecialType, constantValue);
}
else
{
AddNonFlagsEnumConstantValue(enumType, constantValue);
}
}
private static bool IsFlagsEnum(ITypeSymbol typeSymbol)
{
if (typeSymbol.TypeKind != TypeKind.Enum)
{
return false;
}
ImmutableArray<AttributeData>.Enumerator enumerator = typeSymbol.GetAttributes().GetEnumerator();
while (enumerator.MoveNext())
{
IMethodSymbol attributeConstructor = enumerator.Current.AttributeConstructor;
if (attributeConstructor == null)
{
continue;
}
INamedTypeSymbol containingType = attributeConstructor.ContainingType;
if (!attributeConstructor.Parameters.Any() && containingType.Name == "FlagsAttribute")
{
ISymbol containingSymbol = containingType.ContainingSymbol;
if (containingSymbol.Kind == SymbolKind.Namespace && containingSymbol.Name == "System" && ((INamespaceSymbol)containingSymbol.ContainingSymbol).IsGlobalNamespace)
{
return true;
}
}
}
return false;
}
private void AddFlagsEnumConstantValue(INamedTypeSymbol enumType, object constantValue, bool preferNumericValueOrExpandedFlags)
{
ArrayBuilder<EnumField> instance = ArrayBuilder<EnumField>.GetInstance();
GetSortedEnumFields(enumType, instance);
ArrayBuilder<EnumField> instance2 = ArrayBuilder<EnumField>.GetInstance();
try
{
AddFlagsEnumConstantValue(enumType, constantValue, instance, instance2, preferNumericValueOrExpandedFlags);
}
finally
{
instance.Free();
instance2.Free();
}
}
private void AddFlagsEnumConstantValue(INamedTypeSymbol enumType, object constantValue, ArrayBuilder<EnumField> allFieldsAndValues, ArrayBuilder<EnumField> usedFieldsAndValues, bool preferNumericValueOrExpandedFlags)
{
SpecialType specialType = enumType.EnumUnderlyingType.SpecialType;
ulong num = EnumUtilities.ConvertEnumUnderlyingTypeToUInt64(constantValue, specialType);
ulong num2 = num;
if (num2 != 0L)
{
ArrayBuilder<EnumField>.Enumerator enumerator = allFieldsAndValues.GetEnumerator();
while (enumerator.MoveNext())
{
EnumField current = enumerator.Current;
ulong value = current.Value;
if ((!preferNumericValueOrExpandedFlags || value != num) && value != 0L && (num2 & value) == value)
{
usedFieldsAndValues.Add(current);
num2 -= value;
if (num2 == 0L)
{
break;
}
}
}
}
if (num2 == 0L && usedFieldsAndValues.Count > 0)
{
for (int num3 = usedFieldsAndValues.Count - 1; num3 >= 0; num3--)
{
if (num3 != usedFieldsAndValues.Count - 1)
{
AddSpace();
AddBitwiseOr();
AddSpace();
}
((IFieldSymbol)usedFieldsAndValues[num3].IdentityOpt).Accept(NotFirstVisitor);
}
}
else if (preferNumericValueOrExpandedFlags)
{
AddLiteralValue(specialType, constantValue);
}
else
{
EnumField enumField = ((num == 0L) ? EnumField.FindValue(allFieldsAndValues, 0uL) : default(EnumField));
if (!enumField.IsDefault)
{
((IFieldSymbol)enumField.IdentityOpt).Accept(NotFirstVisitor);
}
else
{
AddExplicitlyCastedLiteralValue(enumType, specialType, constantValue);
}
}
}
private static void GetSortedEnumFields(INamedTypeSymbol enumType, ArrayBuilder<EnumField> enumFields)
{
SpecialType specialType = enumType.EnumUnderlyingType.SpecialType;
ImmutableArray<ISymbol>.Enumerator enumerator = enumType.GetMembers().GetEnumerator();
while (enumerator.MoveNext())
{
ISymbol current = enumerator.Current;
if (current.Kind == SymbolKind.Field)
{
IFieldSymbol fieldSymbol = (IFieldSymbol)current;
if (fieldSymbol.HasConstantValue)
{
EnumField item = new EnumField(fieldSymbol.Name, EnumUtilities.ConvertEnumUnderlyingTypeToUInt64(fieldSymbol.ConstantValue, specialType), fieldSymbol);
enumFields.Add(item);
}
}
}
enumFields.Sort(EnumField.Comparer);
}
private void AddNonFlagsEnumConstantValue(INamedTypeSymbol enumType, object constantValue)
{
SpecialType specialType = enumType.EnumUnderlyingType.SpecialType;
ulong value = EnumUtilities.ConvertEnumUnderlyingTypeToUInt64(constantValue, specialType);
ArrayBuilder<EnumField> instance = ArrayBuilder<EnumField>.GetInstance();
GetSortedEnumFields(enumType, instance);
EnumField enumField = EnumField.FindValue(instance, value);
if (!enumField.IsDefault)
{
((IFieldSymbol)enumField.IdentityOpt).Accept(NotFirstVisitor);
}
else
{
AddExplicitlyCastedLiteralValue(enumType, specialType, constantValue);
}
instance.Free();
}
protected abstract bool ShouldRestrictMinimallyQualifyLookupToNamespacesAndTypes();
protected bool NameBoundSuccessfullyToSameSymbol(INamedTypeSymbol symbol)
{
ISymbol symbol2 = SingleSymbolWithArity(ShouldRestrictMinimallyQualifyLookupToNamespacesAndTypes() ? semanticModelOpt.LookupNamespacesAndTypes(positionOpt, null, symbol.Name) : semanticModelOpt.LookupSymbols(positionOpt, null, symbol.Name), symbol.Arity);
if (symbol2 == null)
{
return false;
}
if (symbol2.Equals(symbol.OriginalDefinition))
{
return true;
}
ISymbol symbol3 = SingleSymbolWithArity(semanticModelOpt.LookupNamespacesAndTypes(positionOpt, null, symbol.Name), symbol.Arity);
if (symbol3 == null)
{
return false;
}
ITypeSymbol symbolType = GetSymbolType(symbol2);
ITypeSymbol symbolType2 = GetSymbolType(symbol3);
if (symbolType != null && symbolType2 != null && symbolType.Equals(symbolType2))
{
return symbol3.Equals(symbol.OriginalDefinition);
}
return false;
}
private static ISymbol SingleSymbolWithArity(ImmutableArray<ISymbol> candidates, int desiredArity)
{
ISymbol symbol = null;
ImmutableArray<ISymbol>.Enumerator enumerator = candidates.GetEnumerator();
while (enumerator.MoveNext())
{
ISymbol current = enumerator.Current;
if (current.Kind switch
{
SymbolKind.NamedType => ((INamedTypeSymbol)current).Arity,
SymbolKind.Method => ((IMethodSymbol)current).Arity,
_ => 0,
} == desiredArity)
{
if (symbol != null)
{
symbol = null;
break;
}
symbol = current;
}
}
return symbol;
}
protected static ITypeSymbol GetSymbolType(ISymbol symbol)
{
if (symbol is ILocalSymbol localSymbol)
{
return localSymbol.Type;
}
if (symbol is IFieldSymbol fieldSymbol)
{
return fieldSymbol.Type;
}
if (symbol is IPropertySymbol propertySymbol)
{
return propertySymbol.Type;
}
if (symbol is IParameterSymbol parameterSymbol)
{
return parameterSymbol.Type;
}
if (symbol is IAliasSymbol aliasSymbol)
{
return aliasSymbol.Target as ITypeSymbol;
}
return symbol as ITypeSymbol;
}
}