やらなイカ?

たぶん、iOS/Androidアプリの開発・テスト関係。

Unity 6 で追加された Roslyn Analyzer 関連機能

Unity 6 のマニュアルを見ていたら、Roslyn Analyzer についての機能追加があったので紹介します。

docs.unity3d.com

Microsoft.CodeAnalysis.Csharp 4.3

アナライザおよびコードジェネレーターをビルドするときに使用する Microsoft.CodeAnalysis.Csharp のバージョンが4.3になったことが明記されました。

なお、実際は Unity 2022.3.12f1 から 4.3 に上がっていたことが知られています。

参考:neue cc - 2022年(2024年)のC# Incremental Source Generator開発手法

Report analyzer diagnostics

アナライザおよびコードジェネレーターの実行時間を Editor.log に出力する機能です。

Windows では Edit > PreferencesmacOS では Unity > Settings で Preferencesウィンドウを開き、Diagnostics タブを選択します。

最初は警告が表示されますが、I understand, show me the settings ボタンを押すと設定項目が表示されます。

Code を展開して EnableDomainReloadTimings をonにすると有効化できます。 この機能が有効な間、Consoleウィンドウに次の警告が出ます。

Diagnostic switches are active and may impact performance or degrade your user experience. Switches can be configured through the Diagnostics section in the Preferences window. EnableDomainReloadTimings: True

この状態でコンパイルが走ると、Editor.log に次のようにアナライザごとに使用した時間と割合が出力されます。 これが対象のアセンブリ(DLL)ごとに出ます。

[1134/1140  0s] Csc Library/Bee/artifacts/200b0aEDbg.dag/TestHelper.UI.Tests.dll (+2 others)

Total analyzer execution time: 1.084 seconds.
NOTE: Elapsed time may be less than analyzer execution time because analyzers can run concurrently.

