Skip to content
Closed
95 changes: 94 additions & 1 deletion docs/error-codes.md
Original file line number Diff line number Diff line change
Expand Up @@ -1689,7 +1689,7 @@ The only scopes supported on global unconditional suppressions are 'module', 'ty
it is assumed that the suppression is put on the module. Global unconditional suppressions using invalid scopes are ignored.

```C#
// Invalid scope 'method' used in 'UnconditionalSuppressMessageAttribute' on module 'Warning' with target 'MyTarget'.
// IL2108: Invalid scope 'method' used in 'UnconditionalSuppressMessageAttribute' on module 'Warning' with target 'MyTarget'.
[module: UnconditionalSuppressMessage ("Test suppression with invalid scope", "IL2026", Scope = "method", Target = "MyTarget")]

class Warning
Expand Down Expand Up @@ -1722,6 +1722,99 @@ class Warning
class Derived : UnsafeClass {}
```

#### `IL2110`: Trim analysis: Field 'field' with 'DynamicallyAccessedMembersAttribute' is accessed via reflection. Trimmer can't guarantee availability of the requirements of the field.

- Trimmer currently can't guarantee that all requirements of the `DynamicallyAccessedMembersAttribute` are fulfilled if the field is accessed via reflection.

```C#
[DynamicallyAccessedMembers(DynamicallyAccessedMemeberTypes.PublicMethods)]
Type _field;

void TestMethod()
{
// IL2110: Field '_field' with 'DynamicallyAccessedMembersAttribute' is accessed via reflection. Trimmer can't guarantee availability of the requirements of the field.
typeof(Test).GetField("_field");
}
```

#### `IL2111`: Trim analysis: Method 'method' with parameters or return value with `DynamicallyAccessedMembersAttribute` is accessed via reflection. Trimmer can't guarantee availability of the requirements of the field.

- Trimmer currently can't guarantee that all requirements of the `DynamicallyAccessedMembersAttribute` are fulfilled if the method is accessed via reflection.

```C#
void MethodWithRequirements([DynamicallyAccessedMembers(DynamicallyAccessedMemeberTypes.PublicMethods)] Type type)
{
}

void TestMethod()
{
// IL2111: Method 'MethodWithRequirements' with parameters or return value with `DynamicallyAccessedMembersAttribute` is accessed via reflection. Trimmer can't guarantee availability of the requirements of the field.
typeof(Test).GetMethod("MethodWithRequirements");
}
```

#### `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
96 changes: 73 additions & 23 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 All @@ -23,20 +23,14 @@ public FlowAnnotations (LinkContext context)
_hierarchyInfo = new TypeHierarchyCache (context);
}

public bool RequiresDataFlowAnalysis (MethodDefinition method)
{
return GetAnnotations (method.DeclaringType).TryGetAnnotation (method, out _);
}
public bool RequiresDataFlowAnalysis (MethodDefinition method) =>
GetAnnotations (method.DeclaringType).TryGetAnnotation (method, out _);

public bool RequiresDataFlowAnalysis (FieldDefinition field)
{
return GetAnnotations (field.DeclaringType).TryGetAnnotation (field, out _);
}
public bool RequiresDataFlowAnalysis (FieldDefinition field) =>
GetAnnotations (field.DeclaringType).TryGetAnnotation (field, out _);

public bool RequiresDataFlowAnalysis (GenericParameter genericParameter)
{
return GetGenericParameterAnnotation (genericParameter) != DynamicallyAccessedMemberTypes.None;
}
public bool RequiresDataFlowAnalysis (GenericParameter genericParameter) =>
GetGenericParameterAnnotation (genericParameter) != DynamicallyAccessedMemberTypes.None;

/// <summary>
/// Retrieves the annotations for the given parameter.
Expand All @@ -45,35 +39,38 @@ public bool RequiresDataFlowAnalysis (GenericParameter genericParameter)
/// <returns></returns>
public DynamicallyAccessedMemberTypes GetParameterAnnotation (MethodDefinition method, int parameterIndex)
{
if (GetAnnotations (method.DeclaringType).TryGetAnnotation (method, out var annotation) && annotation.ParameterAnnotations != null) {
if (GetAnnotations (method.DeclaringType).TryGetAnnotation (method, out var annotation) &&
annotation.ParameterAnnotations != null)
return annotation.ParameterAnnotations[parameterIndex];
}

return DynamicallyAccessedMemberTypes.None;
}

public DynamicallyAccessedMemberTypes GetReturnParameterAnnotation (MethodDefinition method)
{
if (GetAnnotations (method.DeclaringType).TryGetAnnotation (method, out var annotation)) {
if (GetAnnotations (method.DeclaringType).TryGetAnnotation (method, out var annotation))
return annotation.ReturnParameterAnnotation;
}

return DynamicallyAccessedMemberTypes.None;
}

public DynamicallyAccessedMemberTypes GetFieldAnnotation (FieldDefinition field)
{
if (GetAnnotations (field.DeclaringType).TryGetAnnotation (field, out var annotation)) {
if (GetAnnotations (field.DeclaringType).TryGetAnnotation (field, out var annotation))
return annotation.Annotation;
}

return DynamicallyAccessedMemberTypes.None;
}

public DynamicallyAccessedMemberTypes GetTypeAnnotation (TypeDefinition type)
{
return GetAnnotations (type).TypeAnnotation;
}
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)
{
Expand All @@ -93,6 +90,59 @@ public DynamicallyAccessedMemberTypes GetGenericParameterAnnotation (GenericPara
return 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;

// 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 _);

TypeAnnotations GetAnnotations (TypeDefinition type)
{
if (!_annotations.TryGetValue (type, out TypeAnnotations value)) {
Expand Down
Loading