Unity向けRoslynAnalyzerをRiderで開発する
Unity向けRoslynAnalyzerをRiderで開発する
WindowsとMac環境で作動するRoslynAnalyzer
をRiderを使用して構築します。
かつてはVisualStudio/Windowsの特権だった気がしますが、いつのまにかRiderにテンプレートが追加されていたので、Riderを介してWindowsとMac両方で開発できるAnalyzer環境を構築します。
リポジトリ
今回作成するサンプルはこちらにあります。
環境
Windows 11 22H2
Unity 2022.2.20
Rider 2024.1.1
Mac 14.4.1
Rider 2024.1
Unity 2022.3.22f1
RoslynTemplateを使用してソリューションを作成
ターゲットフレームワークはインストールされている.NetSDK依存です。
作成場所はUnityのプロジェクト下に置きました。
アナライザのリポジトリ分けて置くのも良さそうです。
UnityProjectRoot
├── Assets
└── Analyzer
└── RoslynAnalyzerSample
└── RoslynAnalyzerSample.sln
アナライザのプロジェクト設定変更
プロジェクトのPackageReference
を変更します。
生成時の参照が3.3.4と4.3.0でした。
このバージョンだとWindows環境でUnityEditor上のコンパイルに反応しなかったので、
VisualStudioのAnalyzer with code fix (.NET Standard)
テンプレートを参考にバージョンを下げます。
<!-- このバージョンだとビルド成果物がunityEditor上のコンパイルで反応しない、Riderのコンパイルには反映される -->
<!-- <ItemGroup>
<PackageReference Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.4">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.3.0"/>
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Workspaces" Version="4.3.0"/>
</ItemGroup> -->
<ItemGroup>
<PackageReference Include="Microsoft.CodeAnalysis.Analyzers" Version="2.9.8">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="3.3.1"/>
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Workspaces" Version="3.3.1"/>
</ItemGroup>
Templateの内容を最小の内容に変更
最小限のサンプルにするため今回使用しないコードを削除します。
大本のTemplate
RoslynAnalyzerSample
└── RoslynAnalyzerSample.sln
└── RoslynAnalyzerSample
└── RoslynAnalyzerSample
├── RoslynAnalyzerSample.csproj
├── SampleCodeFixProvider.cs
├── SampleSemanticAnalyzer.cs
├── SampleSyntaxAnalyzer.cs
└── etc...
└── RoslynAnalyzerSample.Sample
├── RoslynAnalyzerSample.Sample.csproj
└── etc...
└── RoslynAnalyzerSample.Test
├── RoslynAnalyzerSample.Test.csproj
└── etc...
最小化
Analyzerとそのテスト以外を削除します。
RoslynAnalyzerSample
└── RoslynAnalyzerSample.sln
└── RoslynAnalyzerSample
└── RoslynAnalyzerSample
├── RoslynAnalyzerSample.csproj
└── SampleSyntaxAnalyzer.cs
└── RoslynAnalyzerSample.Test
├── RoslynAnalyzerSample.Test.csproj
└── SampleSyntaxAnalyzerTests.cs
今回は環境構築が目的なのでSampleというクラス名があればエラーとして扱うことにします。
SampleSyntaxAnalyzer.cs
using System.Collections.Immutable;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Diagnostics;
namespace RoslynAnalyzerSample;
//class名がSampleならエラーを吐く
[DiagnosticAnalyzer(LanguageNames.CSharp)]
public class SampleSyntaxAnalyzer : DiagnosticAnalyzer
{
private static readonly DiagnosticDescriptor Rule = new("SampleError01", "NamingError", "命名にSampleを使用した", "Naming",
DiagnosticSeverity.Error, isEnabledByDefault: true, description: "サンプルエラー、名前にSampleを使わないでください");
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics { get; } =
ImmutableArray.Create(Rule);
public override void Initialize(AnalysisContext context)
{
context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None);
context.EnableConcurrentExecution();
context.RegisterSyntaxNodeAction(AnalyzeSyntax, SyntaxKind.ClassDeclaration);
}
private void AnalyzeSyntax(SyntaxNodeAnalysisContext context)
{
if (context.Node is not ClassDeclarationSyntax classDeclarationNode)
return;
var classDeclarationIdentifier = classDeclarationNode.Identifier;
if (classDeclarationIdentifier.Text == "Sample")
{
var diagnostic = Diagnostic.Create(Rule,
classDeclarationIdentifier.GetLocation(),
classDeclarationIdentifier.Text);
context.ReportDiagnostic(diagnostic);
}
}
}
SampleSyntaxAnalyzerTests.cs
using System.Threading.Tasks;
using Xunit;
using Verifier =
Microsoft.CodeAnalysis.CSharp.Testing.XUnit.AnalyzerVerifier<
RoslynAnalyzerSample.SampleSyntaxAnalyzer>;
namespace RoslynAnalyzerSample.Tests;
public class SampleSyntaxAnalyzerTests
{
[Fact]
public async Task ClassWithMyCompanyTitle_AlertDiagnostic()
{
const string text = @"
public class Sample
{
}
";
var expected = Verifier.Diagnostic()
.WithLocation(2, 14)
.WithArguments("Sample");
await Verifier.VerifyAnalyzerAsync(text, expected).ConfigureAwait(false);
}
}
リリースビルドを行いUnityにインポートするDLLを用意します。
デフォルトだとこの辺りに出力されます。
RoslynAnalyzerSample\RoslynAnalyzerSample\RoslynAnalyzerSample\bin\Release\netstandard2.0
Unityに導入
公式マニュアルに従い前項で出力したDLLをインポートします。
Plugin Inspector ウィンドウの中で、以下を行います。
Select platforms for plugin の見出しで、Any Platform を無効にします。
Include Platforms の見出しで、Editor と Standalone を無効にします。
Plugin Inspector ウィンドウの Asset Labels の見出しの下にある、青いラベルアイコンをクリックして、Asset Labels サブメニューを表示します。
RoslynAnalyzer という新しいラベルを作成し、割り当てます。
挙動確認
Windows/Macそれぞれの環境で導入を行い、Sampleというclassを定義することでUnityEditor/Rider上でコンパイルエラーが起こせることを確認します。
Windows
Mac
気を付けるポイント
アナライザの挙動に必要な.netの稼働要件をすり合わせておく必要があります。
You must install or update .NET to run this application.
などで実行弾かれます。
次のステップ
公式のサンプルを読んでみる
既存のAnalyzerを読んでみる
コード修正を実装する
参考文献
Discussion