Skip to content

Commit

Permalink
Special case MakeGenericMethod/Type in RUC analyzer (dotnet/linker#…
Browse files Browse the repository at this point in the history
…2209)

* Add MakeGenericMethod and MakeGenericType to the special incompatible members of the RUC analyzer.

* Don't produce diagnostics for MakeGenericMethod/MakeGenericType

* Add comment

* Lint

Commit migrated from dotnet/linker@8b7695a
  • Loading branch information
mateoatr authored Sep 8, 2021
1 parent 6bf7bca commit d8e8004
Show file tree
Hide file tree
Showing 7 changed files with 102 additions and 34 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ public sealed class RequiresUnreferencedCodeAnalyzer : RequiresAnalyzerBase
static readonly DiagnosticDescriptor s_dynamicTypeInvocationRule = DiagnosticDescriptors.GetDiagnosticDescriptor (DiagnosticId.RequiresUnreferencedCode,
new LocalizableResourceString (nameof (SharedStrings.DynamicTypeInvocationTitle), SharedStrings.ResourceManager, typeof (SharedStrings)),
new LocalizableResourceString (nameof (SharedStrings.DynamicTypeInvocationMessage), SharedStrings.ResourceManager, typeof (SharedStrings)));
static readonly DiagnosticDescriptor s_makeGenericTypeRule = DiagnosticDescriptors.GetDiagnosticDescriptor (DiagnosticId.MakeGenericType);
static readonly DiagnosticDescriptor s_makeGenericMethodRule = DiagnosticDescriptors.GetDiagnosticDescriptor (DiagnosticId.MakeGenericMethod);

