📘

C# の静的コード解析から GitLab Code Quality まで

2022/01/01に公開

C# の静的コード解析から GitLab Code Quality まで

ReSharper Command Line Tools による静的コード解析

C# で長年プログラムを書いていますが、一番お気に入りの静的コード解析ツールは JetBrains 社の ReSharper という Visual Studio 拡張です。(もしくは同社の統合開発環境の Rider)

この手の静的コード解析ツールはいろいろとルールを調整しないといけないことが調整しないと行けないことが多いのですが、ReSharper の静的コード解析ツールはデフォルトのルールのままでもそれほど困りません。

また、こういうコードはだめ! だけでなく、こういうコードはどうか? という修正の提案もやってくれます。

for 文で合計を求めるコードを書くと、foreach とか LINQ にしてみたら? と提案します。


ReSharper Command Line Tools はこの ReSharper の静的コード解析とコードクリーンアップツールをコマンドラインに切り出したものです。しかも無料で使えます!

早速使っていきます。

まずは静的コード解析対象のソリューションを作ります。

$ mkdir app1
$ cd app1
$ dotnet new sln
$ dotnet new classlib --output ClassLib
$ dotnet sln add
$ dotnet sln add ClassLib

ReSharper Command Line Tools は .NET Tools 版も提供されているのでこれをインストールします。.NET Tool については.NET ツールの管理方法 を見てください。

$ dotnet new tool-manifest
$ dotnet tool install JetBrains.ReSharper.GlobalTools

インストールが完了したので、早速使ってみます。

$ dotnet jb inspectcode app1.sln --output=inspect-code.xml

JetBrains Inspect Code 2021.3.2
Running on AMD 64 in 64-bit mode, .NET Core 3.1.22 under Linux 5.10.60.1-microsoft-standard-WSL2 #1 SMP Wed Aug 25 23:20:18 UTC 2021
Warning: Starting from version 2021.2, InspectCode builds the target solution before starting the analysis to make sure it only finds relevant code issues.
To explicitly accept the new behavior and suppress this warning, use the '--build' option.
To match the behavior in previous versions and skip the build, use '--no-build'.
Using toolset version 17.0 from /usr/share/dotnet/sdk/6.0.101
Configuration: Debug, Platform: Any CPU
Analyzing files

Analyzing ClassLib.GeneratedMSBuildEditorConfig.editorconfig
Analyzing analysislevel_6_default.editorconfig
Analyzing ClassLib.csproj
Inspecting Class1.cs
Inspection report was written to /home/masakura/works/tmp/app1/inspect-code.xml

生成されたレポートは次のとおりです。Class1 クラスが全く使われてないけどいいの? と提案されています。

<?xml version="1.0" encoding="utf-8"?>
<!-- Generated by JetBrains Inspect Code 2021.3.2 -->
<Report ToolsVersion="213.0.20211222.101123">
  <Information>
    <Solution>app1.sln</Solution>
    <InspectionScope>
      <Element>Solution</Element>
    </InspectionScope>
  </Information>
  <IssueTypes>
    <IssueType Id="UnusedType.Global" Category="Redundancies in Symbol Declarations" CategoryId="DeclarationRedundancy" SubCategory="Type is never used" Description="Type is never used: Non-private accessibility" Severity="SUGGESTION" Global="True" WikiUrl="https://www.jetbrains.com/resharperplatform/help?Keyword=UnusedType.Global" />
  </IssueTypes>
  <Issues>
    <Project Name="ClassLib">
      <Issue TypeId="UnusedType.Global" File="ClassLib\Class1.cs" Offset="34-40" Line="2" Message="Class 'Class1' is never used" />
    </Project>
  </Issues>
</Report>

生の XML は読むのは辛いですよね。テキストとか HTML とか JSON とかにできます。

$ dotnet jb inspectcode app1.sln --output=inspect-code.txt --format=text

...

$ cat inspect-code.txt 
Solution app1.sln
    Project ClassLib
      ClassLib\Class1.cs:2 Class 'Class1' is never used

GitLab Code Quality

GitLab には Code Quality という静的コード解析の結果を Merge Request (GitHub で言うところの Pull Request) に表示する機能があります。詳しいことは静的コード解析と軽量Code Quality を見ていただければ。

残念ながら、ReSharper 形式のレポートを GitLab は理解してくれません。ですので、Code Quality 形式の JSON (静的コード解析フレームワークの Code Climate 形式のサブセット) に変換する必要があります。

変換用のプログラムはいくつか見つかるのですが、ちょうどよさげなのがなかったので自作してみました。

まずは、Ignis.ReSharper.Reporter.Tool をインストールします。

$ dotnet tool install Ignis.ReSharper.Reporter.Tool

先程の XML レポートを変換します。

$ dotnet resharper-reporter --input inspect-code.xml --export="type=codequality;file=code-quality.json"

Load from /home/masakura/works/tmp/app1/inspect-code.xml.
Export to code-quality.json.

出力された JSON ファイルは次のとおりです。

[
  {
    "fingerprint": "e5a2a27d74a2bd4842ff11f7d281cb7ad8bfdfee",
    "description": "Class \u0027Class1\u0027 is never used",
    "severity": "minor",
    "location": {
      "path": "ClassLib/Class1.cs",
      "lines": {
        "begin": 2
      }
    }
  }
]

