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
62 changes: 62 additions & 0 deletions docs/error-codes.md
Original file line number Diff line number Diff line change
Expand Up @@ -1753,6 +1753,68 @@ void TestMethod()
}
```

#### `IL2112` Trim analysis: 'DynamicallyAccessedMembersAttribute' on 'type' or one of its base types references 'member' which requires unreferenced code. [message]. [url]

- A type is annotated with `DynamicallyAccessedMembersAttribute` indicating that the type may dynamically access some members declared on the type or its derived types. This instructs the trimmer to keep the specified members, but one of them is annotated with `RequiresUnreferencedCodeAttribute` which can break functionality when trimming. The `DynamicallyAccessedMembersAttribute` annotation may be directly on the type, or implied by an annotation on one of its base or interface types. This warning originates from the member with `RequiresUnreferencedCodeAttribute`.

```C#
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods)]
public class AnnotatedType {
// Trim analysis warning IL2112: AnnotatedType.Method(): 'DynamicallyAccessedMembersAttribute' on 'AnnotatedType' or one of its
// base types references 'AnnotatedType.Method()' which requires unreferenced code. Using this member is trim unsafe.
[RequiresUnreferencedCode("Using this member is trim unsafe")]
public static void Method() { }
}
```

#### `IL2113` Trim analysis: 'DynamicallyAccessedMembersAttribute' on 'type' or one of its base types references 'member' which requires unreferenced code. [message]. [url]

- A type is annotated with `DynamicallyAccessedMembersAttribute` indicating that the type may dynamically access some members declared on the type or its derived types. This instructs the trimmer to keep the specified members, but a member of one of the base or interface types is annotated with `RequiresUnreferencedCodeAttribute` which can break functionality when trimming. The `DynamicallyAccessedMembersAttribute` annotation may be directly on the type, or implied by an annotation on one of its base or interface types. This warning originates from the type which has `DynamicallyAccessedMembersAttribute` requirements.

```C#
public class BaseType {
[RequiresUnreferencedCode("Using this member is trim unsafe")]
public static void Method() { }
}

// Trim analysis warning IL2113: AnnotatedType: 'DynamicallyAccessedMembersAttribute' on 'AnnotatedType' or one of its
// base types references 'BaseType.Method()' which requires unreferenced code. Using this member is trim unsafe.
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods)]
public class AnnotatedType : BaseType {
}
```

#### `IL2114 ` Trim analysis: 'DynamicallyAccessedMembersAttribute' on 'type' or one of its base types references 'member' which has 'DynamicallyAccessedMembersAttribute' requirements.

- A type is annotated with `DynamicallyAccessedMembersAttribute` indicating that the type may dynamically access some members declared on the type or its derived types. This instructs the trimmer to keep the specified members, but one of them is annotated with `DynamicallyAccessedMembersAttribute` which can not be statically verified. The `DynamicallyAccessedMembersAttribute` annotation may be directly on the type, or implied by an annotation on one of its base or interface types. This warning originates from the member with `DynamicallyAccessedMembersAttribute` requirements.

```C#
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicFields)]
public class AnnotatedType {
// Trim analysis warning IL2114: System.Type AnnotatedType::Field: 'DynamicallyAccessedMembersAttribute' on 'AnnotatedType' or one of its
// base types references 'System.Type AnnotatedType::Field' which has 'DynamicallyAccessedMembersAttribute' requirements .
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)]
public static Type Field;
}
```

#### `IL2115 ` Trim analysis: 'DynamicallyAccessedMembersAttribute' on 'type' or one of its base types references 'member' which has 'DynamicallyAccessedMembersAttribute' requirements.

- A type is annotated with `DynamicallyAccessedMembersAttribute` indicating that the type may dynamically access some members declared on the type or its derived types. This instructs the trimmer to keep the specified members, but a member of one of the base or interface types is annotated with `DynamicallyAccessedMembersAttribute` which can not be statically verified. The `DynamicallyAccessedMembersAttribute` annotation may be directly on the type, or implied by an annotation on one of its base or interface types. This warning originates from the type which has `DynamicallyAccessedMembersAttribute` requirements.

```C#
public class BaseType {
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)]
public static Type Field;
}

