Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Unity開発でのミスを未然に防ぐRoslynアナライザーのすゝめ【DeNA TechCon 2022】

DeNA_Tech
March 17, 2022

Unity開発でのミスを未然に防ぐRoslynアナライザーのすゝめ【DeNA TechCon 2022】

コードをビルドすることなく、ミスを検知できたらゲーム開発が楽になると思いませんか?

.NETの静的解析器であるRoslynアナライザーを使うことで、ビルドをすることなく問題のあるコードを検出してくれるようになります。

また、Roslynアナライザーは各々のエンジニアがカスタムルールを実装できます。例えば弊社では、実行時にリフレクションでインスタンス化されるクラスのコンストラクタが、IL2CPPビルド時に未使用コードと判別され削除されないように、Roslynアナライザーを実装し、導入することでリリースブロッカーとなりえる問題を早期発見できるようにしています。

そんな便利なRoslynアナライザーですが、 情報が少なくハードルが高い印象を持っている人もいるのではないでしょうか。

本セッションではRoslynアナライザーの導入と、カスタムルール実装のハードルを下げるための情報を共有します。

資料内でのリンク集:
p153,154, https://github.com/DeNA/Dena.CodeAnalysis.Testing
p160,161,162,163,164, https://github.com/DeNA/Dena.CodeAnalysis.Testing
p183-1, https://swet.dena.com/entry/2021/05/25/100000
p183-2, https://www.nowsprinting.com/entry/2021/11/01/083258
p183-3, https://www.nowsprinting.com/entry/2021/04/18/200619
p183-4, https://github.com/dotnet/roslyn-analyzers/tree/main/src/Microsoft.CodeAnalysis.BannedApiAnalyzers
p183-5, https://github.com/code-cracker/code-cracker
p183-6, https://github.com/Cysharp/UniTask

◆ You Tube
https://youtu.be/g93LBwvOzxA

◆ You Tube チャンネル登録はこちら↓
https://youtube.com/c/denatech?sub_confirmation=1

◆ Twitter
https://twitter.com/DeNAxTech

◆ DeNA Engineering
https://engineering.dena.com/

◆ DeNA Engineer Blog
https://engineering.dena.com/blog/

◆ DeNA TechCon 2022 公式サイト
https://techcon2022.dena.dev/spring/

DeNA_Tech

March 17, 2022
Tweet

More Decks by DeNA_Tech

Other Decks in Technology

