From b520298953a39a535e0930acb1aa759330f9cb37 Mon Sep 17 00:00:00 2001 From: David Date: Wed, 2 Nov 2022 15:12:06 -0400 Subject: [PATCH] chore: Downgrade minimum version of roslyn required to run generators --- src/Directory.Packages.props | 8 +- .../Common/GenContext/GenerationContext.cs | 95 ++++++++++++++++++- 2 files changed, 97 insertions(+), 6 deletions(-) diff --git a/src/Directory.Packages.props b/src/Directory.Packages.props index 50d0b1d870..1bbc7e466f 100644 --- a/src/Directory.Packages.props +++ b/src/Directory.Packages.props @@ -7,10 +7,10 @@ - - - - + + + + diff --git a/src/Uno.Extensions.Core.Generators/Common/GenContext/GenerationContext.cs b/src/Uno.Extensions.Core.Generators/Common/GenContext/GenerationContext.cs index 4ad25756ef..3b799bac60 100644 --- a/src/Uno.Extensions.Core.Generators/Common/GenContext/GenerationContext.cs +++ b/src/Uno.Extensions.Core.Generators/Common/GenContext/GenerationContext.cs @@ -1,7 +1,11 @@ using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Reflection; +using System.Runtime.CompilerServices; using Microsoft.CodeAnalysis; namespace Uno.Extensions.Generators; @@ -31,8 +35,7 @@ public static bool IsDisabled(this GeneratorExecutionContext context, string dis x.parameter, type: x.attribute.Type, isOptional: x.attribute.IsOptional || x.parameter.GetCustomAttributesData().Any(attr => attr.AttributeType.FullName.Equals("System.Runtime.CompilerServices.NullableAttribute")), - symbol: compilation - .GetTypesByMetadataName(x.attribute.Type) + symbol: GetTypesByMetadataName(compilation, x.attribute.Type) .OrderBy(t => t switch { _ when SymbolEqualityComparer.Default.Equals(t.ContainingAssembly, compilation.Assembly) => 0, @@ -60,4 +63,92 @@ _ when t.IsAccessibleTo(compilation.Assembly) => 1, return default; } } + + + #region GetTypesByMetadataName + // Starting from Roslyn 4.2 there is public method GetTypesByMetadataName which returns all possible types + // (while GetTypeByMetadataName -without a s- returns a type only there **ONE** matching type) + // But as of 2022/11/02 linux and macOS agents are still running with roslyn 4.1.x, + // so using that method directly would cause generators to fail, unless users explicitly add a ref to Microsoft.Net.Compilers.Toolset 4.2.0 + // (Note: that ref cannot be embedded in our packages) + // Note: if roslyn is recent enough, we try to rely on that method using reflection in order to take advantage of the internal caching of roslyn. + private static readonly Func> GetTypesByMetadataName = typeof(Compilation) + .GetMethods(BindingFlags.Instance | BindingFlags.Public) + .FirstOrDefault(method => method is { Name: nameof(GetTypesByMetadataName) } + && method.GetParameters() is { Length: 1 } parameters + && parameters[0].ParameterType == typeof(string) + && method.ReturnType == typeof(ImmutableArray)) + is { } roslynMethod + ? (compilation, fullyQualifiedMetadataName) => (ImmutableArray)roslynMethod.Invoke(compilation, new[] { fullyQualifiedMetadataName }) + : GetTypesByMetadataName_LocalImpl; + + private static readonly ConditionalWeakTable> _getTypesCache = new(); + + /// + /// Gets all types with the compilation's assembly and all referenced assemblies that have the + /// given canonical CLR metadata name. Accessibility to the current assembly is ignored when + /// searching for matching type names. + /// + /// Empty array if no types match. Otherwise, all types that match the name, current assembly first if present. + /// + /// + /// Assemblies can contain multiple modules. Within each assembly, the search is performed based on module's position in the module list of that assembly. When + /// a match is found in one module in an assembly, no further modules within that assembly are searched. + /// + /// Type forwarders are ignored, and not considered part of the assembly where the TypeForwardAttribute is written. + /// + private static ImmutableArray GetTypesByMetadataName_LocalImpl(Compilation compilation, string fullyQualifiedMetadataName) + { + // This imported from / inspired by https://github.com/dotnet/roslyn/blob/afddda5f6775800a32706f7055955042b5cfce7a/src/Compilers/Core/Portable/Compilation/Compilation.cs#L1208 + + ImmutableList val; + lock (_getTypesCache) + { + if (!_getTypesCache.TryGetValue(fullyQualifiedMetadataName, out val)) + { + val = getTypesByMetadataNameImpl(); + _getTypesCache.Add(fullyQualifiedMetadataName, val); + } + } + + return val.ToImmutableArray(); + + ImmutableList getTypesByMetadataNameImpl() + { + List? typesByMetadataName = null; + + // Start with the current assembly, then corlib, then look through all references, to mimic GetTypeByMetadataName search order. + + addIfNotNull(compilation.Assembly.GetTypeByMetadataName(fullyQualifiedMetadataName)); + + var corLib = compilation.ObjectType.ContainingAssembly; + + if (!ReferenceEquals(corLib, compilation.Assembly)) + { + addIfNotNull(corLib.GetTypeByMetadataName(fullyQualifiedMetadataName)); + } + + foreach (var referencedAssembly in compilation.SourceModule.ReferencedAssemblySymbols) + { + if (ReferenceEquals(referencedAssembly, corLib)) + { + continue; + } + + addIfNotNull(referencedAssembly.GetTypeByMetadataName(fullyQualifiedMetadataName)); + } + + return typesByMetadataName?.ToImmutableList() ?? ImmutableList.Empty; + + void addIfNotNull(INamedTypeSymbol? toAdd) + { + if (toAdd != null) + { + typesByMetadataName ??= new List(); + typesByMetadataName.Add(toAdd); + } + } + } + } + #endregion }