--severity all 引数をつけると、コードの問題があった時にコマンドを失敗するようになります。all だけでなく、suggestionwarning などを指定することもできます。

$ dotnet resharper-reporter --input inspect-code.xml --export="type=cod
equality;file=code-quality.json" --severity all

Load from /home/masakura/works/tmp/app1/inspect-code.xml.
Export to code-quality.json.

System.InvalidOperationException: Found problems in code.

...

GitLab CI で静的コード解析を実行し、レポートをアップロードします。(細かい手順は TODO)

lint:
  image: mcr.microsoft.com/dotnet/sdk:6.0
  script:
    - dotnet tool restore
    - dotnet jb inspectcode app1.sln --output=inspect-code.xml
    - dotnet resharper-reporter --input inspect-code.xml --export="type=codequality;file=code-quality.json" --severity all
  artifacts:
    reports:
      codequality: ./code-quality.json
  when: always

こうしておくと、静的コード解析でコードに問題が見つかった場合にレポートを GitLab で確認できるだけでなく、CI ジョブが失敗し、開発者に修正を促すことができます。

ビルドツール NUKE で使う

最近お気に入りの .NET 用ビルドツールの NUKE でも使えます。詳しいことは NUKE Build ノススメを見ていただければ。

まずは、NUKE のグローバルツールをインストールします。

$ dotnet tool install Nuke.GlobalTool

NUKE の初期設定をします。(Do you use git? には No と答えてください)

$ dotnet nuke :setup
NUKE Global Tool version 5.3.0 (Linux,.NETCoreApp,Version=v5.0)
Could not find root directory. Falling back to working directory.
How should the build project be named?
¬  _build
Where should the build project be located?
¬  ./build
Which NUKE version should be used?
¬  5.3.0 (latest release)
Which solution should be the default?
¬  app1.sln
Do you need help getting started with a basic build?
¬  Yes, get me started!
Restore, compile, pack using ...
¬  dotnet CLI
Source files are located in ...
¬  Neither
Move packages to ...
¬  ./output
Where do test projects go?
¬  Same as source
Do you use git?
¬  No, something else
Creating directory '/home/masakura/works/tmp/app1/.nuke'...
Creating directory '/home/masakura/works/tmp/app1/build'...

念の為、コンパイルできるか確認します。

$ dotnet nuke compile

...

═══════════════════════════════════════
Target             Status      Duration
───────────────────────────────────────
Restore            Succeeded       0:03
Compile            Succeeded       0:02
───────────────────────────────────────
Total                              0:06
═══════════════════════════════════════

Build succeeded on 2022/01/01 10:32:09. \(^ᴗ^)/

ReSharper Command Line Tools のタスクは NUKE に含まれていますが、レポート変換ツールは含まれていませんので、インストールします。

dotnet add build package Ignis.ReSharper.Reporter.Nuke

静的コード解析のターゲット Lintbuild/Build.cs に追加します。

Target Lint => _ => _
    .DependsOn(Compile)
    .Executes(() =>
    {
        ReSharperInspectCode(s => s
            .SetTargetPath(Solution)
            .SetOutput(OutputDirectory / "inspect-code.xml")
            .AddProperty("Configuration", Configuration)
            .SetProcessArgumentConfigurator(arguments => arguments
                .Add("--no-build")));
    });

Target CodeQuality => _ => _
    .TriggeredBy(Lint)
    .Executes(() =>
    {
        ReSharperReport(s => s
            .SetInput(OutputDirectory / "inspect-code.xml")
            .SetSeverity(EnsureSeverityLevel.All)
            .AddExport<CodeQualityConverter>(OutputDirectory / "code-quality.json"));
    });

実行すると、パッケージをインストールしてくださいと言われます。

$ dotnet nuke lint

...

Missing package reference/download.
Run one of the following commands:
  - nuke :add-package JetBrains.ReSharper.GlobalTools --version 2021.3.2

素直にインストールします。

$ dotnet nuke :add-package JetBrains.ReSharper.GlobalTools --version 2021.3.2

再度実行します。

$ dotnet nuke lint

...

InvalidOperationException: Found problems in code.

═══════════════════════════════════════
Target             Status      Duration
───────────────────────────────────────
Restore            Succeeded       0:02
Compile            Succeeded       0:02
Lint               Succeeded       0:51
CodeQuality        Failed        < 1sec   // InvalidOperationException: Found problems in code.
───────────────────────────────────────
Total                              0:56
═══════════════════════════════════════

Build failed on 2022/01/01 10:56:58. (╯°□°)╯︵ ┻━┻

うまく動きました! (コードに問題があるので、ビルドが失敗するので正常です)

最後に

ReSharper の静的コード解析ツールは GitLab Code Quality と組み合わせてよく使います。レポートの変換ツールはいくつかあるのですが、少し不満があったので自作することにしました。

  • コードの問題があってもエラーにならない
  • NUKE で動かすのが面倒
  • 何件見つかったとか表示されない (未実装)
  • リストがコンソールに表示されない (未実装)
  • etc...

まだまだ未実装が多いのですが、ぼちぼち強化していきます。何かあれば https://gitlab.com/ignis-build/ignis-resharper-reporter まで!

Discussion