はなちるのマイノート

Unityをメインとした技術ブログ。自分らしくまったりやっていきたいと思いますー!

【C#】SourceGeneratorで生成されたコードはcsprojのNullableが反映されないことを知った

はじめに

最近、SourceGeneratorで生成されたコードはcsprojNullableが反映されないことを知りました。

The global nullable context does not apply for generated code files. Under either strategy, the nullable context is disabled for any source file marked as generated. This means any APIs in generated files are not annotated. There are four ways a file is marked as generated:

1. In the .editorconfig, specify generated_code = true in a section that applies to that file.
2. Put auto-generated or auto-generated in a comment at the top of the file. It can be on any line in that comment, but the comment block must be the first element in the file.
3. Start the file name with TemporaryGeneratedFile_
4. End the file name with .designer.cs, .generated.cs, .g.cs, or .g.i.cs.

Generators can opt-in using the #nullable preprocessor directive.

// DeepL翻訳
グローバルなヌル可能コンテキストは、生成されたコードファイルには適用されません。いずれの戦略においても、生成済みとしてマークされたソースファイルではヌル可能コンテキストが無効化されます。これは、生成ファイル内のAPIにはアノテーションが付与されないことを意味します。ファイルが生成済みとしてマークされる方法は4つあります:

1. .editorconfigファイル内で、該当ファイルに適用されるセクションに `generated_code = true` を指定する。
2. ファイル先頭にコメントとして `auto-generated` または `auto-generated/` を記述する。コメント内の任意の行に記述可能だが、コメントブロックはファイルの最初の要素でなければならない。
3. ファイル名を `TemporaryGeneratedFile_` で開始する。
4. ファイル名を `.designer.cs`、`.generated.cs`、`.g.cs`、または `.g.i.cs` で終了する。

ジェネレータは `#nullable` プリプロセッサ指令を使用して明示的に有効化できる。

learn.microsoft.com

つまりNullableを有効にしたい場合は#nullable enableをファイル毎に指定しないとというわけです。そこに関して本当にそうなのかという実験をしてみたので書き残しておきます。

環境

JetBrains Rider 2025.2

実験

下準備

SourceGeneratorで適当なプロジェクトを作成します。下記コードの場合は警告が正しく表示されます。つまり、生成されたコードでもNullableが適応できているということです。

SourceGenerator側
// SandboxGenerator.cs
using Microsoft.CodeAnalysis;

namespace ISGSandbox;

[Generator(LanguageNames.CSharp)]
public class SandboxGenerator : IIncrementalGenerator
{
    public void Initialize(IncrementalGeneratorInitializationContext context)
    {
        context.RegisterPostInitializationOutput(static initContext =>
        {
            const string source = """
                                  // <auto-generated/>
                                  
                                  #nullable enable
                                  
                                  namespace ISGSandbox;

                                  internal static class GeneratedSandbox
                                  {
                                      internal static string Hoge()
                                      {
                                          var x = new global::System.Random().Next(10) > 5 ? "X" : null;
                                          
                                          // Nullableが適応されていると「Null参照戻り値である可能性があります」という警告が出る
                                          return x;
                                      }
                                  }
                                  """;
            
            initContext.AddSource("GeneratedSandbox.g.cs", source);
        });
    }
}
対象のプロジェクト
// Program.cs
using ISGSandbox;

public class Program
{
    public static void Main(string[] args)
    {
        Console.WriteLine(GeneratedSandbox.Hoge());
    }
}
<Project Sdk="Microsoft.NET.Sdk">

    <PropertyGroup>
        <OutputType>Exe</OutputType>
        <TargetFramework>net8.0</TargetFramework>
        <ImplicitUsings>enable</ImplicitUsings>
        <Nullable>enable</Nullable>
    </PropertyGroup>
    
    <ItemGroup>
        <ProjectReference Include="..\ISGSandbox\ISGSandbox.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false" />
    </ItemGroup>
</Project>

.editorconfigで指定している場合

csprojNullableenable かつ #nullable enableをつけていない 前提で、以下の設定をしました。

  • .editorconfigに生成コードを指定
  • auto-generatedというコメントを冒頭につけない
  • ファイル名の先頭をTemporaryGeneratedFile_にしない
  • 生成されたファイルの名前を.designer.cs, .generated.cs, .g.cs, or .g.i.csで終わらせない
# ISGで生成されたファイル名は「Sandbox.Hoge.cs」の場合
[**/*.Hoge.cs]
generated_code = true

どうやらこの場合は警告が表示されるようでした。自動生成ファイルとして指定されていれば警告が出ないはずなので、どうやらうまくできていないようです...(要調査)

警告が表示されている様子

auto-generatedというコメントを追加している場合

csprojNullableenable かつ #nullable enableをつけていない 前提で、以下の設定をしました。

  • .editorconfigを用意しない
  • auto-generatedというコメントを冒頭につける
  • ファイル名の先頭をTemporaryGeneratedFile_にしない
  • 生成されたファイルの名前を.designer.cs, .generated.cs, .g.cs, or .g.i.csで終わらせない
// <auto-generated/>

この場合は警告が表示されなくなりました。つまり、Nullableが適応されていないということです。

ファイル名の先頭をTemporaryGeneratedFile_にした場合

csprojNullableenable かつ #nullable enableをつけていない 前提で、以下の設定をしました。

  • .editorconfigを用意しない
  • auto-generatedというコメントを冒頭につけない
  • ファイル名の先頭をTemporaryGeneratedFile_にする
  • 生成されたファイルの名前を.designer.cs, .generated.cs, .g.cs, or .g.i.csで終わらせない
initContext.AddSource("TemporaryGeneratedFile_Sandbox.Hoge.cs", source);

この場合は警告が表示されませんでした。つまり、Nullableが適応されていません。

生成されたファイルの名前を

csprojNullableenable かつ #nullable enableをつけていない 前提で、以下の設定をしました。

  • .editorconfigを用意しない
  • auto-generatedというコメントを冒頭につけない
  • ファイル名の先頭をTemporaryGeneratedFile_にしない
  • 生成されたファイルの名前を.designer.cs, .generated.cs, .g.cs, or .g.i.csで終わらせる
initContext.AddSource("Sandbox.g.cs", source);

この場合は警告が表示されませんでした。つまり、Nullableが適応されていません。

#nullable enableを指定した場合

上記のやり方で生成されたコードとマークされていたとしても、#nullable enableを指定すればNullableを有効化できます。

#nullable enable

こうすることで警告が表示されます。

まとめ

結論生成されたコードでNullableを有効化したいなら#nullable enableを指定しようという話です。

また生成されたコードだとマークされることで、csprojNullableが適応されないです。具体的には、

  • .editorconfiggenerated_code = trueを指定する
  • auto-generatedauto-generated/というコメントを冒頭につける
  • ファイル名の先頭をTemporaryGeneratedFile_にする
  • 生成されたファイルの名前を.designer.cs, .generated.cs, .g.cs, or .g.i.csで終わらせる

のいずれかを行うことでマークができます。

ただ実験だと.editorconfigの場合はうまくいきませんでした。これが何に起因しているのか分かっていませんが、後で調べてみたいと思います。