Transcript

  1. こちらに登壇時の映
    像をいれるので、文
    字を載せないように
    して下さい。
    Unity開発でのミスを未然に防ぐ
    Roslynアナライザーのすゝめ
    システム本部品質統括部品質管理部SWET第二グループ
    Kazuma Inagaki

    View Slide

  2. こちらに登壇時の映
    像をいれるので、文
    字を載せないように
    して下さい。
    自己紹介
    - Kazuma Inagaki
    - 21新卒
    - SWETグループ所属
    - Vimmer

    View Slide

  3. こちらに登壇時の映
    像をいれるので、文
    字を載せないように
    して下さい。
    - Roslynアナライザーの概要
    - Roslynアナライザーができること、使い方
    - カスタムルールの作り方
    - カスタムアナライザーをテストする方法
    - 作成したアナライザーをUPMパッケージに内包して配布、利用する
    NOTE: バージョンによってバグによる制約があるため、
    このスライドではUnity 2021.2 + Rider Editor package
    v3.0.9を基準に書いています。
    目次

    View Slide

  4. こちらに登壇時の映
    像をいれるので、文
    字を載せないように
    して下さい。
    - Roslynアナライザーの概要
    - Roslynアナライザーができること、使い方
    - カスタムルールの作り方
    - カスタムアナライザーをテストする方法
    - 作成したアナライザーをUPMパッケージに内包して配布、利用する
    NOTE: バージョンによってバグによる制約があるため、
    このスライドではUnity 2021.2 + Rider Editor package
    v3.0.9を基準に書いています。
    目次

    View Slide

  5. こちらに登壇時の映
    像をいれるので、文
    字を載せないように
    して下さい。
    Roslynアナライザーの概要
    - Roslynとは
    - Roslynアナライザーとは

    View Slide

  6. こちらに登壇時の映
    像をいれるので、文
    字を載せないように
    して下さい。
    Roslynとは
    - C#6.0から導入された.NETコンパイラプラットフォームの通称
    - 公開されているもの
    - C#、VB向けコンパイラ
    - コード生成API
    - コード解析API

    View Slide

  7. こちらに登壇時の映
    像をいれるので、文
    字を載せないように
    して下さい。
    Roslynアナライザーとは
    - Roslynのコード解析APIを用いて作成された、コードの問題を検出するプログ
    ラムのこと

    View Slide

  8. こちらに登壇時の映
    像をいれるので、文
    字を載せないように
    して下さい。
    Roslynアナライザーとは
    - Roslynのコード解析APIを用いて作成された、コードの問題を検出するプログ
    ラムのこと

    View Slide

  9. こちらに登壇時の映
    像をいれるので、文
    字を載せないように
    して下さい。
    コンパイラ
    オレオレルール
    - 型違反
    - 未定義関数
    - 未定義変数
    - etc...
    - コーディング規約
    - 非推奨のAPIの使用
    - etc...
    一般的なルール

    View Slide

  10. こちらに登壇時の映
    像をいれるので、文
    字を載せないように
    して下さい。
    コンパイラ
    オレオレルール
    - 型違反
    - 未定義関数
    - 未定義変数
    - etc...
    - コーディング規約
    - 非推奨のAPIの使用
    - etc...
    一般的なルール

    View Slide

  11. こちらに登壇時の映
    像をいれるので、文
    字を載せないように
    して下さい。
    コンパイラ
    オレオレルール
    - 型違反
    - 未定義関数
    - 未定義変数
    - etc...
    Roslynアナライザーに
    やってもらおう!
    一般的なルール

    View Slide

  12. こちらに登壇時の映
    像をいれるので、文
    字を載せないように
    して下さい。
    どうやってやってもらうんだ..?

    View Slide

  13. こちらに登壇時の映
    像をいれるので、文
    字を載せないように
    して下さい。
    IDE上でRoslynアナライザーを使用
    IDE上にレポートされる
    IDE上で欠陥を作り込む
    Unity上でRoslynアナライザーを使用
    ビルド時にUnityエディタ上にレポートされる
    Unityを開く

    View Slide

  14. こちらに登壇時の映
    像をいれるので、文
    字を載せないように
    して下さい。
    それだけではない!

    View Slide

  15. こちらに登壇時の映
    像をいれるので、文
    字を載せないように
    して下さい。
    コンパイラ オレオレルール
    - コーディング規約
    - 非推奨のAPIの使用
    - etc...

    View Slide

  16. こちらに登壇時の映
    像をいれるので、文
    字を載せないように
    して下さい。
    コンパイラ オレオレルール
    - コーディング規約
    - 非推奨のAPIの使用
    - etc...

    View Slide

  17. こちらに登壇時の映
    像をいれるので、文
    字を載せないように
    して下さい。
    コンパイラ オレオレルール
    - コーディング規約
    - 非推奨のAPIの使用
    - etc...
    エンジニア
    誰でも診断ルールを
    自作できる

    View Slide

  18. こちらに登壇時の映
    像をいれるので、文
    字を載せないように
    して下さい。
    - Roslynアナライザーの概要
    - Roslynアナライザーができること、使い方
    - カスタムルールの作り方
    - カスタムアナライザーをテストする方法
    - 作成したアナライザーをUPMパッケージに内包して配布、利用する
    NOTE: バージョンによってバグによる制約があるため、
    このスライドではUnity 2021.2 + Rider Editor package
    v3.0.9を基準に書いています。
    目次

    View Slide

  19. こちらに登壇時の映
    像をいれるので、文
    字を載せないように
    して下さい。
    Roslynアナライザーができること、使い方
    - 様々な診断ルールを持つアナライザーの紹介
    - Unity上でRoslynアナライザーを使う方法
    - JetBrains Rider上でRoslynアナライザーを使う方法
    - アナライザーTips

    View Slide

  20. こちらに登壇時の映
    像をいれるので、文
    字を載せないように
    して下さい。
    Roslynアナライザーができること、使い方
    - 様々な診断ルールを持つアナライザーの紹介
    - Unity上でRoslynアナライザーを使う方法
    - JetBrains Rider上でRoslynアナライザーを使う方法
    - アナライザーTips

    View Slide

  21. こちらに登壇時の映
    像をいれるので、文
    字を載せないように
    して下さい。
    様々な診断ルールを持つアナライザーの紹介
    - Microsoft.CodeAnalysis.BannedApiAnalyzer
    - 汎用的に特定のAPIを使用禁止にできるアナライザー
    - 例: UniTaskでなくTaskを使用していたら怒るルール
    - codecracker.CSharp
    - dispose漏れを怒るルール(CS0022)
    - DeNA内で作成したアナライザー
    - Preserve属性のつけ忘れを怒るルール

    View Slide

  22. こちらに登壇時の映
    像をいれるので、文
    字を載せないように
    して下さい。
    様々な診断ルールを持つアナライザーの紹介
    - Microsoft.CodeAnalysis.BannedApiAnalyzer
    - 汎用的に特定のAPIを使用禁止にできるアナライザー
    - 例: UniTaskでなくTaskを使用していたら怒るルール
    - codecracker.CSharp
    - dispose漏れを怒るルール(CS0022)
    - DeNA内で作成したアナライザー
    - Preserveのつけ忘れを怒るルール

    View Slide

  23. こちらに登壇時の映
    像をいれるので、文
    字を載せないように
    して下さい。
    public async Task M()
    {
    await Task.Delay(100);
    }
    Taskを使用した例
    UniTaskを使用した例
    public async UniTask M()
    {
    await UniTask.Delay(100);
    }

    View Slide

  24. こちらに登壇時の映
    像をいれるので、文
    字を載せないように
    して下さい。
    public async Task M()
    {
    await Task.Delay(100);
    }
    Taskを使用した例
    UniTaskを使用した例
    UniTaskでなく、Taskを使っている
    public async UniTask M()
    {
    await UniTask.Delay(100);
    }

    View Slide

  25. こちらに登壇時の映
    像をいれるので、文
    字を載せないように
    して下さい。
    public async Task M()
    {
    await Task.Delay(100);
    }
    Taskを使用した例
    UniTaskを使用した例
    Do not used banned APIs
    public async UniTask M()
    {
    await UniTask.Delay(100);
    }

    View Slide

  26. こちらに登壇時の映
    像をいれるので、文
    字を載せないように
    して下さい。
    様々な診断ルールを持つアナライザーの紹介
    - Microsoft.CodeAnalysis.BannedApiAnalyzer
    - 汎用的に特定のAPIを使用禁止にできるアナライザー
    - 例: UniTaskでなくTaskを使用していたら怒るルール
    - codecracker.CSharp
    - dispose漏れを怒るルール(CS0022)
    - DeNA内で作成したアナライザー
    - Preserveのつけ忘れを怒るルール

    View Slide

  27. こちらに登壇時の映
    像をいれるので、文
    字を載せないように
    して下さい。
    public class MustDisposeClass : IDisposable
    {
    }
    void M()
    {
    var hoge = new MustDisposeClass();
    }

    View Slide

  28. こちらに登壇時の映
    像をいれるので、文
    字を載せないように
    して下さい。
    public class MustDisposeClass : IDisposable
    {
    }
    void M()
    {
    var hoge = new MustDisposeClass();
    }
    Disposeができていない!

    View Slide

  29. こちらに登壇時の映
    像をいれるので、文
    字を載せないように
    して下さい。
    public class MustDisposeClass : IDisposable
    {
    }
    void M()
    {
    var hoge = new MustDisposeClass();
    }
    MustDisposeClass should be
    disposed.

    View Slide

  30. こちらに登壇時の映
    像をいれるので、文
    字を載せないように
    して下さい。
    public class MustDisposeClass : IDisposable
    {
    }
    void M()
    {
    var hoge = new MustDisposeClass();
    }

    View Slide

  31. こちらに登壇時の映
    像をいれるので、文
    字を載せないように
    して下さい。
    public class MustDisposeClass : IDisposable
    {
    }
    void M()
    {
    var hoge = new MustDisposeClass();
    }
    using (var hoge = new MustDisposeClass())
    {
    }
    void M()
    {
    var hoge = new MustDisposeClass();
    hoge.Dispose();
    }

    View Slide

  32. こちらに登壇時の映
    像をいれるので、文
    字を載せないように
    して下さい。
    public class MustDisposeClass : IDisposable
    {
    }
    void M()
    {
    var hoge = new MustDisposeClass();
    }
    using (var hoge = new MustDisposeClass())
    {
    }
    void M()
    {
    var hoge = new MustDisposeClass();
    hoge.Dispose();
    }

    View Slide

  33. こちらに登壇時の映
    像をいれるので、文
    字を載せないように
    して下さい。
    様々な診断ルールを持つアナライザーの紹介
    - Microsoft.CodeAnalysis.BannedApiAnalyzer
    - 汎用的に特定のAPIを使用禁止にできるアナライザー
    - 例: UniTaskでなくTaskを使用していたら怒るルール
    - codecracker.CSharp
    - dispose漏れを怒るルール(CS0022)
    - DeNA内で作成したアナライザー
    - Preserve属性のつけ忘れを怒るルール

    View Slide

  34. こちらに登壇時の映
    像をいれるので、文
    字を載せないように
    して下さい。
    var services = new ServiceCollection();
    services.AddSingleton();
    services.AddSingleton();
    DIコンテナへの登録

    View Slide

  35. こちらに登壇時の映
    像をいれるので、文
    字を載せないように
    して下さい。
    var services = new ServiceCollection();
    services.AddSingleton();
    services.AddSingleton();
    DIコンテナへの登録

    View Slide

  36. こちらに登壇時の映
    像をいれるので、文
    字を載せないように
    して下さい。
    public class GoodService : IGoodService
    {
    [Preserve]
    public GoodService()
    {
    }
    }
    public class BadServiceNoInject : IBadServiceNoInject
    {
    public BadServiceNoInject()
    {
    }
    }
    var services = new ServiceCollection();
    services.AddSingleton();
    services.AddSingleton();
    DIコンテナへの登録

    View Slide

  37. こちらに登壇時の映
    像をいれるので、文
    字を載せないように
    して下さい。
    Preserve属性あり
    Preserve属性なし
    public class GoodService : IGoodService
    {
    [Preserve]
    public GoodService()
    {
    }
    }
    public class BadServiceNoInject : IBadServiceNoInject
    {
    public BadServiceNoInject()
    {
    }
    }
    var services = new ServiceCollection();
    services.AddSingleton();
    services.AddSingleton();
    DIコンテナへの登録

    View Slide

  38. こちらに登壇時の映
    像をいれるので、文
    字を載せないように
    して下さい。
    Preserve属性あり
    Preserve属性なし
    public class GoodService : IGoodService
    {
    [Preserve]
    public GoodService()
    {
    }
    }
    public class BadServiceNoInject : IBadServiceNoInject
    {
    public BadServiceNoInject()
    {
    }
    }
    var services = new ServiceCollection();
    services.AddSingleton();
    services.AddSingleton();
    DIコンテナへの登録

    View Slide

  39. こちらに登壇時の映
    像をいれるので、文
    字を載せないように
    して下さい。
    var services = new ServiceCollection();
    services.AddSingleton();
    services.AddSingleton();
    DIコンテナへの登録
    Preserve属性あり
    Preserve属性なし
    public class GoodService : IGoodService
    {
    [Preserve]
    public GoodService()
    {
    }
    }
    public class BadServiceNoInject : IBadServiceNoInject
    {
    public BadServiceNoInject()
    {
    }
    }
    IL2CPPでビルドした時に最適化によって、直接使用されない且つ Preserve
    属性の無いコンストラクタを持つクラスはストリップされる。

    View Slide

  40. こちらに登壇時の映
    像をいれるので、文
    字を載せないように
    して下さい。
    バグの混入 ビルドする クラッシュする
    public class
    BadServiceNoInject :
    IBadServiceNoInject
    {
    public
    BadServiceNoInject ()
    {
    }
    }
    ビルドが成功する 端末等で操作

    View Slide

  41. こちらに登壇時の映
    像をいれるので、文
    字を載せないように
    して下さい。
    アナライザなし
    アナライザあり
    ビルドが失敗する ミスが早く見つかる
    public class
    BadServiceNoInject :
    IBadServiceNoInject
    {
    public
    BadServiceNoInject ()
    {
    }
    }
    クラッシュする
    public class
    BadServiceNoInject :
    IBadServiceNoInject
    {
    public
    BadServiceNoInject ()
    {
    }
    }
    ビルドが成功する 端末等で操作

    View Slide

  42. こちらに登壇時の映
    像をいれるので、文
    字を載せないように
    して下さい。
    Roslynアナライザーができること、使い方
    - 様々な診断ルールを持つアナライザーの紹介
    - Unity上でRoslynアナライザーを使う方法
    - JetBrains Rider上でRoslynアナライザーを使う方法
    - アナライザーTips

    View Slide

  43. こちらに登壇時の映
    像をいれるので、文
    字を載せないように
    して下さい。
    Roslynアナライザーができること、使い方
    - 様々な診断ルールを持つアナライザーの紹介
    - Unity上でRoslynアナライザーを使う方法
    - JetBrains Rider上でRoslynアナライザーを使う方法
    - アナライザーTips

    View Slide

  44. こちらに登壇時の映
    像をいれるので、文
    字を載せないように
    して下さい。
    Unity上でRoslynアナライザーを使う方法
    - Unityへの導入の仕方
    - Unityエディター上でのアナライザーの振る舞い

    View Slide

  45. こちらに登壇時の映
    像をいれるので、文
    字を載せないように
    して下さい。
    Unity上でRoslynアナライザーを使う方法
    - Unityへの導入の仕方
    - Unityエディター上でのアナライザーの振る舞い

    View Slide

  46. こちらに登壇時の映
    像をいれるので、文
    字を載せないように
    して下さい。
    dllを、Unityプロジェクト下の
    任意のフォルダに配置する
    Unity上で設定する アナライザーが動作する
    範囲の設定をする
    Unityへの導入の仕方

    View Slide

  47. こちらに登壇時の映
    像をいれるので、文
    字を載せないように
    して下さい。
    dllを、Unityプロジェクト下の
    任意のフォルダに配置する
    Unity上で設定する アナライザーが動作する
    範囲の設定をする
    Unityへの導入の仕方

    View Slide

  48. こちらに登壇時の映
    像をいれるので、文
    字を載せないように
    して下さい。
    Assets/Hoge/
    ├── Editor
    │ └── YourLibrary.Editor.asmdef
    ├── Runtime
    │ ├── YourLibrary.Runtime.asmdef
    │ └── YourLibraryCode.cs
    └── Tests
    └── YourLibrary.Tests.asmdef

    View Slide

  49. こちらに登壇時の映
    像をいれるので、文
    字を載せないように
    して下さい。
    Assets/Hoge/
    ├── Editor
    │ └── YourLibrary.Editor.asmdef
    ├── Runtime
    │ ├── YourLibrary.Runtime.asmdef
    │ └── YourLibraryCode.cs
    └── Tests
    └── YourLibrary.Tests.asmdef
    Assets/Hoge/
    ├── Analyzers
    │ ├── your.library.analyzers.dll
    │ └── your.library.analyzers.dll.meta
    ├── Editor
    │ └── YourLibrary.Editor.asmdef
    ├── Runtime
    │ ├── YourLibrary.Runtime.asmdef
    │ └── YourLibraryCode.cs
    └── Tests
    └── YourLibrary.Tests.asmdef
    Assets下の任意のディレクトリに
    アナライザーのdllを配置する

    View Slide

  50. こちらに登壇時の映
    像をいれるので、文
    字を載せないように
    して下さい。
    Assets/Hoge/
    ├── Editor
    │ └── YourLibrary.Editor.asmdef
    ├── Runtime
    │ ├── YourLibrary.Runtime.asmdef
    │ └── YourLibraryCode.cs
    └── Tests
    └── YourLibrary.Tests.asmdef
    Assets/Hoge/
    ├── Analyzers
    │ ├── your.library.analyzers.dll
    │ └── your.library.analyzers.dll.meta
    ├── Editor
    │ └── YourLibrary.Editor.asmdef
    ├── Runtime
    │ ├── YourLibrary.Runtime.asmdef
    │ └── YourLibraryCode.cs
    └── Tests
    └── YourLibrary.Tests.asmdef
    Assets下の任意のディレクトリに
    アナライザーのdllを配置する

    View Slide

  51. こちらに登壇時の映
    像をいれるので、文
    字を載せないように
    して下さい。
    dllを、Unityプロジェクト下の
    任意のフォルダに配置する
    Unity上で設定する アナライザーが動作する
    範囲の設定をする
    Unityへの導入の仕方

    View Slide

  52. Unity上での設定の仕方
    1. アナライザーのDLLをProjectウィンドウで選択(インス
    ペクタが開く)
    2. Select Platforms for plugin下のチェックボックスをすべ
    てoff
    3. Applyボタンをクリック
    4. 栞アイコンをクリック
    5. ウィンドウに”RoslynAnalyzer”と入力し、Enterを押す

    View Slide

  53. Unity上での設定の仕方
    1. アナライザーのDLLをProjectウィンドウで選択(インス
    ペクタが開く)
    2. Select Platforms for plugin下のチェックボックスをすべ
    てoff
    3. Applyボタンをクリック
    4. 栞アイコンをクリック
    5. ウィンドウに”RoslynAnalyzer”と入力し、Enterを押す

    View Slide

  54. Unity上での設定の仕方
    1. アナライザーのDLLをProjectウィンドウで選択(インス
    ペクタが開く)
    2. Select Platforms for plugin下のチェックボックスをすべ
    てoff
    3. Applyボタンをクリック
    4. 栞アイコンをクリック
    5. ウィンドウに”RoslynAnalyzer”と入力し、Enterを押す

    View Slide

  55. Unity上での設定の仕方
    1. アナライザーのDLLをProjectウィンドウで選択(インス
    ペクタが開く)
    2. Select Platforms for plugin下のチェックボックスをすべ
    てoff
    3. Applyボタンをクリック
    4. 栞アイコンをクリック
    5. ウィンドウに”RoslynAnalyzer”と入力し、Enterを押す

    View Slide

  56. Unity上での設定の仕方
    1. アナライザーのDLLをProjectウィンドウで選択(インス
    ペクタが開く)
    2. Select Platforms for plugin下のチェックボックスをすべ
    てoff
    3. Applyボタンをクリック
    4. 栞アイコンをクリック
    5. ウィンドウに”RoslynAnalyzer”と入力し、Enterを押す

    View Slide

  57. Unity上での設定の仕方
    1. アナライザーのDLLをProjectウィンドウで選択(インス
    ペクタが開く)
    2. Select Platforms for plugin下のチェックボックスをすべ
    てoff
    3. Applyボタンをクリック
    4. 栞アイコンをクリック
    5. ウィンドウに”RoslynAnalyzer”と入力し、Enterを押す

    View Slide

  58. こちらに登壇時の映
    像をいれるので、文
    字を載せないように
    して下さい。
    dllを、Unityプロジェクト下の
    任意のフォルダに配置する
    Unity上で設定する アナライザーが動作する
    範囲の設定をする
    Unityへの導入の仕方

    View Slide

  59. こちらに登壇時の映
    像をいれるので、文
    字を載せないように
    して下さい。
    アナライザーが動作する範囲の設定をする
    Assets/Hoge/
    ├── Analyzers
    │ ├── your.library.analyzers.dll
    │ └── your.library.analyzers.dll.meta
    ├── Editor
    │ └── YourLibrary.Editor.asmdef
    ├── Runtime
    │ ├── YourLibrary.Runtime.asmdef
    │ └── YourLibraryCode.cs
    └── Tests
    └── YourLibrary.Tests.asmdef

    View Slide

  60. こちらに登壇時の映
    像をいれるので、文
    字を載せないように
    して下さい。
    アナライザーが動作する範囲の設定をする
    Assets/Hoge/
    ├── Analyzers
    │ ├── your.library.analyzers.dll
    │ └── your.library.analyzers.dll.meta
    ├── Editor
    │ └── YourLibrary.Editor.asmdef
    ├── Runtime
    │ ├── YourLibrary.Runtime.asmdef
    │ └── YourLibraryCode.cs
    └── Tests
    └── YourLibrary.Tests.asmdef
    YourLibrary.Runtimeアセンブリに含まれる
    C#スクリプト(YourLibraryCode.cs)
    にアナライザーを適用したい

    View Slide

  61. こちらに登壇時の映
    像をいれるので、文
    字を載せないように
    して下さい。
    アナライザーが動作する範囲の設定をする
    Assets/Hoge/
    ├── Analyzers
    │ ├── your.library.analyzers.dll
    │ └── your.library.analyzers.dll.meta
    ├── Editor
    │ └── YourLibrary.Editor.asmdef
    ├── Runtime
    │ ├── YourLibrary.Runtime.asmdef
    │ └── YourLibraryCode.cs
    └── Tests
    └── YourLibrary.Tests.asmdef
    Assets/Hoge/
    ├── Analyzers
    │ ├── Analyzers.asmdef
    │ ├── dummyFile.cs
    │ ├── your.library.analyzers.dll
    │ └── your.library.analyzers.dll.meta
    ├── Editor
    │ └── YourLibrary.Editor.asmdef
    ├── Runtime
    │ ├── YourLibrary.Runtime.asmdef
    │ └── YourLibraryCode.cs
    └── Tests
    └── YourLibrary.Tests.asmdef
    Assets下のアナライザーDLLが含まれる
    ディレクトリにasmdefとダミーのC#スクリ
    プトを配置する

    View Slide

  62. こちらに登壇時の映
    像をいれるので、文
    字を載せないように
    して下さい。
    アナライザーが動作する範囲の設定をする
    Assets/Hoge/
    ├── Analyzers
    │ ├── your.library.analyzers.dll
    │ └── your.library.analyzers.dll.meta
    ├── Editor
    │ └── YourLibrary.Editor.asmdef
    ├── Runtime
    │ ├── YourLibrary.Runtime.asmdef
    │ └── YourLibraryCode.cs
    └── Tests
    └── YourLibrary.Tests.asmdef
    Assets/Hoge/
    ├── Analyzers
    │ ├── Analyzers.asmdef
    │ ├── dummyFile.cs
    │ ├── your.library.analyzers.dll
    │ └── your.library.analyzers.dll.meta
    ├── Editor
    │ └── YourLibrary.Editor.asmdef
    ├── Runtime
    │ ├── YourLibrary.Runtime.asmdef
    │ └── YourLibraryCode.cs
    └── Tests
    └── YourLibrary.Tests.asmdef
    Analyzersを別アセンブリから参照する際にはア
    センブリの生成が必要です。そのため、何かしら
    のC#スクリプトを置く必要があります。

    View Slide

  63. こちらに登壇時の映
    像をいれるので、文
    字を載せないように
    して下さい。
    アナライザーが動作する範囲の設定をする
    Assets/Hoge/
    ├── Analyzers
    │ ├── Analyzers.asmdef
    │ ├── dummyFile.cs
    │ ├── your.library.analyzers.dll
    │ └── your.library.analyzers.dll.meta
    ├── Editor
    │ └── YourLibrary.Editor.asmdef
    ├── Runtime
    │ ├── YourLibrary.Runtime.asmdef
    │ └── YourLibraryCode.cs
    └── Tests
    └── YourLibrary.Tests.asmdef
    1. アナライザーを適用したい
    asmdefをinspecterで開く
    (無い場合はasmdef作成する)
    2. Assembly Defintion Reference に アナライザーを含む
    アセンブリ(Analyzers)を追加
    3. applyを押す

    View Slide

  64. こちらに登壇時の映
    像をいれるので、文
    字を載せないように
    して下さい。
    アナライザーが動作する範囲の設定をする
    1. アナライザーを適用したい
    asmdefをinspecterで開く
    (無い場合はasmdef作成する)
    2. Assembly Defintion Reference に アナライザーを含む
    アセンブリ(Analyzers)を追加
    3. applyを押す

    View Slide

  65. こちらに登壇時の映
    像をいれるので、文
    字を載せないように
    して下さい。
    アナライザーが動作する範囲の設定をする
    1. アナライザーを適用したい
    asmdefをinspecterで開く
    (無い場合はasmdef作成する)
    2. Assembly Defintion Reference に アナライザーを含む
    アセンブリ(Analyzers)を追加
    3. applyを押す

    View Slide

  66. こちらに登壇時の映
    像をいれるので、文
    字を載せないように
    して下さい。
    Assets/Hoge/
    ├── Analyzers
    │ ├── Analyzers.asmdef
    │ ├── dummyFile.cs
    │ ├── your.library.analyzers.dll
    │ └── your.library.analyzers.dll.meta
    ├── Editor
    │ └── YourLibrary.Editor.asmdef
    ├── Runtime
    │ ├── YourLibrary.Runtime.asmdef
    │ └── YourLibraryCode.cs
    └── Tests
    └── YourLibrary.Tests.asmdef
    アナライザーが動作する範囲の設定をする
    Assets/your.library/
    ├── Editor
    │ └── YourLibrary.Editor.asmdef
    ├── Runtime
    │ ├── YourLibrary.Runtime.asmdef
    │ └── YourLibraryCode.cs
    └── Tests
    └── YourLibrary.Tests.asmdef
    YourLibrary.Runtimeアセンブリに含まれる
    C#コード(YourLibraryCode.cs)に
    アナライザーが適用されます

    View Slide

  67. こちらに登壇時の映
    像をいれるので、文
    字を載せないように
    して下さい。
    Assets/Hoge/
    ├── Analyzers
    │ ├── your.library.analyzers.asmdef
    │ ├── your.library.analyzers.dll
    │ └── your.library.analyzers.dll.meta
    ├── Editor
    │ └── YourLibrary.Editor.asmdef
    ├── Runtime
    │ ├── YourLibrary.Runtime.asmdef
    │ └── YourLibraryCode.cs
    └── Tests
    └── YourLibrary.Tests.asmdef
    アナライザーが動作する範囲の設定をする
    Assets/your.library/
    ├── Editor
    │ └── YourLibrary.Editor.asmdef
    ├── Runtime
    │ ├── YourLibrary.Runtime.asmdef
    │ └── YourLibraryCode.cs
    └── Tests
    └── YourLibrary.Tests.asmdef
    YourLibrary.Runtimeアセンブリに含まれる
    C#コード(YourLibraryCode.cs)に
    アナライザーが適用されます

    View Slide

  68. こちらに登壇時の映
    像をいれるので、文
    字を載せないように
    して下さい。
    Unity上でRoslynアナライザーを使う方法
    - Unityへの導入の仕方
    - Unityエディター上でのアナライザーの振る舞い

    View Slide

  69. こちらに登壇時の映
    像をいれるので、文
    字を載せないように
    して下さい。
    Unityエディター上でのRoslynアナライザーの振る舞い
    ↑のように、重要度に応じた結果が出力される
    重要度がエラーなら、コンパイルエ
    ラーと同等の扱いになる

    View Slide

  70. こちらに登壇時の映
    像をいれるので、文
    字を載せないように
    して下さい。
    Roslynアナライザーができること、使い方
    - 様々な診断ルールを持つアナライザーの紹介
    - Unity上でRoslynアナライザーを使う方法
    - JetBrains Rider上でRoslynアナライザーを使う方法
    - アナライザーTips

    View Slide

  71. こちらに登壇時の映
    像をいれるので、文
    字を載せないように
    して下さい。
    JetBrains Rider上でRoslynアナライザーを使う方法
    Unity上にRoslynアナライザーを導
    入する
    Unityから、Assets | Open C#
    Projectなどの操作をするだけ
    NOTE: Visual Studio、
    VS Codeでの動作は未検証です。

    View Slide

  72. こちらに登壇時の映
    像をいれるので、文
    字を載せないように
    して下さい。
    JetBrains Rider上でRoslynアナライザーを使う方法
    Unity上にRoslynアナライザーを導
    入する
    Unityから、Assets | Open C#
    Projectなどの操作をするだけ
    NOTE: Visual Studio、
    VS Codeでの動作は未検証です。

    View Slide

  73. こちらに登壇時の映
    像をいれるので、文
    字を載せないように
    して下さい。
    JetBrains Rider上でのRoslynアナライザーの振る舞い
    エディタ上に問題箇所がハイライトさ
    れ、修正ポップアップに詳細が表示さ
    れる
    Code | Inspect Code…から実行
    すれば、インスペクション結果ウィンド
    ウに出力される
    NOTE: Visual Studio、
    VS Codeでの動作は未検証です。

    View Slide

  74. こちらに登壇時の映
    像をいれるので、文
    字を載せないように
    して下さい。
    Roslynアナライザーができること、使い方
    - 様々な診断ルールを持つアナライザーの紹介
    - Unity上でRoslynアナライザーを使う方法
    - JetBrains Rider上でRoslynアナライザーを使う方法
    - アナライザーTips

    View Slide

  75. こちらに登壇時の映
    像をいれるので、文
    字を載せないように
    して下さい。
    RoslynアナライザーTips
    - 重要度の設定
    - Roslynアナライザーの診断を抑制する方法
    - RoslynアナライザーをCI上で動かす
    NOTE: Visual Studio、VS Codeでの動作は未検証です。

    View Slide

  76. こちらに登壇時の映
    像をいれるので、文
    字を載せないように
    して下さい。
    RoslynアナライザーTips
    - 重要度の設定
    - Roslynアナライザーの診断を抑制する方法
    - RoslynアナライザーをCI上で動かす
    NOTE: Visual Studio、VS Codeでの動作は未検証です。

    View Slide

  77. こちらに登壇時の映
    像をいれるので、文
    字を載せないように
    して下さい。
    アナライザーの診断ルールごとに診断の重要度(severity)が設定されている
    - error
    - warning
    - suggestion
    - silent
    - none

    View Slide

  78. こちらに登壇時の映
    像をいれるので、文
    字を載せないように
    して下さい。
    - CS1002: エラー
    - CS0219: 警告
    各アナライザにはデフォルトの重
    要度が設定されている

    View Slide

  79. こちらに登壇時の映
    像をいれるので、文
    字を載せないように
    して下さい。
    - CS1002: エラー
    - CS0219: 警告
    各アナライザにはデフォルトの重
    要度が設定されている

    View Slide

  80. こちらに登壇時の映
    像をいれるので、文
    字を載せないように
    して下さい。
    - CS1002: エラー
    - CS0219: 警告
    CS0219をエラーにした
    いなぁ...
    各アナライザにはデフォルトの重
    要度が設定されている

    View Slide

  81. こちらに登壇時の映
    像をいれるので、文
    字を載せないように
    して下さい。
    - ルールセット ファイル
    - ルールセット ファイル
    - EditorConfig ファイル
    - DotSetting ファイル
    重要度設定の方法
    Unity JetBrains Rider

    View Slide

  82. こちらに登壇時の映
    像をいれるので、文
    字を載せないように
    して下さい。
    - ルールセット ファイル
    - ルールセット ファイル
    - EditorConfig ファイル
    - DotSetting ファイル
    重要度設定の方法
    Unity JetBrains Rider

    View Slide

  83. こちらに登壇時の映
    像をいれるので、文
    字を載せないように
    して下さい。
    ルールセット ファイルによる重要度の設定
    Assets/
    ├── your.library.analyzers.dll
    ├── your.library.analyzers.dll.meta
    ├── RethrowError.cs
    ├── Default.ruleset
    └── Subfolder
    ├── Subfoloder.asmdef
    └── RethrowError.cs
    Assets直下に
    Default.rulesetを配置する

    View Slide

  84. こちらに登壇時の映
    像をいれるので、文
    字を載せないように
    して下さい。
    ルールセット ファイルによる重要度の設定
    Assets/
    ├── your.library.analyzers.dll
    ├── your.library.analyzers.dll.meta
    ├── RethrowError.cs
    ├── Default.ruleset
    └── Subfolder
    ├── Subfoloder.asmdef
    └── RethrowError.cs
    Assets直下に ”Default.ruleset” という名前で
    ファイルを配置する






    View Slide

  85. こちらに登壇時の映
    像をいれるので、文
    字を載せないように
    して下さい。
    ルールセット ファイルによる重要度の設定






    View Slide

  86. こちらに登壇時の映
    像をいれるので、文
    字を載せないように
    して下さい。
    ルールセット ファイルによる重要度の設定






    アナライザーのIDを指定 アナライザーの重要度を上書き

    View Slide

  87. こちらに登壇時の映
    像をいれるので、文
    字を載せないように
    して下さい。
    ルールセット ファイルによる重要度の設定






    アナライザーのIDを指定 アナライザーの重要度を上書き

    View Slide

  88. こちらに登壇時の映
    像をいれるので、文
    字を載せないように
    して下さい。
    RoslynアナライザーTips
    - 重要度の設定
    - Roslynアナライザーの診断を抑制する方法
    - RoslynアナライザーをCI上で動かす
    NOTE: Visual Studio、VS Codeでの動作は未検証です。

    View Slide

  89. こちらに登壇時の映
    像をいれるので、文
    字を載せないように
    して下さい。
    アナライザーに怒られるコードを
    仕方なく書いた
    自動生成されたコードに対して
    アナライザーが警告を出してしまう
    なぜ抑制するのか

    View Slide

  90. こちらに登壇時の映
    像をいれるので、文
    字を載せないように
    して下さい。
    Roslynアナライザーの診断を抑制する方法
    - ファイルパス、拡張子単位での抑制(Unityでは不可能)
    - クラス、メソッド単位での抑制
    - ステートメント単位での抑制

    View Slide

  91. こちらに登壇時の映
    像をいれるので、文
    字を載せないように
    して下さい。
    Roslynアナライザーの診断を抑制する方法
    - ファイルパス、拡張子単位での抑制(Unityでは不可能)
    - クラス、メソッド単位での抑制
    - ステートメント単位での抑制

    View Slide

  92. ファイルパス、拡張子単位の抑制
    1. Preferences… | Editor | Inspection Settings を開く
    2. Elements to Skip で除外するフォルダや拡張子のパターン
    を追加する
    3. Saveボタン右のプルダウンで、
    Solution YOUR_PROJECT_NAME
    team-shared を選択して保存

    View Slide

  93. ファイルパス、拡張子単位の抑制
    1. Preferences… | Editor | Inspection Settings を開く
    2. Elements to Skip で除外するフォルダや拡張子のパターン
    を追加する
    3. Saveボタン右のプルダウンで、
    Solution YOUR_PROJECT_NAME
    team-shared を選択して保存

    View Slide

  94. ファイルパス、拡張子単位の抑制
    1. Preferences… | Editor | Inspection Settings を開く
    2. Elements to Skip で除外するフォルダや拡張子のパターン
    を追加する
    3. Saveボタン右のプルダウンで、
    Solution YOUR_PROJECT_NAME
    team-shared を選択して保存

    View Slide

  95. ファイルパス、拡張子単位の抑制
    1. Preferences… | Editor | Inspection Settings を開く
    2. Elements to Skip で除外するフォルダや拡張子のパターン
    を追加する
    3. Saveボタン右のプルダウンで、
    Solution YOUR_PROJECT_NAME
    team-shared を選択して保存

    View Slide

  96. こちらに登壇時の映
    像をいれるので、文
    字を載せないように
    して下さい。
    Roslynアナライザーの診断を抑制する方法
    - ファイルパス、拡張子単位での抑制(Unityでは不可能)
    - クラス、メソッド単位での抑制
    - ステートメント単位での抑制

    View Slide

  97. こちらに登壇時の映
    像をいれるので、文
    字を載せないように
    して下さい。
    public async Task M()
    {
    await Task.Delay(100);
    }
    Do not use Task. Use the UniTask

    View Slide

  98. こちらに登壇時の映
    像をいれるので、文
    字を載せないように
    して下さい。
    public async Task M()
    {
    await Task.Delay(100);
    }
    このメソッド内だけに限り
    使うことを許可したいなぁ ...

    View Slide

  99. こちらに登壇時の映
    像をいれるので、文
    字を載せないように
    して下さい。
    [SuppressMessage("ApiDesign", "RS0030")]
    public async Task M()
    {
    await Task.Delay(100);
    }
    このメソッド内だけに限り
    使うことを許可したいなぁ ...

    View Slide

  100. こちらに登壇時の映
    像をいれるので、文
    字を載せないように
    して下さい。
    [SuppressMessage("ApiDesign", "RS0030")]
    public async Task M()
    {
    await Task.Delay(100);
    }
    このメソッド内だけに限り
    使うことを許可したいなぁ ...
    アナライザーのID
    アナライザーのカテゴリー

    View Slide

  101. こちらに登壇時の映
    像をいれるので、文
    字を載せないように
    して下さい。
    [SuppressMessage("ApiDesign", "RS0030")]
    public async Task M()
    {
    await Task.Delay(100);
    }
    警告を抑制出来る!
    アナライザーのID
    アナライザーのカテゴリー

    View Slide

  102. こちらに登壇時の映
    像をいれるので、文
    字を載せないように
    して下さい。
    Roslynアナライザーの診断を抑制する方法
    - ファイルパス、拡張子単位での抑制(Unityでは不可能)
    - クラス、メソッド単位での抑制
    - ステートメント単位での抑制

    View Slide

  103. こちらに登壇時の映
    像をいれるので、文
    字を載せないように
    して下さい。
    public async Task M()
    {
    await Task.Delay(100);
    await Task.Delay(100);
    }
    Do not use Task. Use the UniTask.

    View Slide

  104. こちらに登壇時の映
    像をいれるので、文
    字を載せないように
    して下さい。
    public async Task M()
    {
    await Task.Delay(100);
    await Task.Delay(100);
    }
    Do not use Task. Use the UniTask.
    このステートメントに限り
    警告を抑制したい

    View Slide

  105. こちらに登壇時の映
    像をいれるので、文
    字を載せないように
    して下さい。
    public async Task M()
    {
    await Task.Delay(100);
    #pragma warning disable RS0030
    await Task.Delay(100);
    #pragma warning restore RS0030
    }

    View Slide

  106. こちらに登壇時の映
    像をいれるので、文
    字を載せないように
    して下さい。
    public async Task M()
    {
    await Task.Delay(100);
    #pragma warning disable RS0030
    await Task.Delay(100);
    #pragma warning restore RS0030
    }

    View Slide

  107. こちらに登壇時の映
    像をいれるので、文
    字を載せないように
    して下さい。
    public async Task M()
    {
    await Task.Delay(100);
    #pragma warning disable RS0030
    await Task.Delay(100);
    #pragma warning restore RS0030
    }

    View Slide

  108. こちらに登壇時の映
    像をいれるので、文
    字を載せないように
    して下さい。
    public async Task M()
    {
    await Task.Delay(100);
    #pragma warning disable RS0030
    await Task.Delay(100);
    #pragma warning restore RS0030
    }
    このステートメントに限り
    抑制出来る!

    View Slide

  109. こちらに登壇時の映
    像をいれるので、文
    字を載せないように
    して下さい。
    RoslynアナライザーTips
    - 重要度の設定
    - Roslynアナライザーの診断を抑制する方法
    - RoslynアナライザーをCI上で動かす
    NOTE: Visual Studio、VS Codeでの動作は未検証です。

    View Slide

  110. コマンドラインからバッ
    チモードでUnityを起動
    コードをPush
    注意: 一般的なlintツールとは異なり、コン
    パイルログに混ざってアナライザーの診断
    結果も出力される
    Copyright: Jenkins project
    https://jenkins.io

    View Slide

  111. こちらに登壇時の映
    像をいれるので、文
    字を載せないように
    して下さい。
    目次
    - Roslynアナライザーの概要
    - Roslynアナライザーができること、使い方
    - カスタムルールの作り方
    - カスタムアナライザをテストする方法
    - 作成したアナライザーをUPMパッケージに内包して配布、利用する
    NOTE: バージョンによってバグによる制約があるため、
    このスライドではUnity 2021.2 + Rider Editor package
    v3.0.9を基準に書いています。

    View Slide

  112. こちらに登壇時の映
    像をいれるので、文
    字を載せないように
    して下さい。
    カスタムルールの作り方
    - ソリューションプロジェクトの作成
    - 定義を検査する例
    - 呼び出しを検査する例

    View Slide

  113. こちらに登壇時の映
    像をいれるので、文
    字を載せないように
    して下さい。
    カスタムルールの作り方
    - ソリューションプロジェクトの作成
    - 定義を検査する例
    - 呼び出しを検査する例

    View Slide

  114. ソリューション・プロジェクトを作成する
    1. ソリューションを作成する
    2. Analyzerのプロジェクトを作成する
    3. プロジェクトをソリューションに追加する
    4. 必要なパッケージをインストールする
    RoslynToyAnalyzer
    ├── RoslynToyAnalyzer
    │ ├── RoslynToyAnalyzer.csproj
    │ └── RoslynToyAnalyzer.cs
    └── RoslynToyAnalyzers.sln
    実行コマンド
    dotnet new sln -o RoslynToyAnalyzer

    View Slide

  115. ソリューション・プロジェクトを作成する
    1. ソリューションを作成する
    2. Analyzerのプロジェクトを作成する
    3. プロジェクトをソリューションに追加する
    4. 必要なパッケージをインストールする
    RoslynToyAnalyzer
    ├── RoslynToyAnalyzer
    │ ├── RoslynToyAnalyzer.csproj
    │ └── RoslynToyAnalyzer.cs
    └── RoslynToyAnalyzers.sln
    実行コマンド
    dotnet new classlib -o RoslynToyAnalyzer

    View Slide

  116. ソリューション・プロジェクトを作成する
    1. ソリューションを作成する
    2. Analyzerのプロジェクトを作成する
    3. プロジェクトをソリューションに追加する
    4. 必要なパッケージをインストールする
    RoslynToyAnalyzer
    ├── RoslynToyAnalyzer
    │ ├── RoslynToyAnalyzer.csproj
    │ └── RoslynToyAnalyzer.cs
    └── RoslynToyAnalyzers.sln
    実行コマンド
    dotnet sln add RoslynToyAnalyzer/RoslynToyAnalyzer.csproj

    View Slide

  117. ソリューション・プロジェクトを作成する
    1. ソリューションを作成する
    2. Analyzerのプロジェクトを作成する
    3. プロジェクトをソリューションに追加する
    4. 必要なパッケージをインストールする
    RoslynToyAnalyzer
    ├── RoslynToyAnalyzer
    │ ├── RoslynToyAnalyzer.csproj
    │ └── RoslynToyAnalyzer.cs
    └── RoslynToyAnalyzers.sln
    実行コマンド
    dotnet add RoslynToyAnalyzer package Microsoft.CodeAnalysis.Analyzers --version 3.3.2
    dotnet add RoslynToyAnalyzer package Microsoft.CodeAnalysis.CSharp --version 3.5.0

    View Slide

  118. ソリューション・プロジェクトを作成する
    1. ソリューションを作成する
    2. Analyzerのプロジェクトを作成する
    3. プロジェクトをソリューションに追加する
    4. 必要なパッケージをインストールする
    RoslynToyAnalyzer
    ├── RoslynToyAnalyzer
    │ ├── RoslynToyAnalyzer.csproj
    │ └── RoslynToyAnalyzer.cs
    └── RoslynToyAnalyzers.sln
    実行コマンド
    dotnet add RoslynToyAnalyzer package Microsoft.CodeAnalysis.Analyzers --version 3.3.2
    dotnet add RoslynToyAnalyzer package Microsoft.CodeAnalysis.CSharp --version 3.5.0

    View Slide

  119. こちらに登壇時の映
    像をいれるので、文
    字を載せないように
    して下さい。
    カスタムルールの作り方
    - ソリューションプロジェクトの作成
    - 定義を検査する例
    - 呼び出しを検査する例

    View Slide

  120. こちらに登壇時の映
    像をいれるので、文
    字を載せないように
    して下さい。
    定義を検査する例
    public class Foo
    {
    }
    NamedTypeのシンボル名に小文字を含む命
    名に対して警告を出す

    View Slide

  121. こちらに登壇時の映
    像をいれるので、文
    字を載せないように
    して下さい。
    定義を検査する例
    public class Foo
    {
    }
    NamedTypeのシンボル名に小文字を含んだ
    名前 Foo が使用されています。

    View Slide

  122. こちらに登壇時の映
    像をいれるので、文
    字を載せないように
    して下さい。
    定義を検査する例
    public class Foo
    {
    }
    NamedTypeって何?

    View Slide

  123. こちらに登壇時の映
    像をいれるので、文
    字を載せないように
    して下さい。
    定義を検査する例
    public class Foo
    {
    }
    NamedTypeはclass、Enum、struct、
    Interfaceを含んだシンボルの名前を意味し
    ます。

    View Slide

  124. こちらに登壇時の映
    像をいれるので、文
    字を載せないように
    して下さい。
    using Microsoft.CodeAnalysis;
    using Microsoft.CodeAnalysis.CSharp;
    using Microsoft.CodeAnalysis.CSharp.Syntax;
    using Microsoft.CodeAnalysis.Diagnostics;
    using System.Collections.Immutable;
    using System.Linq;
    namespace RoslynToyAnalyzer
    {
    [DiagnosticAnalyzer(LanguageNames.CSharp)]
    public class RoslynToyAnalyzer : DiagnosticAnalyzer
    {
    private static readonly DiagnosticDescriptor _toyRule = new DiagnosticDescriptor(
    id: "TOY001",
    title: "NamedTypeのシンボルに小文字を含む命名を禁止するアナライザー",
    messageFormat: "NamedTypeのシンボルに小文字を含んだ命名 {0} が使用されています",
    category: "RoslynToyAnalyzer",
    defaultSeverity: DiagnosticSeverity.Error,
    isEnabledByDefault: true);
    public override ImmutableArray SupportedDiagnostics
    {
    get { return ImmutableArray.Create(_toyRule); }
    }
    public override void Initialize(AnalysisContext context)
    {
    context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None);
    context.EnableConcurrentExecution();
    context.RegisterSymbolAction(AnalyzeSymbol, SymbolKind.NamedType);
    }
    private static void AnalyzeSymbol(SymbolAnalysisContext context)
    {
    var namedTypeSymbol = (INamedTypeSymbol)context.Symbol;
    if (namedTypeSymbol.Name.ToCharArray().Any(char.IsLower))
    {
    var diagnostic = Diagnostic.Create(
    _toyRule,
    namedTypeSymbol.Locations[0],
    namedTypeSymbol.Name);
    context.ReportDiagnostic(diagnostic);
    }
    }
    }

    View Slide

  125. こちらに登壇時の映
    像をいれるので、文
    字を載せないように
    して下さい。
    using Microsoft.CodeAnalysis;
    using Microsoft.CodeAnalysis.CSharp;
    using Microsoft.CodeAnalysis.CSharp.Syntax;
    using Microsoft.CodeAnalysis.Diagnostics;
    using System.Collections.Immutable;
    using System.Linq;
    namespace RoslynToyAnalyzer
    {
    [DiagnosticAnalyzer(LanguageNames.CSharp)]
    public class RoslynToyAnalyzer : DiagnosticAnalyzer
    {
    private static readonly DiagnosticDescriptor _toyRule = new DiagnosticDescriptor(
    id: "TOY001",
    title: "NamedTypeのシンボルに小文字を含む命名を禁止するアナライザー",
    messageFormat: "NamedTypeのシンボルに小文字を含んだ命名 {0} が使用されています",
    category: "RoslynToyAnalyzer",
    defaultSeverity: DiagnosticSeverity.Error,
    isEnabledByDefault: true);
    public override ImmutableArray SupportedDiagnostics
    {
    get { return ImmutableArray.Create(_toyRule); }
    }
    public override void Initialize(AnalysisContext context)
    {
    context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None);
    context.EnableConcurrentExecution();
    context.RegisterSymbolAction(AnalyzeSymbol, SymbolKind.NamedType);
    }
    private static void AnalyzeSymbol(SymbolAnalysisContext context)
    {
    var namedTypeSymbol = (INamedTypeSymbol)context.Symbol;
    if (namedTypeSymbol.Name.ToCharArray().Any(char.IsLower))
    {
    var diagnostic = Diagnostic.Create(
    _toyRule,
    namedTypeSymbol.Locations[0],
    namedTypeSymbol.Name);
    context.ReportDiagnostic(diagnostic);
    }
    }
    }
    アナライザークラス
    アナライザーの診断ルールの宣言
    SupportedDiagnosticsプロパティ(必須プロパティ)
    Initializeメソッド(必須メソッド)
    実際の診断を行うメソッドの実装

    View Slide

  126. こちらに登壇時の映
    像をいれるので、文
    字を載せないように
    して下さい。
    [DiagnosticAnalyzer(LanguageNames.CSharp)]
    public class RoslynToyAnalyzer : DiagnosticAnalyzer
    {
    public override ImmutableArray SupportedDiagnostics
    {
    }
    public override void Initialize(AnalysisContext context)
    {
    }
    }
    アナライザーのベースとなるクラスを継承
    DiagnosticAnalyzer属性をつける
    SupportedDiagnosticsメソッドの実装が必要(後述)
    Initializeメソッドの実装が必要(後述)
    アナライザークラスの作成

    View Slide

  127. こちらに登壇時の映
    像をいれるので、文
    字を載せないように
    して下さい。
    private static readonly DiagnosticDescriptor _toyRule = new DiagnosticDescriptor(
    id: "TOY001",
    title: "NamedTypeのシンボルに小文字を含む命名を禁止するアナライザー
    ",
    messageFormat: "NamedTypeのシンボルに小文字を含んだ命名 {0} が使用されています
    ",
    category: "RoslynToyAnalyzer",
    defaultSeverity: DiagnosticSeverity.Error,
    isEnabledByDefault: true);
    アナライザーのカテゴリを指定
    診断時に出力する警告文のテンプレートを定義
    デフォルトの重要度を設定
    デフォルトで診断が有効な場合はtrueを返す
    アナライザーの診断ルールの宣言
    アナライザーのIDを指定 診断内容を説明するタイトル

    View Slide

  128. こちらに登壇時の映
    像をいれるので、文
    字を載せないように
    して下さい。
    public override ImmutableArray SupportedDiagnostics
    {
    get { return ImmutableArray.Create(_toyRule); }
    }
    作成したアナライザが提供するアナライザーの一覧を配列で返す
    SupportedDiagnosticsプロパティの実装(必須)
    DiagnosticAnalyzerを継承したので、SupportedDiagnosticsプロパティの実装が必要

    View Slide

  129. こちらに登壇時の映
    像をいれるので、文
    字を載せないように
    して下さい。
    public override void Initialize(AnalysisContext context)
    {
    context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None);
    context.EnableConcurrentExecution();
    context.RegisterSymbolAction(AnalyzeSymbol, SymbolKind.NamedType);
    }
    コールバックを受け取りたい契機をAnalysisContextに登録する
    Initializeメソッドの実装(必須)
    Roslynコンパイラによってアナライザーがロードされると呼ばれるメソッド
    DiagnosticAnalyzerを継承したので、Initializeメソッドの実装が必要
    シンボルの意味解析ごとに動作させたいメソッド
    ”AnalyzeSymbol”を、”RegisterSymbolAction”で登録

    View Slide

  130. こちらに登壇時の映
    像をいれるので、文
    字を載せないように
    して下さい。
    private static void AnalyzeSymbol(SymbolAnalysisContext context)
    {
    var namedTypeSymbol = (INamedTypeSymbol)context.Symbol;
    if (namedTypeSymbol.Name.ToCharArray().Any(char.IsLower))
    {
    var diagnostic = Diagnostic.Create(
    _toyRule,
    namedTypeSymbol.Locations[0],
    namedTypeSymbol.Name);
    context.ReportDiagnostic(diagnostic);
    }
    }
    診断に必要な情報を取得
    実際の診断を行うメソッドの実装
    診断結果を作成して返す
    シンボルの名前に小文字を含むか判定

    View Slide

  131. こちらに登壇時の映
    像をいれるので、文
    字を載せないように
    して下さい。
    カスタムルールの作り方
    - ソリューションプロジェクトの作成
    - 定義を検査する例
    - 呼び出しを検査する例

    View Slide

  132. こちらに登壇時の映
    像をいれるので、文
    字を載せないように
    して下さい。
    呼び出しを検査する例
    var hoge = new ArrayList();
    System.Collections.ArrayListをインスタン
    ス化しています。
    System.Collections.Generic.Listを代わりに
    使用してください。

    View Slide

  133. こちらに登壇時の映
    像をいれるので、文
    字を載せないように
    して下さい。
    using System.Collections.Generic;
    using Microsoft.CodeAnalysis;
    using Microsoft.CodeAnalysis.Diagnostics;
    using System.Collections.Immutable;
    using Microsoft.CodeAnalysis.Operations;
    namespace RoslynToyAnalyzer
    {
    [DiagnosticAnalyzer(LanguageNames.CSharp)]
    public class RoslynToyAnalyzer : DiagnosticAnalyzer
    {
    private static readonly DiagnosticDescriptor _toyRule2 = new DiagnosticDescriptor(
    id: "TOY002",
    title: "System.Collections.ArrayListのインスタンス作成を禁止するアナライザー ",
    messageFormat: "System.Collections.ArrayListをインスタンス化しています。 System.Collections.Generic.Listを代わりに使用してくださ
    い。",
    category: "RoslynToyAnalyzer",
    defaultSeverity: DiagnosticSeverity.Error,
    isEnabledByDefault: true);
    public override ImmutableArray SupportedDiagnostics
    {
    get { return ImmutableArray.Create(_toyRule2); }
    }
    public override void Initialize(AnalysisContext context)
    {
    context.RegisterOperationAction(AnalyzeCreationObject, OperationKind.ObjectCreation);
    }
    private static void AnalyzeCreationObject(OperationAnalysisContext context)
    {
    var operation = (IObjectCreationOperation)context.Operation;
    var symbol = operation.Type;
    var symbolFullName = GetSymbolFullName(symbol);
    if (symbolFullName != "System.Collections.ArrayList") return;
    var diagnostic = Diagnostic.Create(
    _toyRule2,
    operation.Syntax.GetLocation(),
    symbol.Name);
    context.ReportDiagnostic(diagnostic);
    }
    private static string GetSymbolFullName(INamespaceOrTypeSymbol symbol)
    {
    var collectedNamespace = new List();
    while (symbol.Name != "")
    {
    collectedNamespace.Add(symbol.Name);
    symbol = symbol.ContainingNamespace;
    }
    collectedNamespace.Reverse();
    return string.Join(".", collectedNamespace);
    }
    }
    }

    View Slide

  134. こちらに登壇時の映
    像をいれるので、文
    字を載せないように
    して下さい。
    using System.Collections.Generic;
    using Microsoft.CodeAnalysis;
    using Microsoft.CodeAnalysis.Diagnostics;
    using System.Collections.Immutable;
    using Microsoft.CodeAnalysis.Operations;
    namespace RoslynToyAnalyzer
    {
    [DiagnosticAnalyzer(LanguageNames.CSharp)]
    public class RoslynToyAnalyzer : DiagnosticAnalyzer
    {
    private static readonly DiagnosticDescriptor _toyRule2 = new DiagnosticDescriptor(
    id: "TOY002",
    title: "System.Collections.ArrayListのインスタンス作成を禁止するアナライザー ",
    messageFormat: "System.Collections.ArrayListをインスタンス化しています。 System.Collections.Generic.Listを代わりに使用してくださ
    い。",
    category: "RoslynToyAnalyzer",
    defaultSeverity: DiagnosticSeverity.Error,
    isEnabledByDefault: true);
    public override ImmutableArray SupportedDiagnostics
    {
    get { return ImmutableArray.Create(_toyRule2); }
    }
    public override void Initialize(AnalysisContext context)
    {
    context.RegisterOperationAction(AnalyzeCreationObject, OperationKind.ObjectCreation);
    }
    private static void AnalyzeCreationObject(OperationAnalysisContext context)
    {
    var operation = (IObjectCreationOperation)context.Operation;
    var symbol = operation.Type;
    var symbolFullName = GetSymbolFullName(symbol);
    if (symbolFullName != "System.Collections.ArrayList") return;
    var diagnostic = Diagnostic.Create(
    _toyRule2,
    operation.Syntax.GetLocation(),
    symbol.Name);
    context.ReportDiagnostic(diagnostic);
    }
    private static string GetSymbolFullName(INamespaceOrTypeSymbol symbol)
    {
    var collectedNamespace = new List();
    while (symbol.Name != "")
    {
    collectedNamespace.Add(symbol.Name);
    symbol = symbol.ContainingNamespace;
    }
    collectedNamespace.Reverse();
    return string.Join(".", collectedNamespace);
    }
    }
    }
    Initializeメソッド(必須メソッド)
    実際の診断を行うメソッド
    完全修飾型名の取得

    View Slide

  135. こちらに登壇時の映
    像をいれるので、文
    字を載せないように
    して下さい。
    public override void Initialize(AnalysisContext context)
    {
    context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None);
    context.EnableConcurrentExecution();
    context.RegisterOperationAction(AnalyzeCreationObject, OperationKind.ObjectCreation);
    }
    コールバックを受け取りたい契機をAnalysisContextに登録する
    Initializeメソッドの実装(必須)
    Roslynコンパイラによってアナライザーがロードされると呼ばれるメソッド
    DiagnosticAnalyzerを継承したので、Initializeメソッドの実装が必要
    オブジェクトのインスタンスが作成されるごとに動作させたいメソッド
    ”AnalyzeCreationObject”を、”RegisterOperationAction”で登録

    View Slide

  136. こちらに登壇時の映
    像をいれるので、文
    字を載せないように
    して下さい。
    public override void Initialize(AnalysisContext context)
    {
    context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None);
    context.EnableConcurrentExecution();
    context.RegisterOperationAction(AnalyzeCreationObject, OperationKind.ObjectCreation);
    }
    Initializeメソッドの実装(必須)
    - メソッド呼び出し(Invocation)
    - 変数初期化(VariableInitializer)
    - 配列の作成(ArrayCreation)
    - Etc…
    See also:
    https://docs.microsoft.com/ja-jp/dotnet/api/microsoft.codeanalysis.operationkind

    View Slide

  137. こちらに登壇時の映
    像をいれるので、文
    字を載せないように
    して下さい。
    private static void AnalyzeCreationObject(OperationAnalysisContext context)
    {
    var operation = (IObjectCreationOperation)context.Operation;
    var symbol = operation.Type;
    var symbolFullName = GetSymbolFullName(symbol);
    if (symbolFullName != "System.Collections.ArrayList") return;
    var diagnostic = Diagnostic.Create(
    _toyRule2,
    operation.Syntax.GetLocation(),
    symbol.Name);
    context.ReportDiagnostic(diagnostic);
    }
    診断に必要な情報を取得
    実際の診断を行うメソッドの実装
    診断結果を作成して返す
    シンボルの完全修飾型名を取得(後述)
    シンボルの型を取得

    View Slide

  138. こちらに登壇時の映
    像をいれるので、文
    字を載せないように
    して下さい。
    private static string GetSymbolFullName(INamespaceOrTypeSymbol symbol)
    {
    var collectedNamespace = new List();
    while (symbol.Name != "")
    {
    collectedNamespace.Add(symbol.Name);
    symbol = symbol.ContainingNamespace;
    }
    collectedNamespace.Reverse();
    return string.Join(".", collectedNamespace);
    }
    シンボルの完全修飾型名を取得するメソッドの実装
    .を連結して文字列として返す
    シンボルの型を再帰的にたどる
    NOTE: .NETでは完全修飾型名には”.”以外の
    文字列が含まれますが、Roslynの既存実装で
    は”+”を使っていないため、それに合わせて
    います。

    View Slide

  139. こちらに登壇時の映
    像をいれるので、文
    字を載せないように
    して下さい。
    目次
    - Roslynアナライザーの概要
    - Roslynアナライザーができること、使い方
    - カスタムルールの作り方
    - カスタムアナライザをテストする方法
    - 作成したアナライザーをUPMパッケージに内包して配布、利用する
    NOTE: バージョンによってバグによる制約があるため、
    このスライドではUnity 2021.2 + Rider Editor package
    v3.0.9を基準に書いています。

    View Slide

  140. こちらに登壇時の映
    像をいれるので、文
    字を載せないように
    して下さい。
    カスタムアナライザーをテストする
    - ソリューションプロジェクトの作成
    - テストコードの解説
    - テストを簡単に書くTips

    View Slide

  141. こちらに登壇時の映
    像をいれるので、文
    字を載せないように
    して下さい。
    カスタムアナライザーをテストする
    - ソリューションプロジェクトの作成
    - テストコードの解説
    - テストを簡単に書くTips

    View Slide

  142. ソリューション・プロジェクトを作成する
    1. テストのプロジェクトを作成
    2. テストプロジェクトをソリューションに追加
    3. テストコードプロジェクトからの参照を追加
    4. テストに必要なパッケージをインストール
    RoslynToyAnalyzer
    ├── RoslynToyAnalyzer
    │ ├── RoslynToyAnalyzer.cs
    │ └── RoslynToyAnalyzer.csproj
    ├── RoslynToyAnalyzer.Test
    │ ├── RoslynToyAnalyzer.Test.csproj
    │ └── RoslynToyAnalyzerTest.cs
    └── RoslynToyAnalyzers.sln
    実行コマンド

    View Slide

  143. ソリューション・プロジェクトを作成する
    1. テストのプロジェクトを作成
    2. テストプロジェクトをソリューションに追加
    3. テストコードプロジェクトからの参照を追加
    4. テストに必要なパッケージをインストール
    RoslynToyAnalyzer
    ├── RoslynToyAnalyzer
    │ ├── RoslynToyAnalyzer.cs
    │ └── RoslynToyAnalyzer.csproj
    ├── RoslynToyAnalyzer.Test
    │ ├── RoslynToyAnalyzer.Test.csproj
    │ └── RoslynToyAnalyzerTest.cs
    └── RoslynToyAnalyzers.sln
    実行コマンド
    dotnet new nunit -o RoslynToyAnalyzer.Test

    View Slide

  144. ソリューション・プロジェクトを作成する
    1. テストのプロジェクトを作成
    2. テストプロジェクトをソリューションへ追加
    3. テストコードプロジェクトからの参照を追加
    4. テストに必要なパッケージをインストール
    RoslynToyAnalyzer
    ├── RoslynToyAnalyzer
    │ ├── RoslynToyAnalyzer.cs
    │ └── RoslynToyAnalyzer.csproj
    ├── RoslynToyAnalyzer.Test
    │ ├── RoslynToyAnalyzer.Test.csproj
    │ └── RoslynToyAnalyzerTest.cs
    └── RoslynToyAnalyzers.sln
    実行コマンド
    dotnet sln RoslynToyAnalyzers.sln add RoslynToyAnalyzer.Test/RoslynToyAnalyzer.Test.csproj

    View Slide

  145. ソリューション・プロジェクトを作成する
    1. テストのプロジェクトを作成
    2. テストプロジェクトをソリューションへ追加
    3. テストコードプロジェクトからの参照を追加
    4. テストに必要なパッケージをインストール
    RoslynToyAnalyzer
    ├── RoslynToyAnalyzer
    │ ├── RoslynToyAnalyzer.cs
    │ └── RoslynToyAnalyzer.csproj
    ├── RoslynToyAnalyzer.Test
    │ ├── RoslynToyAnalyzer.Test.csproj
    │ └── RoslynToyAnalyzerTest.cs
    └── RoslynToyAnalyzers.sln
    実行コマンド
    dotnet add RoslynToyAnalyzer.Test/RoslynToyAnalyzer.Test.csproj reference RoslynToyAnalyzer/RoslynToyAnalyzer.csproj

    View Slide

  146. ソリューション・プロジェクトを作成する
    1. テストのプロジェクトを作成
    2. テストプロジェクトをソリューションへ追加
    3. テストコードプロジェクトからの参照を追加
    4. テストに必要なパッケージをインストール
    RoslynToyAnalyzer
    ├── RoslynToyAnalyzer
    │ ├── RoslynToyAnalyzer.cs
    │ └── RoslynToyAnalyzer.csproj
    ├── RoslynToyAnalyzer.Test
    │ ├── RoslynToyAnalyzer.Test.csproj
    │ └── RoslynToyAnalyzerTest.cs
    └── RoslynToyAnalyzers.sln
    実行コマンド
    dotnet add RoslynToyAnalyzer.Test package Microsoft.CodeAnalysis.Analyzers --version 3.3.2
    dotnet add RoslynToyAnalyzer.Test package Microsoft.CodeAnalysis.CSharp --version 3.5.0
    dotnet add RoslynToyAnalyzer.Test package Microsoft.CodeAnalysis.CSharp.Workspaces --version 3.5.0
    dotnet add RoslynToyAnalyzer.Test package Dena.CodeAnalysis.Testing --version 2.0.0

    View Slide

  147. ソリューション・プロジェクトを作成する
    1. テストのプロジェクトを作成
    2. テストプロジェクトをソリューションへ追加
    3. テストコードプロジェクトからの参照を追加
    4. テストに必要なパッケージをインストール
    RoslynToyAnalyzer
    ├── RoslynToyAnalyzer
    │ ├── RoslynToyAnalyzer.cs
    │ └── RoslynToyAnalyzer.csproj
    ├── RoslynToyAnalyzer.Test
    │ ├── RoslynToyAnalyzer.Test.csproj
    │ └── RoslynToyAnalyzerTest.cs
    └── RoslynToyAnalyzers.sln
    実行コマンド
    dotnet add RoslynToyAnalyzer.Test package Microsoft.CodeAnalysis.Analyzers --version 3.3.2
    dotnet add RoslynToyAnalyzer.Test package Microsoft.CodeAnalysis.CSharp --version 3.5.0
    dotnet add RoslynToyAnalyzer.Test package Microsoft.CodeAnalysis.CSharp.Workspaces --version 3.5.0
    dotnet add RoslynToyAnalyzer.Test package Microsoft.CodeAnalysis.CSharp.Analyzer.Testing.MSTest --version 1.1.0
    dotnet add RoslynToyAnalyzer.Test package Microsoft.CodeAnalysis.CSharp.CodeFix.Testing.MSTest --version 1.1.0
    dotnet add RoslynToyAnalyzer.Test package Microsoft.CodeAnalysis.CSharp.CodeRefactoring.Testing.MSTest --version 1.1.0
    dotnet add RoslynToyAnalyzer.Test package Dena.CodeAnalysis.Testing --version 2.0.0
    実行コマンド
    dotnet add RoslynToyAnalyzer.Test package Microsoft.CodeAnalysis.Analyzers --version 3.3.2
    dotnet add RoslynToyAnalyzer.Test package Microsoft.CodeAnalysis.CSharp --version 3.5.0
    dotnet add RoslynToyAnalyzer.Test package Microsoft.CodeAnalysis.CSharp.Workspaces --version 3.5.0
    dotnet add RoslynToyAnalyzer.Test package Dena.CodeAnalysis.Testing --version 2.0.0

    View Slide

  148. こちらに登壇時の映
    像をいれるので、文
    字を載せないように
    して下さい。
    カスタムアナライザーをテストする
    - ソリューションプロジェクトの作成
    - テストコードの解説
    - テストを簡単に書くTips
    今回はNUnitを用いて説明します

    View Slide

  149. こちらに登壇時の映
    像をいれるので、文
    字を載せないように
    して下さい。
    using System.Linq;
    using System.Threading.Tasks;
    using Dena.CodeAnalysis.CSharp.Testing;
    using Microsoft.CodeAnalysis.Text;
    using NUnit.Framework;
    namespace RoslynToyAnalyzer.Test
    {
    public class RoslynToyAnalyzerTest
    {
        [Test]
    public async Task RoslynToyAnalyzer_クラスのシンボル名に小文字を含む_Toy001がレポートされる()
    {
    const string testData = @"
    ///
    public static class Foo
    {
    }
    ";
    var analyzer = new RoslynToyAnalyzer();
    var actual = await DiagnosticAnalyzerRunner.Run(analyzer, testData);
    Assert.AreEqual(1, actual.Length);
    Assert.AreEqual("TOY001", actual.First().Id);
    Assert.AreEqual("NamedTypeのシンボルに小文字を含んだ命名 Foo が使用されています", actual.First().GetMessage());
    LocationAssert.HaveTheSpan(
    new LinePosition(2, 20),
    new LinePosition(2, 23),
    actual.First().Location);
    }
    }
    }

    View Slide

  150. こちらに登壇時の映
    像をいれるので、文
    字を載せないように
    して下さい。
    using System.Linq;
    using System.Threading.Tasks;
    using Dena.CodeAnalysis.CSharp.Testing;
    using Microsoft.CodeAnalysis.Text;
    using NUnit.Framework;
    namespace RoslynToyAnalyzer.Test
    {
    public class RoslynToyAnalyzerTest
    {
        [Test]
    public async Task RoslynToyAnalyzer_クラスのシンボル名に小文字を含む_Toy001がレポートされる()
    {
    const string testData = @"
    ///
    public static class Foo
    {
    }
    ";
    var analyzer = new RoslynToyAnalyzer();
    var actual = await DiagnosticAnalyzerRunner.Run(analyzer, testData);
    Assert.AreEqual(1, actual.Length);
    Assert.AreEqual("TOY001", actual.First().Id);
    Assert.AreEqual("NamedTypeのシンボルに小文字を含んだ命名 Foo が使用されています", actual.First().GetMessage());
    LocationAssert.HaveTheSpan(
    new LinePosition(2, 20),
    new LinePosition(2, 23),
    actual.First().Location);
    }
    }
    }
    テストメソッドの準備
    テストデータの準備
    テストの実行
    期待値と実測値の比較

    View Slide

  151. こちらに登壇時の映
    像をいれるので、文
    字を載せないように
    して下さい。
    public class RoslynToyAnalyzerTest
    {
        [Test]
    public async Task RoslynToyAnalyzer_クラスのシンボル名に小文字を含む
    _Toy001がレポートされる()
    {
    // ここにテストを書いていく
    }
    }
    テストメソッドの準備
    テストメソッドに[Test]属性をつける

    View Slide

  152. こちらに登壇時の映
    像をいれるので、文
    字を載せないように
    して下さい。
    const string testData = @"
    ///
    public static class Foo
    {
    }
    ";
    テストデータの準備
    アナライザーの診断対象として渡すC#のコードを記述

    View Slide

  153. こちらに登壇時の映
    像をいれるので、文
    字を載せないように
    して下さい。
    var analyzer = new RoslynToyAnalyzer();
    var actual = await DiagnosticAnalyzerRunner.Run(analyzer, testData);
    テストの実行
    テスト対象のアナライザーのインスタンスを作成
    アナライザーのインスタンスとテストデータを渡し、実測値を取得
    (DeNAがOSSとして公開しているパッケージです)
    See also: https://github.com/DeNA/Dena.CodeAnalysis.Testing

    View Slide

  154. こちらに登壇時の映
    像をいれるので、文
    字を載せないように
    して下さい。
    Assert.AreEqual(1, actual.Length);
    Assert.AreEqual("TOY001", actual.First().Id);
    Assert.AreEqual("NamedTypeのシンボルに小文字を含んだ命名 Foo が使用されています
    ", actual.First().GetMessage());
    LocationAssert.HaveTheSpan(
    new LinePosition(2, 20), // 期待する始点
    new LinePosition(2, 23), // 期待する終点
    actual.First().Location);
    実測値と期待値の比較
    アナライザーが出力した警告の数と期待値を比較
    診断結果に含まれる解析対象の位置と期待値(始点と終点)を比較
    (DeNAがOSSとして公開しているパッケージです)
    See also: https://github.com/DeNA/Dena.CodeAnalysis.Testing
    アナライザーのIDと期待値を比較
    アナライザーが出力した警告文と期待値を比較
    差分表示例

    View Slide

  155. こちらに登壇時の映
    像をいれるので、文
    字を載せないように
    して下さい。
    カスタムアナライザーをテストする
    - ソリューションプロジェクトの作成
    - テストコードの解説
    - テストを簡単に書くTips

    View Slide

  156. こちらに登壇時の映
    像をいれるので、文
    字を載せないように
    して下さい。
    これまでに紹介したテストの問題点
    - 警告される位置がわかりにくい
    - 一度にAssertをたくさんしている

    View Slide

  157. こちらに登壇時の映
    像をいれるので、文
    字を載せないように
    して下さい。
    これまでに紹介したテストの問題点
    - 警告される位置がわかりにくい
    - 一度にAssertをたくさんしている

    View Slide

  158. こちらに登壇時の映
    像をいれるので、文
    字を載せないように
    して下さい。
    const string testData = @"
    ///
    public static class Foo
    {
    }
    ";
    テストデータの準備
    アナライザーの診断対象として渡すC#のコードを記述

    View Slide

  159. こちらに登壇時の映
    像をいれるので、文
    字を載せないように
    して下さい。
    const string testData = @"
    ///
    public static class Foo
    {
    }
    ";
    テストデータの準備
    どの部分がアナライザーに怒られることを期待するのかわからない...
    テストの意図が、テストデータを見ただけで分かりづらい...

    View Slide

  160. こちらに登壇時の映
    像をいれるので、文
    字を載せないように
    して下さい。
    public static class {|Foo|TOY001|クラス名のシンボルに小文字を含んだ命名 Foo が使用されています
    |}
    {
    }
    public static class Foo
    {
    }
    参考
    https://github.com/dotnet/roslyn-analyzers/blob/main/src/Microsoft.CodeAnalysis.BannedApiAnalyzers/UnitTests/SymbolIsBannedAnalyzerTests.cs

    View Slide

  161. こちらに登壇時の映
    像をいれるので、文
    字を載せないように
    して下さい。
    public static class {|Foo|TOY001|クラス名のシンボルに小文字を含んだ命名 Foo が使用されています
    |}
    {
    }
    public static class Foo
    {
    }
    警告が出る箇所の期待値
    警告が出る位置
    期待するアナライザー
    のID
    期待する警告文
    参考
    https://github.com/dotnet/roslyn-analyzers/blob/main/src/Microsoft.CodeAnalysis.BannedApiAnalyzers/UnitTests/SymbolIsBannedAnalyzerTests.cs

    View Slide

  162. こちらに登壇時の映
    像をいれるので、文
    字を載せないように
    して下さい。
    public static class {|Foo|TOY001|クラス名のシンボルに小文字を含んだ命名 Foo が使用されています
    |}
    {
    }
    public static class Foo
    {
    }
    警告が出る箇所の期待値
    警告が出る位置
    期待するアナライザー
    のID
    期待する警告文
    マーカーを抽出する
    参考
    https://github.com/dotnet/roslyn-analyzers/blob/main/src/Microsoft.CodeAnalysis.BannedApiAnalyzers/UnitTests/SymbolIsBannedAnalyzerTests.cs

    View Slide

  163. こちらに登壇時の映
    像をいれるので、文
    字を載せないように
    して下さい。
    public static class {|Foo|TOY001|クラス名のシンボルに小文字を含んだ命名 Foo が使用されています
    |}
    {
    }
    public static class Foo
    {
    }
    警告が出る箇所の期待値
    警告が出る位置
    期待するアナライザー
    のID
    期待する警告文
    マーカーを抽出する
    マーカーを除外し、ソースコードを
    作成
    マーカーから期待値(DiagnosticのList)
    を作成
    参考
    https://github.com/dotnet/roslyn-analyzers/blob/main/src/Microsoft.CodeAnalysis.BannedApiAnalyzers/UnitTests/SymbolIsBannedAnalyzerTests.cs

    View Slide

  164. こちらに登壇時の映
    像をいれるので、文
    字を載せないように
    して下さい。
    public static class {|Foo|TOY001|クラス名のシンボルに小文字を含んだ命名 Foo が使用されています
    |}
    {
    }
    public static class Foo
    {
    }
    警告が出る箇所の期待値
    警告が出る位置
    期待するアナライザー
    のID
    期待する警告文
    マーカーを除外し、ソースコードを
    作成
    マーカーから期待値(DiagnosticのList)
    を作成
    アサーションで実測値と比較する
    アナライザーに渡し、実測値を作成する
    参考
    https://github.com/dotnet/roslyn-analyzers/blob/main/src/Microsoft.CodeAnalysis.BannedApiAnalyzers/UnitTests/SymbolIsBannedAnalyzerTests.cs

    View Slide

  165. こちらに登壇時の映
    像をいれるので、文
    字を載せないように
    して下さい。
        var analyzer = new RoslynToyAnalyzer();
        var (source, expectedDiagnostics) =
        ReadAndParseTestData.CreateSourceAndExpectedDiagnosticFromFile(@"
    ///
    public static class {|Foo|TOY001|NamedTypeのシンボルに小文字を含んだ命名 Foo が使用されています
    |}
    {
    }
    ");
        var actualDiagnostics = await DiagnosticAnalyzerRunner.Run(
    analyzer,
    source
        );
    テストの意図が、テストデータを見ただけでわかる!
    テストの期待値をマーカーから自動で生成できる!

    View Slide

  166. こちらに登壇時の映
    像をいれるので、文
    字を載せないように
    して下さい。
    これまでに紹介したテストの問題点
    - 警告される位置がわかりにくい
    - 一度にAssertをたくさんしている

    View Slide

  167. こちらに登壇時の映
    像をいれるので、文
    字を載せないように
    して下さい。
    Assert.AreEqual(expectedDiagnostics.Count, actualDiagnostics.Length);
    Assert.AreEqual(expectedDiagnostics.First().Id, actualDiagnostics.First().Id);
    Assert.AreEqual(expectedDiagnostics.First().GetMessage(), actualDiagnostics.First().GetMessage());
    Assert.AreEqual(expectedDiagnostics.First().Location.GetLineSpan(), actualDiagnostics.First().Location.GetLineSpan());
    Assertを一度にたくさんしている

    View Slide

  168. こちらに登壇時の映
    像をいれるので、文
    字を載せないように
    して下さい。
    Assert.AreEqual(expectedDiagnostics.Count, actualDiagnostics.Length);
    Assert.AreEqual(expectedDiagnostics.First().Id, actualDiagnostics.First().Id);
    Assert.AreEqual(expectedDiagnostics.First().GetMessage(), actualDiagnostics.First().GetMessage());
    Assert.AreEqual(expectedDiagnostics.First().Location.GetLineSpan(), actualDiagnostics.First().Location.GetLineSpan());
    - レポートされた問題の数
    - レポートされた問題のファイル内の位置
    - アナライザーの ID
    - レポートされた問題を人間にわかりやすく説明したメッ
    セージ
    を比較している。

    View Slide

  169. こちらに登壇時の映
    像をいれるので、文
    字を載せないように
    して下さい。
    Assert.AreEqual(expectedDiagnostics.Count, actualDiagnostics.Length);
    Assert.AreEqual(expectedDiagnostics.First().Id, actualDiagnostics.First().Id);
    Assert.AreEqual(expectedDiagnostics.First().GetMessage(), actualDiagnostics.First().GetMessage());
    Assert.AreEqual(expectedDiagnostics.First().Location.GetLineSpan(), actualDiagnostics.First().Location.GetLineSpan());
    これらを一度に比較したい!

    View Slide

  170. こちらに登壇時の映
    像をいれるので、文
    字を載せないように
    して下さい。
    Assert.AreEqual(expectedDiagnostics.Count, actualDiagnostics.Length);
    Assert.AreEqual(expectedDiagnostics.First().Id, actualDiagnostics.First().Id);
    Assert.AreEqual(expectedDiagnostics.First().GetMessage(), actualDiagnostics.First().GetMessage());
    Assert.AreEqual(expectedDiagnostics.First().Location.GetLineSpan(), actualDiagnostics.First().Location.GetLineSpan());

    View Slide

  171. こちらに登壇時の映
    像をいれるので、文
    字を載せないように
    して下さい。
    DiagnosticsAssert.AreEqual(IEnumerable expected, IEnumerable actual);
    レポートされた問題の数( DiagnosticのIEnumerableの個数)
    レポートされた問題のファイル内の位置(ファイルパス含む)
    アナライザーの ID
    メッセージ
    の4つの観点を比較している
    Assert.AreEqual(expectedDiagnostics.Count, actualDiagnostics.Length);
    Assert.AreEqual(expectedDiagnostics.First().Id, actualDiagnostics.First().Id);
    Assert.AreEqual(expectedDiagnostics.First().GetMessage(), actualDiagnostics.First().GetMessage());
    Assert.AreEqual(expectedDiagnostics.First().Location.GetLineSpan(), actualDiagnostics.First().Location.GetLineSpan());

    View Slide

  172. こちらに登壇時の映
    像をいれるので、文
    字を載せないように
    して下さい。
    DiagnosticsAssert.AreEqual(IEnumerable expected, IEnumerable actual);
    レポートされた問題の数( DiagnosticのIEnumerableの個数)
    レポートされた問題のファイル内の位置(ファイルパス含む)
    アナライザーの ID
    メッセージ
    の4つの観点を比較している
    Assert.Fail failed. Missing 1 diagnostics, extra 1 diagnostics of all 1 diagnostics:
    missing path/to/file1.cs: (1,2)-(3,4), CS1001, Identifier expected
    extra path/to/file2.cs: (5,6)-(7,8), CS1002, ; expected
    出力例
    Assert.AreEqual(expectedDiagnostics.Count, actualDiagnostics.Length);
    Assert.AreEqual(expectedDiagnostics.First().Id, actualDiagnostics.First().Id);
    Assert.AreEqual(expectedDiagnostics.First().GetMessage(), actualDiagnostics.First().GetMessage());
    Assert.AreEqual(expectedDiagnostics.First().Location.GetLineSpan(), actualDiagnostics.First().Location.GetLineSpan());

    View Slide

  173. こちらに登壇時の映
    像をいれるので、文
    字を載せないように
    して下さい。
    - Roslynアナライザーの概要
    - Roslynアナライザーができること、使い方
    - カスタムルールの作り方
    - カスタムアナライザーをテストする方法
    - 作成したアナライザーをUPMパッケージに内包して配布、利用する
    NOTE: バージョンによってバグによる制約があるため、
    このスライドではUnity 2021.2 + Rider Editor package
    v3.0.9を基準に書いています。
    目次

    View Slide

  174. こちらに登壇時の映
    像をいれるので、文
    字を載せないように
    して下さい。
    アナライザーをUPMパッケージとして配布、利用する
    - 配布する方法
    - 利用する方法

    View Slide

  175. こちらに登壇時の映
    像をいれるので、文
    字を載せないように
    して下さい。
    アナライザーをUPMパッケージとして配布、利用する
    - 配布する方法
    - 利用する方法

    View Slide

  176. こちらに登壇時の映
    像をいれるので、文
    字を載せないように
    して下さい。
    アナライザを作成
    楽に配布したい
    気軽に使ってほしい

    View Slide

  177. こちらに登壇時の映
    像をいれるので、文
    字を載せないように
    して下さい。
    配布する方法
    UPMパッケージを配布用に作成する Roslynアナライザーのdllを内包する
    OpenUPM、gitリポジトリ等で配布する
    自作UPMパッケージ Roslynアナライザーのdllを同梱する

    View Slide

  178. こちらに登壇時の映
    像をいれるので、文
    字を載せないように
    して下さい。
    アナライザーをUPMパッケージとして配布、利用する
    - 配布する方法
    - 利用する方法

    View Slide

  179. こちらに登壇時の映
    像をいれるので、文
    字を載せないように
    して下さい。
    UPMパッケージを
    インストールする
    Asmdefの参照によるスコープ設定
    NOTE: 既出のスライドを参照
    NOTE:
    Code Editor Package for VS v1.2.4以降、Code Editor Package for VSCode v2.0.11以降でも利
    用可能
    利用する方法

    View Slide

  180. こちらに登壇時の映
    像をいれるので、文
    字を載せないように
    して下さい。

    View Slide

  181. こちらに登壇時の映
    像をいれるので、文
    字を載せないように
    して下さい。
    Roslynアナライザーは...
    コンパイラが検知できない
    欠陥を検知できる
    自作、配布が
    簡単にできる
    ニーズに合った
    利用ができる

    View Slide

  182. こちらに登壇時の映
    像をいれるので、文
    字を載せないように
    して下さい。
    Roslynアナライザーは...
    コンパイラが検知できない
    欠陥を検知できる
    自作、配布が
    簡単にできる
    ニーズに合った
    利用ができる

    View Slide

  183. こちらに登壇時の映
    像をいれるので、文
    字を載せないように
    して下さい。
    参考にしたサイト
    - Unityプロジェクト向けRoslynアナライザの作り方
    - https://swet.dena.com/entry/2021/05/25/100000
    - カスタムRoslyn AnalyzerをUPMパッケージとして配布する
    - https://www.nowsprinting.com/entry/2021/11/01/083258
    - Unity 2020.時点のRoslyn Analyzerサポート状況まとめ
    - https://www.nowsprinting.com/entry/2021/04/18/200619
    - Microsoft.CodeAnalysis.BannedApiAnalyzer
    - https://github.com/dotnet/roslyn-analyzers/tree/main/src/Microsoft.CodeAnalysis.BannedApiAnalyzers
    - codecracker.CSharp
    - https://github.com/code-cracker/code-cracker
    - UniTask
    - https://github.com/Cysharp/UniTask

    View Slide

  184. こちらに登壇時の映
    像をいれるので、文
    字を載せないように
    して下さい。

    View Slide