static readonly Action<OperationAnalysisContext> s_dynamicTypeInvocation = operationContext => {
if (FindContainingSymbol (operationContext, DiagnosticTargets.All) is ISymbol containingSymbol &&
Expand Down Expand Up @@ -85,7 +87,7 @@ public sealed class RequiresUnreferencedCodeAnalyzer : RequiresAnalyzerBase
};

public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics =>
ImmutableArray.Create (s_dynamicTypeInvocationRule, s_requiresUnreferencedCodeRule, s_requiresUnreferencedCodeAttributeMismatch);
ImmutableArray.Create (s_dynamicTypeInvocationRule, s_makeGenericMethodRule, s_makeGenericTypeRule, s_requiresUnreferencedCodeRule, s_requiresUnreferencedCodeAttributeMismatch);

private protected override string RequiresAttributeName => RequiresUnreferencedCodeAttribute;

Expand All @@ -100,8 +102,37 @@ public sealed class RequiresUnreferencedCodeAnalyzer : RequiresAnalyzerBase
protected override bool IsAnalyzerEnabled (AnalyzerOptions options, Compilation compilation) =>
options.IsMSBuildPropertyValueTrue (MSBuildPropertyOptionNames.EnableTrimAnalyzer, compilation);

protected override ImmutableArray<ISymbol> GetSpecialIncompatibleMembers (Compilation compilation)
{
var incompatibleMembers = ImmutableArray.CreateBuilder<ISymbol> ();
var typeType = compilation.GetTypeByMetadataName ("System.Type");
if (typeType != null) {
incompatibleMembers.AddRange (typeType.GetMembers ("MakeGenericType").OfType<IMethodSymbol> ());
}

var methodInfoType = compilation.GetTypeByMetadataName ("System.Reflection.MethodInfo");
if (methodInfoType != null) {
incompatibleMembers.AddRange (methodInfoType.GetMembers ("MakeGenericMethod").OfType<IMethodSymbol> ());
}

return incompatibleMembers.ToImmutable ();
}

protected override bool ReportSpecialIncompatibleMembersDiagnostic (OperationAnalysisContext operationContext, ImmutableArray<ISymbol> specialIncompatibleMembers, ISymbol member)
{
if (member is IMethodSymbol method && ImmutableArrayOperations.Contains (specialIncompatibleMembers, member, SymbolEqualityComparer.Default) &&
(method.Name == "MakeGenericMethod" || method.Name == "MakeGenericType")) {
// These two RUC-annotated APIs are intrinsically handled by the trimmer, which will not produce any
// RUC warning related to them. For unrecognized reflection patterns realted to generic type/method
// creation IL2055/IL2060 should be used instead.
return true;
}

return false;
}

private protected override ImmutableArray<(Action<OperationAnalysisContext> Action, OperationKind[] OperationKind)> ExtraOperationActions =>
ImmutableArray.Create ((s_dynamicTypeInvocation, new OperationKind[] { OperationKind.DynamicInvocation }));
ImmutableArray.Create ((s_dynamicTypeInvocation, new OperationKind[] { OperationKind.DynamicInvocation }));

private protected override ImmutableArray<(Action<SyntaxNodeAnalysisContext> Action, SyntaxKind[] SyntaxKind)> ExtraSyntaxNodeActions =>
ImmutableArray.Create ((s_constructorConstraint, new SyntaxKind[] { SyntaxKind.GenericName }));
Expand Down
2 changes: 2 additions & 0 deletions src/tools/illink/src/ILLink.Shared/DiagnosticId.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ public enum DiagnosticId
// Linker diagnostic ids.
RequiresUnreferencedCode = 2026,
RequiresUnreferencedCodeAttributeMismatch = 2046,
MakeGenericType = 2055,
MakeGenericMethod = 2060,
RequiresUnreferencedCodeOnStaticConstructor = 2116,

// Single-file diagnostic ids.
Expand Down
6 changes: 6 additions & 0 deletions src/tools/illink/src/ILLink.Shared/SharedStrings.resx
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,12 @@
<data name="DynamicTypeInvocationTitle" xml:space="preserve">
<value>Using dynamic types might cause types or members to be removed by trimmer.</value>
</data>
<data name="MakeGenericMethodMessage" xml:space="preserve">
<value>Call to '{0}' can not be statically analyzed. It's not possible to guarantee the availability of requirements of the generic method.</value>
</data>
<data name="MakeGenericTypeMessage" xml:space="preserve">
<value>Call to '{0}' can not be statically analyzed. It's not possible to guarantee the availability of requirements of the generic type.</value>
</data>
<data name="RequiresUnreferencedCodeOnStaticConstructorMessage" xml:space="preserve">
<value>'RequiresUnreferencedCodeAttribute' cannot be placed directly on static constructor '{0}', consider placing 'RequiresUnreferencedCodeAttribute' on the type declaration instead.</value>
</data>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using ILLink.Shared;
using Mono.Cecil;
using Mono.Cecil.Cil;
using Mono.Linker.Steps;
Expand Down Expand Up @@ -745,10 +746,8 @@ public override bool HandleCall (MethodBody callingMethodBody, MethodReference c
}
}
if (hasUncheckedAnnotation) {
reflectionContext.RecordUnrecognizedPattern (
2055,
$"Call to '{calledMethodDefinition.GetDisplayName ()}' can not be statically analyzed. " +
$"It's not possible to guarantee the availability of requirements of the generic type.");
reflectionContext.RecordUnrecognizedPattern ((int) DiagnosticId.MakeGenericType,
new DiagnosticString (DiagnosticId.MakeGenericType).GetMessage (calledMethodDefinition.GetDisplayName ()));
}
}

Expand All @@ -758,10 +757,8 @@ public override bool HandleCall (MethodBody callingMethodBody, MethodReference c
reflectionContext.RecordHandledPattern ();
else {
// We have no way to "include more" to fix this if we don't know, so we have to warn
reflectionContext.RecordUnrecognizedPattern (
2055,
$"Call to '{calledMethodDefinition.GetDisplayName ()}' can not be statically analyzed. " +
$"It's not possible to guarantee the availability of requirements of the generic type.");
reflectionContext.RecordUnrecognizedPattern ((int) DiagnosticId.MakeGenericType,
new DiagnosticString (DiagnosticId.MakeGenericType).GetMessage (calledMethodDefinition.GetDisplayName ()));
}
}

