diff --git a/src/tools/illink/illink.sln b/src/tools/illink/illink.sln index 87e99d208c7fb..9b8d904942baa 100644 --- a/src/tools/illink/illink.sln +++ b/src/tools/illink/illink.sln @@ -223,6 +223,7 @@ Global SolutionGuid = {E43A3901-42B0-48CA-BB36-5CD40A99A6EE} EndGlobalSection GlobalSection(SharedMSBuildProjectFiles) = preSolution + test\Trimming.Tests.Shared\Trimming.Tests.Shared.projitems*{400a1561-b6b6-482d-9e4c-3ddaede5bd07}*SharedItemsImports = 5 src\ILLink.Shared\ILLink.Shared.projitems*{dd28e2b1-057b-4b4d-a04d-b2ebd9e76e46}*SharedItemsImports = 5 src\ILLink.Shared\ILLink.Shared.projitems*{f1a44a78-34ee-408b-8285-9a26f0e7d4f2}*SharedItemsImports = 5 src\ILLink.Shared\ILLink.Shared.projitems*{ff598e93-8e9e-4091-9f50-61a7572663ae}*SharedItemsImports = 13 diff --git a/src/tools/illink/src/linker/CompatibilitySuppressions.xml b/src/tools/illink/src/linker/CompatibilitySuppressions.xml index 4a0a6296c4e2f..7bf0a1e0ce697 100644 --- a/src/tools/illink/src/linker/CompatibilitySuppressions.xml +++ b/src/tools/illink/src/linker/CompatibilitySuppressions.xml @@ -253,6 +253,10 @@ CP0001 T:Mono.Linker.ILogger + + CP0001 + T:Mono.Linker.InterfaceImplementor + CP0001 T:Mono.Linker.InternalErrorException @@ -1481,10 +1485,6 @@ CP0002 M:Mono.Linker.OverrideInformation.get_IsOverrideOfInterfaceMember - - CP0002 - M:Mono.Linker.OverrideInformation.get_IsStaticInterfaceMethodPair - CP0002 M:Mono.Linker.Steps.BaseStep.get_MarkingHelpers diff --git a/src/tools/illink/src/linker/Linker.Steps/MarkStep.cs b/src/tools/illink/src/linker/Linker.Steps/MarkStep.cs index 0d0543dcdcf56..32fd98cbe039c 100644 --- a/src/tools/illink/src/linker/Linker.Steps/MarkStep.cs +++ b/src/tools/illink/src/linker/Linker.Steps/MarkStep.cs @@ -701,17 +701,16 @@ void ProcessVirtualMethod (MethodDefinition method) var defaultImplementations = Annotations.GetDefaultInterfaceImplementations (method); if (defaultImplementations is not null) { foreach (var dimInfo in defaultImplementations) { - ProcessDefaultImplementation (dimInfo.ImplementingType, dimInfo.InterfaceImpl, dimInfo.DefaultInterfaceMethod); + ProcessDefaultImplementation (dimInfo); - var ov = new OverrideInformation (method, dimInfo.DefaultInterfaceMethod, Context, dimInfo.InterfaceImpl); - if (IsInterfaceImplementationMethodNeededByTypeDueToInterface (ov, dimInfo.ImplementingType)) - MarkMethod (ov.Override, new DependencyInfo (DependencyKind.Override, ov.Base), ScopeStack.CurrentScope.Origin); + if (IsInterfaceImplementationMethodNeededByTypeDueToInterface (dimInfo)) + MarkMethod (dimInfo.Override, new DependencyInfo (DependencyKind.Override, dimInfo.Base), ScopeStack.CurrentScope.Origin); } } var overridingMethods = Annotations.GetOverrides (method); if (overridingMethods is not null) { - foreach (var ov in overridingMethods) { - if (IsInterfaceImplementationMethodNeededByTypeDueToInterface (ov, ov.Override.DeclaringType)) + foreach (OverrideInformation ov in overridingMethods) { + if (IsInterfaceImplementationMethodNeededByTypeDueToInterface (ov)) MarkMethod (ov.Override, new DependencyInfo (DependencyKind.Override, ov.Base), ScopeStack.CurrentScope.Origin); } } @@ -819,13 +818,14 @@ bool RequiresInterfaceRecursively (TypeDefinition typeToExamine, TypeDefinition return false; } - void ProcessDefaultImplementation (TypeDefinition typeWithDefaultImplementedInterfaceMethod, InterfaceImplementation implementation, MethodDefinition implementationMethod) + void ProcessDefaultImplementation (OverrideInformation ov) { - if ((!implementationMethod.IsStatic && !Annotations.IsInstantiated (typeWithDefaultImplementedInterfaceMethod)) - || implementationMethod.IsStatic && !Annotations.IsRelevantToVariantCasting (typeWithDefaultImplementedInterfaceMethod)) + Debug.Assert (ov.IsOverrideOfInterfaceMember); + if ((!ov.Override.IsStatic && !Annotations.IsInstantiated (ov.InterfaceImplementor.Implementor)) + || ov.Override.IsStatic && !Annotations.IsRelevantToVariantCasting (ov.InterfaceImplementor.Implementor)) return; - MarkInterfaceImplementation (implementation); + MarkInterfaceImplementation (ov.InterfaceImplementor.InterfaceImplementation); } void MarkMarshalSpec (IMarshalInfoProvider spec, in DependencyInfo reason) @@ -2549,11 +2549,11 @@ bool IsMethodNeededByTypeDueToPreservedScope (MethodDefinition method) /// /// Returns true if the override method is required due to the interface that the base method is declared on. See doc at for explanation of logic. /// - bool IsInterfaceImplementationMethodNeededByTypeDueToInterface (OverrideInformation overrideInformation, TypeDefinition typeThatImplsInterface) + bool IsInterfaceImplementationMethodNeededByTypeDueToInterface (OverrideInformation overrideInformation) { var @base = overrideInformation.Base; var method = overrideInformation.Override; - Debug.Assert (@base.DeclaringType.IsInterface); + Debug.Assert (overrideInformation.IsOverrideOfInterfaceMember); if (@base is null || method is null || @base.DeclaringType is null) return false; @@ -2562,7 +2562,7 @@ bool IsInterfaceImplementationMethodNeededByTypeDueToInterface (OverrideInformat // If the interface implementation is not marked, do not mark the implementation method // A type that doesn't implement the interface isn't required to have methods that implement the interface. - InterfaceImplementation? iface = overrideInformation.MatchingInterfaceImplementation; + InterfaceImplementation? iface = overrideInformation.InterfaceImplementor.InterfaceImplementation; if (!((iface is not null && Annotations.IsMarked (iface)) || IsInterfaceImplementationMarkedRecursively (method.DeclaringType, @base.DeclaringType))) return false; @@ -2580,12 +2580,12 @@ bool IsInterfaceImplementationMethodNeededByTypeDueToInterface (OverrideInformat // If the method is static and the implementing type is relevant to variant casting, mark the implementation method. // A static method may only be called through a constrained call if the type is relevant to variant casting. if (@base.IsStatic) - return Annotations.IsRelevantToVariantCasting (typeThatImplsInterface) + return Annotations.IsRelevantToVariantCasting (overrideInformation.InterfaceImplementor.Implementor) || IgnoreScope (@base.DeclaringType.Scope); // If the implementing type is marked as instantiated, mark the implementation method. // If the type is not instantiated, do not mark the implementation method - return Annotations.IsInstantiated (typeThatImplsInterface); + return Annotations.IsInstantiated (overrideInformation.InterfaceImplementor.Implementor); } static bool IsSpecialSerializationConstructor (MethodDefinition method) @@ -3256,7 +3256,7 @@ protected virtual void ProcessMethod (MethodDefinition method, in DependencyInfo // Only if the interface method is referenced, then all the methods which implemented must be kept, but not the other way round. if (!markAllOverrides && Context.Resolve (@base) is MethodDefinition baseDefinition - && new OverrideInformation.OverridePair (baseDefinition, method).IsStaticInterfaceMethodPair ()) + && baseDefinition.DeclaringType.IsInterface && baseDefinition.IsStatic && method.IsStatic) continue; MarkMethod (@base, new DependencyInfo (DependencyKind.MethodImplOverride, method), ScopeStack.CurrentScope.Origin); MarkExplicitInterfaceImplementation (method, @base); diff --git a/src/tools/illink/src/linker/Linker/Annotations.cs b/src/tools/illink/src/linker/Linker/Annotations.cs index 8f7747cba3543..a7b3198265e81 100644 --- a/src/tools/illink/src/linker/Linker/Annotations.cs +++ b/src/tools/illink/src/linker/Linker/Annotations.cs @@ -462,7 +462,7 @@ public bool IsPublic (IMetadataTokenProvider provider) /// DefaultInterfaceMethod is the method that implements . /// /// The interface method to find default implementations for - public IEnumerable<(TypeDefinition ImplementingType, InterfaceImplementation InterfaceImpl, MethodDefinition DefaultInterfaceMethod)>? GetDefaultInterfaceImplementations (MethodDefinition method) + public IEnumerable? GetDefaultInterfaceImplementations (MethodDefinition method) { return TypeMapInfo.GetDefaultInterfaceImplementations (method); } diff --git a/src/tools/illink/src/linker/Linker/InterfaceImplementor.cs b/src/tools/illink/src/linker/Linker/InterfaceImplementor.cs new file mode 100644 index 0000000000000..e981ce872703f --- /dev/null +++ b/src/tools/illink/src/linker/Linker/InterfaceImplementor.cs @@ -0,0 +1,59 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Diagnostics; +using Mono.Cecil; + +namespace Mono.Linker +{ + public class InterfaceImplementor + { + /// + /// The type that implements . + /// + public TypeDefinition Implementor { get; } + /// + /// The .interfaceimpl on that points to + /// + public InterfaceImplementation InterfaceImplementation { get; } + /// + /// The type of the interface that is implemented by + /// + public TypeDefinition InterfaceType { get; } + + public InterfaceImplementor (TypeDefinition implementor, InterfaceImplementation interfaceImplementation, TypeDefinition interfaceType, IMetadataResolver resolver) + { + Implementor = implementor; + InterfaceImplementation = interfaceImplementation; + InterfaceType = interfaceType; + Debug.Assert(resolver.Resolve (interfaceImplementation.InterfaceType) == interfaceType); + } + + public static InterfaceImplementor Create(TypeDefinition implementor, TypeDefinition interfaceType, IMetadataResolver resolver) + { + foreach(InterfaceImplementation iface in implementor.Interfaces) { + if (resolver.Resolve(iface.InterfaceType) == interfaceType) { + return new InterfaceImplementor(implementor, iface, interfaceType, resolver); + } + } + + Queue ifacesToCheck = new (); + ifacesToCheck.Enqueue(implementor); + while (ifacesToCheck.Count > 0) { + var currentIface = ifacesToCheck.Dequeue (); + + foreach(InterfaceImplementation ifaceImpl in currentIface.Interfaces) { + var iface = resolver.Resolve (ifaceImpl.InterfaceType); + if (iface == interfaceType) { + return new InterfaceImplementor(implementor, ifaceImpl, interfaceType, resolver); + } + ifacesToCheck.Enqueue (iface); + } + } + throw new InvalidOperationException ($"Type '{implementor.FullName}' does not implement interface '{interfaceType.FullName}' directly or through any interfaces"); + } + } +} diff --git a/src/tools/illink/src/linker/Linker/OverrideInformation.cs b/src/tools/illink/src/linker/Linker/OverrideInformation.cs index 077353eb2ee7b..0727d5d25c19a 100644 --- a/src/tools/illink/src/linker/Linker/OverrideInformation.cs +++ b/src/tools/illink/src/linker/Linker/OverrideInformation.cs @@ -3,71 +3,39 @@ using System.Diagnostics; using Mono.Cecil; +using System.Diagnostics.CodeAnalysis; namespace Mono.Linker { [DebuggerDisplay ("{Override}")] public class OverrideInformation { - readonly ITryResolveMetadata resolver; - readonly OverridePair _pair; - private InterfaceImplementation? _matchingInterfaceImplementation; + public MethodDefinition Base { get; } - public OverrideInformation (MethodDefinition @base, MethodDefinition @override, ITryResolveMetadata resolver, InterfaceImplementation? matchingInterfaceImplementation = null) - { - _pair = new OverridePair (@base, @override); - _matchingInterfaceImplementation = matchingInterfaceImplementation; - this.resolver = resolver; - } - public readonly record struct OverridePair (MethodDefinition Base, MethodDefinition Override) - { - public bool IsStaticInterfaceMethodPair () => Base.DeclaringType.IsInterface && Base.IsStatic && Override.IsStatic; - public InterfaceImplementation? GetMatchingInterfaceImplementation (ITryResolveMetadata resolver) - { - if (!Base.DeclaringType.IsInterface) - return null; - var interfaceType = Base.DeclaringType; - foreach (var @interface in Override.DeclaringType.Interfaces) { - if (resolver.TryResolve (@interface.InterfaceType)?.Equals (interfaceType) == true) { - return @interface; - } - } - return null; - } - } + public MethodDefinition Override { get; } - public MethodDefinition Base { get => _pair.Base; } - public MethodDefinition Override { get => _pair.Override; } - public InterfaceImplementation? MatchingInterfaceImplementation { - get { - if (_matchingInterfaceImplementation is not null) - return _matchingInterfaceImplementation; - _matchingInterfaceImplementation = _pair.GetMatchingInterfaceImplementation (resolver); - return _matchingInterfaceImplementation; - } - } + internal InterfaceImplementor? InterfaceImplementor { get; } - public bool IsOverrideOfInterfaceMember { - get { - if (MatchingInterfaceImplementation != null) - return true; - - return Base.DeclaringType.IsInterface; - } + internal OverrideInformation (MethodDefinition @base, MethodDefinition @override, InterfaceImplementor? interfaceImplementor = null) + { + Base = @base; + Override = @override; + InterfaceImplementor = interfaceImplementor; + // Ensure we have an interface implementation if the base method is from an interface and the override method is on a class + Debug.Assert(@base.DeclaringType.IsInterface && interfaceImplementor != null + || !@base.DeclaringType.IsInterface && interfaceImplementor == null); + // Ensure the interfaceImplementor is for the interface we expect + Debug.Assert (@base.DeclaringType.IsInterface ? interfaceImplementor!.InterfaceType == @base.DeclaringType : true); } - public TypeDefinition? InterfaceType { - get { - if (!IsOverrideOfInterfaceMember) - return null; + public InterfaceImplementation? MatchingInterfaceImplementation + => InterfaceImplementor?.InterfaceImplementation; - if (MatchingInterfaceImplementation != null) - return resolver.TryResolve (MatchingInterfaceImplementation.InterfaceType); - - return Base.DeclaringType; - } - } + public TypeDefinition? InterfaceType + => InterfaceImplementor?.InterfaceType; - public bool IsStaticInterfaceMethodPair => _pair.IsStaticInterfaceMethodPair (); + [MemberNotNullWhen (true, nameof (InterfaceImplementor), nameof (MatchingInterfaceImplementation))] + public bool IsOverrideOfInterfaceMember + => InterfaceImplementor != null; } } diff --git a/src/tools/illink/src/linker/Linker/TypeMapInfo.cs b/src/tools/illink/src/linker/Linker/TypeMapInfo.cs index ef58e69c36fd2..bb2836a804d7b 100644 --- a/src/tools/illink/src/linker/Linker/TypeMapInfo.cs +++ b/src/tools/illink/src/linker/Linker/TypeMapInfo.cs @@ -29,9 +29,11 @@ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // +using System; using System.Collections.Generic; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; +using System.Linq; using Mono.Cecil; namespace Mono.Linker @@ -43,7 +45,7 @@ public class TypeMapInfo readonly LinkContext context; protected readonly Dictionary> base_methods = new Dictionary> (); protected readonly Dictionary> override_methods = new Dictionary> (); - protected readonly Dictionary> default_interface_implementations = new Dictionary> (); + protected readonly Dictionary> default_interface_implementations = new Dictionary> (); public TypeMapInfo (LinkContext context) { @@ -92,41 +94,41 @@ public void EnsureProcessed (AssemblyDefinition assembly) /// DefaultInterfaceMethod is the method that implements . /// /// The interface method to find default implementations for - public IEnumerable<(TypeDefinition ImplementingType, InterfaceImplementation InterfaceImpl, MethodDefinition DefaultImplementationMethod)>? GetDefaultInterfaceImplementations (MethodDefinition baseMethod) + public IEnumerable? GetDefaultInterfaceImplementations (MethodDefinition baseMethod) { default_interface_implementations.TryGetValue (baseMethod, out var ret); return ret; } - public void AddBaseMethod (MethodDefinition method, MethodDefinition @base, InterfaceImplementation? matchingInterfaceImplementation) + public void AddBaseMethod (MethodDefinition method, MethodDefinition @base, InterfaceImplementor? interfaceImplementor) { if (!base_methods.TryGetValue (method, out List? methods)) { methods = new List (); base_methods[method] = methods; } - methods.Add (new OverrideInformation (@base, method, context, matchingInterfaceImplementation)); + methods.Add (new OverrideInformation (@base, method, interfaceImplementor)); } - public void AddOverride (MethodDefinition @base, MethodDefinition @override, InterfaceImplementation? matchingInterfaceImplementation = null) + public void AddOverride (MethodDefinition @base, MethodDefinition @override, InterfaceImplementor? interfaceImplementor = null) { if (!override_methods.TryGetValue (@base, out List? methods)) { methods = new List (); override_methods.Add (@base, methods); } - methods.Add (new OverrideInformation (@base, @override, context, matchingInterfaceImplementation)); + methods.Add (new OverrideInformation (@base, @override, interfaceImplementor)); } - public void AddDefaultInterfaceImplementation (MethodDefinition @base, TypeDefinition implementingType, (InterfaceImplementation, MethodDefinition) matchingInterfaceImplementation) + public void AddDefaultInterfaceImplementation (MethodDefinition @base, InterfaceImplementor interfaceImplementor, MethodDefinition defaultImplementationMethod) { Debug.Assert(@base.DeclaringType.IsInterface); if (!default_interface_implementations.TryGetValue (@base, out var implementations)) { - implementations = new List<(TypeDefinition, InterfaceImplementation, MethodDefinition)> (); + implementations = new List (); default_interface_implementations.Add (@base, implementations); } - implementations.Add ((implementingType, matchingInterfaceImplementation.Item1, matchingInterfaceImplementation.Item2)); + implementations.Add (new (@base, defaultImplementationMethod, interfaceImplementor)); } protected virtual void MapType (TypeDefinition type) @@ -168,14 +170,14 @@ void MapInterfaceMethodsInTypeHierarchy (TypeDefinition type) // Try to find an implementation with a name/sig match on the current type MethodDefinition? exactMatchOnType = TryMatchMethod (type, interfaceMethod); if (exactMatchOnType != null) { - AnnotateMethods (resolvedInterfaceMethod, exactMatchOnType); + AnnotateMethods (resolvedInterfaceMethod, exactMatchOnType, new (type, interfaceImpl.OriginalImpl, resolvedInterfaceMethod.DeclaringType, context)); continue; } // Next try to find an implementation with a name/sig match in the base hierarchy var @base = GetBaseMethodInTypeHierarchy (type, interfaceMethod); if (@base != null) { - AnnotateMethods (resolvedInterfaceMethod, @base, interfaceImpl.OriginalImpl); + AnnotateMethods (resolvedInterfaceMethod, @base, new (type, interfaceImpl.OriginalImpl, resolvedInterfaceMethod.DeclaringType, context)); continue; } } @@ -211,24 +213,29 @@ void MapVirtualMethod (MethodDefinition method) if (@base == null) return; + Debug.Assert(!@base.DeclaringType.IsInterface); + AnnotateMethods (@base, method); } void MapOverrides (MethodDefinition method) { - foreach (MethodReference override_ref in method.Overrides) { - MethodDefinition? @override = context.TryResolve (override_ref); - if (@override == null) + foreach (MethodReference baseMethodRef in method.Overrides) { + MethodDefinition? baseMethod = context.TryResolve (baseMethodRef); + if (baseMethod == null) continue; - - AnnotateMethods (@override, method); + if (baseMethod.DeclaringType.IsInterface) { + AnnotateMethods (baseMethod, method, InterfaceImplementor.Create (method.DeclaringType, baseMethod.DeclaringType, context)); + } else { + AnnotateMethods (baseMethod, method); + } } } - void AnnotateMethods (MethodDefinition @base, MethodDefinition @override, InterfaceImplementation? matchingInterfaceImplementation = null) + void AnnotateMethods (MethodDefinition @base, MethodDefinition @override, InterfaceImplementor? interfaceImplementor = null) { - AddBaseMethod (@override, @base, matchingInterfaceImplementation); - AddOverride (@base, @override, matchingInterfaceImplementation); + AddBaseMethod (@override, @base, interfaceImplementor); + AddOverride (@base, @override, interfaceImplementor); } MethodDefinition? GetBaseMethodInTypeHierarchy (MethodDefinition method) @@ -290,7 +297,7 @@ void AnnotateMethods (MethodDefinition @base, MethodDefinition @override, Interf /// /// The InterfaceImplementation on that points to the DeclaringType of . /// - void FindAndAddDefaultInterfaceImplementations (TypeDefinition typeThatImplementsInterface, TypeDefinition typeThatMayHaveDIM, MethodDefinition interfaceMethodToBeImplemented, InterfaceImplementation implOfInterface) + void FindAndAddDefaultInterfaceImplementations (TypeDefinition typeThatImplementsInterface, TypeDefinition typeThatMayHaveDIM, MethodDefinition interfaceMethodToBeImplemented, InterfaceImplementation originalInterfaceImpl) { // Go over all interfaces, trying to find a method that is an explicit MethodImpl of the // interface method in question. @@ -305,7 +312,7 @@ void FindAndAddDefaultInterfaceImplementations (TypeDefinition typeThatImplement foreach (var potentialImplMethod in potentialImplInterface.Methods) { if (potentialImplMethod == interfaceMethodToBeImplemented && !potentialImplMethod.IsAbstract) { - AddDefaultInterfaceImplementation (interfaceMethodToBeImplemented, typeThatImplementsInterface, (implOfInterface, potentialImplMethod)); + AddDefaultInterfaceImplementation (interfaceMethodToBeImplemented, new (typeThatImplementsInterface, originalInterfaceImpl, interfaceMethodToBeImplemented.DeclaringType, context), potentialImplMethod); foundImpl = true; break; } @@ -314,9 +321,9 @@ void FindAndAddDefaultInterfaceImplementations (TypeDefinition typeThatImplement continue; // This method is an override of something. Let's see if it's the method we are looking for. - foreach (var @override in potentialImplMethod.Overrides) { - if (context.TryResolve (@override) == interfaceMethodToBeImplemented) { - AddDefaultInterfaceImplementation (interfaceMethodToBeImplemented, typeThatImplementsInterface, (implOfInterface, potentialImplMethod)); + foreach (var baseMethod in potentialImplMethod.Overrides) { + if (context.TryResolve (baseMethod) == interfaceMethodToBeImplemented) { + AddDefaultInterfaceImplementation (interfaceMethodToBeImplemented, new (typeThatImplementsInterface, originalInterfaceImpl, interfaceMethodToBeImplemented.DeclaringType, context), @potentialImplMethod); foundImpl = true; break; } @@ -330,7 +337,7 @@ void FindAndAddDefaultInterfaceImplementations (TypeDefinition typeThatImplement // We haven't found a MethodImpl on the current interface, but one of the interfaces // this interface requires could still provide it. if (!foundImpl) { - FindAndAddDefaultInterfaceImplementations (typeThatImplementsInterface, potentialImplInterface, interfaceMethodToBeImplemented, implOfInterface); + FindAndAddDefaultInterfaceImplementations (typeThatImplementsInterface, potentialImplInterface, interfaceMethodToBeImplemented, originalInterfaceImpl); } } } diff --git a/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/generated/ILLink.RoslynAnalyzer.Tests.Generator/ILLink.RoslynAnalyzer.Tests.TestCaseGenerator/Inheritance.InterfacesTests.g.cs b/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/generated/ILLink.RoslynAnalyzer.Tests.Generator/ILLink.RoslynAnalyzer.Tests.TestCaseGenerator/Inheritance.InterfacesTests.g.cs index 2e1a2bbcb3454..649b8449527f7 100644 --- a/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/generated/ILLink.RoslynAnalyzer.Tests.Generator/ILLink.RoslynAnalyzer.Tests.TestCaseGenerator/Inheritance.InterfacesTests.g.cs +++ b/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/generated/ILLink.RoslynAnalyzer.Tests.Generator/ILLink.RoslynAnalyzer.Tests.TestCaseGenerator/Inheritance.InterfacesTests.g.cs @@ -15,6 +15,12 @@ public Task CanDisableUnusedInterfaces () return RunTest (allowMissingWarnings: true); } + [Fact] + public Task InterfaceImplementedThroughBaseInterface () + { + return RunTest (allowMissingWarnings: true); + } + [Fact] public Task InterfaceOnUninstantiatedTypeRemoved () { diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/Dependencies/InterfaceImplementedThroughBaseInterface.il b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/Dependencies/InterfaceImplementedThroughBaseInterface.il new file mode 100644 index 0000000000000..61080f8b7d066 --- /dev/null +++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/Dependencies/InterfaceImplementedThroughBaseInterface.il @@ -0,0 +1,48 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +.assembly extern mscorlib { } + +.assembly 'library' { } + +.class interface public auto ansi abstract beforefieldinit IBase +{ + // Methods + .method public hidebysig newslot abstract virtual + instance void M () cil managed + { + } // end of method IBase::M + +} // end of class IBase + +.class interface public auto ansi abstract beforefieldinit IDerived + implements IBase +{ +} // end of class IDerived + +.class public auto ansi beforefieldinit C + extends [System.Runtime]System.Object + implements IDerived +{ + // Methods + .method private final hidebysig newslot virtual + instance void IBase.M () cil managed + { + .override method instance void IBase::M() + // Method begins at RVA 0x2050 + // Code size 2 (0x2) + .maxstack 8 + + IL_0001: ret + } // end of method C::IBase.M + + .method public hidebysig specialname rtspecialname + instance void .ctor () cil managed + { + // Method begins at RVA 0x2053 + // Code size 8 (0x8) + .maxstack 8 + + IL_0007: ret + } // end of method C::.ctor +} diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/InterfaceImplementedThroughBaseInterface.cs b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/InterfaceImplementedThroughBaseInterface.cs new file mode 100644 index 0000000000000..e701fb9c28ba6 --- /dev/null +++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/InterfaceImplementedThroughBaseInterface.cs @@ -0,0 +1,34 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Mono.Linker.Tests.Cases.Expectations.Assertions; +using Mono.Linker.Tests.Cases.Expectations.Metadata; + +namespace Mono.Linker.Tests.Cases.Inheritance.Interfaces +{ + [SetupLinkerArgument ("--skip-unresolved", "true")] + [SetupLinkerArgument ("-a", "test.exe", "library")] + [SetupLinkerArgument ("-a", "library.dll", "library")] + [TestCaseRequirements (TestRunCharacteristics.SupportsDefaultInterfaceMethods, "Requires support for default interface methods")] + [Define ("IL_ASSEMBLY_AVAILABLE")] + [SetupCompileBefore ("library.dll", new[] { "Dependencies/InterfaceImplementedThroughBaseInterface.il" })] + [SkipILVerify] + +#if IL_ASSEMBLY_AVAILABLE + [KeptMemberInAssembly ("library.dll", typeof(C), "IBase.M()")] +#endif + [KeptMember(".ctor()")] + public class InterfaceImplementedThroughBaseInterface + { + public static void Main () + { + } + } +} + +