diff options
author | Jackson Schuster <36744439+jtschuster@users.noreply.github.com> | 2022-04-12 23:34:39 +0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-04-12 23:34:39 +0300 |
commit | 8c33ad3ad8cba002301cf7341ba9003379247edb (patch) | |
tree | 36a5fd293855a21df9a1f3ec3332c6a2c12a0b6c /test | |
parent | 805c6a7f868348bc2400fcacb4f00998bd67b60d (diff) |
Don't remove unused interfaces in library mode (#2711)
This will make sure that we keep all interfaces and all interface method implementations on such type.
* Change the solution to rely on the optimization setting only
* wip
* Add implicit interface implementation case
* Edit tests to remove issue-specific code and names
* Mark members of CollectedType as kept
* Add private interface test and simplify external interface example
* Replace early exit for non-interfaces
* License headers and use IsMethodNeededByTypeDueToPreservedScope instead of Interface specific version
* Add check for static methods before skipping virtual marking
Check for optimization before skipping marking interface methods for
PreservedScope
Add doc comments
* Add more clarifying comments, move static method check, rename method
Rename IsVirtualNeededByInstantiatedTypeDueToPreservedScope to IsOverrideNeededByInstantiatedTypeDueToPreservedScope
Added more comments describing purpose of methods
Moved the static interface method check to the method that is called on all types regardless of instantiation
* Renames and comment cleanup + tests
This doesn't change product behavior, only renamed methods and improved comments.
Added new tests for the RootLibrary:
- Added a dependency to an "copy" assembly (mainly because I can define a static interface in it)
- Added more combinations to the interfaces/classes in the test
- Since this uses static interface methods I had to enable "preview" language features for the test project and for the test infra.
* More tests for interface behavior
Co-authored-by: vitek-karas <vitek.karas@microsoft.com>
Co-authored-by: vitek-karas <10670590+vitek-karas@users.noreply.github.com>
Diffstat (limited to 'test')
7 files changed, 489 insertions, 3 deletions
diff --git a/test/ILLink.RoslynAnalyzer.Tests/generated/ILLink.RoslynAnalyzer.Tests.Generator/ILLink.RoslynAnalyzer.Tests.TestCaseGenerator/Inheritance.InterfacesTests.g.cs b/test/ILLink.RoslynAnalyzer.Tests/generated/ILLink.RoslynAnalyzer.Tests.Generator/ILLink.RoslynAnalyzer.Tests.TestCaseGenerator/Inheritance.InterfacesTests.g.cs index 7b604a38a..74ea1c72d 100644 --- a/test/ILLink.RoslynAnalyzer.Tests/generated/ILLink.RoslynAnalyzer.Tests.Generator/ILLink.RoslynAnalyzer.Tests.TestCaseGenerator/Inheritance.InterfacesTests.g.cs +++ b/test/ILLink.RoslynAnalyzer.Tests/generated/ILLink.RoslynAnalyzer.Tests.Generator/ILLink.RoslynAnalyzer.Tests.TestCaseGenerator/Inheritance.InterfacesTests.g.cs @@ -21,5 +21,11 @@ namespace ILLink.RoslynAnalyzer.Tests.Inheritance return RunTest (allowMissingWarnings: true); } + [Fact] + public Task InterfaceVariants () + { + return RunTest (allowMissingWarnings: true); + } + } }
\ No newline at end of file diff --git a/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/Dependencies/CopyLibrary.cs b/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/Dependencies/CopyLibrary.cs new file mode 100644 index 000000000..655198709 --- /dev/null +++ b/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/Dependencies/CopyLibrary.cs @@ -0,0 +1,17 @@ +// 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. + +namespace Mono.Linker.Tests.Cases.Inheritance.Interfaces.Dependencies +{ + public interface ICopyLibraryInterface + { + void CopyLibraryInterfaceMethod (); + void CopyLibraryExplicitImplementationInterfaceMethod (); + } + + public interface ICopyLibraryStaticInterface + { + static abstract void CopyLibraryStaticInterfaceMethod (); + static abstract void CopyLibraryExplicitImplementationStaticInterfaceMethod (); + } +} diff --git a/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/InterfaceOnUninstantiatedTypeRemoved.cs b/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/InterfaceOnUninstantiatedTypeRemoved.cs index 6b2cbd511..1c4a08112 100644 --- a/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/InterfaceOnUninstantiatedTypeRemoved.cs +++ b/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/InterfaceOnUninstantiatedTypeRemoved.cs @@ -3,7 +3,6 @@ using Mono.Linker.Tests.Cases.Expectations.Metadata; namespace Mono.Linker.Tests.Cases.Inheritance.Interfaces { - [SetupLinkerArgument ("--disable-opt", "unusedinterfaces")] public class InterfaceOnUninstantiatedTypeRemoved { public static void Main () diff --git a/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/InterfaceVariants.cs b/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/InterfaceVariants.cs new file mode 100644 index 000000000..f9d54d009 --- /dev/null +++ b/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/InterfaceVariants.cs @@ -0,0 +1,242 @@ +// 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 Mono.Linker.Tests.Cases.Expectations.Assertions; +using Mono.Linker.Tests.Cases.Expectations.Helpers; +using Mono.Linker.Tests.Cases.Expectations.Metadata; +using Mono.Linker.Tests.Cases.Inheritance.Interfaces.Dependencies; + +namespace Mono.Linker.Tests.Cases.Inheritance.Interfaces +{ + [SetupCompileBefore ("copylibrary.dll", new[] { "Dependencies/CopyLibrary.cs" })] + [SetupLinkerAction ("copy", "copylibrary")] + public class InterfaceVariants + { + public static void Main () + { + Type t = typeof (UninstantiatedPublicClassWithInterface); + t = typeof (UninstantiatedPublicClassWithImplicitlyImplementedInterface); + t = typeof (UninstantiatedPublicClassWithPrivateInterface); + + UninstantiatedPublicClassWithInterface.InternalStaticInterfaceMethodUsed (); + InstantiatedClassWithInterfaces.InternalStaticInterfaceMethodUsed (); + + // Use all public interfaces - they're marked as public only to denote them as "used" + typeof (IPublicInterface).RequiresPublicMethods (); + typeof (IPublicStaticInterface).RequiresPublicMethods (); + + var a = new InstantiatedClassWithInterfaces (); + } + + [Kept] + [KeptInterface (typeof (IEnumerator))] + [KeptInterface (typeof (IPublicInterface))] + [KeptInterface (typeof (IPublicStaticInterface))] + [KeptInterface (typeof (IInternalStaticInterfaceWithUsedMethod))] // https://github.com/dotnet/linker/issues/2733 + [KeptInterface (typeof (ICopyLibraryInterface))] + [KeptInterface (typeof (ICopyLibraryStaticInterface))] + public class UninstantiatedPublicClassWithInterface : + IPublicInterface, + IPublicStaticInterface, + IInternalInterface, + IInternalStaticInterface, + IInternalStaticInterfaceWithUsedMethod, + IEnumerator, + ICopyLibraryInterface, + ICopyLibraryStaticInterface + { + internal UninstantiatedPublicClassWithInterface () { } + + [Kept] + public void PublicInterfaceMethod () { } + + [Kept] + void IPublicInterface.ExplicitImplementationPublicInterfaceMethod () { } + + [Kept] + public static void PublicStaticInterfaceMethod () { } + + [Kept] + static void IPublicStaticInterface.ExplicitImplementationPublicStaticInterfaceMethod () { } + + public void InternalInterfaceMethod () { } + + void IInternalInterface.ExplicitImplementationInternalInterfaceMethod () { } + + public static void InternalStaticInterfaceMethod () { } + + static void IInternalStaticInterface.ExplicitImplementationInternalStaticInterfaceMethod () { } + + [Kept] + public static void InternalStaticInterfaceMethodUsed () { } + + [Kept] + [ExpectBodyModified] + bool IEnumerator.MoveNext () { throw new PlatformNotSupportedException (); } + + [Kept] + object IEnumerator.Current { + [Kept] + [ExpectBodyModified] + get { throw new PlatformNotSupportedException (); } + } + + [Kept] + void IEnumerator.Reset () { } + + [Kept] + public void CopyLibraryInterfaceMethod () { } + + [Kept] + void ICopyLibraryInterface.CopyLibraryExplicitImplementationInterfaceMethod () { } + + [Kept] + public static void CopyLibraryStaticInterfaceMethod () { } + + [Kept] + static void ICopyLibraryStaticInterface.CopyLibraryExplicitImplementationStaticInterfaceMethod () { } + } + + [Kept] + [KeptInterface (typeof (IFormattable))] + public class UninstantiatedPublicClassWithImplicitlyImplementedInterface : IInternalInterface, IFormattable + { + internal UninstantiatedPublicClassWithImplicitlyImplementedInterface () { } + + public void InternalInterfaceMethod () { } + + void IInternalInterface.ExplicitImplementationInternalInterfaceMethod () { } + + [Kept] + [ExpectBodyModified] + [ExpectLocalsModified] + public string ToString (string format, IFormatProvider formatProvider) + { + return "formatted string"; + } + } + + [Kept] + [KeptInterface (typeof (IEnumerator))] + [KeptInterface (typeof (IPublicInterface))] + [KeptInterface (typeof (IPublicStaticInterface))] + [KeptInterface (typeof (IInternalStaticInterfaceWithUsedMethod))] // https://github.com/dotnet/linker/issues/2733 + [KeptInterface (typeof (ICopyLibraryInterface))] + [KeptInterface (typeof (ICopyLibraryStaticInterface))] + public class InstantiatedClassWithInterfaces : + IPublicInterface, + IPublicStaticInterface, + IInternalInterface, + IInternalStaticInterface, + IInternalStaticInterfaceWithUsedMethod, + IEnumerator, + ICopyLibraryInterface, + ICopyLibraryStaticInterface + { + [Kept] + public InstantiatedClassWithInterfaces () { } + + [Kept] + public void PublicInterfaceMethod () { } + + [Kept] + void IPublicInterface.ExplicitImplementationPublicInterfaceMethod () { } + + [Kept] + public static void PublicStaticInterfaceMethod () { } + + [Kept] + static void IPublicStaticInterface.ExplicitImplementationPublicStaticInterfaceMethod () { } + + public void InternalInterfaceMethod () { } + + void IInternalInterface.ExplicitImplementationInternalInterfaceMethod () { } + + public static void InternalStaticInterfaceMethod () { } + + static void IInternalStaticInterface.ExplicitImplementationInternalStaticInterfaceMethod () { } + + [Kept] + public static void InternalStaticInterfaceMethodUsed () { } + + [Kept] + bool IEnumerator.MoveNext () { throw new PlatformNotSupportedException (); } + + [Kept] + object IEnumerator.Current { [Kept] get { throw new PlatformNotSupportedException (); } } + + [Kept] + void IEnumerator.Reset () { } + + [Kept] + public void CopyLibraryInterfaceMethod () { } + + [Kept] + void ICopyLibraryInterface.CopyLibraryExplicitImplementationInterfaceMethod () { } + + [Kept] + public static void CopyLibraryStaticInterfaceMethod () { } + + [Kept] + static void ICopyLibraryStaticInterface.CopyLibraryExplicitImplementationStaticInterfaceMethod () { } + } + + [Kept] + public class UninstantiatedPublicClassWithPrivateInterface : IPrivateInterface + { + internal UninstantiatedPublicClassWithPrivateInterface () { } + + void IPrivateInterface.PrivateInterfaceMethod () { } + } + + [Kept] + public interface IPublicInterface + { + [Kept] + void PublicInterfaceMethod (); + + [Kept] + void ExplicitImplementationPublicInterfaceMethod (); + } + + [Kept] + public interface IPublicStaticInterface + { + [Kept] + static abstract void PublicStaticInterfaceMethod (); + + [Kept] + static abstract void ExplicitImplementationPublicStaticInterfaceMethod (); + } + + internal interface IInternalInterface + { + void InternalInterfaceMethod (); + + void ExplicitImplementationInternalInterfaceMethod (); + } + + internal interface IInternalStaticInterface + { + static abstract void InternalStaticInterfaceMethod (); + + static abstract void ExplicitImplementationInternalStaticInterfaceMethod (); + } + + // The interface methods themselves are not used, but the implentation of these methods is + // https://github.com/dotnet/linker/issues/2733 + [Kept] + internal interface IInternalStaticInterfaceWithUsedMethod + { + [Kept] // https://github.com/dotnet/linker/issues/2733 + static abstract void InternalStaticInterfaceMethodUsed (); + } + + private interface IPrivateInterface + { + void PrivateInterfaceMethod (); + } + } +} diff --git a/test/Mono.Linker.Tests.Cases/Libraries/Dependencies/CopyLibrary.cs b/test/Mono.Linker.Tests.Cases/Libraries/Dependencies/CopyLibrary.cs new file mode 100644 index 000000000..0f31e8277 --- /dev/null +++ b/test/Mono.Linker.Tests.Cases/Libraries/Dependencies/CopyLibrary.cs @@ -0,0 +1,17 @@ +// 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. + +namespace Mono.Linker.Tests.Cases.Libraries.Dependencies +{ + public interface ICopyLibraryInterface + { + void CopyLibraryInterfaceMethod (); + void CopyLibraryExplicitImplementationInterfaceMethod (); + } + + public interface ICopyLibraryStaticInterface + { + static abstract void CopyLibraryStaticInterfaceMethod (); + static abstract void CopyLibraryExplicitImplementationStaticInterfaceMethod (); + } +} diff --git a/test/Mono.Linker.Tests.Cases/Libraries/RootLibrary.cs b/test/Mono.Linker.Tests.Cases/Libraries/RootLibrary.cs index 8cc979367..1534573ea 100644 --- a/test/Mono.Linker.Tests.Cases/Libraries/RootLibrary.cs +++ b/test/Mono.Linker.Tests.Cases/Libraries/RootLibrary.cs @@ -1,11 +1,18 @@ +// 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.Diagnostics.CodeAnalysis; -using System.Runtime.CompilerServices; using System.Runtime.Serialization; using Mono.Linker.Tests.Cases.Expectations.Assertions; using Mono.Linker.Tests.Cases.Expectations.Metadata; +using Mono.Linker.Tests.Cases.Libraries.Dependencies; namespace Mono.Linker.Tests.Cases.Libraries { + [SetupCompileBefore ("copylibrary.dll", new[] { "Dependencies/CopyLibrary.cs" })] + [SetupLinkerAction ("copy", "copylibrary")] [SetupLinkerArgument ("-a", "test.exe", "library")] [SetupLinkerArgument ("--enable-opt", "ipconstprop")] [VerifyMetadataNames] @@ -172,6 +179,204 @@ namespace Mono.Linker.Tests.Cases.Libraries public interface I { } + + [Kept] + [KeptInterface (typeof (IEnumerator))] + [KeptInterface (typeof (IPublicInterface))] + [KeptInterface (typeof (IPublicStaticInterface))] + [KeptInterface (typeof (IInternalInterface))] + [KeptInterface (typeof (IInternalStaticInterface))] + [KeptInterface (typeof (ICopyLibraryInterface))] + [KeptInterface (typeof (ICopyLibraryStaticInterface))] + public class UninstantiatedPublicClassWithInterface : + IPublicInterface, + IPublicStaticInterface, + IInternalInterface, + IInternalStaticInterface, + IEnumerator, + ICopyLibraryInterface, + ICopyLibraryStaticInterface + { + internal UninstantiatedPublicClassWithInterface () { } + + [Kept] + public void PublicInterfaceMethod () { } + + [Kept] + void IPublicInterface.ExplicitImplementationPublicInterfaceMethod () { } + + [Kept] + public static void PublicStaticInterfaceMethod () { } + + [Kept] + static void IPublicStaticInterface.ExplicitImplementationPublicStaticInterfaceMethod () { } + + [Kept] + public void InternalInterfaceMethod () { } + + void IInternalInterface.ExplicitImplementationInternalInterfaceMethod () { } + + [Kept] + public static void InternalStaticInterfaceMethod () { } + + static void IInternalStaticInterface.ExplicitImplementationInternalStaticInterfaceMethod () { } + + [Kept] + bool IEnumerator.MoveNext () { throw new PlatformNotSupportedException (); } + + [Kept] + object IEnumerator.Current { [Kept] get { throw new PlatformNotSupportedException (); } } + + [Kept] + void IEnumerator.Reset () { } + + [Kept] + public void CopyLibraryInterfaceMethod () { } + + [Kept] + void ICopyLibraryInterface.CopyLibraryExplicitImplementationInterfaceMethod () { } + + [Kept] + public static void CopyLibraryStaticInterfaceMethod () { } + + [Kept] + static void ICopyLibraryStaticInterface.CopyLibraryExplicitImplementationStaticInterfaceMethod () { } + } + + [Kept] + [KeptInterface (typeof (IInternalInterface))] + [KeptInterface (typeof (IFormattable))] + public class UninstantiatedPublicClassWithImplicitlyImplementedInterface : IInternalInterface, IFormattable + { + internal UninstantiatedPublicClassWithImplicitlyImplementedInterface () { } + + [Kept] + public void InternalInterfaceMethod () { } + + void IInternalInterface.ExplicitImplementationInternalInterfaceMethod () { } + + [Kept] + public string ToString (string format, IFormatProvider formatProvider) + { + return "formatted string"; + } + } + + [Kept] + [KeptInterface (typeof (IEnumerator))] + [KeptInterface (typeof (IPublicInterface))] + [KeptInterface (typeof (IPublicStaticInterface))] + [KeptInterface (typeof (IInternalInterface))] + [KeptInterface (typeof (IInternalStaticInterface))] + [KeptInterface (typeof (ICopyLibraryInterface))] + [KeptInterface (typeof (ICopyLibraryStaticInterface))] + public class InstantiatedClassWithInterfaces : + IPublicInterface, + IPublicStaticInterface, + IInternalInterface, + IInternalStaticInterface, + IEnumerator, + ICopyLibraryInterface, + ICopyLibraryStaticInterface + { + [Kept] + public InstantiatedClassWithInterfaces () { } + + [Kept] + public void PublicInterfaceMethod () { } + + [Kept] + void IPublicInterface.ExplicitImplementationPublicInterfaceMethod () { } + + [Kept] + public static void PublicStaticInterfaceMethod () { } + + [Kept] + static void IPublicStaticInterface.ExplicitImplementationPublicStaticInterfaceMethod () { } + + [Kept] + public void InternalInterfaceMethod () { } + + void IInternalInterface.ExplicitImplementationInternalInterfaceMethod () { } + + [Kept] + public static void InternalStaticInterfaceMethod () { } + + static void IInternalStaticInterface.ExplicitImplementationInternalStaticInterfaceMethod () { } + + [Kept] + bool IEnumerator.MoveNext () { throw new PlatformNotSupportedException (); } + + [Kept] + object IEnumerator.Current { [Kept] get { throw new PlatformNotSupportedException (); } } + + [Kept] + void IEnumerator.Reset () { } + + [Kept] + public void CopyLibraryInterfaceMethod () { } + + [Kept] + void ICopyLibraryInterface.CopyLibraryExplicitImplementationInterfaceMethod () { } + + [Kept] + public static void CopyLibraryStaticInterfaceMethod () { } + + [Kept] + static void ICopyLibraryStaticInterface.CopyLibraryExplicitImplementationStaticInterfaceMethod () { } + } + + [Kept] + [KeptInterface (typeof (IPrivateInterface))] + public class UninstantiatedPublicClassWithPrivateInterface : IPrivateInterface + { + internal UninstantiatedPublicClassWithPrivateInterface () { } + + void IPrivateInterface.PrivateInterfaceMethod () { } + } + + [Kept] + public interface IPublicInterface + { + [Kept] + void PublicInterfaceMethod (); + + [Kept] + void ExplicitImplementationPublicInterfaceMethod (); + } + + [Kept] + public interface IPublicStaticInterface + { + [Kept] + static abstract void PublicStaticInterfaceMethod (); + + [Kept] + static abstract void ExplicitImplementationPublicStaticInterfaceMethod (); + } + + [Kept] + internal interface IInternalInterface + { + void InternalInterfaceMethod (); + + void ExplicitImplementationInternalInterfaceMethod (); + } + + [Kept] + internal interface IInternalStaticInterface + { + [Kept] // https://github.com/dotnet/linker/issues/2733 + static abstract void InternalStaticInterfaceMethod (); + + static abstract void ExplicitImplementationInternalStaticInterfaceMethod (); + } + + [Kept] + private interface IPrivateInterface + { + void PrivateInterfaceMethod (); + } } internal class RootLibrary_Internal diff --git a/test/Mono.Linker.Tests/TestCasesRunner/TestCaseCompiler.cs b/test/Mono.Linker.Tests/TestCasesRunner/TestCaseCompiler.cs index 8aeb92da8..991c1860e 100644 --- a/test/Mono.Linker.Tests/TestCasesRunner/TestCaseCompiler.cs +++ b/test/Mono.Linker.Tests/TestCasesRunner/TestCaseCompiler.cs @@ -234,7 +234,7 @@ namespace Mono.Linker.Tests.TestCasesRunner #if NETCOREAPP protected virtual NPath CompileCSharpAssemblyWithRoslyn (CompilerOptions options) { - var languageVersion = LanguageVersion.Default; + var languageVersion = LanguageVersion.Preview; var compilationOptions = new CSharpCompilationOptions ( outputKind: options.OutputPath.FileName.EndsWith (".exe") ? OutputKind.ConsoleApplication : OutputKind.DynamicallyLinkedLibrary, assemblyIdentityComparer: DesktopAssemblyIdentityComparer.Default |