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
489 stars 190 forks source link

Blazor - Namespace ignored when using source generated type in a parameter #7903

Open MizardX opened 1 year ago

MizardX commented 1 year ago

Is there an existing issue for this?

Describe the bug

I have a source-generated enum IconTypes. If I create a component with a parameter with that type, I get a strange error message that the type can't be found in the global namespace.

Expected Behavior

The IconTypes parameter should work without build errors.

Steps To Reproduce

  1. Create new solution from the template "Blazor WebAssembly App Empty" named MyProject.
  2. Add a new class library project MySourceGenerator.csproj:

    <Project Sdk="Microsoft.NET.Sdk">
    <PropertyGroup>
    <TargetFramework>netstandard2.0</TargetFramework>
    <LangVersion>Latest</LangVersion>
    <IncludeBuildOutput>false</IncludeBuildOutput>
    </PropertyGroup>
    
    <ItemGroup>
    <PackageReference Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.3">
      <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
      <PrivateAssets>all</PrivateAssets>
    </PackageReference>
    <PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.3.1" />
    </ItemGroup>
    </Project>
  3. Add a new file IconGenerator.cs to the MySourceGenerator project
    
    using Microsoft.CodeAnalysis;
    using Microsoft.CodeAnalysis.Text;
    using System.Text;

namespace MySourceGenerator;

[Generator] public class IconGenerator : ISourceGenerator { public void Execute(GeneratorExecutionContext context) { context.AddSource("IconTypes.g.cs", SourceText.From(""" using System;

            namespace MySourceGenerator;

            public enum IconTypes
            {
                Circle = 1
            }
            """, encoding: Encoding.UTF8));
}

public void Initialize(GeneratorInitializationContext context)
{
}

}

4. In the `MyProject` project, add a reference to the `MySourceGenerator` project.
```xml
<ItemGroup>
    <ProjectReference Include="..\MySourceGenerator\MySourceGenerator.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false" />
</ItemGroup>
  1. In the MyProject project, add a new file as Components/Icon.cs:
    
    using Microsoft.AspNetCore.Components;
    using Microsoft.AspNetCore.Components.Rendering;
    using MySourceGenerator;

namespace MyProject.Components;

public class Icon : ComponentBase { [Parameter] public IconTypes Type { get; set; }

protected override void BuildRenderTree(RenderTreeBuilder builder)
{
}

}

6. In the file `Pages/Index.razor` replace the content with:
```html
@page "/"

@using MyProject.Components
@using MySourceGenerator

<Icon Type="IconTypes.Circle" />
  1. Possibly restart VS, for it to reload the Source Generator.
  2. Build the solution.

Exceptions (if any)

Rebuild started...
------ Rebuild All started: Project: MySourceGenerator, Configuration: Debug Any CPU ------
Restored ...\MyProject\MyProject\MyProject.csproj (in 264 ms).
Restored ...\MyProject\MySourceGenerator\MySourceGenerator.csproj (in 264 ms).
MySourceGenerator -> ...\MyProject\MySourceGenerator\bin\Debug\netstandard2.0\MySourceGenerator.dll
------ Rebuild All started: Project: MyProject, Configuration: Debug Any CPU ------
...\MyProject\MyProject\Microsoft.NET.Sdk.Razor.SourceGenerators\Microsoft.NET.Sdk.Razor.SourceGenerators.RazorSourceGenerator\Pages_Index_razor.g.cs(83,137,83,146): error CS0400: The type or namespace name 'IconTypes' could not be found in the global namespace (are you missing an assembly reference?)
...\MyProject\MyProject\Pages\Index.razor(8,13,8,29): error CS1503: Argument 1: cannot convert from 'MySourceGenerator.IconTypes' to 'IconTypes'
Done building project "MyProject.csproj" -- FAILED.
========== Rebuild All: 1 succeeded, 1 failed, 0 skipped ==========
========== Elapsed 00:02,852 ==========

.NET Version

7.0.100

Anything else?

If I replace

[Parameter]
public IconTypes Type { get; set; }

with

[Parameter]
public MySourceGenerator.IconTypes Type { get; set; }

the project builds without errors.

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

Host: Version: 7.0.0 Architecture: x64 Commit: d099f075e4

.NET SDKs installed: 2.1.202 [C:\Program Files\dotnet\sdk] 2.1.526 [C:\Program Files\dotnet\sdk] 2.2.203 [C:\Program Files\dotnet\sdk] 3.0.100 [C:\Program Files\dotnet\sdk] 3.1.425 [C:\Program Files\dotnet\sdk] 5.0.303 [C:\Program Files\dotnet\sdk] 6.0.202 [C:\Program Files\dotnet\sdk] 6.0.306 [C:\Program Files\dotnet\sdk] 6.0.403 [C:\Program Files\dotnet\sdk] 7.0.100 [C:\Program Files\dotnet\sdk]