Time (s)    %   Analyzer
   0.754   69   IDisposableAnalyzers, Version=4.0.4.0, Culture=neutral, PublicKeyToken=3feb74da3c492280
   0.258   23      IDisposableAnalyzers.FieldAndPropertyDeclarationAnalyzer (IDISP002, IDISP006, IDISP008)
   0.252   23      IDisposableAnalyzers.CreationAnalyzer (IDISP004, IDISP014)
   0.181   16      IDisposableAnalyzers.AssignmentAnalyzer (IDISP001, IDISP003, IDISP008)
   0.036    3      IDisposableAnalyzers.LocalDeclarationAnalyzer (IDISP001, IDISP007)
   0.009   <1      IDisposableAnalyzers.DisposeCallAnalyzer (IDISP007, IDISP016, IDISP017)
   0.008   <1      IDisposableAnalyzers.ReturnValueAnalyzer (IDISP005, IDISP011, IDISP012, IDISP013)
   0.006   <1      IDisposableAnalyzers.ArgumentAnalyzer (IDISP001, IDISP003)
   0.001   <1      IDisposableAnalyzers.MethodReturnValuesAnalyzer (IDISP015)
   0.001   <1      IDisposableAnalyzers.SuppressFinalizeAnalyzer (IDISP024)
   0.001   <1      IDisposableAnalyzers.ClassDeclarationAnalyzer (IDISP025, IDISP026)
  <0.001   <1      IDisposableAnalyzers.DisposeMethodAnalyzer (IDISP009, IDISP010, IDISP018, IDISP019, IDISP020, IDISP021, IDISP023)
  <0.001   <1      IDisposableAnalyzers.UsingStatementAnalyzer (IDISP007)
  <0.001   <1      IDisposableAnalyzers.FinalizerAnalyzer (IDISP022, IDISP023)
  <0.001   <1      IDisposableAnalyzers.SemanticModelCacheAnalyzer (SyntaxTreeCacheAnalyzer)

   0.330   30   nunit.analyzers, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
   0.102    9      NUnit.Analyzers.TestCaseSourceUsage.TestCaseSourceUsesStringAnalyzer (NUnit1002, NUnit1011, NUnit1015, NUnit1016, NUnit1017, NUnit1018, NUnit1019, NUnit1020, NUnit1029, NUnit1030)
   0.051    4      NUnit.Analyzers.DisposeFieldsInTearDown.DisposeFieldsAndPropertiesInTearDownAnalyzer (NUnit1032)
   0.046    4      NUnit.Analyzers.TestMethodAccessibilityLevel.TestMethodAccessibilityLevelAnalyzer (NUnit1026)
   0.031    2      NUnit.Analyzers.ValueSourceUsage.ValueSourceUsageAnalyzer (NUnit1021, NUnit1022, NUnit1023, NUnit1024, NUnit1025)
   0.016    1      NUnit.Analyzers.TestMethodUsage.TestMethodUsageAnalyzer (NUnit1005, NUnit1006, NUnit1007, NUnit1012, NUnit1013, NUnit1014, NUnit1027)
   0.012    1      NUnit.Analyzers.StringConstraintWrongActualType.StringConstraintWrongActualTypeAnalyzer (NUnit2024)
   0.009   <1      NUnit.Analyzers.SameAsOnValueTypes.SameAsOnValueTypesAnalyzer (NUnit2040)
   0.008   <1      NUnit.Analyzers.ComparableTypes.ComparableTypesAnalyzer (NUnit2041, NUnit2042)
   0.007   <1      NUnit.Analyzers.SameAsIncompatibleTypes.SameAsIncompatibleTypesAnalyzer (NUnit2020)
   0.006   <1      NUnit.Analyzers.EqualToIncompatibleTypes.EqualToIncompatibleTypesAnalyzer (NUnit2021)
   0.004   <1      NUnit.Analyzers.WithinUsage.WithinUsageAnalyzer (NUnit2047)
   0.004   <1      NUnit.Analyzers.ParallelizableUsage.ParallelizableUsageAnalyzer (NUnit1008, NUnit1009, NUnit1010)
   0.004   <1      NUnit.Analyzers.NullConstraintUsage.NullConstraintUsageAnalyzer (NUnit2023)
   0.004   <1      NUnit.Analyzers.DelegateRequired.DelegateRequiredAnalyzer (NUnit2044)
   0.004   <1      NUnit.Analyzers.SameActualExpectedValue.SameActualExpectedValueAnalyzer (NUnit2009)
   0.003   <1      NUnit.Analyzers.MissingProperty.MissingPropertyAnalyzer (NUnit2022)
   0.003   <1      NUnit.Analyzers.IgnoreCaseUsage.IgnoreCaseUsageAnalyzer (NUnit2008)
   0.003   <1      NUnit.Analyzers.SomeItemsIncompatibleTypes.SomeItemsIncompatibleTypesAnalyzer (NUnit2026)
   0.003   <1      NUnit.Analyzers.ClassicModelAssertUsage.ClassicModelAssertUsageAnalyzer (NUnit2001, NUnit2002, NUnit2003, NUnit2004, NUnit2005, NUnit2006, NUnit2015, NUnit2016, NUnit2017, NUnit2018, NUnit2019, NUnit2027, NUnit2028, NUnit2029, NUnit2030, NUnit2031, NUnit2032, NUnit2033, NUnit2034, NUnit2035, NUnit2036, NUnit2037, NUnit2038, NUnit2039)
   0.002   <1      NUnit.Analyzers.ConstActualValueUsage.ConstActualValueUsageAnalyzer (NUnit2007)
   0.002   <1      NUnit.Analyzers.TestCaseUsage.TestCaseUsageAnalyzer (NUnit1001, NUnit1003, NUnit1004)
   0.002   <1      NUnit.Analyzers.UpdateStringFormatToInterpolatableString.UpdateStringFormatToInterpolatableStringAnalyzer (NUnit2050)
   0.001   <1      NUnit.Analyzers.ValuesUsage.ValuesUsageAnalyzer (NUnit1031)
   0.001   <1      NUnit.Analyzers.CollectionAssertUsage.CollectionAssertUsageAnalyzer (NUnit2049)
   0.001   <1      NUnit.Analyzers.StringAssertUsage.StringAssertUsageAnalyzer (NUnit2048)
  <0.001   <1      NUnit.Analyzers.DiagnosticSuppressors.NonNullableFieldOrPropertyIsUninitializedSuppressor ()
  <0.001   <1      NUnit.Analyzers.DiagnosticSuppressors.AvoidUninstantiatedInternalClassSuppressor ()
  <0.001   <1      NUnit.Analyzers.DiagnosticSuppressors.DereferencePossiblyNullReferenceSuppressor ()
  <0.001   <1      NUnit.Analyzers.UseAssertMultiple.UseAssertMultipleAnalyzer (NUnit2045)
  <0.001   <1      NUnit.Analyzers.ConstraintUsage.SomeItemsConstraintUsageAnalyzer (NUnit2014)
  <0.001   <1      NUnit.Analyzers.DiagnosticSuppressors.TypesThatOwnDisposableFieldsShouldBeDisposableSuppressor ()
  <0.001   <1      NUnit.Analyzers.NonTestMethodAccessibilityLevel.NonTestMethodAccessibilityLevelAnalyzer (NUnit1028)
  <0.001   <1      NUnit.Analyzers.ConstraintUsage.ComparisonConstraintUsageAnalyzer (NUnit2043)
  <0.001   <1      NUnit.Analyzers.ConstraintUsage.StringConstraintUsageAnalyzer (NUnit2011, NUnit2012, NUnit2013)
  <0.001   <1      NUnit.Analyzers.ConstraintUsage.EqualConstraintUsageAnalyzer (NUnit2010)
  <0.001   <1      NUnit.Analyzers.ContainsConstraintWrongActualType.ContainsConstraintWrongActualTypeAnalyzer (NUnit2025)
  <0.001   <1      NUnit.Analyzers.UseCollectionConstraint.UseCollectionConstraintAnalyzer (NUnit2046)
