| File: ApiLifecycle\AssemblyAnalysis.cs | Web Access |
| Project: src\src\Analyzers\Microsoft.Analyzers.Local\Microsoft.Analyzers.Local.csproj (Microsoft.Analyzers.Local) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System; using System.Collections.Generic; using System.Linq; using Microsoft.CodeAnalysis; using Microsoft.Extensions.LocalAnalyzers.ApiLifecycle.Model; using Microsoft.Extensions.LocalAnalyzers.Utilities; namespace Microsoft.Extensions.LocalAnalyzers.ApiLifecycle; internal sealed class AssemblyAnalysis { public Assembly Assembly { get; } public HashSet<TypeDef> MissingTypes { get; } = []; public Dictionary<ISymbol, List<string>> MissingConstraints { get; } = new(SymbolEqualityComparer.Default); public Dictionary<ISymbol, List<string>> MissingBaseTypes { get; } = new(SymbolEqualityComparer.Default); public HashSet<Method> MissingMethods { get; } = []; public HashSet<Prop> MissingProperties { get; } = []; public HashSet<Field> MissingFields { get; } = []; public HashSet<(ISymbol symbol, Stage stage)> FoundInBaseline { get; } = []; public HashSet<ISymbol> NotFoundInBaseline { get; } = new(SymbolEqualityComparer.Default); #pragma warning disable SA1118 // Parameter should not span multiple lines private static readonly SymbolDisplayFormat _format = new(globalNamespaceStyle: SymbolDisplayGlobalNamespaceStyle.OmittedAsContaining, typeQualificationStyle: SymbolDisplayTypeQualificationStyle.NameAndContainingTypesAndNamespaces, genericsOptions: SymbolDisplayGenericsOptions.IncludeTypeParameters | SymbolDisplayGenericsOptions.IncludeTypeConstraints | SymbolDisplayGenericsOptions.IncludeVariance, kindOptions: SymbolDisplayKindOptions.IncludeNamespaceKeyword | SymbolDisplayKindOptions.IncludeTypeKeyword | SymbolDisplayKindOptions.IncludeMemberKeyword, parameterOptions: SymbolDisplayParameterOptions.IncludeExtensionThis | SymbolDisplayParameterOptions.IncludeParamsRefOut | SymbolDisplayParameterOptions.IncludeType | SymbolDisplayParameterOptions.IncludeName | SymbolDisplayParameterOptions.IncludeDefaultValue, miscellaneousOptions: SymbolDisplayMiscellaneousOptions.UseSpecialTypes | SymbolDisplayMiscellaneousOptions.IncludeNullableReferenceTypeModifier, delegateStyle: SymbolDisplayDelegateStyle.NameAndSignature); private static readonly SymbolDisplayFormat _formatNoVariance = new(globalNamespaceStyle: SymbolDisplayGlobalNamespaceStyle.OmittedAsContaining, typeQualificationStyle: SymbolDisplayTypeQualificationStyle.NameAndContainingTypesAndNamespaces, genericsOptions: SymbolDisplayGenericsOptions.IncludeTypeParameters, propertyStyle: SymbolDisplayPropertyStyle.ShowReadWriteDescriptor, memberOptions: SymbolDisplayMemberOptions.IncludeType | SymbolDisplayMemberOptions.IncludeModifiers | SymbolDisplayMemberOptions.IncludeExplicitInterface | SymbolDisplayMemberOptions.IncludeParameters | SymbolDisplayMemberOptions.IncludeContainingType | SymbolDisplayMemberOptions.IncludeRef, kindOptions: SymbolDisplayKindOptions.IncludeNamespaceKeyword | SymbolDisplayKindOptions.IncludeTypeKeyword | SymbolDisplayKindOptions.IncludeMemberKeyword, parameterOptions: SymbolDisplayParameterOptions.IncludeExtensionThis | SymbolDisplayParameterOptions.IncludeParamsRefOut | SymbolDisplayParameterOptions.IncludeType | SymbolDisplayParameterOptions.IncludeName | SymbolDisplayParameterOptions.IncludeDefaultValue, miscellaneousOptions: SymbolDisplayMiscellaneousOptions.UseSpecialTypes | SymbolDisplayMiscellaneousOptions.IncludeNullableReferenceTypeModifier, delegateStyle: SymbolDisplayDelegateStyle.NameAndSignature); private static readonly SymbolDisplayFormat _shortSymbolNameFormat = new(globalNamespaceStyle: SymbolDisplayGlobalNamespaceStyle.OmittedAsContaining, typeQualificationStyle: SymbolDisplayTypeQualificationStyle.NameAndContainingTypesAndNamespaces, genericsOptions: SymbolDisplayGenericsOptions.IncludeTypeParameters, miscellaneousOptions: SymbolDisplayMiscellaneousOptions.UseSpecialTypes | SymbolDisplayMiscellaneousOptions.IncludeNullableReferenceTypeModifier, delegateStyle: SymbolDisplayDelegateStyle.NameAndSignature); private static readonly SymbolDisplayFormat _enumType = new(globalNamespaceStyle: SymbolDisplayGlobalNamespaceStyle.OmittedAsContaining, typeQualificationStyle: SymbolDisplayTypeQualificationStyle.NameAndContainingTypesAndNamespaces, genericsOptions: SymbolDisplayGenericsOptions.IncludeTypeParameters | SymbolDisplayGenericsOptions.IncludeTypeConstraints | SymbolDisplayGenericsOptions.IncludeVariance, propertyStyle: SymbolDisplayPropertyStyle.ShowReadWriteDescriptor, memberOptions: SymbolDisplayMemberOptions.IncludeType | SymbolDisplayMemberOptions.IncludeExplicitInterface | SymbolDisplayMemberOptions.IncludeParameters | SymbolDisplayMemberOptions.IncludeContainingType | SymbolDisplayMemberOptions.IncludeModifiers | SymbolDisplayMemberOptions.IncludeRef, kindOptions: SymbolDisplayKindOptions.IncludeNamespaceKeyword | SymbolDisplayKindOptions.IncludeMemberKeyword, parameterOptions: SymbolDisplayParameterOptions.IncludeExtensionThis | SymbolDisplayParameterOptions.IncludeParamsRefOut | SymbolDisplayParameterOptions.IncludeType | SymbolDisplayParameterOptions.IncludeName | SymbolDisplayParameterOptions.IncludeDefaultValue, miscellaneousOptions: SymbolDisplayMiscellaneousOptions.UseSpecialTypes | SymbolDisplayMiscellaneousOptions.IncludeNullableReferenceTypeModifier, delegateStyle: SymbolDisplayDelegateStyle.NameAndSignature); #pragma warning restore SA1118 // Parameter should not span multiple lines public AssemblyAnalysis(Assembly assembly) { Assembly = assembly; foreach (var type in Assembly.Types) { _ = MissingTypes.Add(type); foreach (var method in type.Methods) { _ = MissingMethods.Add(method); } foreach (var property in type.Properties) { _ = MissingProperties.Add(property); } foreach (var field in type.Fields) { _ = MissingFields.Add(field); } } } public void AnalyzeType(INamedTypeSymbol type) { var typeSignature = type.ToDisplayString(_format); var typeModifiersAndName = PrependModifiers(typeSignature, type); var typeDef = Array.Find(Assembly.Types, x => x.ModifiersAndName == typeModifiersAndName); if (typeDef == null) { _ = NotFoundInBaseline.Add(type); return; } var baseTypes = new HashSet<string>(type.AllInterfaces.Select(x => x.ToDisplayString(_shortSymbolNameFormat))); var baseType = type.BaseType; if (type.EnumUnderlyingType != null) { baseTypes.Clear(); if (type.EnumUnderlyingType.SpecialType != SpecialType.System_Int32) { _ = baseTypes.Add(type.EnumUnderlyingType.ToDisplayString(_format)); } } else { if (baseType != null && baseType.SpecialType != SpecialType.System_Object && baseType.SpecialType != SpecialType.System_ValueType) { _ = baseTypes.Add(baseType.ToDisplayString(_shortSymbolNameFormat)); } } foreach (var @base in typeDef.BaseTypes) { if (!baseTypes.Contains(@base)) { if (MissingBaseTypes.TryGetValue(type, out var collection)) { collection.Add(@base); } else { MissingBaseTypes.Add(type, [@base]); } } } var constraints = new HashSet<string>(Utils.GetConstraints(typeSignature)); foreach (var constraint in typeDef.Constraints) { if (!constraints.Contains(constraint)) { if (MissingConstraints.TryGetValue(type, out var collection)) { collection.Add(constraint); } else { MissingConstraints.Add(type, [constraint]); } } } _ = MissingTypes.Remove(typeDef); _ = FoundInBaseline.Add((type, typeDef.Stage)); if (type.TypeKind != TypeKind.Delegate) { var members = type .GetMembers() .Where(member => member.IsExternallyVisible()) .Where(member => { if (member.Kind != SymbolKind.Method) { return true; } var method = (IMethodSymbol)member; return method.MethodKind is not MethodKind.PropertyGet and not MethodKind.PropertySet and not MethodKind.EventAdd and not MethodKind.EventRemove; }); foreach (var member in members) { if (member is IMethodSymbol methodSymbol) { var methodSignature = member.ToDisplayString(_formatNoVariance) + ";"; var method = Array.Find(typeDef.Methods, x => x.Member == methodSignature); if (method != null) { _ = MissingMethods.Remove(method); _ = FoundInBaseline.Add((member, method.Stage)); } else { _ = NotFoundInBaseline.Add(member); } } else if (member is IPropertySymbol) { var propSignature = member.ToDisplayString(_formatNoVariance); var prop = Array.Find(typeDef.Properties, x => x.Member == propSignature); if (prop != null) { _ = MissingProperties.Remove(prop); _ = FoundInBaseline.Add((member, prop.Stage)); } else { _ = NotFoundInBaseline.Add(member); } } else if (member is IFieldSymbol fieldSym) { var fieldSignature = member.ToDisplayString(_formatNoVariance); var t = fieldSym.GetFieldOrPropertyType() as INamedTypeSymbol; if (t?.EnumUnderlyingType != null) { // enum value definitions need special attention fieldSignature = "const " + t.ToDisplayString(_enumType) + " " + fieldSignature; } var field = Array.Find(typeDef.Fields, x => x.Member == fieldSignature); if (field != null) { _ = MissingFields.Remove(field); _ = FoundInBaseline.Add((member, field.Stage)); } else { _ = NotFoundInBaseline.Add(member); } } } } } private static string PrependModifiers(string typeSignature, INamedTypeSymbol type) { if (type.EnumUnderlyingType != null) { return typeSignature; } if (typeSignature.StartsWith("class", StringComparison.Ordinal) || typeSignature.StartsWith("record", StringComparison.Ordinal)) { var modifiers = new List<string>(6); if (type.IsStatic) { modifiers.Add("static"); } if (type.IsSealed) { modifiers.Add("sealed"); } if (type.IsAbstract) { modifiers.Add("abstract"); } if (type.IsVirtual) { modifiers.Add("virtual"); } if (type.IsOverride) { modifiers.Add("override"); } typeSignature = string.Join(" ", modifiers) + " " + typeSignature; } return Utils.StripBaseAndConstraints(typeSignature); } }