.NET runtimes installed: Microsoft.AspNetCore.All 2.1.15 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.All] Microsoft.AspNetCore.All 2.1.17 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.All] Microsoft.AspNetCore.All 2.1.18 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.All] Microsoft.AspNetCore.All 2.1.19 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.All] Microsoft.AspNetCore.All 2.1.21 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.All] Microsoft.AspNetCore.All 2.1.22 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.All] Microsoft.AspNetCore.All 2.1.25 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.All] Microsoft.AspNetCore.All 2.1.27 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.All] Microsoft.AspNetCore.All 2.1.30 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.All] Microsoft.AspNetCore.All 2.2.4 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.All] Microsoft.AspNetCore.App 2.1.15 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App] Microsoft.AspNetCore.App 2.1.17 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App] Microsoft.AspNetCore.App 2.1.18 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App] Microsoft.AspNetCore.App 2.1.19 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App] Microsoft.AspNetCore.App 2.1.21 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App] Microsoft.AspNetCore.App 2.1.22 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App] Microsoft.AspNetCore.App 2.1.25 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App] Microsoft.AspNetCore.App 2.1.27 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App] Microsoft.AspNetCore.App 2.1.30 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App] Microsoft.AspNetCore.App 2.2.4 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App] Microsoft.AspNetCore.App 3.0.0 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App] Microsoft.AspNetCore.App 3.1.31 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App] Microsoft.AspNetCore.App 5.0.9 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App] Microsoft.AspNetCore.App 5.0.15 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App] Microsoft.AspNetCore.App 5.0.17 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App] Microsoft.AspNetCore.App 6.0.5 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App] Microsoft.AspNetCore.App 6.0.10 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App] Microsoft.AspNetCore.App 6.0.11 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App] Microsoft.AspNetCore.App 7.0.0 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App] Microsoft.NETCore.App 2.0.9 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App] Microsoft.NETCore.App 2.1.15 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App] Microsoft.NETCore.App 2.1.17 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App] Microsoft.NETCore.App 2.1.18 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App] Microsoft.NETCore.App 2.1.19 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App] Microsoft.NETCore.App 2.1.21 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App] Microsoft.NETCore.App 2.1.22 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App] Microsoft.NETCore.App 2.1.25 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App] Microsoft.NETCore.App 2.1.26 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App] Microsoft.NETCore.App 2.1.27 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App] Microsoft.NETCore.App 2.1.30 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App] Microsoft.NETCore.App 2.2.4 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App] Microsoft.NETCore.App 3.0.0 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App] Microsoft.NETCore.App 3.1.31 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App] Microsoft.NETCore.App 5.0.9 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App] Microsoft.NETCore.App 5.0.15 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App] Microsoft.NETCore.App 5.0.17 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App] Microsoft.NETCore.App 6.0.10 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App] Microsoft.NETCore.App 6.0.11 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App] Microsoft.NETCore.App 7.0.0 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App] Microsoft.WindowsDesktop.App 3.0.0 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App] Microsoft.WindowsDesktop.App 3.1.31 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App] Microsoft.WindowsDesktop.App 5.0.9 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App] Microsoft.WindowsDesktop.App 5.0.15 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App] Microsoft.WindowsDesktop.App 5.0.17 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App] Microsoft.WindowsDesktop.App 6.0.10 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App] Microsoft.WindowsDesktop.App 6.0.11 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App] Microsoft.WindowsDesktop.App 7.0.0 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]

Other architectures found: arm64 [C:\Program Files\dotnet] registered at [HKLM\SOFTWARE\dotnet\Setup\InstalledVersions\arm64\InstallLocation] 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

@MizardX Unfortunately this is currently a limitation with Roslyn source generators (which Razor is based on under the hood).

Roslyn generators run independently of each other, meaning one generator can't see code produced by another one. Because Razor is implemented as a Source Generator, it's not able to see the code being produced by your generator. When the source generator attempts to determine the Type of the Type parameter, it fails (because it doesn't know anything about it) and so treats the type as an opaque string that is just copied verbatim (as opposed to adding the correct using etc). When you fully qualify the type name in the C# file, it still just copies it verbatim, but because it's fully qualified it will now correctly compile in the generated code.

At this time there is no workaround, you just can't use Source Generated types in a razor file.

I suggest adding your input to issue https://github.com/dotnet/roslyn/issues/57239 that tracks allowing scenarios like this to be implemented.

chsienki commented 1 year ago

Interestingly intellisense (today) shows the type correctly, because it's not running as part of the Source Generator. If / when we switch that out, these types should stop showing up there too.

DustinCampbell commented 1 year ago

@chsienki: When we saw this in tooling triage yesterday, we imagined that this is a reasonable scenario for the Razor source generator (or some domain of generators) to always run after other generators. It seemed to us to be a perfectly reasonable scenario for a user to generate, say, their data-access layer and then expect to be able to use it in Razor code.

chsienki commented 1 year ago

@DustinCampbell Yep it's a request we've heard lots of times and have thoughts around how to do it, but nothing shovel-ready yet.

It's gets complicated because, for instance, a user might want to use the regex generator from Razor too (a request we've heard before) which would required at least three passes in this scenario. We want to solve it but it's not trivial unfortunately.

Adding @KathleenDollard for visibility/ thoughts

Sibusten commented 1 year ago

I just ran into this when installing the new SDK (7.0.100). Strangely, it was working fine on the old SDK (6.0.403). I looked at the razor generated output and noticed that types in the new SDK are now being prepending with global::, which caused the error in my project. In both cases, the project was using .NET 6.

6.0.403:

__builder.AddAttribute(7, "LabelAlignment", global::Microsoft.AspNetCore.Components.CompilerServices.RuntimeHelpers.TypeCheck<LabelAlignment>(

7.0.100:

__builder.AddAttribute(7, "LabelAlignment", global::Microsoft.AspNetCore.Components.CompilerServices.RuntimeHelpers.TypeCheck<global::LabelAlignment>(

I was able to isolate the rest of the generated code and move it to a separate class library, which solved the compilation issue with Blazor.

KrystofZacek commented 1 year ago

I was banging my head exactly against this the whole day after updating to VS2022 17.6 and ,NET7. I have a ZackLib.DataGrid razor component having ZackLib.GridColumn.SortOrder as a parameter typed WITHOUT full qualification (the DataGrid has "using ZackLib"). That dreaded thing put the ZackLib.GridColumn.SortOrder to global:GridColumn.SortOrder resulting in failed compilation. Declaring the parameter as full qualified resolved the problem but it is a completely unexpected behavior. In .NET6 there was no problem with that.