Skip to content
Open
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
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ static ImmutableArray<DiagnosticDescriptor> GetSupportedDiagnostics ()
diagDescriptorsArrayBuilder.Add (DiagnosticDescriptors.GetDiagnosticDescriptor ((DiagnosticId) i));
}
diagDescriptorsArrayBuilder.Add (DiagnosticDescriptors.GetDiagnosticDescriptor ((DiagnosticId) 2110));
diagDescriptorsArrayBuilder.Add (DiagnosticDescriptors.GetDiagnosticDescriptor ((DiagnosticId) 2111));

return diagDescriptorsArrayBuilder.ToImmutable ();
}
Expand Down Expand Up @@ -173,6 +174,13 @@ SymbolKind.Field when member is IFieldSymbol privateFieldSymbol &&
memberTypes.HasFlag (DynamicallyAccessedMemberTypes.NonPublicFields) &&
privateFieldSymbol.GetDynamicallyAccessedMemberTypes () != DynamicallyAccessedMemberTypes.None
=> DiagnosticId.DynamicallyAccessedMembersUnsuportedReflectionAccessInField,

SymbolKind.Method when member is IMethodSymbol publicMethodSymbol &&
publicMethodSymbol.DeclaredAccessibility == Accessibility.Public &&
memberTypes.HasFlag (DynamicallyAccessedMemberTypes.PublicMethods) &&
publicMethodSymbol.GetDynamicallyAccessedMemberTypesOnParameters () != DynamicallyAccessedMemberTypes.None
=> DiagnosticId.DynamicallyAccessedMembersUnsuportedReflectionAccessInMethod,

_ => null,
};
return diagnosticId != null;
Expand Down
17 changes: 17 additions & 0 deletions src/ILLink.RoslynAnalyzer/ISymbolExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,23 @@ internal static DynamicallyAccessedMemberTypes GetDynamicallyAccessedMemberTypes
return (DynamicallyAccessedMemberTypes) dynamicallyAccessedMembers!.ConstructorArguments[0].Value!;
}

internal static DynamicallyAccessedMemberTypes GetDynamicallyAccessedMemberTypesOnParameters (this IMethodSymbol methodSymbol)
{

AttributeData? dynamicallyAccessedMembers = null;
foreach (var parameter in methodSymbol.Parameters) {
if (parameter.TryGetAttribute (DynamicallyAccessedMembersAnalyzer.DynamicallyAccessedMembersAttribute, out AttributeData? attribute)) {
dynamicallyAccessedMembers = attribute;
break;
}
}

if (dynamicallyAccessedMembers == null)
return DynamicallyAccessedMemberTypes.None;

return (DynamicallyAccessedMemberTypes) dynamicallyAccessedMembers.ConstructorArguments[0].Value!;
}

