diff --git a/src/ILLink.RoslynAnalyzer/RequiresUnreferencedCodeAnalyzer.cs b/src/ILLink.RoslynAnalyzer/RequiresUnreferencedCodeAnalyzer.cs index 052c560e05b8..441b846e4700 100644 --- a/src/ILLink.RoslynAnalyzer/RequiresUnreferencedCodeAnalyzer.cs +++ b/src/ILLink.RoslynAnalyzer/RequiresUnreferencedCodeAnalyzer.cs @@ -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 s_dynamicTypeInvocation = operationContext => { if (FindContainingSymbol (operationContext, DiagnosticTargets.All) is ISymbol containingSymbol && @@ -85,7 +87,7 @@ public sealed class RequiresUnreferencedCodeAnalyzer : RequiresAnalyzerBase }; public override ImmutableArray 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 @@ public sealed class RequiresUnreferencedCodeAnalyzer : RequiresAnalyzerBase protected override bool IsAnalyzerEnabled (AnalyzerOptions options, Compilation compilation) => options.IsMSBuildPropertyValueTrue (MSBuildPropertyOptionNames.EnableTrimAnalyzer, compilation); + protected override ImmutableArray GetSpecialIncompatibleMembers (Compilation compilation) + { + var incompatibleMembers = ImmutableArray.CreateBuilder (); + var typeType = compilation.GetTypeByMetadataName ("System.Type"); + if (typeType != null) { + incompatibleMembers.AddRange (typeType.GetMembers ("MakeGenericType").OfType ()); + } + + var methodInfoType = compilation.GetTypeByMetadataName ("System.Reflection.MethodInfo"); + if (methodInfoType != null) { + incompatibleMembers.AddRange (methodInfoType.GetMembers ("MakeGenericMethod").OfType ()); + } + + return incompatibleMembers.ToImmutable (); + } + + protected override bool ReportSpecialIncompatibleMembersDiagnostic (OperationAnalysisContext operationContext, ImmutableArray 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 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 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 f586a6b43761..c52dcc895322 100644 --- a/src/ILLink.Shared/DiagnosticId.cs +++ b/src/ILLink.Shared/DiagnosticId.cs @@ -5,6 +5,8 @@ public enum DiagnosticId // 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 70b50dbbf443..c78c81edefe2 100644 --- a/src/ILLink.Shared/SharedStrings.resx +++ b/src/ILLink.Shared/SharedStrings.resx @@ -171,6 +171,12 @@ Using dynamic types might cause types or members to be removed by trimmer. + + Call to '{0}' can not be statically analyzed. It's not possible to guarantee the availability of requirements of the generic method. + + + Call to '{0}' can not be statically analyzed. It's not possible to guarantee the availability of requirements of the generic type. + 'RequiresUnreferencedCodeAttribute' cannot be placed directly on static constructor '{0}', consider placing 'RequiresUnreferencedCodeAttribute' on the type declaration instead. diff --git a/src/linker/Linker.Dataflow/ReflectionMethodBodyScanner.cs b/src/linker/Linker.Dataflow/ReflectionMethodBodyScanner.cs index 2b3d940957b6..c57a14b707f5 100644 --- a/src/linker/Linker.Dataflow/ReflectionMethodBodyScanner.cs +++ b/src/linker/Linker.Dataflow/ReflectionMethodBodyScanner.cs @@ -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; @@ -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 ())); } } @@ -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 ())); } } @@ -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 ( @@ -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 ( @@ -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))); } } @@ -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 (); } diff --git a/src/linker/Resources/Strings.Designer.cs b/src/linker/Resources/Strings.Designer.cs index 6211cd6c64f3..df64816a28e8 100644 --- a/src/linker/Resources/Strings.Designer.cs +++ b/src/linker/Resources/Strings.Designer.cs @@ -60,15 +60,6 @@ internal Strings() { } } - /// - /// 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.. - /// - internal static string IL2060 { - get { - return ResourceManager.GetString("IL2060", resourceCulture); - } - } - /// /// 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.. /// diff --git a/src/linker/Resources/Strings.resx b/src/linker/Resources/Strings.resx index 4a8297ed69ee..0ffbe886a84b 100644 --- a/src/linker/Resources/Strings.resx +++ b/src/linker/Resources/Strings.resx @@ -117,9 +117,6 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - Call to '{0}' can not be statically analyzed. It's not possible to guarantee the availability of requirements of the generic method. - '{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. diff --git a/test/ILLink.RoslynAnalyzer.Tests/RequiresUnreferencedCodeAnalyzerTests.cs b/test/ILLink.RoslynAnalyzer.Tests/RequiresUnreferencedCodeAnalyzerTests.cs index 9b448970edc0..345b6d7c0b26 100644 --- a/test/ILLink.RoslynAnalyzer.Tests/RequiresUnreferencedCodeAnalyzerTests.cs +++ b/test/ILLink.RoslynAnalyzer.Tests/RequiresUnreferencedCodeAnalyzerTests.cs @@ -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); + } } }