Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions src/ILLink.RoslynAnalyzer/TrimAnalysis/HandleCallAction.cs
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,17 @@ private partial IEnumerable<SystemTypeValue> 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) { }

Expand Down
51 changes: 51 additions & 0 deletions src/ILLink.Shared/TrimAnalysis/HandleCallAction.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -686,6 +735,8 @@ internal static DynamicallyAccessedMemberTypes GetDynamicallyAccessedMemberTypes

private partial IEnumerable<SystemTypeValue> 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);
Expand Down
11 changes: 11 additions & 0 deletions src/linker/Linker.Dataflow/HandleCallAction.cs
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,17 @@ private partial IEnumerable<SystemTypeValue> 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);

Expand Down
52 changes: 2 additions & 50 deletions src/linker/Linker.Dataflow/ReflectionMethodBodyScanner.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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<MultiValue> parameterValues = methodParams;
if (calledMethodDefinition.HasImplicitThis ()) {
Expand Down Expand Up @@ -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
//
Expand Down
3 changes: 1 addition & 2 deletions test/ILLink.RoslynAnalyzer.Tests/DataFlowTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand Down
6 changes: 6 additions & 0 deletions test/ILLink.RoslynAnalyzer.Tests/ReflectionTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,12 @@ public Task RuntimeReflectionExtensionsCalls ()
return RunTest ();
}

[Fact]
public Task TypeBaseTypeUseViaReflection ()
{
return RunTest ();
}

[Fact]
public Task TypeHierarchyReflectionWarnings ()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,12 +61,6 @@ public Task RunClassConstructorUsedViaReflection ()
return RunTest (allowMissingWarnings: true);
}

[Fact]
public Task TypeBaseTypeUseViaReflection ()
{
return RunTest (allowMissingWarnings: true);
}

[Fact]
public Task TypeDelegator ()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 ());
Expand Down