From 9bc98cdc55e7e0fa51211a4ad13ef4c6161aa21f Mon Sep 17 00:00:00 2001 From: Sven Boemer Date: Tue, 10 Jun 2025 22:05:56 +0000 Subject: [PATCH 1/6] Reapply "Implement type name resolution for ILLink analyzer (#106209)" (#106343) This reverts commit 5ee1b8660cb2953183a2fff7cc36ef5eaf1e6783. --- .../DynamicallyAccessedMembersAnalyzer.cs | 6 +- .../ILLink.RoslynAnalyzer.csproj | 4 + .../TrimAnalysis/GenericArgumentDataFlow.cs | 23 +-- .../TrimAnalysis/HandleCallAction.cs | 5 +- .../TrimAnalysis/ReflectionAccessAnalyzer.cs | 14 +- ...RequireDynamicallyAccessedMembersAction.cs | 31 +++- .../TrimAnalysisAssignmentPattern.cs | 7 +- ...TrimAnalysisGenericInstantiationPattern.cs | 7 +- .../TrimAnalysisMethodCallPattern.cs | 5 +- .../TrimAnalysisReflectionAccessPattern.cs | 3 +- .../TrimAnalysis/TrimAnalysisVisitor.cs | 8 +- .../TrimAnalysis/TypeNameResolver.cs | 149 ++++++++++++++++++ .../DynamicallyAccessedMembersCodeFixTests.cs | 28 ++-- .../TestCaseCompilation.cs | 2 +- .../DataFlow/AssemblyQualifiedNameDataflow.cs | 2 +- .../DataFlow/AttributeConstructorDataflow.cs | 2 +- .../DataFlow/AttributeFieldDataflow.cs | 2 +- .../DataFlow/AttributePropertyDataflow.cs | 8 +- .../GenericParameterWarningLocation.cs | 8 +- .../DataFlow/GetTypeDataFlow.cs | 6 +- .../Reflection/TypeUsedViaReflection.cs | 62 ++++---- .../TestCasesRunner/ResultChecker.cs | 8 +- 22 files changed, 291 insertions(+), 99 deletions(-) create mode 100644 src/tools/illink/src/ILLink.RoslynAnalyzer/TrimAnalysis/TypeNameResolver.cs diff --git a/src/tools/illink/src/ILLink.RoslynAnalyzer/DynamicallyAccessedMembersAnalyzer.cs b/src/tools/illink/src/ILLink.RoslynAnalyzer/DynamicallyAccessedMembersAnalyzer.cs index 1f31934d9b898b..20c72c961161fe 100644 --- a/src/tools/illink/src/ILLink.RoslynAnalyzer/DynamicallyAccessedMembersAnalyzer.cs +++ b/src/tools/illink/src/ILLink.RoslynAnalyzer/DynamicallyAccessedMembersAnalyzer.cs @@ -57,6 +57,7 @@ public static ImmutableArray GetSupportedDiagnostics () diagDescriptorsArrayBuilder.Add (DiagnosticDescriptors.GetDiagnosticDescriptor (DiagnosticId.UnrecognizedTypeNameInTypeGetType)); diagDescriptorsArrayBuilder.Add (DiagnosticDescriptors.GetDiagnosticDescriptor (DiagnosticId.UnrecognizedParameterInMethodCreateInstance)); diagDescriptorsArrayBuilder.Add (DiagnosticDescriptors.GetDiagnosticDescriptor (DiagnosticId.ParametersOfAssemblyCreateInstanceCannotBeAnalyzed)); + diagDescriptorsArrayBuilder.Add (DiagnosticDescriptors.GetDiagnosticDescriptor (DiagnosticId.TypeNameIsNotAssemblyQualified)); diagDescriptorsArrayBuilder.Add (DiagnosticDescriptors.GetDiagnosticDescriptor (DiagnosticId.ReturnValueDoesNotMatchFeatureGuards)); diagDescriptorsArrayBuilder.Add (DiagnosticDescriptors.GetDiagnosticDescriptor (DiagnosticId.InvalidFeatureGuard)); @@ -122,11 +123,12 @@ public override void Initialize (AnalysisContext context) var location = GetPrimaryLocation (type.Locations); + var typeNameResolver = new TypeNameResolver (context.Compilation); if (type.BaseType is INamedTypeSymbol baseType) - GenericArgumentDataFlow.ProcessGenericArgumentDataFlow (location, baseType, context.ReportDiagnostic); + GenericArgumentDataFlow.ProcessGenericArgumentDataFlow (typeNameResolver, location, baseType, context.ReportDiagnostic); foreach (var interfaceType in type.Interfaces) - GenericArgumentDataFlow.ProcessGenericArgumentDataFlow (location, interfaceType, context.ReportDiagnostic); + GenericArgumentDataFlow.ProcessGenericArgumentDataFlow (typeNameResolver, location, interfaceType, context.ReportDiagnostic); DynamicallyAccessedMembersTypeHierarchy.ApplyDynamicallyAccessedMembersToTypeHierarchy (location, type, context.ReportDiagnostic); }, SymbolKind.NamedType); diff --git a/src/tools/illink/src/ILLink.RoslynAnalyzer/ILLink.RoslynAnalyzer.csproj b/src/tools/illink/src/ILLink.RoslynAnalyzer/ILLink.RoslynAnalyzer.csproj index bc410523d5d716..c3b0b1d1847b48 100644 --- a/src/tools/illink/src/ILLink.RoslynAnalyzer/ILLink.RoslynAnalyzer.csproj +++ b/src/tools/illink/src/ILLink.RoslynAnalyzer/ILLink.RoslynAnalyzer.csproj @@ -15,10 +15,13 @@ optimize the analyzer even in Debug builds. Note: we still use the Debug configuration to get Debug asserts. --> true + true + + @@ -28,6 +31,7 @@ all contentfiles + diff --git a/src/tools/illink/src/ILLink.RoslynAnalyzer/TrimAnalysis/GenericArgumentDataFlow.cs b/src/tools/illink/src/ILLink.RoslynAnalyzer/TrimAnalysis/GenericArgumentDataFlow.cs index 5dd24d55b2a40c..8b1aab92366749 100644 --- a/src/tools/illink/src/ILLink.RoslynAnalyzer/TrimAnalysis/GenericArgumentDataFlow.cs +++ b/src/tools/illink/src/ILLink.RoslynAnalyzer/TrimAnalysis/GenericArgumentDataFlow.cs @@ -12,31 +12,32 @@ namespace ILLink.RoslynAnalyzer.TrimAnalysis { internal static class GenericArgumentDataFlow { - public static void ProcessGenericArgumentDataFlow (Location location, INamedTypeSymbol type, Action reportDiagnostic) + public static void ProcessGenericArgumentDataFlow (TypeNameResolver typeNameResolver, Location location, INamedTypeSymbol type, Action? reportDiagnostic) { while (type is { IsGenericType: true }) { - ProcessGenericArgumentDataFlow (location, type.TypeArguments, type.TypeParameters, reportDiagnostic); + ProcessGenericArgumentDataFlow (typeNameResolver, location, type.TypeArguments, type.TypeParameters, reportDiagnostic); type = type.ContainingType; } } - public static void ProcessGenericArgumentDataFlow (Location location, IMethodSymbol method, Action reportDiagnostic) + public static void ProcessGenericArgumentDataFlow (TypeNameResolver typeNameResolver, Location location, IMethodSymbol method, Action? reportDiagnostic) { - ProcessGenericArgumentDataFlow (location, method.TypeArguments, method.TypeParameters, reportDiagnostic); + ProcessGenericArgumentDataFlow (typeNameResolver, location, method.TypeArguments, method.TypeParameters, reportDiagnostic); - ProcessGenericArgumentDataFlow (location, method.ContainingType, reportDiagnostic); + ProcessGenericArgumentDataFlow (typeNameResolver, location, method.ContainingType, reportDiagnostic); } - public static void ProcessGenericArgumentDataFlow (Location location, IFieldSymbol field, Action reportDiagnostic) + public static void ProcessGenericArgumentDataFlow (TypeNameResolver typeNameResolver, Location location, IFieldSymbol field, Action? reportDiagnostic) { - ProcessGenericArgumentDataFlow (location, field.ContainingType, reportDiagnostic); + ProcessGenericArgumentDataFlow (typeNameResolver, location, field.ContainingType, reportDiagnostic); } static void ProcessGenericArgumentDataFlow ( + TypeNameResolver typeNameResolver, Location location, ImmutableArray typeArguments, ImmutableArray typeParameters, - Action reportDiagnostic) + Action? reportDiagnostic) { var diagnosticContext = new DiagnosticContext (location, reportDiagnostic); for (int i = 0; i < typeArguments.Length; i++) { @@ -45,14 +46,14 @@ static void ProcessGenericArgumentDataFlow ( var genericParameterValue = new GenericParameterValue (typeParameters[i]); if (genericParameterValue.DynamicallyAccessedMemberTypes != DynamicallyAccessedMemberTypes.None) { SingleValue genericArgumentValue = SingleValueExtensions.FromTypeSymbol (typeArgument)!; - var reflectionAccessAnalyzer = new ReflectionAccessAnalyzer (reportDiagnostic, typeHierarchyType: null); - var requireDynamicallyAccessedMembersAction = new RequireDynamicallyAccessedMembersAction (diagnosticContext, reflectionAccessAnalyzer); + var reflectionAccessAnalyzer = new ReflectionAccessAnalyzer (reportDiagnostic, typeNameResolver, typeHierarchyType: null); + var requireDynamicallyAccessedMembersAction = new RequireDynamicallyAccessedMembersAction (typeNameResolver, location, reportDiagnostic, reflectionAccessAnalyzer); requireDynamicallyAccessedMembersAction.Invoke (genericArgumentValue, genericParameterValue); } // Recursively process generic argument data flow on the generic argument if it itself is generic if (typeArgument is INamedTypeSymbol namedTypeArgument && namedTypeArgument.IsGenericType) - ProcessGenericArgumentDataFlow (location, namedTypeArgument, reportDiagnostic); + ProcessGenericArgumentDataFlow (typeNameResolver, location, namedTypeArgument, reportDiagnostic); } } diff --git a/src/tools/illink/src/ILLink.RoslynAnalyzer/TrimAnalysis/HandleCallAction.cs b/src/tools/illink/src/ILLink.RoslynAnalyzer/TrimAnalysis/HandleCallAction.cs index 620e9fa582bb17..b1aa3c72a55484 100644 --- a/src/tools/illink/src/ILLink.RoslynAnalyzer/TrimAnalysis/HandleCallAction.cs +++ b/src/tools/illink/src/ILLink.RoslynAnalyzer/TrimAnalysis/HandleCallAction.cs @@ -28,6 +28,7 @@ internal partial struct HandleCallAction ValueSetLattice _multiValueLattice; public HandleCallAction ( + TypeNameResolver typeNameResolver, Location location, ISymbol owningSymbol, IOperation operation, @@ -39,8 +40,8 @@ public HandleCallAction ( _isNewObj = operation.Kind == OperationKind.ObjectCreation; _diagnosticContext = new DiagnosticContext (location, reportDiagnostic); _annotations = FlowAnnotations.Instance; - _reflectionAccessAnalyzer = new (reportDiagnostic, typeHierarchyType: null); - _requireDynamicallyAccessedMembersAction = new (_diagnosticContext, _reflectionAccessAnalyzer); + _reflectionAccessAnalyzer = new (reportDiagnostic, typeNameResolver, typeHierarchyType: null); + _requireDynamicallyAccessedMembersAction = new (typeNameResolver, location, reportDiagnostic, _reflectionAccessAnalyzer); _multiValueLattice = multiValueLattice; } diff --git a/src/tools/illink/src/ILLink.RoslynAnalyzer/TrimAnalysis/ReflectionAccessAnalyzer.cs b/src/tools/illink/src/ILLink.RoslynAnalyzer/TrimAnalysis/ReflectionAccessAnalyzer.cs index 506a4912d9d9d0..e26869dd9ee3ef 100644 --- a/src/tools/illink/src/ILLink.RoslynAnalyzer/TrimAnalysis/ReflectionAccessAnalyzer.cs +++ b/src/tools/illink/src/ILLink.RoslynAnalyzer/TrimAnalysis/ReflectionAccessAnalyzer.cs @@ -15,12 +15,19 @@ namespace ILLink.RoslynAnalyzer.TrimAnalysis readonly struct ReflectionAccessAnalyzer { readonly Action? _reportDiagnostic; + readonly INamedTypeSymbol? _typeHierarchyType; - public ReflectionAccessAnalyzer (Action? reportDiagnostic, INamedTypeSymbol? typeHierarchyType) + readonly TypeNameResolver _typeNameResolver; + + public ReflectionAccessAnalyzer( + Action? reportDiagnostic, + TypeNameResolver typeNameResolver, + INamedTypeSymbol? typeHierarchyType) { _reportDiagnostic = reportDiagnostic; _typeHierarchyType = typeHierarchyType; + _typeNameResolver = typeNameResolver; } #pragma warning disable CA1822 // Mark members as static - the other partial implementations might need to be instance methods @@ -196,5 +203,10 @@ void GetDiagnosticsForField (Location location, IFieldSymbol fieldSymbol) diagnosticContext.AddDiagnostic (DiagnosticId.DynamicallyAccessedMembersFieldAccessedViaReflection, fieldSymbol.GetDisplayName ()); } } + + internal bool TryResolveTypeNameAndMark (string typeName, in DiagnosticContext diagnosticContext, bool needsAssemblyName, [NotNullWhen (true)] out ITypeSymbol? type) + { + return _typeNameResolver.TryResolveTypeName (typeName, diagnosticContext, out type, needsAssemblyName); + } } } diff --git a/src/tools/illink/src/ILLink.RoslynAnalyzer/TrimAnalysis/RequireDynamicallyAccessedMembersAction.cs b/src/tools/illink/src/ILLink.RoslynAnalyzer/TrimAnalysis/RequireDynamicallyAccessedMembersAction.cs index 5226aad4f0320b..91fdb54ef83944 100644 --- a/src/tools/illink/src/ILLink.RoslynAnalyzer/TrimAnalysis/RequireDynamicallyAccessedMembersAction.cs +++ b/src/tools/illink/src/ILLink.RoslynAnalyzer/TrimAnalysis/RequireDynamicallyAccessedMembersAction.cs @@ -1,40 +1,55 @@ // 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.Diagnostics; using System.Diagnostics.CodeAnalysis; +using System.Reflection.Metadata; +using Microsoft.CodeAnalysis; using ILLink.RoslynAnalyzer.TrimAnalysis; using ILLink.Shared.TypeSystemProxy; +using System.Collections.Immutable; namespace ILLink.Shared.TrimAnalysis { internal partial struct RequireDynamicallyAccessedMembersAction { + readonly Location _location; + readonly Action? _reportDiagnostic; readonly ReflectionAccessAnalyzer _reflectionAccessAnalyzer; + readonly TypeNameResolver _typeNameResolver; #pragma warning disable CA1822 // Mark members as static - the other partial implementations might need to be instance methods #pragma warning disable IDE0060 // Unused parameters - should be removed once methods are actually implemented public RequireDynamicallyAccessedMembersAction ( - DiagnosticContext diagnosticContext, + TypeNameResolver typeNameResolver, + Location location, + Action? reportDiagnostic, ReflectionAccessAnalyzer reflectionAccessAnalyzer) { - _diagnosticContext = diagnosticContext; + _typeNameResolver = typeNameResolver; + _location = location; + _reportDiagnostic = reportDiagnostic; _reflectionAccessAnalyzer = reflectionAccessAnalyzer; + _diagnosticContext = new (location, reportDiagnostic); } public partial bool TryResolveTypeNameAndMark (string typeName, bool needsAssemblyName, out TypeProxy type) { - // TODO: Implement type name resolution to type symbol - // https://github.com/dotnet/runtime/issues/95118 + var diagnosticContext = new DiagnosticContext (_location, _reportDiagnostic); + if (_reflectionAccessAnalyzer.TryResolveTypeNameAndMark (typeName, diagnosticContext, needsAssemblyName, out ITypeSymbol? foundType)) { + if (foundType is INamedTypeSymbol namedType && namedType.IsGenericType) + GenericArgumentDataFlow.ProcessGenericArgumentDataFlow (_typeNameResolver, _location, namedType, _reportDiagnostic); - // Important corner cases: - // IL2105 (see it's occurences in the tests) - non-assembly qualified type name which doesn't resolve warns - // - will need to figure out what analyzer should do around this. + type = new TypeProxy (foundType); + return true; + } type = default; return false; } private partial void MarkTypeForDynamicallyAccessedMembers (in TypeProxy type, DynamicallyAccessedMemberTypes dynamicallyAccessedMemberTypes) => - _reflectionAccessAnalyzer.GetReflectionAccessDiagnostics (_diagnosticContext.Location, type.Type, dynamicallyAccessedMemberTypes); + _reflectionAccessAnalyzer.GetReflectionAccessDiagnostics (_location, type.Type, dynamicallyAccessedMemberTypes); } } diff --git a/src/tools/illink/src/ILLink.RoslynAnalyzer/TrimAnalysis/TrimAnalysisAssignmentPattern.cs b/src/tools/illink/src/ILLink.RoslynAnalyzer/TrimAnalysis/TrimAnalysisAssignmentPattern.cs index 3a40e899277f65..370d52581cd8bf 100644 --- a/src/tools/illink/src/ILLink.RoslynAnalyzer/TrimAnalysis/TrimAnalysisAssignmentPattern.cs +++ b/src/tools/illink/src/ILLink.RoslynAnalyzer/TrimAnalysis/TrimAnalysisAssignmentPattern.cs @@ -55,7 +55,7 @@ public TrimAnalysisAssignmentPattern Merge ( public void ReportDiagnostics (DataFlowAnalyzerContext context, Action reportDiagnostic) { - var diagnosticContext = new DiagnosticContext (Operation.Syntax.GetLocation (), reportDiagnostic); + var location = Operation.Syntax.GetLocation (); if (context.EnableTrimAnalyzer && !OwningSymbol.IsInRequiresUnreferencedCodeAttributeScope (out _) && !FeatureContext.IsEnabled (RequiresUnreferencedCodeAnalyzer.FullyQualifiedRequiresUnreferencedCodeAttribute)) { @@ -66,8 +66,9 @@ public void ReportDiagnostics (DataFlowAnalyzerContext context, Action reportDiagnostic) { var location = Operation.Syntax.GetLocation (); - var reflectionAccessAnalyzer = new ReflectionAccessAnalyzer (reportDiagnostic, typeHierarchyType: null); + var typeNameResolver = new TypeNameResolver (context.Compilation); + var reflectionAccessAnalyzer = new ReflectionAccessAnalyzer (reportDiagnostic, typeNameResolver, typeHierarchyType: null); if (context.EnableTrimAnalyzer && !OwningSymbol.IsInRequiresUnreferencedCodeAttributeScope (out _) && !FeatureContext.IsEnabled (RequiresUnreferencedCodeAnalyzer.FullyQualifiedRequiresUnreferencedCodeAttribute)) { diff --git a/src/tools/illink/src/ILLink.RoslynAnalyzer/TrimAnalysis/TrimAnalysisVisitor.cs b/src/tools/illink/src/ILLink.RoslynAnalyzer/TrimAnalysis/TrimAnalysisVisitor.cs index e2fc5fbee8f0bf..db85aa8f368144 100644 --- a/src/tools/illink/src/ILLink.RoslynAnalyzer/TrimAnalysis/TrimAnalysisVisitor.cs +++ b/src/tools/illink/src/ILLink.RoslynAnalyzer/TrimAnalysis/TrimAnalysisVisitor.cs @@ -43,6 +43,8 @@ internal sealed class TrimAnalysisVisitor : LocalDataFlowVisitor< FeatureChecksVisitor _featureChecksVisitor; + readonly TypeNameResolver _typeNameResolver; + public TrimAnalysisVisitor ( Compilation compilation, LocalStateAndContextLattice, FeatureContextLattice> lattice, @@ -57,6 +59,7 @@ public TrimAnalysisVisitor ( _multiValueLattice = lattice.LocalStateLattice.Lattice.ValueLattice; TrimAnalysisPatterns = trimAnalysisPatterns; _featureChecksVisitor = new FeatureChecksVisitor (dataFlowAnalyzerContext); + _typeNameResolver = new TypeNameResolver (compilation); } public override FeatureChecksValue GetConditionValue (IOperation branchValueOperation, StateValue state) @@ -297,7 +300,7 @@ public override MultiValue HandleMethodCall ( // Especially with DAM on type, this can lead to incorrectly analyzed code (as in unknown type which leads // to noise). ILLink has the same problem currently: https://github.com/dotnet/linker/issues/1952 - HandleCall (operation, OwningSymbol, calledMethod, instance, arguments, Location.None, null, _multiValueLattice, out MultiValue methodReturnValue); + HandleCall (_typeNameResolver, operation, OwningSymbol, calledMethod, instance, arguments, Location.None, null, _multiValueLattice, out MultiValue methodReturnValue); // This will copy the values if necessary TrimAnalysisPatterns.Add (new TrimAnalysisMethodCallPattern ( @@ -323,6 +326,7 @@ public override MultiValue HandleMethodCall ( } internal static void HandleCall( + TypeNameResolver typeNameResolver, IOperation operation, ISymbol owningSymbol, IMethodSymbol calledMethod, @@ -333,7 +337,7 @@ internal static void HandleCall( ValueSetLattice multiValueLattice, out MultiValue methodReturnValue) { - var handleCallAction = new HandleCallAction (location, owningSymbol, operation, multiValueLattice, reportDiagnostic); + var handleCallAction = new HandleCallAction (typeNameResolver, location, owningSymbol, operation, multiValueLattice, reportDiagnostic); MethodProxy method = new (calledMethod); var intrinsicId = Intrinsics.GetIntrinsicIdForMethod (method); if (!handleCallAction.Invoke (method, instance, arguments, intrinsicId, out methodReturnValue)) diff --git a/src/tools/illink/src/ILLink.RoslynAnalyzer/TrimAnalysis/TypeNameResolver.cs b/src/tools/illink/src/ILLink.RoslynAnalyzer/TrimAnalysis/TypeNameResolver.cs new file mode 100644 index 00000000000000..a91797e4316ebc --- /dev/null +++ b/src/tools/illink/src/ILLink.RoslynAnalyzer/TrimAnalysis/TypeNameResolver.cs @@ -0,0 +1,149 @@ +// 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.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Reflection.Metadata; +using Microsoft.CodeAnalysis; +using ILLink.RoslynAnalyzer.TrimAnalysis; +using ILLink.Shared.TypeSystemProxy; +using System.Collections.Immutable; + +namespace ILLink.Shared.TrimAnalysis +{ + internal struct TypeNameResolver + { + readonly Compilation _compilation; + + static readonly TypeNameParseOptions s_typeNameParseOptions = new () { MaxNodes = int.MaxValue }; + + public TypeNameResolver (Compilation compilation) + { + _compilation = compilation; + } + + public bool TryResolveTypeName ( + string typeNameString, + in DiagnosticContext diagnosticContext, + out ITypeSymbol? type, + bool needsAssemblyName) + { + type = null; + if (!TypeName.TryParse (typeNameString.AsSpan (), out TypeName? typeName, s_typeNameParseOptions)) + return false; + + if (needsAssemblyName && !IsFullyQualified (typeName)) { + diagnosticContext.AddDiagnostic (DiagnosticId.TypeNameIsNotAssemblyQualified, typeNameString); + return false; + } + + type = ResolveTypeName (_compilation.Assembly, typeName); + return type != null; + + static bool IsFullyQualified (TypeName typeName) + { + if (typeName.AssemblyName is null) + return false; + + if (typeName.IsArray || typeName.IsPointer || typeName.IsByRef) + return IsFullyQualified (typeName.GetElementType ()); + + if (typeName.IsConstructedGenericType) { + foreach (var arg in typeName.GetGenericArguments ()) { + if (!IsFullyQualified (arg)) + return false; + } + } + + return true; + } + } + + ITypeSymbol? ResolveTypeName (IAssemblySymbol assembly, TypeName typeName) + { + if (typeName.IsSimple) + return GetSimpleType (assembly, typeName); + + if (typeName.IsConstructedGenericType) + return GetGenericType (assembly, typeName); + + if (typeName.IsArray || typeName.IsPointer || typeName.IsByRef) + { + if (ResolveTypeName (assembly, typeName.GetElementType ()) is not ITypeSymbol type) + return null; + + if (typeName.IsArray) + return typeName.IsSZArray ? _compilation.CreateArrayTypeSymbol (type) : _compilation.CreateArrayTypeSymbol (type, typeName.GetArrayRank ()); + + // Roslyn doesn't have a representation for byref types + // (the byrefness is considered part of the symbol, not its type) + if (typeName.IsByRef) + return null; + + if (typeName.IsPointer) + return _compilation.CreatePointerTypeSymbol (type); + + Debug.Fail ("Unreachable"); + return null; + } + + return null; + } + + private ITypeSymbol? GetSimpleType (IAssemblySymbol assembly, TypeName typeName) + { + IAssemblySymbol module = assembly; + if (typeName.AssemblyName is AssemblyNameInfo assemblyName) { + if (ResolveAssembly (assemblyName) is not IAssemblySymbol resolvedAssembly) + return null; + module = resolvedAssembly; + } + + if (GetSimpleTypeFromModule (typeName, module) is ITypeSymbol type) + return type; + + // The analyzer doesn't see the core library, so can't fall back to lookup up types in corelib. + return null; + } + + private static ITypeSymbol? GetSimpleTypeFromModule (TypeName typeName, IAssemblySymbol module) + { + string fullName = TypeNameHelpers.Unescape (typeName.FullName); + return module.GetTypeByMetadataName (fullName); + } + + private ITypeSymbol? GetGenericType (IAssemblySymbol assembly, TypeName typeName) + { + if (ResolveTypeName (assembly, typeName.GetGenericTypeDefinition ()) is not INamedTypeSymbol typeDefinition) + return null; + + ImmutableArray typeArguments = typeName.GetGenericArguments (); + ITypeSymbol[] instantiation = new ITypeSymbol[typeArguments.Length]; + for (int i = 0; i < typeArguments.Length; i++) + { + if (ResolveTypeName (assembly, typeArguments[i]) is not ITypeSymbol type) + return null; + instantiation[i] = type; + } + return typeDefinition.Construct (instantiation); + } + + IAssemblySymbol? ResolveAssembly (AssemblyNameInfo? assemblyName) + { + if (assemblyName is null) + return null; + + if (_compilation.Assembly.Name == assemblyName.Name) + return _compilation.Assembly; + + foreach (var metadataReference in _compilation.References) { + if (_compilation.GetAssemblyOrModuleSymbol (metadataReference) is not IAssemblySymbol asmSym) + continue; + if (asmSym.Name == assemblyName.Name) + return asmSym; + } + return null; + } + } +} diff --git a/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/DynamicallyAccessedMembersCodeFixTests.cs b/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/DynamicallyAccessedMembersCodeFixTests.cs index 1f7017bff311b4..9468360d241119 100644 --- a/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/DynamicallyAccessedMembersCodeFixTests.cs +++ b/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/DynamicallyAccessedMembersCodeFixTests.cs @@ -101,7 +101,7 @@ class C [return: DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicFields)] static string M(Type t) { M2(t); - return "Foo"; + return "Foo, test"; } static void M2([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods)] Type t) {} @@ -117,7 +117,7 @@ class C [return: DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicFields)] static string M([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods)] Type t) { M2(t); - return "Foo"; + return "Foo, test"; } static void M2([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods)] Type t) {} @@ -187,7 +187,7 @@ class C [return: DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicFields)] static string M([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.NonPublicMethods)] Type t) { M2(t); - return "Foo"; + return "Foo, test"; } static void M2([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods)] Type t) {} @@ -1067,7 +1067,7 @@ await VerifyDynamicallyAccessedMembersCodeFix ( } [Fact] - public async Task CodeFix_IL2075_ReutrnAttributeLeavesOnCodeFix () + public async Task CodeFix_IL2075_ReturnAttributeLeavesOnCodeFix () { var test = $$$""" namespace System @@ -1078,7 +1078,7 @@ class C : TestSystemTypeBase public static string Main() { GetC().GetMethod("Foo"); - return "Foo"; + return "Foo, test"; } private static Type GetC () @@ -1097,7 +1097,7 @@ class C : TestSystemTypeBase public static string Main() { GetC().GetMethod("Foo"); - return "Foo"; + return "Foo, test"; } [return: DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods)] @@ -1608,7 +1608,7 @@ public static void Main() private string M1() { M2(this); - return "Foo"; + return "Foo, test"; } private static void M2([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods)] Type t) @@ -1633,7 +1633,7 @@ public static void Main() private string M1() { M2(this); - return "Foo"; + return "Foo, test"; } private static void M2([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods)] Type t) @@ -1676,7 +1676,7 @@ class C : TestSystemTypeBase { public static void Main() { - new C().M1("Foo"); + new C().M1("Foo, test"); } [return: DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.NonPublicMethods)] @@ -1700,7 +1700,7 @@ class C : TestSystemTypeBase { public static void Main() { - new C().M1("Foo"); + new C().M1("Foo, test"); } [return: DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.NonPublicMethods)] @@ -1736,7 +1736,7 @@ await VerifyDynamicallyAccessedMembersCodeFix ( // /0/Test0.cs(193,4): warning IL2065: Value passed to implicit 'this' parameter of method 'System.C.M1()' can not be statically determined // and may not meet 'DynamicallyAccessedMembersAttribute' requirements. VerifyCS.Diagnostic(DiagnosticId.ImplicitThisCannotBeStaticallyDetermined) - .WithSpan(193, 4, 193, 21) + .WithSpan(193, 4, 193, 27) .WithArguments("System.C.M1(String)") }); } @@ -1858,7 +1858,7 @@ class C : TestSystemTypeBase { public static void Main() { - new C().M1("Foo"); + new C().M1("Foo, test"); } [return: DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods)] @@ -1878,7 +1878,7 @@ class C : TestSystemTypeBase { public static void Main() { - new C().M1("Foo"); + new C().M1("Foo, test"); } [return: DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods)] @@ -1907,7 +1907,7 @@ await VerifyDynamicallyAccessedMembersCodeFix ( // /0/Test0.cs(193,4): warning IL2065: Value passed to implicit 'this' parameter of method 'System.C.M1()' can not be statically determined // and may not meet 'DynamicallyAccessedMembersAttribute' requirements. VerifyCS.Diagnostic(DiagnosticId.ImplicitThisCannotBeStaticallyDetermined) - .WithSpan(193, 4, 193, 21) + .WithSpan(193, 4, 193, 27) .WithArguments("System.C.M1(String)") }); } diff --git a/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/TestCaseCompilation.cs b/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/TestCaseCompilation.cs index 1a51ba84083287..58c57155aaada7 100644 --- a/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/TestCaseCompilation.cs +++ b/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/TestCaseCompilation.cs @@ -44,7 +44,7 @@ public static (CompilationWithAnalyzers Compilation, SemanticModel SemanticModel var sources = new List () { src }; sources.AddRange (additionalSources ?? Array.Empty ()); var comp = CSharpCompilation.Create ( - assemblyName: Guid.NewGuid ().ToString ("N"), + assemblyName: "test", syntaxTrees: sources, references: SourceGenerators.Tests.LiveReferencePack.GetMetadataReferences().Add(mdRef).AddRange(additionalReferences), new CSharpCompilationOptions (consoleApplication ? OutputKind.ConsoleApplication : OutputKind.DynamicallyLinkedLibrary)); diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/DataFlow/AssemblyQualifiedNameDataflow.cs b/src/tools/illink/test/Mono.Linker.Tests.Cases/DataFlow/AssemblyQualifiedNameDataflow.cs index c32a472e625bcc..85330b4b7e60f1 100644 --- a/src/tools/illink/test/Mono.Linker.Tests.Cases/DataFlow/AssemblyQualifiedNameDataflow.cs +++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/DataFlow/AssemblyQualifiedNameDataflow.cs @@ -58,7 +58,7 @@ static void TestConstructors () RequireNothing (type); } - [ExpectedWarning ("IL2122", "Type 'System.Invalid.TypeName' is not assembly qualified. " + "Type name strings used for dynamically accessing a type should be assembly qualified.", Tool.Trimmer | Tool.NativeAot, "https://github.com/dotnet/runtime/issues/95118")] + [ExpectedWarning ("IL2122", "Type 'System.Invalid.TypeName' is not assembly qualified. Type name strings used for dynamically accessing a type should be assembly qualified.")] static void TestUnqualifiedTypeNameWarns () { RequirePublicConstructors ("System.Invalid.TypeName"); diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/DataFlow/AttributeConstructorDataflow.cs b/src/tools/illink/test/Mono.Linker.Tests.Cases/DataFlow/AttributeConstructorDataflow.cs index 99831466c3a3dc..0adb2664ca8a3b 100644 --- a/src/tools/illink/test/Mono.Linker.Tests.Cases/DataFlow/AttributeConstructorDataflow.cs +++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/DataFlow/AttributeConstructorDataflow.cs @@ -25,7 +25,7 @@ class AttributeConstructorDataflow [KeepsPublicMethods ("Mono.Linker.Tests.Cases.DataFlow.AttributeConstructorDataflow+ClassWithKeptPublicMethods, test")] [KeepsPublicFields (null, null)] [TypeArray (new Type[] { typeof (AttributeConstructorDataflow) })] - [ExpectedWarning ("IL2026", "--ClassWithKeptPublicMethods--", Tool.Trimmer | Tool.NativeAot, "https://github.com/dotnet/linker/issues/2273")] + [ExpectedWarning ("IL2026", "--ClassWithKeptPublicMethods--")] public static void Main () { typeof (AttributeConstructorDataflow).GetMethod ("Main").GetCustomAttribute (typeof (KeepsPublicConstructorAttribute)); diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/DataFlow/AttributeFieldDataflow.cs b/src/tools/illink/test/Mono.Linker.Tests.Cases/DataFlow/AttributeFieldDataflow.cs index e7683d1f33806a..bb3c1d4bebb1aa 100644 --- a/src/tools/illink/test/Mono.Linker.Tests.Cases/DataFlow/AttributeFieldDataflow.cs +++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/DataFlow/AttributeFieldDataflow.cs @@ -40,7 +40,7 @@ public static void TestKeepsPublicMethods () [Kept] [KeptAttributeAttribute (typeof (KeepsPublicMethodsAttribute))] - [ExpectedWarning ("IL2026", "--ClassWithKeptPublicMethods--", Tool.Trimmer | Tool.NativeAot, "https://github.com/dotnet/runtime/issues/95118")] + [ExpectedWarning ("IL2026", "--ClassWithKeptPublicMethods--")] [KeepsPublicMethods (TypeName = "Mono.Linker.Tests.Cases.DataFlow.AttributeFieldDataflow+ClassWithKeptPublicMethods, test")] public static void TestKeepsPublicMethodsString () { diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/DataFlow/AttributePropertyDataflow.cs b/src/tools/illink/test/Mono.Linker.Tests.Cases/DataFlow/AttributePropertyDataflow.cs index de7186fc7f54a4..ba094199740ea1 100644 --- a/src/tools/illink/test/Mono.Linker.Tests.Cases/DataFlow/AttributePropertyDataflow.cs +++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/DataFlow/AttributePropertyDataflow.cs @@ -55,8 +55,7 @@ public static void TestKeepsPublicMethods () [Kept] [KeptAttributeAttribute (typeof (KeepsPublicMethodsAttribute))] - // Trimmer/NativeAot only for now - [ExpectedWarning ("IL2026", "--ClassWithKeptPublicMethodsKeptByName--", Tool.Trimmer | Tool.NativeAot, "https://github.com/dotnet/runtime/issues/95118")] + [ExpectedWarning ("IL2026", "--ClassWithKeptPublicMethodsKeptByName--")] [KeepsPublicMethods (TypeName = "Mono.Linker.Tests.Cases.DataFlow.AttributePropertyDataflow+AttributesOnMethod+ClassWithKeptPublicMethodsKeptByName, test")] public static void TestKeepsPublicMethodsByName () { @@ -211,8 +210,7 @@ class AttributeWithConditionalExpression { [Kept] [KeptAttributeAttribute (typeof (KeepsPublicMethodsAttribute))] - // Trimmer/NativeAot only for now - [ExpectedWarning ("IL2026", "--ClassWithKeptPublicMethods--", Tool.Trimmer | Tool.NativeAot, "https://github.com/dotnet/runtime/issues/95118")] + [ExpectedWarning ("IL2026", "--ClassWithKeptPublicMethods--")] [KeepsPublicMethods (TypeName = 1 + 1 == 2 ? "Mono.Linker.Tests.Cases.DataFlow.AttributePropertyDataflow+AttributeWithConditionalExpression+ClassWithKeptPublicMethodsKeptByName, test" : null)] public static void Test () { @@ -224,7 +222,7 @@ public static void Test () // where the owning symbol is not a method. [Kept] [KeptAttributeAttribute (typeof (KeepsPublicMethodsAttribute))] - [ExpectedWarning ("IL2026", "--ClassWithKeptPublicMethods--", Tool.Trimmer | Tool.NativeAot, "https://github.com/dotnet/runtime/issues/95118")] + [ExpectedWarning ("IL2026", "--ClassWithKeptPublicMethods--")] [KeepsPublicMethods (TypeName = 1 + 1 == 2 ? "Mono.Linker.Tests.Cases.DataFlow.AttributePropertyDataflow+AttributeWithConditionalExpression+ClassWithKeptPublicMethodsKeptByName, test" : null)] public static int field; diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/DataFlow/GenericParameterWarningLocation.cs b/src/tools/illink/test/Mono.Linker.Tests.Cases/DataFlow/GenericParameterWarningLocation.cs index f28802c174d116..5809d062835327 100644 --- a/src/tools/illink/test/Mono.Linker.Tests.Cases/DataFlow/GenericParameterWarningLocation.cs +++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/DataFlow/GenericParameterWarningLocation.cs @@ -1372,7 +1372,7 @@ class AnnotatedString { static void MethodWithAnnotatedParameter ([DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] string typeName) { } - [ExpectedWarning ("IL2026", "TypeWithRUCMethod.PrivateRUCMethod", Tool.Trimmer | Tool.NativeAot, "https://github.com/dotnet/runtime/issues/95118")] + [ExpectedWarning ("IL2026", "TypeWithRUCMethod.PrivateRUCMethod")] static void AnnotatedParameter () { MethodWithAnnotatedParameter ("Mono.Linker.Tests.Cases.DataFlow.GenericParameterWarningLocation+MethodBody+TypeWithPrivateMethods`1[[" @@ -1380,7 +1380,7 @@ static void AnnotatedParameter () + "]], test"); } - [ExpectedWarning ("IL2026", "TypeWithRUCMethod.PrivateRUCMethod", Tool.Trimmer | Tool.NativeAot, "https://github.com/dotnet/runtime/issues/95118")] + [ExpectedWarning ("IL2026", "TypeWithRUCMethod.PrivateRUCMethod")] [return: DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] static string AnnotatedReturnValue () { @@ -1392,7 +1392,7 @@ static string AnnotatedReturnValue () [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] static string _annotatedField; - [ExpectedWarning ("IL2026", "TypeWithRUCMethod.PrivateRUCMethod", Tool.Trimmer | Tool.NativeAot, "https://github.com/dotnet/runtime/issues/95118")] + [ExpectedWarning ("IL2026", "TypeWithRUCMethod.PrivateRUCMethod")] static void AnnotatedField () { _annotatedField = "Mono.Linker.Tests.Cases.DataFlow.GenericParameterWarningLocation+MethodBody+TypeWithPrivateMethods`1[[" @@ -1410,7 +1410,7 @@ public static void Test () class TypeGetType { - [ExpectedWarning ("IL2026", "TypeWithRUCMethod.PrivateRUCMethod", Tool.Trimmer | Tool.NativeAot, "https://github.com/dotnet/runtime/issues/95118")] + [ExpectedWarning ("IL2026", "TypeWithRUCMethod.PrivateRUCMethod")] static void SpecificType () { Type.GetType ("Mono.Linker.Tests.Cases.DataFlow.GenericParameterWarningLocation+MethodBody+TypeWithPrivateMethods`1[[" diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/DataFlow/GetTypeDataFlow.cs b/src/tools/illink/test/Mono.Linker.Tests.Cases/DataFlow/GetTypeDataFlow.cs index 31ef05ef3e55bf..5bb64c5e9c2cf5 100644 --- a/src/tools/illink/test/Mono.Linker.Tests.Cases/DataFlow/GetTypeDataFlow.cs +++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/DataFlow/GetTypeDataFlow.cs @@ -168,8 +168,8 @@ public void Method1 () { } [RequiresUnreferencedCode ("--Method2--")] public void Method2 () { } - [ExpectedWarning ("IL2026", "--Method1--", Tool.Trimmer | Tool.NativeAot, "https://github.com/dotnet/runtime/issues/95118")] - [ExpectedWarning ("IL2026", "--Method2--", Tool.Trimmer | Tool.NativeAot, "https://github.com/dotnet/runtime/issues/95118")] + [ExpectedWarning ("IL2026", "--Method1--")] + [ExpectedWarning ("IL2026", "--Method2--")] public static void Test () { Type.GetType ("Mono.Linker.Tests.Cases.DataFlow." + nameof (GetTypeDataFlow) + "+" + nameof (TypeWithWarnings)).RequiresPublicMethods (); @@ -183,7 +183,7 @@ class OverConstTypeName [RequiresUnreferencedCode ("--Method1--")] public void Method1 () { } - [ExpectedWarning ("IL2026", "--Method1--", Tool.Trimmer | Tool.NativeAot, "https://github.com/dotnet/runtime/issues/95118")] + [ExpectedWarning ("IL2026", "--Method1--")] public static void Test () { Type.GetType (s_ConstTypeName).RequiresPublicMethods (); diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/Reflection/TypeUsedViaReflection.cs b/src/tools/illink/test/Mono.Linker.Tests.Cases/Reflection/TypeUsedViaReflection.cs index 83ff6fc5e3f950..23b0a5dccb8b8d 100644 --- a/src/tools/illink/test/Mono.Linker.Tests.Cases/Reflection/TypeUsedViaReflection.cs +++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/Reflection/TypeUsedViaReflection.cs @@ -86,7 +86,7 @@ public static void TestEmptyString () public class Full { } [Kept] - [ExpectedWarning ("IL2026", nameof (Full), Tool.Trimmer | Tool.NativeAot, "https://github.com/dotnet/runtime/issues/95118")] + [ExpectedWarning ("IL2026", nameof (Full))] public static void TestFullString () { const string reflectionTypeKeptString = "Mono.Linker.Tests.Cases.Reflection.TypeUsedViaReflection+Full, test, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null"; @@ -101,7 +101,7 @@ public static void TestFullString () public class Generic { } [Kept] - [ExpectedWarning ("IL2026", "Generic", Tool.Trimmer | Tool.NativeAot, "https://github.com/dotnet/runtime/issues/95118")] + [ExpectedWarning ("IL2026", "Generic")] public static void TestGenericString () { const string reflectionTypeKeptString = "Mono.Linker.Tests.Cases.Reflection.TypeUsedViaReflection+Generic`1, test, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null"; @@ -121,7 +121,7 @@ public class GenericInstantiation { } public class GenericArgument { } [Kept] - [ExpectedWarning ("IL2026", "GenericInstantiation", Tool.Trimmer | Tool.NativeAot, "https://github.com/dotnet/runtime/issues/95118")] + [ExpectedWarning ("IL2026", "GenericInstantiation")] public static void TestGenericInstantiation () { const string reflectionTypeKeptString = "Mono.Linker.Tests.Cases.Reflection.TypeUsedViaReflection+GenericInstantiation`1[[Mono.Linker.Tests.Cases.Reflection.TypeUsedViaReflection+GenericArgument]]"; @@ -141,7 +141,7 @@ public class GenericInstantiationFullString { } public class GenericArgumentFullString { } [Kept] - [ExpectedWarning ("IL2026", "GenericInstantiationFullString", Tool.Trimmer | Tool.NativeAot, "https://github.com/dotnet/runtime/issues/95118")] + [ExpectedWarning ("IL2026", "GenericInstantiationFullString")] public static void TestGenericInstantiationFullString () { const string reflectionTypeKeptString = "Mono.Linker.Tests.Cases.Reflection.TypeUsedViaReflection+GenericInstantiationFullString`1[" @@ -158,7 +158,7 @@ public static void TestGenericInstantiationFullString () public class GenericInstantiationOverCoreLib { } [Kept] - [ExpectedWarning ("IL2026", "GenericInstantiationOverCoreLib", Tool.Trimmer | Tool.NativeAot, "https://github.com/dotnet/runtime/issues/95118")] + [ExpectedWarning ("IL2026", "GenericInstantiationOverCoreLib", Tool.Trimmer | Tool.NativeAot, "Analyzer can't resolve type names from corelib")] public static void TestGenericInstantiationOverCoreLib () { // Note: the argument type should not be assembly-qualified for this test, which is checking that @@ -175,7 +175,7 @@ public static void TestGenericInstantiationOverCoreLib () public class FullConst { } [Kept] - [ExpectedWarning ("IL2026", nameof (FullConst), Tool.Trimmer | Tool.NativeAot, "https://github.com/dotnet/runtime/issues/95118")] + [ExpectedWarning ("IL2026", nameof (FullConst))] public static void TestFullStringConst () { const string reflectionTypeKeptString = "Mono.Linker.Tests.Cases.Reflection.TypeUsedViaReflection+FullConst, test, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null"; @@ -190,7 +190,7 @@ public static void TestFullStringConst () public class TypeAsmName { } [Kept] - [ExpectedWarning ("IL2026", nameof (TypeAsmName), Tool.Trimmer | Tool.NativeAot, "https://github.com/dotnet/runtime/issues/95118")] + [ExpectedWarning ("IL2026", nameof (TypeAsmName))] public static void TestTypeAsmName () { const string reflectionTypeKeptString = "Mono.Linker.Tests.Cases.Reflection.TypeUsedViaReflection+TypeAsmName, test"; @@ -205,7 +205,7 @@ public static void TestTypeAsmName () public class AType { } [Kept] - [ExpectedWarning ("IL2026", nameof (AType), Tool.Trimmer | Tool.NativeAot, "https://github.com/dotnet/runtime/issues/95118")] + [ExpectedWarning ("IL2026", nameof (AType))] public static void TestType () { const string reflectionTypeKeptString = "Mono.Linker.Tests.Cases.Reflection.TypeUsedViaReflection+AType"; @@ -214,11 +214,15 @@ public static void TestType () } [Kept] +#if !NATIVEAOT + [KeptMember (".ctor()")] +#endif [KeptAttributeAttribute (typeof (RequiresUnreferencedCodeAttribute))] [RequiresUnreferencedCode (nameof (Pointer))] public class Pointer { } [Kept] + [ExpectedWarning ("IL2026", nameof (Pointer), Tool.Trimmer, "https://github.com/dotnet/runtime/issues/106214")] public static void TestPointer () { const string reflectionTypeKeptString = "Mono.Linker.Tests.Cases.Reflection.TypeUsedViaReflection+Pointer*"; @@ -227,11 +231,15 @@ public static void TestPointer () } [Kept] +#if !NATIVEAOT + [KeptMember (".ctor()")] +#endif [KeptAttributeAttribute (typeof (RequiresUnreferencedCodeAttribute))] [RequiresUnreferencedCode (nameof (Reference))] public class Reference { } [Kept] + [ExpectedWarning ("IL2026", nameof (Reference), Tool.Trimmer, "https://github.com/dotnet/runtime/issues/106214")] public static void TestReference () { const string reflectionTypeKeptString = "Mono.Linker.Tests.Cases.Reflection.TypeUsedViaReflection+Reference&"; @@ -322,7 +330,7 @@ class N3 } [Kept] - [ExpectedWarning ("IL2026", "N3", Tool.Trimmer | Tool.NativeAot, "https://github.com/dotnet/runtime/issues/95118")] + [ExpectedWarning ("IL2026", "N3")] static void TestDeeplyNested () { var typeKept = Type.GetType ("Mono.Linker.Tests.Cases.Reflection.TypeUsedViaReflection+Nested1+N2+N3"); @@ -353,8 +361,8 @@ class TypeFromBranchA { } class TypeFromBranchB { } [Kept] - [ExpectedWarning ("IL2026", nameof (TypeFromBranchA), Tool.Trimmer | Tool.NativeAot, "https://github.com/dotnet/runtime/issues/95118")] - [ExpectedWarning ("IL2026", nameof (TypeFromBranchB), Tool.Trimmer | Tool.NativeAot, "https://github.com/dotnet/runtime/issues/95118")] + [ExpectedWarning ("IL2026", nameof (TypeFromBranchA))] + [ExpectedWarning ("IL2026", nameof (TypeFromBranchB))] static void TestTypeFromBranch (int b) { string name = null; @@ -423,7 +431,7 @@ static void TestTypeUsingCaseUnknownByTheLinker2 () public class OverloadWith3Parameters { } [Kept] - [ExpectedWarning ("IL2026", nameof (OverloadWith3Parameters), Tool.Trimmer | Tool.NativeAot, "https://github.com/dotnet/runtime/issues/95118")] + [ExpectedWarning ("IL2026", nameof (OverloadWith3Parameters))] static void TestTypeOverloadWith3Parameters () { const string reflectionTypeKeptString = "Mono.Linker.Tests.Cases.Reflection.TypeUsedViaReflection+OverloadWith3Parameters"; @@ -435,11 +443,11 @@ static void TestTypeOverloadWith3Parameters () [Kept] [KeptMember (".ctor()")] [KeptAttributeAttribute (typeof (RequiresUnreferencedCodeAttribute))] - [RequiresUnreferencedCode (nameof (OverloadWith3Parameters))] + [RequiresUnreferencedCode (nameof (OverloadWith4Parameters))] public class OverloadWith4Parameters { } [Kept] - [ExpectedWarning ("IL2026", nameof (OverloadWith4Parameters), Tool.Trimmer | Tool.NativeAot, "https://github.com/dotnet/runtime/issues/95118")] + [ExpectedWarning ("IL2026", nameof (OverloadWith4Parameters))] static void TestTypeOverloadWith4Parameters () { const string reflectionTypeKeptString = "Mono.Linker.Tests.Cases.Reflection.TypeUsedViaReflection+OverloadWith4Parameters"; @@ -464,11 +472,11 @@ static void TestTypeOverloadWith5ParametersWithIgnoreCase () [Kept] [KeptMember (".ctor()")] [KeptAttributeAttribute (typeof (RequiresUnreferencedCodeAttribute))] - [RequiresUnreferencedCode (nameof (OverloadWith3Parameters))] + [RequiresUnreferencedCode (nameof (OverloadWith5ParametersWithIgnoreCase))] public class OverloadWith5ParametersWithoutIgnoreCase { } [Kept] - [ExpectedWarning ("IL2026", nameof (OverloadWith5ParametersWithoutIgnoreCase), Tool.Trimmer | Tool.NativeAot, "https://github.com/dotnet/runtime/issues/95118")] + [ExpectedWarning ("IL2026", nameof (OverloadWith5ParametersWithIgnoreCase))] static void TestTypeOverloadWith5ParametersWithoutIgnoreCase () { const string reflectionTypeKeptString = "Mono.Linker.Tests.Cases.Reflection.TypeUsedViaReflection+OverloadWith5ParametersWithoutIgnoreCase"; @@ -524,7 +532,7 @@ static void TestUnknownIgnoreCase5Params (int num) [Kept] [KeptMember (".ctor()")] [KeptAttributeAttribute (typeof (RequiresUnreferencedCodeAttribute))] - [RequiresUnreferencedCode (nameof (OverloadWith3Parameters))] + [RequiresUnreferencedCode ("GenericTypeWithAnnotations_OuterType")] public class GenericTypeWithAnnotations_OuterType< [KeptAttributeAttribute (typeof (DynamicallyAccessedMembersAttribute))] [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.NonPublicProperties)] T> @@ -533,7 +541,7 @@ public class GenericTypeWithAnnotations_OuterType< [Kept] [KeptAttributeAttribute (typeof (RequiresUnreferencedCodeAttribute))] - [RequiresUnreferencedCode (nameof (OverloadWith3Parameters))] + [RequiresUnreferencedCode (nameof (GenericTypeWithAnnotations_InnerType))] public class GenericTypeWithAnnotations_InnerType { [Kept] @@ -544,9 +552,9 @@ private static void PrivateMethod () { } } [Kept] - [ExpectedWarning ("IL2026", "GenericTypeWithAnnotations_OuterType", Tool.Trimmer | Tool.NativeAot, "https://github.com/dotnet/runtime/issues/95118")] - [ExpectedWarning ("IL2026", nameof (GenericTypeWithAnnotations_InnerType), "PrivateProperty.get", Tool.Trimmer | Tool.NativeAot, "https://github.com/dotnet/runtime/issues/95118")] - [ExpectedWarning ("IL2026", nameof (GenericTypeWithAnnotations_InnerType), "PrivateProperty.set", Tool.Trimmer | Tool.NativeAot, "https://github.com/dotnet/runtime/issues/95118")] + [ExpectedWarning ("IL2026", "GenericTypeWithAnnotations_OuterType")] + [ExpectedWarning ("IL2026", nameof (GenericTypeWithAnnotations_InnerType), "PrivateProperty.get")] + [ExpectedWarning ("IL2026", nameof (GenericTypeWithAnnotations_InnerType), "PrivateProperty.set")] static void TestGenericTypeWithAnnotations () { const string reflectionTypeKeptString = "Mono.Linker.Tests.Cases.Reflection.TypeUsedViaReflection+GenericTypeWithAnnotations_OuterType`1[" @@ -617,25 +625,25 @@ static void TestEscapedTypeName () class AssemblyTypeResolutionBehavior { [Kept] - [ExpectedWarning ("IL2122", Tool.Trimmer | Tool.NativeAot, "https://github.com/dotnet/runtime/issues/95118")] + [ExpectedWarning ("IL2122")] static void TestRequireTypeInSameAssemblyAsGetType () { RequireHelper.RequireType ("Mono.Linker.Tests.Cases.Reflection.Dependencies.TypeDefinedInSameAssemblyAsGetType"); } [Kept] - [ExpectedWarning ("IL2122", Tool.Trimmer | Tool.NativeAot, "https://github.com/dotnet/runtime/issues/95118")] + [ExpectedWarning ("IL2122")] static void TestRequireTypeInSameAssemblyAsCallToRequireType () { RequireHelper.RequireType ("Mono.Linker.Tests.Cases.Reflection.TypeUsedViaReflection+AssemblyTypeResolutionBehavior+TypeDefinedInSameAssemblyAsCallToRequireType"); } [Kept] - [ExpectedWarning ("IL2122", Tool.Trimmer | Tool.NativeAot, "https://github.com/dotnet/runtime/issues/95118")] + [ExpectedWarning ("IL2122")] static void TestRequireTypeWithNonAssemblyQualifiedGenericArguments () { RequireHelper.RequireType ("Mono.Linker.Tests.Cases.Reflection.TypeUsedViaReflection+AssemblyTypeResolutionBehavior+Generic`1[[System.Int32]], test"); } [Kept] - [ExpectedWarning ("IL2122", Tool.Trimmer | Tool.NativeAot, "https://github.com/dotnet/runtime/issues/95118")] + [ExpectedWarning ("IL2122")] static void TestRequireTypeWithNonAssemblyQualifiedArrayType () { RequireHelper.RequireType ("Mono.Linker.Tests.Cases.Reflection.TypeUsedViaReflection+AssemblyTypeResolutionBehavior+Generic`1[" + "[Mono.Linker.Tests.Cases.Reflection.TypeUsedViaReflection+AssemblyTypeResolutionBehavior+ArrayElementGenericArgumentType]" @@ -643,7 +651,7 @@ static void TestRequireTypeWithNonAssemblyQualifiedArrayType () { } [Kept] - [ExpectedWarning ("IL2122", Tool.Trimmer | Tool.NativeAot, "https://github.com/dotnet/runtime/issues/95118")] + [ExpectedWarning ("IL2122")] static void TestRequireTypeWithNonAssemblyQualifiedPointerType () { RequireHelper.RequireType ("Mono.Linker.Tests.Cases.Reflection.TypeUsedViaReflection+AssemblyTypeResolutionBehavior+Generic`1[" + "[Mono.Linker.Tests.Cases.Reflection.TypeUsedViaReflection+AssemblyTypeResolutionBehavior+PointerElementGenericArgumentType]" @@ -651,7 +659,7 @@ static void TestRequireTypeWithNonAssemblyQualifiedPointerType () { } [Kept] - [ExpectedWarning ("IL2122", Tool.Trimmer | Tool.NativeAot, "https://github.com/dotnet/runtime/issues/95118")] + [ExpectedWarning ("IL2122")] static void TestRequireTypeWithNonAssemblyQualifiedByRefType () { RequireHelper.RequireType ("Mono.Linker.Tests.Cases.Reflection.TypeUsedViaReflection+AssemblyTypeResolutionBehavior+Generic`1[" + "[Mono.Linker.Tests.Cases.Reflection.TypeUsedViaReflection+AssemblyTypeResolutionBehavior+ByRefElementGenericArgumentType]" diff --git a/src/tools/illink/test/Mono.Linker.Tests/TestCasesRunner/ResultChecker.cs b/src/tools/illink/test/Mono.Linker.Tests/TestCasesRunner/ResultChecker.cs index 6660d9d23db089..cf615b3c4da0b0 100644 --- a/src/tools/illink/test/Mono.Linker.Tests/TestCasesRunner/ResultChecker.cs +++ b/src/tools/illink/test/Mono.Linker.Tests/TestCasesRunner/ResultChecker.cs @@ -1050,7 +1050,6 @@ void VerifyLoggedMessages (AssemblyDefinition original, TrimmingTestLogger logge int? unexpectedWarningCodeNumber = unexpectedWarningCode == null ? null : int.Parse (unexpectedWarningCode.Substring (2)); - MessageContainer? unexpectedWarningMessage = null; foreach (var mc in unmatchedMessages) { if (mc.Category != MessageCategory.Warning) continue; @@ -1062,12 +1061,7 @@ void VerifyLoggedMessages (AssemblyDefinition original, TrimmingTestLogger logge if (attrProvider is IMemberDefinition attrMember && (mc.Origin?.Provider is IMemberDefinition member) && member.FullName.Contains (attrMember.FullName) != true) continue; - unexpectedWarningMessage = mc; - break; - } - - if (unexpectedWarningMessage is not null) { - unexpectedMessageWarnings.Add ($"Unexpected warning found: {unexpectedWarningMessage}"); + unexpectedMessageWarnings.Add ($"Unexpected warning found: {mc}"); } } From 9dfbead5d6729906b290b5f971bf48a57ea07d6b Mon Sep 17 00:00:00 2001 From: Sven Boemer Date: Tue, 10 Jun 2025 22:17:29 +0000 Subject: [PATCH 2/6] Fix build - Remove TypeNameHelpers - Pass resolver through new code --- .../DynamicallyAccessedMembersAnalyzer.cs | 2 +- .../DynamicallyAccessedMembersTypeHierarchy.cs | 8 ++++++-- .../ILLink.RoslynAnalyzer/ILLink.RoslynAnalyzer.csproj | 1 - .../TrimAnalysis/TypeNameResolver.cs | 2 +- 4 files changed, 8 insertions(+), 5 deletions(-) diff --git a/src/tools/illink/src/ILLink.RoslynAnalyzer/DynamicallyAccessedMembersAnalyzer.cs b/src/tools/illink/src/ILLink.RoslynAnalyzer/DynamicallyAccessedMembersAnalyzer.cs index 20c72c961161fe..c7e7f830aa56b3 100644 --- a/src/tools/illink/src/ILLink.RoslynAnalyzer/DynamicallyAccessedMembersAnalyzer.cs +++ b/src/tools/illink/src/ILLink.RoslynAnalyzer/DynamicallyAccessedMembersAnalyzer.cs @@ -130,7 +130,7 @@ public override void Initialize (AnalysisContext context) foreach (var interfaceType in type.Interfaces) GenericArgumentDataFlow.ProcessGenericArgumentDataFlow (typeNameResolver, location, interfaceType, context.ReportDiagnostic); - DynamicallyAccessedMembersTypeHierarchy.ApplyDynamicallyAccessedMembersToTypeHierarchy (location, type, context.ReportDiagnostic); + DynamicallyAccessedMembersTypeHierarchy.ApplyDynamicallyAccessedMembersToTypeHierarchy (typeNameResolver, location, type, context.ReportDiagnostic); }, SymbolKind.NamedType); context.RegisterSymbolAction (context => { VerifyMemberOnlyApplyToTypesOrStrings (context, context.Symbol); diff --git a/src/tools/illink/src/ILLink.RoslynAnalyzer/DynamicallyAccessedMembersTypeHierarchy.cs b/src/tools/illink/src/ILLink.RoslynAnalyzer/DynamicallyAccessedMembersTypeHierarchy.cs index 9b6ea59ba10459..da591fc96c3368 100644 --- a/src/tools/illink/src/ILLink.RoslynAnalyzer/DynamicallyAccessedMembersTypeHierarchy.cs +++ b/src/tools/illink/src/ILLink.RoslynAnalyzer/DynamicallyAccessedMembersTypeHierarchy.cs @@ -12,14 +12,18 @@ namespace ILLink.RoslynAnalyzer { sealed class DynamicallyAccessedMembersTypeHierarchy { - public static void ApplyDynamicallyAccessedMembersToTypeHierarchy (Location typeLocation, INamedTypeSymbol type, Action reportDiagnostic) + public static void ApplyDynamicallyAccessedMembersToTypeHierarchy ( + TypeNameResolver typeNameResolver, + Location typeLocation, + INamedTypeSymbol type, + Action reportDiagnostic) { var annotation = FlowAnnotations.GetTypeAnnotation (type); // We need to apply annotations to this type, and its base/interface types (recursively) // But the annotations on base/interfaces may already be applied so we don't need to apply those // again (and should avoid doing so as it would produce extra warnings). - var reflectionAccessAnalyzer = new ReflectionAccessAnalyzer (reportDiagnostic, type); + var reflectionAccessAnalyzer = new ReflectionAccessAnalyzer (reportDiagnostic, typeNameResolver, type); if (type.BaseType is INamedTypeSymbol baseType) { var baseAnnotation = FlowAnnotations.GetTypeAnnotation (baseType); var annotationToApplyToBase = Annotations.GetMissingMemberTypes (annotation, baseAnnotation); diff --git a/src/tools/illink/src/ILLink.RoslynAnalyzer/ILLink.RoslynAnalyzer.csproj b/src/tools/illink/src/ILLink.RoslynAnalyzer/ILLink.RoslynAnalyzer.csproj index c3b0b1d1847b48..972594f1e075d3 100644 --- a/src/tools/illink/src/ILLink.RoslynAnalyzer/ILLink.RoslynAnalyzer.csproj +++ b/src/tools/illink/src/ILLink.RoslynAnalyzer/ILLink.RoslynAnalyzer.csproj @@ -20,7 +20,6 @@ - diff --git a/src/tools/illink/src/ILLink.RoslynAnalyzer/TrimAnalysis/TypeNameResolver.cs b/src/tools/illink/src/ILLink.RoslynAnalyzer/TrimAnalysis/TypeNameResolver.cs index a91797e4316ebc..f0f476bac05f53 100644 --- a/src/tools/illink/src/ILLink.RoslynAnalyzer/TrimAnalysis/TypeNameResolver.cs +++ b/src/tools/illink/src/ILLink.RoslynAnalyzer/TrimAnalysis/TypeNameResolver.cs @@ -109,7 +109,7 @@ static bool IsFullyQualified (TypeName typeName) private static ITypeSymbol? GetSimpleTypeFromModule (TypeName typeName, IAssemblySymbol module) { - string fullName = TypeNameHelpers.Unescape (typeName.FullName); + string fullName = TypeName.Unescape (typeName.FullName); return module.GetTypeByMetadataName (fullName); } From 0d7feb30e2c31a2e57552e0498c5c71c0ebce86f Mon Sep 17 00:00:00 2001 From: Sven Boemer Date: Tue, 10 Jun 2025 22:19:23 +0000 Subject: [PATCH 3/6] Fix ILLink test The referenced issue has since been fixed --- .../Reflection/TypeUsedViaReflection.cs | 8 -------- 1 file changed, 8 deletions(-) diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/Reflection/TypeUsedViaReflection.cs b/src/tools/illink/test/Mono.Linker.Tests.Cases/Reflection/TypeUsedViaReflection.cs index 23b0a5dccb8b8d..ed5aece0fa11f2 100644 --- a/src/tools/illink/test/Mono.Linker.Tests.Cases/Reflection/TypeUsedViaReflection.cs +++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/Reflection/TypeUsedViaReflection.cs @@ -214,15 +214,11 @@ public static void TestType () } [Kept] -#if !NATIVEAOT - [KeptMember (".ctor()")] -#endif [KeptAttributeAttribute (typeof (RequiresUnreferencedCodeAttribute))] [RequiresUnreferencedCode (nameof (Pointer))] public class Pointer { } [Kept] - [ExpectedWarning ("IL2026", nameof (Pointer), Tool.Trimmer, "https://github.com/dotnet/runtime/issues/106214")] public static void TestPointer () { const string reflectionTypeKeptString = "Mono.Linker.Tests.Cases.Reflection.TypeUsedViaReflection+Pointer*"; @@ -231,15 +227,11 @@ public static void TestPointer () } [Kept] -#if !NATIVEAOT - [KeptMember (".ctor()")] -#endif [KeptAttributeAttribute (typeof (RequiresUnreferencedCodeAttribute))] [RequiresUnreferencedCode (nameof (Reference))] public class Reference { } [Kept] - [ExpectedWarning ("IL2026", nameof (Reference), Tool.Trimmer, "https://github.com/dotnet/runtime/issues/106214")] public static void TestReference () { const string reflectionTypeKeptString = "Mono.Linker.Tests.Cases.Reflection.TypeUsedViaReflection+Reference&"; From 4f1aebea81794329a365e8c5f202e6cd510833c9 Mon Sep 17 00:00:00 2001 From: Sven Boemer Date: Tue, 10 Jun 2025 22:30:52 +0000 Subject: [PATCH 4/6] Add back TypeNameHelpers The analyzer shouldn't use TypeName.Unescape which was added in .NET 10 (since Roslyn references Sytem.Reflection.Metadata 9.0.0). --- .../TrimAnalysis/TypeNameHelpers.cs | 58 +++++++++++++++++++ .../TrimAnalysis/TypeNameResolver.cs | 2 +- 2 files changed, 59 insertions(+), 1 deletion(-) create mode 100644 src/tools/illink/src/ILLink.RoslynAnalyzer/TrimAnalysis/TypeNameHelpers.cs diff --git a/src/tools/illink/src/ILLink.RoslynAnalyzer/TrimAnalysis/TypeNameHelpers.cs b/src/tools/illink/src/ILLink.RoslynAnalyzer/TrimAnalysis/TypeNameHelpers.cs new file mode 100644 index 00000000000000..0f02c93626e331 --- /dev/null +++ b/src/tools/illink/src/ILLink.RoslynAnalyzer/TrimAnalysis/TypeNameHelpers.cs @@ -0,0 +1,58 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Reflection.Metadata; +using System.Text; + +namespace System.Reflection.Metadata +{ + // Once Roslyn references System.Reflection.Metadata 10.0.0 or later, + // we can replace this with TypeName.Unescape. + internal static class TypeNameHelpers + { + private const char EscapeCharacter = '\\'; + + /// + /// Removes escape characters from the string (if there were any found). + /// + internal static string Unescape(string name) + { + int indexOfEscapeCharacter = name.IndexOf(EscapeCharacter); + if (indexOfEscapeCharacter < 0) + { + return name; + } + + return Unescape(name, indexOfEscapeCharacter); + + static string Unescape(string name, int indexOfEscapeCharacter) + { + // this code path is executed very rarely (IL Emit or pure IL with chars not allowed in C# or F#) + var sb = new ValueStringBuilder(stackalloc char[64]); + sb.EnsureCapacity(name.Length); + sb.Append(name.AsSpan(0, indexOfEscapeCharacter)); + + for (int i = indexOfEscapeCharacter; i < name.Length;) + { + char c = name[i++]; + + if (c != EscapeCharacter) + { + sb.Append(c); + } + else if (i < name.Length && name[i] == EscapeCharacter) // escaped escape character ;) + { + sb.Append(c); + // Consume the escaped escape character, it's important for edge cases + // like escaped escape character followed by another escaped char (example: "\\\\\\+") + i++; + } + } + + return sb.ToString(); + } + } + } +} diff --git a/src/tools/illink/src/ILLink.RoslynAnalyzer/TrimAnalysis/TypeNameResolver.cs b/src/tools/illink/src/ILLink.RoslynAnalyzer/TrimAnalysis/TypeNameResolver.cs index f0f476bac05f53..59b89c4d952296 100644 --- a/src/tools/illink/src/ILLink.RoslynAnalyzer/TrimAnalysis/TypeNameResolver.cs +++ b/src/tools/illink/src/ILLink.RoslynAnalyzer/TrimAnalysis/TypeNameResolver.cs @@ -109,7 +109,7 @@ static bool IsFullyQualified (TypeName typeName) private static ITypeSymbol? GetSimpleTypeFromModule (TypeName typeName, IAssemblySymbol module) { - string fullName = TypeName.Unescape (typeName.FullName); + string fullName = TypeNameHelpers.Unescape (typeName.FullName); return module.GetTypeByMetadataName (fullName); } From 8d2d0154afbf06eaa67ad43f7ceb218c0fa89c6b Mon Sep 17 00:00:00 2001 From: Sven Boemer Date: Fri, 1 Aug 2025 14:16:55 -0700 Subject: [PATCH 5/6] Include TypeName parser sources --- .../src/System.Reflection.Metadata.csproj | 2 +- .../System/Reflection/Metadata/TypeName.cs | 2 +- ...rserOptions.cs => TypeNameParseOptions.cs} | 0 .../ILLink.RoslynAnalyzer.csproj | 16 ++++- .../TrimAnalysis/TypeNameHelpers.cs | 58 ------------------- .../TrimAnalysis/TypeNameResolver.cs | 2 +- 6 files changed, 18 insertions(+), 62 deletions(-) rename src/libraries/System.Reflection.Metadata/src/System/Reflection/Metadata/{TypeNameParserOptions.cs => TypeNameParseOptions.cs} (100%) delete mode 100644 src/tools/illink/src/ILLink.RoslynAnalyzer/TrimAnalysis/TypeNameHelpers.cs diff --git a/src/libraries/System.Reflection.Metadata/src/System.Reflection.Metadata.csproj b/src/libraries/System.Reflection.Metadata/src/System.Reflection.Metadata.csproj index fe54d54a895170..18206a3e46f984 100644 --- a/src/libraries/System.Reflection.Metadata/src/System.Reflection.Metadata.csproj +++ b/src/libraries/System.Reflection.Metadata/src/System.Reflection.Metadata.csproj @@ -123,7 +123,7 @@ The System.Reflection.Metadata library is built-in as part of the shared framewo - + diff --git a/src/libraries/System.Reflection.Metadata/src/System/Reflection/Metadata/TypeName.cs b/src/libraries/System.Reflection.Metadata/src/System/Reflection/Metadata/TypeName.cs index 5bae2f20dd557a..2e90ec5bb80548 100644 --- a/src/libraries/System.Reflection.Metadata/src/System/Reflection/Metadata/TypeName.cs +++ b/src/libraries/System.Reflection.Metadata/src/System/Reflection/Metadata/TypeName.cs @@ -194,7 +194,7 @@ public string FullName private void AppendFullName(ref ValueStringBuilder builder) { // This is a recursive method over potentially hostile input. Protection against DoS is offered - // via the [Try]Parse method and TypeNameParserOptions.MaxNodes property at construction time. + // via the [Try]Parse method and TypeNameParseOptions.MaxNodes property at construction time. // This FullName property getter and related methods assume that this TypeName instance has an // acceptable node count. // diff --git a/src/libraries/System.Reflection.Metadata/src/System/Reflection/Metadata/TypeNameParserOptions.cs b/src/libraries/System.Reflection.Metadata/src/System/Reflection/Metadata/TypeNameParseOptions.cs similarity index 100% rename from src/libraries/System.Reflection.Metadata/src/System/Reflection/Metadata/TypeNameParserOptions.cs rename to src/libraries/System.Reflection.Metadata/src/System/Reflection/Metadata/TypeNameParseOptions.cs diff --git a/src/tools/illink/src/ILLink.RoslynAnalyzer/ILLink.RoslynAnalyzer.csproj b/src/tools/illink/src/ILLink.RoslynAnalyzer/ILLink.RoslynAnalyzer.csproj index 972594f1e075d3..f02bd4222231ca 100644 --- a/src/tools/illink/src/ILLink.RoslynAnalyzer/ILLink.RoslynAnalyzer.csproj +++ b/src/tools/illink/src/ILLink.RoslynAnalyzer/ILLink.RoslynAnalyzer.csproj @@ -23,6 +23,21 @@ + + + + + + + + + + + true + System.Reflection.Metadata.SR + + + @@ -30,7 +45,6 @@ all contentfiles - diff --git a/src/tools/illink/src/ILLink.RoslynAnalyzer/TrimAnalysis/TypeNameHelpers.cs b/src/tools/illink/src/ILLink.RoslynAnalyzer/TrimAnalysis/TypeNameHelpers.cs deleted file mode 100644 index 7d403bf0364d7b..00000000000000 --- a/src/tools/illink/src/ILLink.RoslynAnalyzer/TrimAnalysis/TypeNameHelpers.cs +++ /dev/null @@ -1,58 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.Diagnostics; -using System.Diagnostics.CodeAnalysis; -using System.Reflection.Metadata; -using System.Text; - -namespace System.Reflection.Metadata -{ - // Once Roslyn references System.Reflection.Metadata 10.0.0 or later, - // we can replace this with TypeName.Unescape. - internal static class TypeNameHelpers - { - private const char EscapeCharacter = '\\'; - - /// - /// Removes escape characters from the string (if there were any found). - /// - internal static string Unescape(string name) - { - int indexOfEscapeCharacter = name.IndexOf(EscapeCharacter); - if (indexOfEscapeCharacter < 0) - { - return name; - } - - return Unescape(name, indexOfEscapeCharacter); - - static string Unescape(string name, int indexOfEscapeCharacter) - { - // this code path is executed very rarely (IL Emit or pure IL with chars not allowed in C# or F#) - var sb = new ValueStringBuilder(stackalloc char[64]); - sb.EnsureCapacity(name.Length); - sb.Append(name.AsSpan(0, indexOfEscapeCharacter)); - - for (int i = indexOfEscapeCharacter; i < name.Length;) - { - char c = name[i++]; - - if (c != EscapeCharacter) - { - sb.Append(c); - } - else if (i < name.Length && name[i] == EscapeCharacter) // escaped escape character ;) - { - sb.Append(c); - // Consume the escaped escape character, it's important for edge cases - // like escaped escape character followed by another escaped char (example: "\\\\\\+") - i++; - } - } - - return sb.ToString(); - } - } - } -} diff --git a/src/tools/illink/src/ILLink.RoslynAnalyzer/TrimAnalysis/TypeNameResolver.cs b/src/tools/illink/src/ILLink.RoslynAnalyzer/TrimAnalysis/TypeNameResolver.cs index a3d29b64661ee1..77400e898a5529 100644 --- a/src/tools/illink/src/ILLink.RoslynAnalyzer/TrimAnalysis/TypeNameResolver.cs +++ b/src/tools/illink/src/ILLink.RoslynAnalyzer/TrimAnalysis/TypeNameResolver.cs @@ -113,7 +113,7 @@ static bool IsFullyQualified(TypeName typeName) private static ITypeSymbol? GetSimpleTypeFromModule(TypeName typeName, IAssemblySymbol module) { - string fullName = TypeNameHelpers.Unescape(typeName.FullName); + string fullName = TypeName.Unescape(typeName.FullName); return module.GetTypeByMetadataName(fullName); } From 027dc0caf1f0779821678d75a180942fe0e9c32f Mon Sep 17 00:00:00 2001 From: Sven Boemer Date: Mon, 4 Aug 2025 10:28:14 -0700 Subject: [PATCH 6/6] Fix source build --- .../src/ILLink.RoslynAnalyzer/ILLink.RoslynAnalyzer.csproj | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/tools/illink/src/ILLink.RoslynAnalyzer/ILLink.RoslynAnalyzer.csproj b/src/tools/illink/src/ILLink.RoslynAnalyzer/ILLink.RoslynAnalyzer.csproj index f02bd4222231ca..49b09a3c07510b 100644 --- a/src/tools/illink/src/ILLink.RoslynAnalyzer/ILLink.RoslynAnalyzer.csproj +++ b/src/tools/illink/src/ILLink.RoslynAnalyzer/ILLink.RoslynAnalyzer.csproj @@ -8,6 +8,9 @@ false Latest $(NoWarn);CS8524 + + $(NoWarn);CS0436 cs