[           0s] Csc Library/Bee/artifacts/200b0aEDbg.dag/TestHelper.UI.Tests.dll (+2 others) [CacheWrite 00000000000000000000000000000004]

Tips: Editor.log は、Consoleウィンドウ右上の > Open Editor Log で開けます(macOSの場合コンソールappで開きます)。

Additional files

Assets フォルダ下に拡張子 .additionalfile のファイルを置くと、ファイルパスを Additional files としてアナライザに渡すことができます。 たとえば Foo.DemoAnalyzers.additionalfile というファイルを置くと、csproj ファイルに次のように追加されます。

<ItemGroup>
    <AdditionalFiles Include="Assets/Foo.DemoAnalyzers.additionalfile" />
</ItemGroup>

ただし、ファイル名は Filename.[Analyzer Name].additionalfile と決まっているため、たとえば BannedApiAnalyzers のように規定のファイル名しか受け付けないアナライザには使用できません。

参考:How to use Microsoft.CodeAnalysis.BannedApiAnalyzers

これについては BannedApiAnalyzers に Issue #78124 が立っていますが、Unity側でなんとかしてほしいというのはそれはそう*1

なおこの機能、マニュアルに記載されたのは Unity 6.0 ですが、APIリファレンスを見ると Unity 2021.3 からあったようです。

Roslyn global config file

記事タイトルから逸脱しますが、この機能は Unity 6.2 時点でもマニュアルには未記載、APIリファレンスには Unity 2021.3 から記載されていたものです。

拡張子 .globalconfig のファイルを置くと、アナライザの重大度(severity)を設定できます。つまり ruleset ファイル を置き換えるものです。

ファイルフォーマットは、先頭に is_global = true を書く以外は EditorConfig の重大度設定と同じ*2で、たとえば次のように書きます。

is_global = true
dotnet_diagnostic.IDISP001.severity = error

なお、置き場所やファイル名にはルールがあります。 Default.globalconfig(全アセンブリに効く)や Assembly-CSharp.globalconfig(Assembly-CSharpに効く)は Assets 直下に、 アセンブリ個々にはアセンブリ定義ファイル(.asmdef)と同じディレクトリに任意の名前で置きます。

また、csproj ファイルに GlobalAnalyzerConfigFiles として追加されるため、IDE でもUnityエディタと同じ設定を共有できます。 しかし、JetBrains Rider には本稿執筆時点で次のバグがあり、対応が待たれます。[10/7 追記]

参考:

*1:BannedApiAnalyzers 側で拡張子だけ許容すれば使えるようにはなるのですが、それを既存のアナライザ全部に対してやるのかという話

*2:ファイルタイプやパスによるセクション分けはできず、またコードスタイルを書いても無視されます