Expand Down Expand Up @@ -853,9 +850,8 @@ public override bool HandleCall (MethodBody callingMethodBody, MethodReference c
if (hasTypeArguments) {
// We don't know what method the `MakeGenericMethod` was called on, so we have to assume
// that the method may have requirements which we can't fullfil -> warn.
reflectionContext.RecordUnrecognizedPattern (
2060, string.Format (Resources.Strings.IL2060,
DiagnosticUtilities.GetMethodSignatureDisplayName (calledMethod)));
reflectionContext.RecordUnrecognizedPattern ((int) DiagnosticId.MakeGenericMethod,
new DiagnosticString (DiagnosticId.MakeGenericMethod).GetMessage (DiagnosticUtilities.GetMethodSignatureDisplayName (calledMethod)));
}

RequireDynamicallyAccessedMembers (
Expand All @@ -869,9 +865,8 @@ public override bool HandleCall (MethodBody callingMethodBody, MethodReference c
if (hasTypeArguments) {
// We don't know what method the `MakeGenericMethod` was called on, so we have to assume
// that the method may have requirements which we can't fullfil -> warn.
reflectionContext.RecordUnrecognizedPattern (
2060, string.Format (Resources.Strings.IL2060,
DiagnosticUtilities.GetMethodSignatureDisplayName (calledMethod)));
reflectionContext.RecordUnrecognizedPattern ((int) DiagnosticId.MakeGenericMethod,
new DiagnosticString (DiagnosticId.MakeGenericMethod).GetMessage (DiagnosticUtilities.GetMethodSignatureDisplayName (calledMethod)));
}

RequireDynamicallyAccessedMembers (
Expand Down Expand Up @@ -1718,9 +1713,8 @@ public override bool HandleCall (MethodBody callingMethodBody, MethodReference c
} else {
// We don't know what method the `MakeGenericMethod` was called on, so we have to assume
// that the method may have requirements which we can't fullfil -> warn.
reflectionContext.RecordUnrecognizedPattern (
2060, string.Format (Resources.Strings.IL2060,
DiagnosticUtilities.GetMethodSignatureDisplayName (calledMethod)));
reflectionContext.RecordUnrecognizedPattern ((int) DiagnosticId.MakeGenericMethod,
new DiagnosticString (DiagnosticId.MakeGenericMethod).GetMessage (DiagnosticUtilities.GetMethodSignatureDisplayName (calledMethod)));
}
}

Expand Down Expand Up @@ -2403,9 +2397,8 @@ void ValidateGenericMethodInstantiation (
}

if (!AnalyzeGenericInstatiationTypeArray (genericParametersArray, ref reflectionContext, reflectionMethod, genericMethod.GenericParameters)) {
reflectionContext.RecordUnrecognizedPattern (
2060,
string.Format (Resources.Strings.IL2060, DiagnosticUtilities.GetMethodSignatureDisplayName (reflectionMethod)));
reflectionContext.RecordUnrecognizedPattern ((int) DiagnosticId.MakeGenericMethod,
new DiagnosticString (DiagnosticId.MakeGenericMethod).GetMessage (DiagnosticUtilities.GetMethodSignatureDisplayName (reflectionMethod)));
} else {
reflectionContext.RecordHandledPattern ();
}
Expand Down
9 changes: 0 additions & 9 deletions src/tools/illink/src/linker/Resources/Strings.Designer.cs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 0 additions & 3 deletions src/tools/illink/src/linker/Resources/Strings.resx
Original file line number Diff line number Diff line change
Expand Up @@ -117,9 +117,6 @@
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="IL2060" xml:space="preserve">
<value>Call to '{0}' can not be statically analyzed. It's not possible to guarantee the availability of requirements of the generic method.</value>
</data>
<data name="IL2067" xml:space="preserve">
<value>'{0}' argument does not satisfy {4} in call to '{1}'. The parameter '{2}' of method '{3}' does not have matching annotations. The source value must declare at least the same requirements as those declared on the target location it is assigned to.</value>
</data>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -920,5 +920,53 @@ static void M0 ()

return VerifyRequiresUnreferencedCodeAnalyzer (source);
}

[Fact]
public Task TestMakeGenericMethodUsage ()
{
var source = @"
using System.Diagnostics.CodeAnalysis;
using System.Reflection;
class C
{
static void M1 (MethodInfo methodInfo)
{
methodInfo.MakeGenericMethod (typeof (C));
}
[RequiresUnreferencedCode (""Message from RUC"")]
static void M2 (MethodInfo methodInfo)
{
methodInfo.MakeGenericMethod (typeof (C));
}
}";

return VerifyRequiresUnreferencedCodeAnalyzer (source);
}

[Fact]
public Task TestMakeGenericTypeUsage ()
{
var source = @"
using System;
using System.Diagnostics.CodeAnalysis;
class C
{
static void M1 (Type t)
{
typeof (Nullable<>).MakeGenericType (typeof (C));
}
[RequiresUnreferencedCode (""Message from RUC"")]
static void M2 (Type t)
{
typeof (Nullable<>).MakeGenericType (typeof (C));
}
}";

return VerifyRequiresUnreferencedCodeAnalyzer (source);
}
}
}

0 comments on commit d8e8004

Please sign in to comment.