| File: CompilationExtensions.cs | Web Access |
| Project: src\runtime\src\tools\illink\src\ILLink.RoslynAnalyzer\ILLink.RoslynAnalyzer.csproj (ILLink.RoslynAnalyzer) |
// Copyright (c) .NET Foundation and contributors. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. using System; using System.Collections.Immutable; using System.Threading.Tasks; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp.Syntax; namespace ILLink.RoslynAnalyzer { // Copied from: https://github.com/dotnet/roslyn/blob/main/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/CompilationExtensions.cs public static class CompilationExtensions { /// <summary> /// Gets a type by its metadata name to use for code analysis within a <see cref="Compilation"/>. This method /// attempts to find the "best" symbol to use for code analysis, which is the symbol matching the first of the /// following rules. /// /// <list type="number"> /// <item><description> /// If only one type with the given name is found within the compilation and its referenced assemblies, that /// type is returned regardless of accessibility. /// </description></item> /// <item><description> /// If the current <paramref name="compilation"/> defines the symbol, that symbol is returned. /// </description></item> /// <item><description> /// If exactly one referenced assembly defines the symbol in a manner that makes it visible to the current /// <paramref name="compilation"/>, that symbol is returned. /// </description></item> /// <item><description> /// Otherwise, this method returns <see langword="null"/>. /// </description></item> /// </list> /// </summary> /// <param name="compilation">The <see cref="Compilation"/> to consider for analysis.</param> /// <param name="fullyQualifiedMetadataName">The fully-qualified metadata type name to find.</param> /// <returns>The symbol to use for code analysis; otherwise, <see langword="null"/>.</returns> public static INamedTypeSymbol? GetBestTypeByMetadataName(this Compilation compilation, string fullyQualifiedMetadataName) { // Try to get the unique type with this name, ignoring accessibility var type = compilation.GetTypeByMetadataName(fullyQualifiedMetadataName); // Otherwise, try to get the unique type with this name originally defined in 'compilation' type ??= compilation.Assembly.GetTypeByMetadataName(fullyQualifiedMetadataName); // Otherwise, try to get the unique accessible type with this name from a reference if (type is null) { foreach (var module in compilation.Assembly.Modules) { foreach (var referencedAssembly in module.ReferencedAssemblySymbols) { var currentType = referencedAssembly.GetTypeByMetadataName(fullyQualifiedMetadataName); if (currentType is null) continue; switch (currentType.GetResultantVisibility()) { case SymbolVisibility.Public: case SymbolVisibility.Internal when referencedAssembly.GivesAccessTo(compilation.Assembly): break; default: continue; } if (type is object) { // Multiple visible types with the same metadata name are present return null; } type = currentType; } } } return type; } // copied from https://github.com/dotnet/roslyn/blob/main/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/ISymbolExtensions.cs private static SymbolVisibility GetResultantVisibility(this ISymbol symbol) { // Start by assuming it's visible. SymbolVisibility visibility = SymbolVisibility.Public; switch (symbol.Kind) { case SymbolKind.Alias: // Aliases are uber private. They're only visible in the same file that they // were declared in. return SymbolVisibility.Private; case SymbolKind.Parameter: // Parameters are only as visible as their containing symbol return GetResultantVisibility(symbol.ContainingSymbol); case SymbolKind.TypeParameter: // Type Parameters are private. return SymbolVisibility.Private; } while (symbol != null && symbol.Kind != SymbolKind.Namespace) { switch (symbol.DeclaredAccessibility) { // If we see anything private, then the symbol is private. case Accessibility.NotApplicable: case Accessibility.Private: return SymbolVisibility.Private; // If we see anything internal, then knock it down from public to // internal. case Accessibility.Internal: case Accessibility.ProtectedAndInternal: visibility = SymbolVisibility.Internal; break; // For anything else (Public, Protected, ProtectedOrInternal), the // symbol stays at the level we've gotten so far. } symbol = symbol.ContainingSymbol; } return visibility; } // Copied from: https://github.com/dotnet/roslyn/blob/main/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Utilities/SymbolVisibility.cs private enum SymbolVisibility { Public = 0, Internal = 1, Private = 2, Friend = Internal, } internal static bool HasAttributeSuffix(this string name, bool isCaseSensitive) { const string AttributeSuffix = "Attribute"; var comparison = isCaseSensitive ? StringComparison.Ordinal : StringComparison.OrdinalIgnoreCase; return name.Length > AttributeSuffix.Length && name.EndsWith(AttributeSuffix, comparison); } public static ImmutableArray<T> ToImmutableArray<T>(this ReadOnlySpan<T> span) { switch (span.Length) { case 0: return ImmutableArray<T>.Empty; case 1: return ImmutableArray.Create(span[0]); case 2: return ImmutableArray.Create(span[0], span[1]); case 3: return ImmutableArray.Create(span[0], span[1], span[2]); case 4: return ImmutableArray.Create(span[0], span[1], span[2], span[3]); default: var builder = ImmutableArray.CreateBuilder<T>(span.Length); foreach (var item in span) builder.Add(item); return builder.MoveToImmutable(); } } public static SimpleNameSyntax GetUnqualifiedName(this NameSyntax name) => name switch { AliasQualifiedNameSyntax alias => alias.Name, QualifiedNameSyntax qualified => qualified.Right, SimpleNameSyntax simple => simple, _ => throw new InvalidOperationException("Unreachable"), }; public static INamedTypeSymbol? TaskType(this Compilation compilation) => compilation.GetTypeByMetadataName(typeof(Task).FullName!); public static INamedTypeSymbol? TaskOfTType(this Compilation compilation) => compilation.GetTypeByMetadataName(typeof(Task<>).FullName!); } }