diff --git a/src/ILLink.RoslynAnalyzer/TrimAnalysis/HandleCallAction.cs b/src/ILLink.RoslynAnalyzer/TrimAnalysis/HandleCallAction.cs index 6e743f9b71b5..f05a5d3e1bf1 100644 --- a/src/ILLink.RoslynAnalyzer/TrimAnalysis/HandleCallAction.cs +++ b/src/ILLink.RoslynAnalyzer/TrimAnalysis/HandleCallAction.cs @@ -62,6 +62,17 @@ private partial IEnumerable GetNestedTypesOnType (TypeProxy typ yield return new SystemTypeValue (new TypeProxy (nestedType)); } + private partial bool TryGetBaseType (TypeProxy type, out TypeProxy? baseType) + { + if (type.Type.BaseType is not null) { + baseType = new TypeProxy (type.Type.BaseType); + return true; + } + + baseType = null; + return false; + } + // TODO: Does the analyzer need to do something here? private partial void MarkStaticConstructor (TypeProxy type) { } diff --git a/src/ILLink.Shared/TrimAnalysis/HandleCallAction.cs b/src/ILLink.Shared/TrimAnalysis/HandleCallAction.cs index 002c213b9034..c20e3b6f0c7a 100644 --- a/src/ILLink.Shared/TrimAnalysis/HandleCallAction.cs +++ b/src/ILLink.Shared/TrimAnalysis/HandleCallAction.cs @@ -540,6 +540,55 @@ public bool Invoke (MethodProxy calledMethod, MultiValue instanceValue, IReadOnl } break; + // + // Type.BaseType + // + case IntrinsicId.Type_get_BaseType: { + foreach (var value in instanceValue) { + if (value is ValueWithDynamicallyAccessedMembers valueWithDynamicallyAccessedMembers) { + DynamicallyAccessedMemberTypes propagatedMemberTypes = DynamicallyAccessedMemberTypes.None; + if (valueWithDynamicallyAccessedMembers.DynamicallyAccessedMemberTypes == DynamicallyAccessedMemberTypes.All) + propagatedMemberTypes = DynamicallyAccessedMemberTypes.All; + else { + // PublicConstructors are not propagated to base type + + if (valueWithDynamicallyAccessedMembers.DynamicallyAccessedMemberTypes.HasFlag (DynamicallyAccessedMemberTypes.PublicEvents)) + propagatedMemberTypes |= DynamicallyAccessedMemberTypes.PublicEvents; + + if (valueWithDynamicallyAccessedMembers.DynamicallyAccessedMemberTypes.HasFlag (DynamicallyAccessedMemberTypes.PublicFields)) + propagatedMemberTypes |= DynamicallyAccessedMemberTypes.PublicFields; + + if (valueWithDynamicallyAccessedMembers.DynamicallyAccessedMemberTypes.HasFlag (DynamicallyAccessedMemberTypes.PublicMethods)) + propagatedMemberTypes |= DynamicallyAccessedMemberTypes.PublicMethods; + + // PublicNestedTypes are not propagated to base type + + // PublicParameterlessConstructor is not propagated to base type + + if (valueWithDynamicallyAccessedMembers.DynamicallyAccessedMemberTypes.HasFlag (DynamicallyAccessedMemberTypes.PublicProperties)) + propagatedMemberTypes |= DynamicallyAccessedMemberTypes.PublicProperties; + + if (valueWithDynamicallyAccessedMembers.DynamicallyAccessedMemberTypes.HasFlag (DynamicallyAccessedMemberTypes.Interfaces)) + propagatedMemberTypes |= DynamicallyAccessedMemberTypes.Interfaces; + } + + AddReturnValue (GetMethodReturnValue (calledMethod, propagatedMemberTypes)); + } else if (value is SystemTypeValue systemTypeValue) { + if (TryGetBaseType (systemTypeValue.RepresentedType, out var baseType)) + AddReturnValue (new SystemTypeValue (baseType.Value)); + else + AddReturnValue (GetMethodReturnValue (calledMethod, returnValueDynamicallyAccessedMemberTypes)); + } else if (value == NullValue.Instance) { + // Ignore nulls - null.BaseType will fail at runtime, but it has no effect on static analysis + continue; + } else { + // Unknown input - propagate a return value without any annotation - we know it's a Type but we know nothing about it + AddReturnValue (GetMethodReturnValue (calledMethod, returnValueDynamicallyAccessedMemberTypes)); + } + } + } + break; + case IntrinsicId.None: methodReturnValue = MultiValueLattice.Top; return false; @@ -686,6 +735,8 @@ internal static DynamicallyAccessedMemberTypes GetDynamicallyAccessedMemberTypes private partial IEnumerable GetNestedTypesOnType (TypeProxy type, string name, BindingFlags? bindingFlags); + private partial bool TryGetBaseType (TypeProxy type, [NotNullWhen (true)] out TypeProxy? baseType); + private partial void MarkStaticConstructor (TypeProxy type); private partial void MarkEventsOnTypeHierarchy (TypeProxy type, string name, BindingFlags? bindingFlags); diff --git a/src/linker/Linker.Dataflow/HandleCallAction.cs b/src/linker/Linker.Dataflow/HandleCallAction.cs index de8dcf8f2418..00371df0705a 100644 --- a/src/linker/Linker.Dataflow/HandleCallAction.cs +++ b/src/linker/Linker.Dataflow/HandleCallAction.cs @@ -68,6 +68,17 @@ private partial IEnumerable GetNestedTypesOnType (TypeProxy typ yield return new SystemTypeValue (new TypeProxy (nestedType)); } + private partial bool TryGetBaseType (TypeProxy type, out TypeProxy? baseType) + { + if (type.Type.BaseType is TypeReference baseTypeRef && _context.TryResolve (baseTypeRef) is TypeDefinition baseTypeDefinition) { + baseType = new TypeProxy (baseTypeDefinition); + return true; + } + + baseType = null; + return false; + } + private partial void MarkStaticConstructor (TypeProxy type) => _reflectionMethodBodyScanner.MarkStaticConstructor (_analysisContext, type.Type); diff --git a/src/linker/Linker.Dataflow/ReflectionMethodBodyScanner.cs b/src/linker/Linker.Dataflow/ReflectionMethodBodyScanner.cs index 61325ae6be76..8531974cc702 100644 --- a/src/linker/Linker.Dataflow/ReflectionMethodBodyScanner.cs +++ b/src/linker/Linker.Dataflow/ReflectionMethodBodyScanner.cs @@ -289,7 +289,8 @@ public override bool HandleCall (MethodBody callingMethodBody, MethodReference c case IntrinsicId.Type_GetMethod: case IntrinsicId.Type_GetNestedType: case IntrinsicId.Expression_Property when calledMethod.HasParameterOfType (1, "System.Reflection.MethodInfo"): - case var fieldOrPropertyInstrinsic when fieldOrPropertyInstrinsic == IntrinsicId.Expression_Field || fieldOrPropertyInstrinsic == IntrinsicId.Expression_Property: { + case var fieldOrPropertyInstrinsic when fieldOrPropertyInstrinsic == IntrinsicId.Expression_Field || fieldOrPropertyInstrinsic == IntrinsicId.Expression_Property: + case IntrinsicId.Type_get_BaseType: { var instanceValue = MultiValueLattice.Top; IReadOnlyList parameterValues = methodParams; if (calledMethodDefinition.HasImplicitThis ()) { @@ -576,55 +577,6 @@ public override bool HandleCall (MethodBody callingMethodBody, MethodReference c } break; - // - // Type.BaseType - // - case IntrinsicId.Type_get_BaseType: { - foreach (var value in methodParams[0]) { - if (value is ValueWithDynamicallyAccessedMembers valueWithDynamicallyAccessedMembers) { - DynamicallyAccessedMemberTypes propagatedMemberTypes = DynamicallyAccessedMemberTypes.None; - if (valueWithDynamicallyAccessedMembers.DynamicallyAccessedMemberTypes == DynamicallyAccessedMemberTypes.All) - propagatedMemberTypes = DynamicallyAccessedMemberTypes.All; - else { - // PublicConstructors are not propagated to base type - - if (valueWithDynamicallyAccessedMembers.DynamicallyAccessedMemberTypes.HasFlag (DynamicallyAccessedMemberTypes.PublicEvents)) - propagatedMemberTypes |= DynamicallyAccessedMemberTypes.PublicEvents; - - if (valueWithDynamicallyAccessedMembers.DynamicallyAccessedMemberTypes.HasFlag (DynamicallyAccessedMemberTypes.PublicFields)) - propagatedMemberTypes |= DynamicallyAccessedMemberTypes.PublicFields; - - if (valueWithDynamicallyAccessedMembers.DynamicallyAccessedMemberTypes.HasFlag (DynamicallyAccessedMemberTypes.PublicMethods)) - propagatedMemberTypes |= DynamicallyAccessedMemberTypes.PublicMethods; - - // PublicNestedTypes are not propagated to base type - - // PublicParameterlessConstructor is not propagated to base type - - if (valueWithDynamicallyAccessedMembers.DynamicallyAccessedMemberTypes.HasFlag (DynamicallyAccessedMemberTypes.PublicProperties)) - propagatedMemberTypes |= DynamicallyAccessedMemberTypes.PublicProperties; - - if (valueWithDynamicallyAccessedMembers.DynamicallyAccessedMemberTypes.HasFlag (DynamicallyAccessedMemberTypes.Interfaces)) - propagatedMemberTypes |= DynamicallyAccessedMemberTypes.Interfaces; - } - - methodReturnValue = MultiValueLattice.Meet (methodReturnValue, GetMethodReturnValue (calledMethodDefinition, propagatedMemberTypes)); - } else if (value is SystemTypeValue systemTypeValue) { - if (systemTypeValue.RepresentedType.Type.BaseType is TypeReference baseTypeRef && _context.TryResolve (baseTypeRef) is TypeDefinition baseTypeDefinition) - methodReturnValue = MultiValueLattice.Meet (methodReturnValue, new SystemTypeValue (baseTypeDefinition)); - else - methodReturnValue = MultiValueLattice.Meet (methodReturnValue, GetMethodReturnValue (calledMethodDefinition)); - } else if (value == NullValue.Instance) { - // Ignore nulls - null.BaseType will fail at runtime, but it has no effect on static analysis - continue; - } else { - // Unknown input - propagate a return value without any annotation - we know it's a Type but we know nothing about it - methodReturnValue = MultiValueLattice.Meet (methodReturnValue, GetMethodReturnValue (calledMethodDefinition)); - } - } - } - break; - // // System.Activator // diff --git a/test/ILLink.RoslynAnalyzer.Tests/DataFlowTests.cs b/test/ILLink.RoslynAnalyzer.Tests/DataFlowTests.cs index 955d7ba04055..8ada1cff3be1 100644 --- a/test/ILLink.RoslynAnalyzer.Tests/DataFlowTests.cs +++ b/test/ILLink.RoslynAnalyzer.Tests/DataFlowTests.cs @@ -201,8 +201,7 @@ public Task SuppressWarningWithLinkAttributes () [Fact] public Task TypeBaseTypeDataFlow () { - // https://github.com/dotnet/linker/issues/2273 - return RunTest (allowMissingWarnings: true); + return RunTest (); } [Fact] diff --git a/test/ILLink.RoslynAnalyzer.Tests/ReflectionTests.cs b/test/ILLink.RoslynAnalyzer.Tests/ReflectionTests.cs index 0e12517ab9f3..395ec25c86ef 100644 --- a/test/ILLink.RoslynAnalyzer.Tests/ReflectionTests.cs +++ b/test/ILLink.RoslynAnalyzer.Tests/ReflectionTests.cs @@ -163,6 +163,12 @@ public Task RuntimeReflectionExtensionsCalls () return RunTest (); } + [Fact] + public Task TypeBaseTypeUseViaReflection () + { + return RunTest (); + } + [Fact] public Task TypeHierarchyReflectionWarnings () { diff --git a/test/ILLink.RoslynAnalyzer.Tests/generated/ILLink.RoslynAnalyzer.Tests.Generator/ILLink.RoslynAnalyzer.Tests.TestCaseGenerator/ReflectionTests.g.cs b/test/ILLink.RoslynAnalyzer.Tests/generated/ILLink.RoslynAnalyzer.Tests.Generator/ILLink.RoslynAnalyzer.Tests.TestCaseGenerator/ReflectionTests.g.cs index 6e1bf553d245..353ac8968134 100644 --- a/test/ILLink.RoslynAnalyzer.Tests/generated/ILLink.RoslynAnalyzer.Tests.Generator/ILLink.RoslynAnalyzer.Tests.TestCaseGenerator/ReflectionTests.g.cs +++ b/test/ILLink.RoslynAnalyzer.Tests/generated/ILLink.RoslynAnalyzer.Tests.Generator/ILLink.RoslynAnalyzer.Tests.TestCaseGenerator/ReflectionTests.g.cs @@ -61,12 +61,6 @@ public Task RunClassConstructorUsedViaReflection () return RunTest (allowMissingWarnings: true); } - [Fact] - public Task TypeBaseTypeUseViaReflection () - { - return RunTest (allowMissingWarnings: true); - } - [Fact] public Task TypeDelegator () { diff --git a/test/Mono.Linker.Tests.Cases/DataFlow/TypeBaseTypeDataFlow.cs b/test/Mono.Linker.Tests.Cases/DataFlow/TypeBaseTypeDataFlow.cs index f1cfccc3af58..646994d112e1 100644 --- a/test/Mono.Linker.Tests.Cases/DataFlow/TypeBaseTypeDataFlow.cs +++ b/test/Mono.Linker.Tests.Cases/DataFlow/TypeBaseTypeDataFlow.cs @@ -67,7 +67,11 @@ static void TestAllPropagated ([DynamicallyAccessedMembers (DynamicallyAccessedM derivedType.BaseType.RequiresAll (); } - [ExpectedWarning ("IL2062", nameof (TestAllPropagated))] + // This is a very special case - normally there's basically no way to "new up" a Type instance via the "new" operator + // so the analyzer doesn't handle this case at all - meaning it sees it as empty value. + // Unlike linker which sees an unknown value and thus warns that it doesn't fulfill the All annotation. + // It's OK for the analyzer to be more forgiving in this case, due to the very special circumstances. + [ExpectedWarning ("IL2062", nameof (TestAllPropagated), ProducedBy = ProducedBy.Trimmer)] public static void Test () { TestAllPropagated (new TestSystemTypeBase ());