// Trim analysis warning IL2115: AnnotatedType: 'DynamicallyAccessedMembersAttribute' on 'AnnotatedType' or one of its
// base types references 'System.Type BaseType::Field' which has 'DynamicallyAccessedMembersAttribute' requirements .
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicFields)]
public class AnnotatedType : BaseType {
}
```

## Single-File Warning Codes

#### `IL3000`: 'member' always returns an empty string for assemblies embedded in a single-file app. If the path to the app directory is needed, consider calling 'System.AppContext.BaseDirectory'
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Licensed to the .NET Foundation under one or more agreements.
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

Expand Down Expand Up @@ -118,7 +118,8 @@ public DynamicallyAccessedMembersTypeHierarchy (LinkContext context, MarkStep ma
// so we need to apply the annotation to this type as well
using var _ = _scopeStack.PushScope (new MessageOrigin (type));
var reflectionMethodBodyScanner = new ReflectionMethodBodyScanner (_context, _markStep, _scopeStack);
var reflectionPatternContext = new ReflectionPatternContext (_context, true, _scopeStack.CurrentScope.Origin, type);
// Set up a context to report warnings on access to annotated members, with the annotated type as the origin.
var reflectionPatternContext = new ReflectionPatternContext (_context, reportingEnabled: true, _scopeStack.CurrentScope.Origin, type);
reflectionMethodBodyScanner.ApplyDynamicallyAccessedMembersToType (ref reflectionPatternContext, type, annotation);
reflectionPatternContext.Dispose ();
}
Expand All @@ -128,7 +129,6 @@ public DynamicallyAccessedMembersTypeHierarchy (LinkContext context, MarkStep ma

public DynamicallyAccessedMemberTypes ApplyDynamicallyAccessedMembersToTypeHierarchy (
ReflectionMethodBodyScanner reflectionMethodBodyScanner,
ref ReflectionPatternContext reflectionPatternContext,
TypeDefinition type)
{
Debug.Assert (_context.Annotations.IsMarked (type));
Expand All @@ -142,6 +142,9 @@ public DynamicallyAccessedMemberTypes ApplyDynamicallyAccessedMembersToTypeHiera
return annotation;

// Apply the effective annotation for the type
using var _ = _scopeStack.PushScope (new MessageOrigin (type));
// Set up a context to report warnings on access to annotated members, with the annotated type as the origin.
var reflectionPatternContext = new ReflectionPatternContext (_context, reportingEnabled: true, _scopeStack.CurrentScope.Origin, type);
reflectionMethodBodyScanner.ApplyDynamicallyAccessedMembersToType (ref reflectionPatternContext, type, annotation);

// Mark it as applied in the cache
Expand Down Expand Up @@ -174,7 +177,7 @@ public DynamicallyAccessedMemberTypes ApplyDynamicallyAccessedMembersToTypeHiera
break;

foreach (var candidateType in candidateTypes) {
ApplyDynamicallyAccessedMembersToTypeHierarchyInner (reflectionMethodBodyScanner, ref reflectionPatternContext, candidateType);
ApplyDynamicallyAccessedMembersToTypeHierarchyInner (reflectionMethodBodyScanner, candidateType);
}
}

Expand All @@ -183,7 +186,6 @@ public DynamicallyAccessedMemberTypes ApplyDynamicallyAccessedMembersToTypeHiera

bool ApplyDynamicallyAccessedMembersToTypeHierarchyInner (
ReflectionMethodBodyScanner reflectionMethodBodyScanner,
ref ReflectionPatternContext reflectionPatternContext,
TypeDefinition type)
{
(var annotation, var applied) = GetCachedInfoForTypeInHierarchy (type);
Expand All @@ -196,13 +198,13 @@ bool ApplyDynamicallyAccessedMembersToTypeHierarchyInner (

TypeDefinition baseType = type.BaseType?.Resolve ();
if (baseType != null)
applied = ApplyDynamicallyAccessedMembersToTypeHierarchyInner (reflectionMethodBodyScanner, ref reflectionPatternContext, baseType);
applied = ApplyDynamicallyAccessedMembersToTypeHierarchyInner (reflectionMethodBodyScanner, baseType);

if (!applied && type.HasInterfaces) {
foreach (InterfaceImplementation iface in type.Interfaces) {
var interfaceType = iface.InterfaceType.Resolve ();
if (interfaceType != null) {
if (ApplyDynamicallyAccessedMembersToTypeHierarchyInner (reflectionMethodBodyScanner, ref reflectionPatternContext, interfaceType)) {
if (ApplyDynamicallyAccessedMembersToTypeHierarchyInner (reflectionMethodBodyScanner, interfaceType)) {
applied = true;
break;
}
Expand All @@ -211,6 +213,9 @@ bool ApplyDynamicallyAccessedMembersToTypeHierarchyInner (
}

if (applied) {
using var _ = _scopeStack.PushScope (new MessageOrigin (type));
// Set up a context to report warnings on access to annotated members, with the annotated type as the origin.
var reflectionPatternContext = new ReflectionPatternContext (_context, reportingEnabled: true, _scopeStack.CurrentScope.Origin, type);
reflectionMethodBodyScanner.ApplyDynamicallyAccessedMembersToType (ref reflectionPatternContext, type, annotation);
_typesInDynamicallyAccessedMembersHierarchy[type] = (annotation, true);
}
Expand Down
63 changes: 56 additions & 7 deletions src/linker/Linker.Dataflow/FlowAnnotations.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Licensed to the .NET Foundation under one or more agreements.
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

Expand Down Expand Up @@ -65,6 +65,13 @@ public DynamicallyAccessedMemberTypes GetFieldAnnotation (FieldDefinition field)
public DynamicallyAccessedMemberTypes GetTypeAnnotation (TypeDefinition type) =>
GetAnnotations (type).TypeAnnotation;

public bool ShouldWarnWhenAccessedForReflection (IMemberDefinition provider) =>
provider switch {
MethodDefinition method => ShouldWarnWhenAccessedForReflection (method),
FieldDefinition field => ShouldWarnWhenAccessedForReflection (field),
_ => false
};

public DynamicallyAccessedMemberTypes GetGenericParameterAnnotation (GenericParameter genericParameter)
{
TypeDefinition declaringType = _context.Resolve (genericParameter.DeclaringType);
Expand All @@ -83,13 +90,55 @@ public DynamicallyAccessedMemberTypes GetGenericParameterAnnotation (GenericPara
return DynamicallyAccessedMemberTypes.None;
}

public bool ShouldWarnWhenAccessedForReflection (MethodDefinition method) =>
GetAnnotations (method.DeclaringType).TryGetAnnotation (method, out var annotation) &&
(annotation.ParameterAnnotations != null || annotation.ReturnParameterAnnotation != DynamicallyAccessedMemberTypes.None);
public bool ShouldWarnWhenAccessedForReflection (MethodDefinition method)
{
if (!GetAnnotations (method.DeclaringType).TryGetAnnotation (method, out var annotation))
return false;

if (annotation.ParameterAnnotations == null && annotation.ReturnParameterAnnotation == DynamicallyAccessedMemberTypes.None)
return false;

public bool MethodHasNoAnnotatedParameters (MethodDefinition method) =>
GetAnnotations (method.DeclaringType).TryGetAnnotation (method, out var annotation) &&
annotation.ParameterAnnotations == null;
// If the method only has annotation on the return value and it's not virtual avoid warning.
// Return value annotations are "consumed" by the caller of a method, and as such there is nothing
// wrong calling these dynamically. The only problem can happen if something overrides a virtual
// method with annotated return value at runtime - in this case the trimmer can't validate
// that the method will return only types which fulfill the annotation's requirements.
// For example:
// class BaseWithAnnotation
// {
// [return: DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicFields)]
// public abstract Type GetTypeWithFields();
// }
//
// class UsingTheBase
// {
// public void PrintFields(Base base)
// {
// // No warning here - GetTypeWithFields is correctly annotated to allow GetFields on the return value.
// Console.WriteLine(string.Join(" ", base.GetTypeWithFields().GetFields().Select(f => f.Name)));
// }
// }
//
// If at runtime (through ref emit) something generates code like this:
// class DerivedAtRuntimeFromBase
// {
// // No point in adding annotation on the return value - nothing will look at it anyway
// // Linker will not see this code, so there are no checks
// public override Type GetTypeWithFields() { return typeof(TestType); }
// }
//
// If TestType from above is trimmed, it may note have all its fields, and there would be no warnings generated.
// But there has to be code like this somewhere in the app, in order to generate the override:
// class RuntimeTypeGenerator
// {
// public MethodInfo GetBaseMethod()
// {
// // This must warn - that the GetTypeWithFields has annotation on the return value
// return typeof(BaseWithAnnotation).GetMethod("GetTypeWithFields");
// }
// }
return method.IsVirtual || annotation.ParameterAnnotations != null;
}

public bool ShouldWarnWhenAccessedForReflection (FieldDefinition field) =>
GetAnnotations (field.DeclaringType).TryGetAnnotation (field, out _);
Expand Down
24 changes: 12 additions & 12 deletions src/linker/Linker.Dataflow/ReflectionMethodBodyScanner.cs
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ public void ApplyDynamicallyAccessedMembersToType (ref ReflectionPatternContext
// Handle cases where a type has no members but annotations are to be applied to derived type members
reflectionPatternContext.RecordHandledPattern ();

MarkTypeForDynamicallyAccessedMembers (ref reflectionPatternContext, type, annotation);
MarkTypeForDynamicallyAccessedMembers (ref reflectionPatternContext, type, annotation, DependencyKind.DynamicallyAccessedMemberOnType);
}

ValueNode GetValueNodeForCustomAttributeArgument (CustomAttributeArgument argument)
Expand Down Expand Up @@ -1032,7 +1032,7 @@ public override bool HandleCall (MethodBody callingMethodBody, MethodReference c
MarkType (ref reflectionContext, staticType);

var annotation = _markStep.DynamicallyAccessedMembersTypeHierarchy
.ApplyDynamicallyAccessedMembersToTypeHierarchy (this, ref reflectionContext, staticType);
.ApplyDynamicallyAccessedMembersToTypeHierarchy (this, staticType);

reflectionContext.RecordHandledPattern ();

Expand Down Expand Up @@ -2230,7 +2230,7 @@ void RequireDynamicallyAccessedMembers (ref ReflectionPatternContext reflectionC
reflectionContext.RecordHandledPattern ();
}
} else if (uniqueValue is SystemTypeValue systemTypeValue) {
MarkTypeForDynamicallyAccessedMembers (ref reflectionContext, systemTypeValue.TypeRepresented, requiredMemberTypes);
MarkTypeForDynamicallyAccessedMembers (ref reflectionContext, systemTypeValue.TypeRepresented, requiredMemberTypes, DependencyKind.DynamicallyAccessedMember);
} else if (uniqueValue is KnownStringValue knownStringValue) {
TypeReference typeRef = _context.TypeNameResolver.ResolveTypeName (knownStringValue.Contents, reflectionContext.Source, out AssemblyDefinition typeAssembly);
TypeDefinition foundType = ResolveToTypeDefinition (typeRef);
Expand All @@ -2239,7 +2239,7 @@ void RequireDynamicallyAccessedMembers (ref ReflectionPatternContext reflectionC
reflectionContext.RecordHandledPattern ();
} else {
MarkType (ref reflectionContext, typeRef);
MarkTypeForDynamicallyAccessedMembers (ref reflectionContext, foundType, requiredMemberTypes);
MarkTypeForDynamicallyAccessedMembers (ref reflectionContext, foundType, requiredMemberTypes, DependencyKind.DynamicallyAccessedMember);
_context.MarkingHelpers.MarkMatchingExportedType (foundType, typeAssembly, new DependencyInfo (DependencyKind.DynamicallyAccessedMember, foundType));
}
} else if (uniqueValue == NullValue.Instance) {
Expand Down Expand Up @@ -2286,31 +2286,31 @@ void RequireDynamicallyAccessedMembers (ref ReflectionPatternContext reflectionC

static bool HasBindingFlag (BindingFlags? bindingFlags, BindingFlags? search) => bindingFlags != null && (bindingFlags & search) == search;

void MarkTypeForDynamicallyAccessedMembers (ref ReflectionPatternContext reflectionContext, TypeDefinition typeDefinition, DynamicallyAccessedMemberTypes requiredMemberTypes)
void MarkTypeForDynamicallyAccessedMembers (ref ReflectionPatternContext reflectionContext, TypeDefinition typeDefinition, DynamicallyAccessedMemberTypes requiredMemberTypes, DependencyKind dependencyKind)
{
foreach (var member in typeDefinition.GetDynamicallyAccessedMembers (_context, requiredMemberTypes)) {
switch (member) {
case MethodDefinition method:
MarkMethod (ref reflectionContext, method, DependencyKind.DynamicallyAccessedMember);
MarkMethod (ref reflectionContext, method, dependencyKind);
break;
case FieldDefinition field:
MarkField (ref reflectionContext, field, DependencyKind.DynamicallyAccessedMember);
MarkField (ref reflectionContext, field, dependencyKind);
break;
case TypeDefinition nestedType:
DependencyInfo nestedDependencyInfo = new DependencyInfo (DependencyKind.DynamicallyAccessedMember, reflectionContext.Source);
DependencyInfo nestedDependencyInfo = new DependencyInfo (dependencyKind, reflectionContext.Source);
reflectionContext.RecordRecognizedPattern (nestedType, () => _markStep.MarkEntireType (nestedType, includeBaseAndInterfaceTypes: true, nestedDependencyInfo));
break;
case PropertyDefinition property:
MarkProperty (ref reflectionContext, property, DependencyKind.DynamicallyAccessedMember);
MarkProperty (ref reflectionContext, property, dependencyKind);
break;
case EventDefinition @event:
MarkEvent (ref reflectionContext, @event, DependencyKind.DynamicallyAccessedMember);
MarkEvent (ref reflectionContext, @event, dependencyKind);
break;
case InterfaceImplementation interfaceImplementation:
MarkInterfaceImplementation (ref reflectionContext, interfaceImplementation, DependencyKind.DynamicallyAccessedMember);
MarkInterfaceImplementation (ref reflectionContext, interfaceImplementation, dependencyKind);
break;
case null:
DependencyInfo dependencyInfo = new DependencyInfo (DependencyKind.DynamicallyAccessedMember, reflectionContext.Source);
DependencyInfo dependencyInfo = new DependencyInfo (dependencyKind, reflectionContext.Source);
reflectionContext.RecordRecognizedPattern (typeDefinition, () => _markStep.MarkEntireType (typeDefinition, includeBaseAndInterfaceTypes: true, dependencyInfo));
break;
}
Expand Down
Loading