dotnet / razor

Compiler and tooling experience for Razor ASP.NET Core apps in Visual Studio, Visual Studio for Mac, and VS Code.
https://asp.net
MIT License
502 stars 195 forks source link

CS8669 - #nullable enable does not work in razor files unless in code block #8720

Open Ogglas opened 1 year ago

Ogglas commented 1 year ago

Is there an existing issue for this?

Describe the bug

I'm writing a new component based on this:

https://github.com/microsoft/fluentui-blazor/blob/main/src/Microsoft.Fast.Components.FluentUI/Components/Search/FluentSearch.razor

Doing this I got the error CS8669 inside a razor file:

The annotation for nullable reference types should only be used in code within a '#nullable' annotations context. Auto-generated code requires an explicit '#nullable' directive in source.

I was able to disable the warning like this:

@{#pragma warning disable CS8669}
<fluent-search @ref=Element
               value=@Value
               current-value="@BindConverter.FormatValue(CurrentValue)"
               @onchange="@(EventCallback.Factory.CreateBinder<string?>(this, __value => CurrentValueAsString = __value, CurrentValueAsString))"
               @oninput="@(EventCallback.Factory.CreateBinder<string?>(this, __value => CurrentValueAsString = __value, CurrentValueAsString))"
               @attributes="AdditionalAttributes">
    @ChildContent
</fluent-search>
@{#pragma warning restore CS8669}

However this will not work:

@{#nullable enable}
<fluent-search @ref=Element
               value=@Value
               current-value="@BindConverter.FormatValue(CurrentValue)"
               @onchange="@(EventCallback.Factory.CreateBinder<string?>(this, __value => CurrentValueAsString = __value, CurrentValueAsString))"
               @oninput="@(EventCallback.Factory.CreateBinder<string?>(this, __value => CurrentValueAsString = __value, CurrentValueAsString))"
               @attributes="AdditionalAttributes">
    @ChildContent
</fluent-search>
@{#nullable disable}

It does not work to add @{#nullable enable} on top of the file either.

If it is inside a code block everything works as expected:

@code {
    private Timer timer = null;

#nullable enable
    NewFluentSearch? searchTest;
#nullable disable
...
}

Expected Behavior

@{#nullable enable} and @{#nullable disable} should work everywhere inside a razor file where CS8669 and similar errors can occur.

Steps To Reproduce

No response

Exceptions (if any)

No response

.NET Version

7.0.203

Anything else?

ASP.NET Core version: 7.0.5 Visual Studio 2022 Version 17.5.5 .NET SDK: Version: 7.0.203 Commit: 5b005c19f5

Runtime Environment: OS Name: Windows OS Version: 10.0.22621 OS Platform: Windows RID: win10-x64 Base Path: C:\Program Files\dotnet\sdk\7.0.203\

Host: Version: 7.0.5 Architecture: x64 Commit: 8042d61b17

.NET SDKs installed: 7.0.203 [C:\Program Files\dotnet\sdk]

.NET runtimes installed: Microsoft.AspNetCore.App 6.0.16 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App] Microsoft.AspNetCore.App 7.0.5 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App] Microsoft.NETCore.App 6.0.16 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App] Microsoft.NETCore.App 7.0.5 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App] Microsoft.WindowsDesktop.App 6.0.16 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App] Microsoft.WindowsDesktop.App 7.0.5 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]

Other architectures found: x86 [C:\Program Files (x86)\dotnet] registered at [HKLM\SOFTWARE\dotnet\Setup\InstalledVersions\x86\InstallLocation]

Environment variables: Not set

global.json file: Not found

Learn more: https://aka.ms/dotnet/info

Download .NET: https://aka.ms/dotnet/download

chsienki commented 1 year ago

@333fred I assume this is because we wrap basically every chunk of emitted code with #nullable directives?

idg10 commented 1 year ago

It appears to be this code that does that:

https://github.com/dotnet/razor/blob/852f223a1c5a7acc160fb85f36215cd0dfe89d55/src/Compiler/Microsoft.AspNetCore.Razor.Language/src/CodeGeneration/CodeWriterExtensions.cs#L750-L753

When I first saw this I was baffled. It disables nullability if SuppressNullabilityEnforcement is false. You'd think it would only disable it if the options said to suppress nullability.

But it appears that this setting actually reflects whether the version of C# in use understands nullability. That property might better be understood as DoNotEmitAnythingRelatingToNullability. It is true if you're on an old (before C# 8, i.e., pre .NET Core 3) language version.

Apparently Razor just has a baked in "we don't do #nullable enable assumption. So it always emits that #nullable disable unless it thinks the version of C# is so old that the compiler won't understand the directive. In either case, its intent is not to have nullability enabled.

But the slightly baffling thing is that in Visual Studio, some of the tooling seems to act as though nullability is in fact enabled. So you can end up getting diagnostics telling you that you've failed to deal with nullability. (Apparently the design-time tooling in the IDE doesn't understand that nullability is in fact disabled.)