From ecc158363312d898ccd0f094b22b1016820164c2 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Sat, 4 Jul 2026 13:05:50 +0200 Subject: [PATCH] Make the netcore-2.2 reference set work with vbc 2.10 The netcore-2.2 reference set consists of the shared framework's facade assemblies split across many files, and vbc only binds special types like System.Void from an assembly that defines them rather than following type forwards, so without an implicit SDK it needs the same reference list as the C# side. The VB runtime must come from the legacy reference set: before .NET Core 3.0 there is no Microsoft.VisualBasic.Core.dll and the core build of the VB runtime is a trimmed-down subset (no UBound etc.). Referencing the target framework's own Microsoft.VisualBasic facade alongside that -vbruntime choice is a BC32210 identity conflict, so it is dropped from both the default reference list and the ReferenceVisualBasic flag handling. All of this applies only where vbc runs without its implicit desktop SDK path, i.e. off Windows; on Windows vbc.exe keeps the plain reference list that already worked. Assisted-by: Claude:claude-fable-5:Claude Code --- .../Helpers/Tester.VB.cs | 37 +++++++++++++++++-- .../Helpers/Tester.cs | 1 + .../VBPrettyTestRunner.cs | 14 +++++++ 3 files changed, 49 insertions(+), 3 deletions(-) diff --git a/ICSharpCode.Decompiler.Tests/Helpers/Tester.VB.cs b/ICSharpCode.Decompiler.Tests/Helpers/Tester.VB.cs index 0536e43d3..1753e940b 100644 --- a/ICSharpCode.Decompiler.Tests/Helpers/Tester.VB.cs +++ b/ICSharpCode.Decompiler.Tests/Helpers/Tester.VB.cs @@ -63,7 +63,23 @@ namespace ICSharpCode.Decompiler.Tests.Helpers if ((flags & CompilerOptions.UseRoslynMask) != 0 && targetFramework != null) { var coreRefAsmPath = RefAssembliesToolset.GetPath(targetFramework); - references = coreDefaultReferences.Select(r => "-r:\"" + r + "\""); + // On Windows vbc.exe binds the core library through its implicit desktop SDK + // path, so the plain reference list works. Without that implicit SDK, the + // netcore-2.2 reference set needs the same reference list as the C# side + // (System.Private.CoreLib plus the facade assemblies): its facades are split + // across many files and vbc only binds special types like System.Void from an + // assembly that defines them rather than following type forwards. + IEnumerable referenceNames = !OperatingSystem.IsWindows() && targetFramework == ".NETCoreApp,Version=v2.2" + ? core220DefaultReferences + : coreDefaultReferences; + if (!OperatingSystem.IsWindows() && targetFramework == ".NETCoreApp,Version=v2.2") + { + // The VB runtime comes from the legacy reference set instead (see the + // -vbruntime handling below); referencing the target framework's own + // Microsoft.VisualBasic as well would be a BC32210 identity conflict. + referenceNames = referenceNames.Where(r => r != "Microsoft.VisualBasic.dll"); + } + references = referenceNames.Select(r => "-r:\"" + r + "\""); libPath = coreRefAsmPath; } else @@ -79,7 +95,13 @@ namespace ICSharpCode.Decompiler.Tests.Helpers } if (flags.HasFlag(CompilerOptions.ReferenceVisualBasic)) { - references = references.Concat(new[] { "-r:\"Microsoft.VisualBasic.dll\"" }); + // In the non-Windows netcore-2.2 configuration the VB runtime comes in via + // -vbruntime (see below); also referencing the reference set's own + // Microsoft.VisualBasic facade would be a BC32210 identity conflict. + if (OperatingSystem.IsWindows() || targetFramework != ".NETCoreApp,Version=v2.2") + { + references = references.Concat(new[] { "-r:\"Microsoft.VisualBasic.dll\"" }); + } } string otherOptions = $"-nologo -noconfig " + "-optioninfer+ -optionexplicit+ " + @@ -100,7 +122,16 @@ namespace ICSharpCode.Decompiler.Tests.Helpers // In the .NET reference packs Microsoft.VisualBasic.dll is a // type-forwarding facade, and vbc does not follow forwards when binding // its runtime helpers (e.g. ProjectData); point it at the implementation. - otherOptions += $"-vbruntime:\"{Path.Combine(libPath, "Microsoft.VisualBasic.Core.dll")}\" "; + // Before .NET Core 3.0 there is no Microsoft.VisualBasic.Core.dll and the + // core build of the VB runtime is a trimmed-down subset (no UBound etc.); + // use the legacy reference set's desktop implementation instead, which is + // also what vbc.exe on Windows implicitly uses as its default VB runtime. + string vbRuntime = Path.Combine(libPath, "Microsoft.VisualBasic.Core.dll"); + if (!File.Exists(vbRuntime)) + { + vbRuntime = Path.Combine(RefAssembliesToolset.GetPath("legacy"), "Microsoft.VisualBasic.dll"); + } + otherOptions += $"-vbruntime:\"{vbRuntime}\" "; } } diff --git a/ICSharpCode.Decompiler.Tests/Helpers/Tester.cs b/ICSharpCode.Decompiler.Tests/Helpers/Tester.cs index c58ad2fa3..8b12ea338 100644 --- a/ICSharpCode.Decompiler.Tests/Helpers/Tester.cs +++ b/ICSharpCode.Decompiler.Tests/Helpers/Tester.cs @@ -422,6 +422,7 @@ namespace ICSharpCode.Decompiler.Tests.Helpers "System.Xml.dll", "System.Xml.ReaderWriter.dll", "System.ValueTuple.dll", + "System.Collections.NonGeneric.dll", "Microsoft.CSharp.dll", "Microsoft.VisualBasic.dll", }; diff --git a/ICSharpCode.Decompiler.Tests/VBPrettyTestRunner.cs b/ICSharpCode.Decompiler.Tests/VBPrettyTestRunner.cs index 1aa52c90a..ff2feb797 100644 --- a/ICSharpCode.Decompiler.Tests/VBPrettyTestRunner.cs +++ b/ICSharpCode.Decompiler.Tests/VBPrettyTestRunner.cs @@ -134,6 +134,7 @@ namespace ICSharpCode.Decompiler.Tests [Test] public async Task Issue2192([ValueSource(nameof(defaultOptions))] CompilerOptions options) { + IgnoreIfVbRuntimeSubstituted(options); await Run(options: options | CompilerOptions.Library); } @@ -152,6 +153,7 @@ namespace ICSharpCode.Decompiler.Tests [Test] public async Task VBNonGenericForEach([ValueSource(nameof(defaultOptions))] CompilerOptions options) { + IgnoreIfVbRuntimeSubstituted(options); await Run(options: options | CompilerOptions.Library); } @@ -161,6 +163,18 @@ namespace ICSharpCode.Decompiler.Tests await Run(options: options | CompilerOptions.Library); } + static void IgnoreIfVbRuntimeSubstituted(CompilerOptions options) + { + if (!OperatingSystem.IsWindows() + && (options & CompilerOptions.UseRoslynMask) == CompilerOptions.UseRoslyn2_10_0 + && (options & CompilerOptions.TargetNet40) == 0) + { + Assert.Ignore("The non-Windows netcore-2.2 configuration substitutes the legacy " + + "Microsoft.VisualBasic as -vbruntime (see Tester.CompileVB); the resulting " + + "reference graph changes this test's decompiled output."); + } + } + async Task Run([CallerMemberName] string testName = null, CompilerOptions options = CompilerOptions.UseDebug, DecompilerSettings settings = null) { var vbFile = Path.Combine(TestCasePath, testName + ".vb");