From 83fdf386f1cd16aed0d05a1e217eb9084c440b7a Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Sun, 2 Aug 2026 07:48:09 +0200 Subject: [PATCH] Run InterfaceTests against the legacy csc and mcs compilers too Widening the compiler matrix answers the open review question on the Issue3230 fixture guard empirically: a class naming its own nested interface in its base list is a Roslyn-era relaxation. The legacy csc rejects every such shape with CS0146 (circular base class dependency, it never reaches the accessibility check), mcs 2.6.4 rejects them with CS0122/CS0146, and mcs 5.23 accepts them all, rejects naming a base class's protected nested interface with the same CS0122 as Roslyn, and emits the same transitive InterfaceImpl metadata. The fixtures are therefore gated to ROSLYN || MCS5, which exercises the base-list filter on mcs-generated metadata as well. The pre-existing class C needs MCS2-specific expected output because mcs 2.6.4 reorders interface-impl rows and explicit implementations in metadata. Assisted-by: Claude:claude-fable-5:Claude Code --- .../PrettyTestRunner.cs | 2 +- .../TestCases/Pretty/InterfaceTests.cs | 18 +++++++++++++++++- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs b/ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs index 5bb191410..644b12a44 100644 --- a/ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs +++ b/ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs @@ -921,7 +921,7 @@ namespace ICSharpCode.Decompiler.Tests } [Test] - public async Task InterfaceTests([ValueSource(nameof(defaultOptions))] CompilerOptions cscOptions) + public async Task InterfaceTests([ValueSource(nameof(defaultOptionsWithMcs))] CompilerOptions cscOptions) { await RunForLibrary(cscOptions: cscOptions); } diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/InterfaceTests.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/InterfaceTests.cs index d53684028..7346b5af9 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/InterfaceTests.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/InterfaceTests.cs @@ -130,7 +130,13 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty } } #endif + // mcs 2.6.4 emits interface-impl rows depth-first and explicit implementations + // ahead of ordinary members. +#if MCS2 && EXPECTED_OUTPUT + public class C : IA, IA2, IB +#else public class C : IA2, IA, IB +#endif { int IA.Property1 { get { @@ -157,14 +163,22 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty remove { } } +#if MCS2 && EXPECTED_OUTPUT + void IA.Method() + { + throw new NotImplementedException(); + } +#endif public int Finalize() { return 0; } +#if !(MCS2 && EXPECTED_OUTPUT) void IA.Method() { throw new NotImplementedException(); } +#endif } internal interface IInterfacesCannotDeclareDtors @@ -172,7 +186,9 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty int Finalize(); } -#if ROSLYN + // Naming your own nested interface in the base list is a Roslyn-era relaxation + // shared by mcs 5.23; the legacy csc rejects it with CS0146, mcs 2.6.4 with CS0122. +#if ROSLYN || MCS5 private class Issue3230_F : Issue3230_F.IFoo { protected interface IFoo