internal static DynamicallyAccessedMemberTypes GetDynamicallyAccessedMemberTypesOnReturnType (this IMethodSymbol methodSymbol)
{
AttributeData? dynamicallyAccessedMembers = null;
Expand Down
51 changes: 51 additions & 0 deletions src/ILLink.RoslynAnalyzer/ReflectionMethodBodyScanner.cs
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,23 @@ enum IntrinsicId
static IntrinsicId GetIntrinsicIdForMethod (IMethodSymbol calledMethod)
{
return calledMethod.Name switch {
// System.Type.GetMethod (string)
// System.Type.GetMethod (string, BindingFlags)
// System.Type.GetMethod (string, Type[])
// System.Type.GetMethod (string, Type[], ParameterModifier[])
// System.Type.GetMethod (string, BindingFlags, Type[])
// System.Type.GetMethod (string, BindingFlags, Binder, Type[], ParameterModifier[])
// System.Type.GetMethod (string, BindingFlags, Binder, CallingConventions, Type[], ParameterModifier[])
// System.Type.GetMethod (string, int, Type[])
// System.Type.GetMethod (string, int, Type[], ParameterModifier[]?)
// System.Type.GetMethod (string, int, BindingFlags, Binder?, Type[], ParameterModifier[]?)
// System.Type.GetMethod (string, int, BindingFlags, Binder?, CallingConventions, Type[], ParameterModifier[]?)
"GetMethod" when calledMethod.ContainingType.ContainingNamespace.GetDisplayName () == "System"
&& calledMethod.ContainingType.Name == "Type"
&& calledMethod.Parameters[0].Type.ContainingSymbol.ToDisplayString () == "System"
&& calledMethod.Parameters[0].Type.Name == "String"
=> IntrinsicId.Type_GetMethod,

// System.Type.GetField (string)
// System.Type.GetField (string, BindingFlags)
"GetField" when calledMethod.ContainingType.ContainingNamespace.GetDisplayName () == "System"
Expand All @@ -91,6 +108,40 @@ public static bool HandleCall (OperationAnalysisContext context, IInvocationOper
var calledMethod = invocationOperation.TargetMethod;
switch (GetIntrinsicIdForMethod (calledMethod)) {

//
// GetMethod (string)
// GetMethod (string, BindingFlags)
// GetMethod (string, Type[])
// GetMethod (string, Type[], ParameterModifier[])
// GetMethod (string, BindingFlags, Type[])
// GetMethod (string, BindingFlags, Binder, Type[], ParameterModifier[])
// GetMethod (string, BindingFlags, Binder, CallingConventions, Type[], ParameterModifier[])
// GetMethod (string, int, Type[])
// GetMethod (string, int, Type[], ParameterModifier[]?)
// GetMethod (string, int, BindingFlags, Binder?, Type[], ParameterModifier[]?)
// GetMethod (string, int, BindingFlags, Binder?, CallingConventions, Type[], ParameterModifier[]?)
//
case IntrinsicId.Type_GetMethod: {
BindingFlags? bindingFlags;
// @TODO - only handling the first 2 cases here
if (calledMethod.Parameters.Length > 1 && calledMethod.Parameters[1].Type.GetDisplayName () == "System.Reflection.BindingFlags")
bindingFlags = GetBindingFlagsFromValue (invocationOperation.Arguments[1]);
else
// Assume a default value for BindingFlags for methods that don't use BindingFlags as a parameter
bindingFlags = BindingFlags.Public;

DynamicallyAccessedMemberTypes memberTypes = GetDynamicallyAccessedMemberTypesFromBindingFlagsForMethods (bindingFlags);

var source = DynamicallyAccessedMembersAnalyzer.TryGetSymbolFromOperation (invocationOperation.Instance, context);
if (source != null) {
// Lets generate the
context.ReportDiagnostic (Diagnostic.Create (DiagnosticDescriptors.GetDiagnosticDescriptor ((DiagnosticId) DiagnosticId.DynamicallyAccessedMembersUnsuportedReflectionAccessInMethod), invocationOperation.Syntax.GetLocation (), calledMethod));
//DynamicallyAccessedMembersAnalyzer.VerifyAnnotations (source.Value, memberTypes, context, invocationOperation.Syntax.GetLocation ());
}

break;
}

//
// GetField (string)
// GetField (string, BindingFlags)
Expand Down
1 change: 1 addition & 0 deletions src/ILLink.Shared/DiagnosticId.cs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ public enum DiagnosticId
DynamicallyAccessedMembersMismatchTypeArgumentTargetsGenericParameter = 2091,
// Dynamically Accessed Members Unsuported Reflection Access
DynamicallyAccessedMembersUnsuportedReflectionAccessInField = 2110,
DynamicallyAccessedMembersUnsuportedReflectionAccessInMethod = 2111,

// Single-file diagnostic ids.
AvoidAssemblyLocationInSingleFile = 3000,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1218,5 +1218,58 @@ static void Reflection ()
// (12,3): warning IL2110: Field 'AnnotatedField._annotatedField' with 'DynamicallyAccessedMembersAttribute' is accessed via reflection. Trimmer can't guarantee availability of the requirements of the field.
VerifyCS.Diagnostic (DiagnosticId.DynamicallyAccessedMembersUnsuportedReflectionAccessInField).WithSpan (12, 3, 12, 55).WithArguments ("AnnotatedField._annotatedField"));
}

[Fact]
public Task AnnotatedMethodParametersReflection ()
{
var source = @"
using System.Diagnostics.CodeAnalysis;
//using System.Reflection;
using System;

class AnnotatedMethodParameters
{
public static void MethodWithSingleAnnotatedParameter (
[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] Type type)
{ }

static void Reflection ()
{
typeof (AnnotatedMethodParameters).GetMethod (nameof (MethodWithSingleAnnotatedParameter));//, BindingFlags.CreateInstance);
}
}";
return VerifyDynamicallyAccessedMembersAnalyzer (source,
// (14,3): warning IL2111:
VerifyCS.Diagnostic (DiagnosticId.DynamicallyAccessedMembersUnsuportedReflectionAccessInMethod)
.WithSpan (14, 3, 14, 93)
.WithArguments ("AnnotatedMethodParameters.MethodWithSingleAnnotatedParameter(System.Type)"));
}

[Fact]
public Task AnnotatedMethodParametersReflectionWithBindingFlags ()
{
var source = @"
using System.Diagnostics.CodeAnalysis;
using System.Reflection;
using System;

class AnnotatedMethodParameters
{
public static void MethodWithSingleAnnotatedParameter (
[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] Type type)
{ }

static void Reflection ()
{
typeof (AnnotatedMethodParameters).GetMethod (nameof (MethodWithSingleAnnotatedParameter), BindingFlags.CreateInstance);
}
}";
return VerifyDynamicallyAccessedMembersAnalyzer (source,
// (14,3): warning IL2111:
VerifyCS.Diagnostic (DiagnosticId.DynamicallyAccessedMembersUnsuportedReflectionAccessInMethod)
.WithSpan (14, 3, 14, 122)
.WithArguments ("System.Type.GetMethod(string, System.Reflection.BindingFlags)"));
}

}
}