TUnit icon indicating copy to clipboard operation
TUnit copied to clipboard

Error: ModuleInitializerAttribute is inaccessible due to its protection level in .NET Framework

Open sliekens opened this issue 1 month ago • 14 comments

The automatic Polyfill reference for legacy frameworks seems to have internal visibility, and TUnit can't access it.

$ dotnet run
/workspaces/repro/MyTests/obj/Debug/net481/TUnit.Core.SourceGenerator/TUnit.Core.SourceGenerator.CodeGenerators.DisableReflectionScannerGenerator/DisableReflectionScanner.g.cs(7,46): error CS0122: 'ModuleInitializerAttribute' is inaccessible due to its protection level
/workspaces/repro/MyTests/obj/Debug/net481/TUnit.Core.SourceGenerator/TUnit.Core.SourceGenerator.CodeGenerators.AssemblyLoaderGenerator/AssemblyLoader.g.cs(7,46): error CS0122: 'ModuleInitializerAttribute' is inaccessible due to its protection level

Repro steps (tested in WSL2 with Mono 6.12 / .NET 10 RC2 CLI for bootstrapping the project):

dotnet new console -o MyTests -f net10.0
dotnet add MyTests package TUnit

sed -i 's/net10.0/net481/g' MyTests/MyTests.csproj
sed -i '/<Nullable>enable<\/Nullable>/a \    <LangVersion>14</LangVersion>' MyTests/MyTests.csproj

rm MyTests/Program.cs
cat << 'EOF' > MyTests/MyTests.cs
namespace MyTests;

public class MyTests
{
    [Test]
    public async Task MyTest()
    {
        await Assert.That(true).IsTrue();
    }
}
EOF

dotnet run --project MyTests

As a workaround, I disabled the automatic polyfills and installed it manually:

sed -i '/<LangVersion>14<\/LangVersion>/a \    <EnableTUnitPolyfills>false</EnableTUnitPolyfills>' MyTests/MyTests.csproj
dotnet add MyTests package Polyfill
dotnet run --project MyTests

sliekens avatar Nov 08 '25 15:11 sliekens

I think it will be easier to solve this with Polyfill 9.0.0 which adds support for Microsoft.CodeAnalysis.EmbeddedAttribute since https://github.com/SimonCropp/Polyfill/pull/388

I think you can embed Polyfill 9.0.0 in TUnit.Core.SourceGenerator with <PolyUseEmbeddedAttribute>true</PolyUseEmbeddedAttribute> and the result should be a better experience that doesn't rely on workarounds with EnableTUnitPolyfills. https://github.com/SimonCropp/Polyfill/blob/main/consuming.md#recommended-consuming-pattern

sliekens avatar Nov 11 '25 18:11 sliekens

Thanks for that tip @sliekens !

Does this work better for you in v1.1.0?

thomhurst avatar Nov 12 '25 19:11 thomhurst

Unfortunately no, the error persists. It appears that the implicitly added polyfills are not being added to the compilation.

I compared the msbuild.binlog (generated via dotnet build -bl) for implicit and for explicit polyfills.

Implicit: only MyTest.cs is added to the compilation.

Image

Explicit: all files provided by Polyfill are also added to the compilation.

Image

sliekens avatar Nov 12 '25 21:11 sliekens

I think the reason is that dotnet build is not seeing the implicitly added PackageReference

Image

Compare with explicit reference

Image

sliekens avatar Nov 12 '25 21:11 sliekens

@thomhurst if you want a look for yourself, here are the binlogs.zip, you can view them with MSBuild Log Viewer

sliekens avatar Nov 12 '25 21:11 sliekens

@thomhurst I have some bad news, this looks like it works this way by design:

Guidance for the content of MSBuild props and targets

NuGet does not limit how you author .props and .targets as they will vary based on the need of the package author and the target projects themselves.

There are a few things that must not be done in packages' .props and .targets, such as not specifying properties and items that affect restore, as those will be automatically excluded.

Some examples of properties that must not be added or updated: TargetFramework, TargetFrameworkMoniker, TargetPlatformMoniker, AssetTargetFallback etc.

Some examples of items that must not be added or updated: PackageReference, PackageVersion, PackageDownload, etc. https://learn.microsoft.com/en-us/nuget/concepts/msbuild-props-and-targets#guidance-for-the-content-of-msbuild-props-and-targets

Sounds like the PackageReference items added by TUnit.Core.targets are automatically excluded again 🫤.

sliekens avatar Nov 12 '25 21:11 sliekens

Dang. I guess this'll just have to be a manual step then for .NET framework users

thomhurst avatar Nov 12 '25 21:11 thomhurst

Or would the Poly EmbeddedAttribute + PolyPublic + Standard package reference approach work for non .NET core apps 🤔

thomhurst avatar Nov 12 '25 22:11 thomhurst

Sounds crazy enough to work. My first instinct was that PolyPublic would introduce ambiguous type references, but then I guess the EmbeddedAttribute could solve that. But this some serious off road shit 😄.

sliekens avatar Nov 12 '25 22:11 sliekens

@SimonCropp Can you share any wisdom? 😅

thomhurst avatar Nov 12 '25 23:11 thomhurst

actually @Youssef1313 is the subject matter expert here

is there a repro solution i can play with?

SimonCropp avatar Nov 12 '25 23:11 SimonCropp

I haven't played with the repro but I think TUnit users will have to add the Polyfill package reference themselves and enable the use of embedded attribute.

Alternatively, if TUnit wants to really add the package ref automatically, this is only possible via an MSBuild SDK, similar to MSTest.Sdk.

Youssef1313 avatar Nov 13 '25 04:11 Youssef1313

Damn, an Sdk just for that seems a bit overkill. Might just have to rely on users doing this manually then

thomhurst avatar Nov 13 '25 07:11 thomhurst

FWIW I am totally fine with this being a manual step for Framework targets. I think they broadly fall into one of two categories :

  1. Enterprise legacy maintainers who have no need or want for modern approaches with source generators, they will stay on what they are already using (MSTest, NUnit...)
  2. Library authors who are already Polyfill ing their legacy targets for other reasons

sliekens avatar Nov 13 '25 07:11 sliekens