Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add nullable reference type annotations to System.Text.Json source gen #79613

Merged
merged 17 commits into from
Dec 16, 2022

Conversation

eiriktsarpalis
Copy link
Member

@eiriktsarpalis eiriktsarpalis commented Dec 13, 2022

NRE's have been a frequent occurrence when making changes to the STJ source generator. This PR enables nullable reference type annotations to the STJ source generator components. Because source generator projects need to target netstandard2.0, this is done by adding a new project, System.Text.Json.SourceGenerator.NetCoreApp that targets net8.0 as an unused class library.

Additionally, this PR makes the sourcegen test suite target the Roslyn v4.4 instead of v4.0. The changes have been factored into a separate commit fe78570 for easier reviewing.

These infrastructural changes are in anticipation of implementing #58770, which needs to make use of APIs only available in Roslyn v4.4.

@ghost
Copy link

ghost commented Dec 13, 2022

Tagging subscribers to this area: @dotnet/area-system-text-json, @gregsdennis
See info in area-owners.md if you want to be subscribed.

Issue Details

NRE's have been a frequent occurrence when making changes to the STJ source generator. This PR enables nullable reference type annotations to the STJ source generator components. Because source generator projects need to target netstandard2.0, this is done by adding a new project, System.Text.Json.SourceGenerator.NetCoreApp that targets net8.0 as an unused class library.

Additionally, this PR makes the sourcegen test suite target the Roslyn v4.4 instead of v4.0. The changes have been factored into a separate commit fe78570 for easier reviewing.

Author: eiriktsarpalis
Assignees: eiriktsarpalis
Labels:

area-System.Text.Json

Milestone: -

@eiriktsarpalis eiriktsarpalis added this to the 8.0.0 milestone Dec 13, 2022
@ViktorHofer
Copy link
Member

ViktorHofer commented Dec 13, 2022

We should use msbuild's multi-targeting feature here which was designed for such use-cases. I don't think that introducing new projects per target framework for source generators is scalable. Every project adds to the overall build graph and restore time.

Because source generator projects need to target netstandard2.0, this is done by adding a new project, System.Text.Json.SourceGenerator.NetCoreApp that targets net8.0 as an unused class library.

Why can't you multi-target the existing roslyn4.4 project and add net8.0 there? Did you hit this error?

<Error Condition="'%(_AnalyzerPackFile.TargetFramework)' != 'netstandard2.0'"
Text="Analyzers must only target netstandard2.0 since they run in the compiler which targets netstandard2.0. The following files were found to target '%(_AnalyzerPackFile.TargetFramework)': @(_AnalyzerPackFile)" />
. That's our own validation that can be changed to make multi-targeting possible (of course without shipping the assembly).

@eiriktsarpalis
Copy link
Member Author

eiriktsarpalis commented Dec 13, 2022

Why can't you multi-target the existing roslyn4.4 project and add net8.0 there? Did you hit this error?

Multi-targeting the sourcegen project themselves produced a bit of msbuild weirdness which I tried to address, but ultimately I couldn't fix it. That particular error you're referencing wasn't hit actually, it was primarily complaining that v8 version of System.Runtime could not be found (I'm missing precise error logs right now).

I should add that the Directory.Build.targets logic can be evaded by defining the AnalyzerRoslynVersion property conditionally only for netstandard2.0. This doesn't solve the problem though -- it would seem that multi-targeting and source generators don't work together well.

@ViktorHofer
Copy link
Member

I think we should make multi-targeting work here. I can take a look tomorrow morning.

@ViktorHofer
Copy link
Member

ViktorHofer commented Dec 14, 2022

@eiriktsarpalis I just pushed a commit to your branch to allow our source-generator projects to multi-target. Note that this comes with a number of pitfalls:

  • With a lot of experimentation, I got targeting the live shared framework working but this is fragile to say the least. I.e. I had to reference a ton of shim projects like System.Runtime.Extensions as the Roslyn nuget packages often compile against netcoreapp3.1 (and these assemblies still contain references to these now shim assemblies). Ideally, we would ask Roslyn to include the very latest TFM in their packages to avoid these unnecessary shim project builds.
  • Now that a source generators targets the live shared framework, we suddenly need to think about layering which wasn't a big concern with the prebuilt .NET Standard TFM. While today, we have a split between reference and source projects, we eventually want to eliminate that which would implicate that a source generator's dependency must not use that source generator in the corresponding source project. Also, source generators outside of the shared framework must not be referenced from inside the shared framework as that would cause a circular dependency in the graph.

Before merging, I would like @ericstj to review my changes as this is an entirely new approach with possible unknown implications and/or regressions.

@eiriktsarpalis
Copy link
Member Author

With a lot of experimentation, I got targeting the live shared framework working but this is fragile to say the least. I.e. I had to reference a ton of shim projects like System.Runtime.Extensions as the Roslyn nuget packages often compile against netcoreapp3.1 (and these assemblies still contain references to these now shim assemblies). Ideally, we would ask Roslyn to include the very latest TFM in their packages to avoid these unnecessary shim project builds.

Would using a standalone project be more reliable in that case? It achieves the same end goal with the inconvenience of one placeholder project being included in the solution.

@ViktorHofer
Copy link
Member

Would using a standalone project be more reliable in that case? It achieves the same end goal with the inconvenience of one placeholder project being included in the solution.

No, that doesn't make any difference. It's mostly about using the live shared framework vs. using a prebuilt. Using the live shared framework is a good goal to have but slightly more work. I would hope that with time, .NETCoreApp targeting source generators will become more natural.

Directory.Build.targets Outdated Show resolved Hide resolved
Copy link
Member

@krwq krwq left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

.cs files changes look good to me

Copy link
Member

@ViktorHofer ViktorHofer left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The PR is now ready and I added some documentation around multi-targeting source generators and what to watch out for (when consuming a live built source generator).

Copy link
Contributor

@layomia layomia left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is awesome.

@eiriktsarpalis
Copy link
Member Author

Test failures are unrelated.

@eiriktsarpalis eiriktsarpalis merged commit 97a51cc into dotnet:main Dec 16, 2022
@eiriktsarpalis eiriktsarpalis deleted the stj-sourcegen-nullability branch December 16, 2022 18:44
@ghost ghost locked as resolved and limited conversation to collaborators Jan 15, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants