diff options
author | Mateo Torres-Ruiz <mateoatr@users.noreply.github.com> | 2021-09-08 21:10:56 +0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-09-08 21:10:56 +0300 |
commit | 8b7695af6debcbdaad531d521f8a8e10c553541a (patch) | |
tree | ebe54f6f1f85e817a5ec731363ea62c99a0f9079 | |
parent | 4f28a6571fdf56b2c06dfb03fda41efb0ea4c9ed (diff) |
Special case `MakeGenericMethod/Type` in RUC analyzer (#2209)
* Add MakeGenericMethod and MakeGenericType to the special incompatible members of the RUC analyzer.
* Don't produce diagnostics for MakeGenericMethod/MakeGenericType
* Add comment
* Lint
-rw-r--r-- | src/ILLink.RoslynAnalyzer/RequiresUnreferencedCodeAnalyzer.cs | 35 | ||||
-rw-r--r-- | src/ILLink.Shared/DiagnosticId.cs | 2 | ||||
-rw-r--r-- | src/ILLink.Shared/SharedStrings.resx | 6 | ||||
-rw-r--r-- | src/linker/Linker.Dataflow/ReflectionMethodBodyScanner.cs | 33 | ||||
-rw-r--r-- | src/linker/Resources/Strings.Designer.cs | 9 | ||||
-rw-r--r-- | src/linker/Resources/Strings.resx | 3 | ||||
-rw-r--r-- | test/ILLink.RoslynAnalyzer.Tests/RequiresUnreferencedCodeAnalyzerTests.cs | 48 |
7 files changed, 102 insertions, 34 deletions
diff --git a/src/ILLink.RoslynAnalyzer/RequiresUnreferencedCodeAnalyzer.cs b/src/ILLink.RoslynAnalyzer/RequiresUnreferencedCodeAnalyzer.cs index 052c560e0..441b846e4 100644 --- a/src/ILLink.RoslynAnalyzer/RequiresUnreferencedCodeAnalyzer.cs +++ b/src/ILLink.RoslynAnalyzer/RequiresUnreferencedCodeAnalyzer.cs @@ -24,6 +24,8 @@ namespace ILLink.RoslynAnalyzer 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 && @@ -85,7 +87,7 @@ namespace ILLink.RoslynAnalyzer }; 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; @@ -100,8 +102,37 @@ namespace ILLink.RoslynAnalyzer 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 })); diff --git a/src/ILLink.Shared/DiagnosticId.cs b/src/ILLink.Shared/DiagnosticId.cs index f586a6b43..c52dcc895 100644 --- a/src/ILLink.Shared/DiagnosticId.cs +++ b/src/ILLink.Shared/DiagnosticId.cs @@ -5,6 +5,8 @@ // Linker diagnostic ids. RequiresUnreferencedCode = 2026, RequiresUnreferencedCodeAttributeMismatch = 2046, + MakeGenericType = 2055, + MakeGenericMethod = 2060, RequiresUnreferencedCodeOnStaticConstructor = 2116, // Single-file diagnostic ids. diff --git a/src/ILLink.Shared/SharedStrings.resx b/src/ILLink.Shared/SharedStrings.resx index 70b50dbbf..c78c81ede 100644 --- a/src/ILLink.Shared/SharedStrings.resx +++ b/src/ILLink.Shared/SharedStrings.resx @@ -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> diff --git a/src/linker/Linker.Dataflow/ReflectionMethodBodyScanner.cs b/src/linker/Linker.Dataflow/ReflectionMethodBodyScanner.cs index 2b3d94095..c57a14b70 100644 --- a/src/linker/Linker.Dataflow/ReflectionMethodBodyScanner.cs +++ b/src/linker/Linker.Dataflow/ReflectionMethodBodyScanner.cs @@ -7,6 +7,7 @@ using System.Collections.Generic; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Linq; +using ILLink.Shared; using Mono.Cecil; using Mono.Cecil.Cil; using Mono.Linker.Steps; @@ -745,10 +746,8 @@ namespace Mono.Linker.Dataflow } } 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 ())); } } @@ -758,10 +757,8 @@ namespace Mono.Linker.Dataflow 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 ())); } } @@ -853,9 +850,8 @@ namespace Mono.Linker.Dataflow 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 ( @@ -869,9 +865,8 @@ namespace Mono.Linker.Dataflow 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 ( @@ -1718,9 +1713,8 @@ namespace Mono.Linker.Dataflow } 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))); } } @@ -2403,9 +2397,8 @@ namespace Mono.Linker.Dataflow } 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 (); } diff --git a/src/linker/Resources/Strings.Designer.cs b/src/linker/Resources/Strings.Designer.cs index 6211cd6c6..df64816a2 100644 --- a/src/linker/Resources/Strings.Designer.cs +++ b/src/linker/Resources/Strings.Designer.cs @@ -61,15 +61,6 @@ namespace Mono.Linker.Resources { } /// <summary> - /// Looks up a localized string similar to Call to '{0}' can not be statically analyzed. It's not possible to guarantee the availability of requirements of the generic method.. - /// </summary> - internal static string IL2060 { - get { - return ResourceManager.GetString("IL2060", resourceCulture); - } - } - - /// <summary> /// Looks up a localized string similar to '{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.. /// </summary> internal static string IL2067 { diff --git a/src/linker/Resources/Strings.resx b/src/linker/Resources/Strings.resx index 4a8297ed6..0ffbe886a 100644 --- a/src/linker/Resources/Strings.resx +++ b/src/linker/Resources/Strings.resx @@ -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> diff --git a/test/ILLink.RoslynAnalyzer.Tests/RequiresUnreferencedCodeAnalyzerTests.cs b/test/ILLink.RoslynAnalyzer.Tests/RequiresUnreferencedCodeAnalyzerTests.cs index 9b448970e..345b6d7c0 100644 --- a/test/ILLink.RoslynAnalyzer.Tests/RequiresUnreferencedCodeAnalyzerTests.cs +++ b/test/ILLink.RoslynAnalyzer.Tests/RequiresUnreferencedCodeAnalyzerTests.cs @@ -920,5 +920,53 @@ class C 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); + } } } |