ReSharperコマンドラインツールでコマンドラインからコードインスペクションを実施する

# はじめに
個人的に、.NET のコードをクリーンに保つためにReSharperは外すことのできない選択だと思います。
ただ、Visual Studio以外に有償のツールを導入するハードルはそれなりにあり、チーム全員に導入することが難しいことがあります。
今回は、無償で利用できるReSharperのコマンドラインツールから、コードインスペクションを実施してみましょう。

ReSharperのコマンドラインツールは下記のページに説明があります。
https://pleiades.io/help/resharper/ReSharper_Command_Line_Tools.html

# インストール

複数のプロジェクトに影響するグローバルインストールと、特定のプロジェクトだけに影響するローカルインストールがあります。どちらを選ぶかは利用方法に依存しますが、グローバルインストールを行う場合は下記のコマンドでインストールできます。

``` sh
dotnet tool install -g JetBrains.ReSharper.GlobalTools
```

インストールが成功すると`jb`コマンドが利用可能になり、下記の3つのサブコマンドを利用できるようになります。利用方法はサブコマンドのページを確認してください。

- [CleanupCode](https://pleiades.io/help/resharper/CleanupCode.html)
- [DupCode](https://pleiades.io/help/resharper/dupFinder.html)
- [InspectCode](https://pleiades.io/help/resharper/InspectCode.html)

# コードインスペクションをコマンドラインで実行してみる。

ReSharperの設定ファイル(DotSettings)や、EditorConfigをもとにソリューションやプロジェクトに対しコードインスペクションを実施しするコマンドです。
まずはこんな感じの行けてないコードに対してコードインスペクションをかけてみましょう。
ReSharperは問題があるコードを見つけるとコードエディターのスクロールバーに色付けしてくれるので問題がわかりやすいですね。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/36535/41aa6737-4c53-a3f5-ad03-c34fce13852a.png)

ソリューションファイルの存在するディレクトリで下記のコマンドを実行します。

```
jb inspectcode ConsoleApp3.sln -o=c:\temp\out.xml
```

コードインスペクションの結果が-oで指定したパスにXMLで吐き出されます。
デフォルトではサジェスチョン（提案）以上の項目について指摘されます。

``` xml:out.xml
<?xml version="1.0" encoding="utf-8"?>
<!-- Generated by JetBrains Inspect Code 2020.3.2 -->
<Report ToolsVersion="203.0.20201229.113241">
  <Information>
    <Solution>ConsoleApp3.sln</Solution>
    <InspectionScope>
      <Element>Solution</Element>
    </InspectionScope>
  </Information>
  <IssueTypes>
    <IssueType Id="ClassNeverInstantiated.Global" Category="Potential Code Quality Issues" CategoryId="CodeSmell" SubCategory="Class is never instantiated" Description="Class is never instantiated: Non-private accessibility" Severity="SUGGESTION" Global="True" WikiUrl="https://www.jetbrains.com/resharperplatform/help?Keyword=ClassNeverInstantiated.Global" />
    <IssueType Id="InconsistentNaming" Category="Constraints Violations" CategoryId="ConstraintViolation" Description="Inconsistent Naming" Severity="WARNING" WikiUrl="https://www.jetbrains.com/resharperplatform/help?Keyword=InconsistentNaming" />
    <IssueType Id="InvalidXmlDocComment" Category="Potential Code Quality Issues" CategoryId="CodeSmell" Description="Invalid XML documentation comment" Severity="WARNING" />
    <IssueType Id="NotAccessedVariable.Compiler" Category="Compiler Warnings" CategoryId="CompilerWarnings" Description="Non-accessed local variable" Severity="WARNING" />
    <IssueType Id="PossibleMultipleEnumeration" Category="Potential Code Quality Issues" CategoryId="CodeSmell" Description="Possible multiple enumeration" Severity="WARNING" WikiUrl="https://www.jetbrains.com/resharperplatform/help?Keyword=PossibleMultipleEnumeration" />
    <IssueType Id="RedundantUsingDirective" Category="Redundancies in Code" CategoryId="CodeRedundancy" Description="Redundant using directive" Severity="WARNING" WikiUrl="https://www.jetbrains.com/resharperplatform/help?Keyword=RedundantUsingDirective" />
    <IssueType Id="ReplaceWithSingleCallToFirstOrDefault" Category="Common Practices and Code Improvements" CategoryId="BestPractice" SubCategory="Replace with single call to FirstOrDefault(..)" Description="Replace with single call to FirstOrDefault(..): Replace with single call to FirstOrDefault(..)" Severity="SUGGESTION" />
    <IssueType Id="UnusedParameter.Local" Category="Redundancies in Symbol Declarations" CategoryId="DeclarationRedundancy" SubCategory="Unused parameter" Description="Unused parameter: Private accessibility" Severity="WARNING" />
    <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="ConsoleApp3">
      <Issue TypeId="RedundantUsingDirective" File="ConsoleApp3\Program.cs" Offset="35-53" Line="3" Message="Using directive is not required by the code and can be safely removed" />
      <Issue TypeId="ClassNeverInstantiated.Global" File="ConsoleApp3\Program.cs" Offset="93-100" Line="7" Message="Class 'Program' is never instantiated" />
      <Issue TypeId="InvalidXmlDocComment" File="ConsoleApp3\Program.cs" Offset="198-201" Line="12" Message="Cannot resolve parameter 'arg'" />
      <Issue TypeId="InvalidXmlDocComment" File="ConsoleApp3\Program.cs" Offset="247-251" Line="13" Message="Parameter 'args' has no matching param tag in the XML comment for ConsoleApp3.Program.Main (but other parameters do)" />
      <Issue TypeId="UnusedParameter.Local" File="ConsoleApp3\Program.cs" Offset="247-251" Line="13" Message="Parameter 'args' is never used" />
      <Issue TypeId="InconsistentNaming" File="ConsoleApp3\Program.cs" Offset="346-359" Line="16" Message="Name 'UpperCamelVar' does not match rule 'Local variables'. Suggested name is 'upperCamelVar'." />
      <Issue TypeId="NotAccessedVariable.Compiler" File="ConsoleApp3\Program.cs" Offset="392-396" Line="17" Message="Local variable 'str1' is only assigned but its value is never used" />
      <Issue TypeId="ReplaceWithSingleCallToFirstOrDefault" File="ConsoleApp3\Program.cs" Offset="435-485" Line="18" Message="Replace with single call to FirstOrDefault(..)" />
      <Issue TypeId="PossibleMultipleEnumeration" File="ConsoleApp3\Program.cs" Offset="778-790" Line="24" Message="Possible multiple enumeration" />
      <Issue TypeId="PossibleMultipleEnumeration" File="ConsoleApp3\Program.cs" Offset="832-844" Line="25" Message="Possible multiple enumeration" />
      <Issue TypeId="UnusedType.Global" File="ConsoleApp3\Program.cs" Offset="951-962" Line="31" Message="Class 'UnUsedClass' is never used" />
    </Project>
  </Issues>
</Report>
```

結果のXMLでは、今回発見された指摘の種類がReport/IssueTypesに列挙され、その後のReport/Issuesに実際に指摘されたソースファイルと行番号、列位置が出力されます。下記のような指摘があるようですね。

- 3行目: 利用していないUsing
- 7行目: インスタンス化されないクラス
- 12行目, 13行目: ドキュメンテーションコメントが仮引数が食い違っている
- 13行目: 利用していない引数
- 16行目: ローカル変数名がルールに従っていない
- 17行目: 省略可能なクエリ
- 18行目: 利用されないローカル変数
- 24行目, 25行目: 複数回の列挙
- 31行目: 利用していないクラス

# 警告の抑制
今回は利用していないクラス（型）やプロパティーは除外することにしましょう。

コードインスペクトのプロパティーを構成する方法はいくつかありますが、今回は`.editorconfig`を利用します。プロジェクトに`.editorconfig`が存在しない場合は、Visual Studioの追加メニューで追加することができます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/36535/242a07fd-41bd-02b0-6948-fcccf9ff139f.png)
また、すでにReSharperのコードインスペクションの設定を行っている場合は、ReSharperのオプション -> Code Editing -> General Formatter Styleの`Write current style to .editorconfig ...`から.editorconfigファイルをExportすることができます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/36535/2b598106-d183-9148-3bb9-3bd502b68669.png)

ReSharperの設定をExportしたら次のような.editorconfigが作成されました。

``` ini:ReSharperから出力した.editorconfig
[*.{c,c++,cc,cginc,compute,cp,cpp,cu,cuh,cxx,h,hh,hlsl,hlsli,hlslinc,hpp,hxx,inc,inl,ino,ipp,mpp,mq4,mq5,mqh,proto,tpp,usf,ush}]
indent_style = tab
indent_size = tab
tab_width = 4

[*.{asax,ascx,aspx,axaml,cs,cshtml,css,htm,html,js,jsx,master,paml,razor,skin,ts,tsx,vb,xaml,xamlx,xoml}]
indent_style = space
indent_size = 4
tab_width = 4

[*.{appxmanifest,axml,build,config,csproj,dbml,discomap,dtd,json,jsproj,lsproj,njsproj,nuspec,proj,props,resjson,resw,resx,StyleCop,targets,tasks,vbproj,xml,xsd}]
indent_style = space
indent_size = 2
tab_width = 2

[*]

# Microsoft .NET properties
csharp_new_line_before_members_in_object_initializers = false
csharp_preferred_modifier_order = public, private, protected, internal, new, abstract, virtual, sealed, override, static, readonly, extern, unsafe, volatile, async:suggestion
csharp_style_var_elsewhere = true:suggestion
csharp_style_var_for_built_in_types = true:suggestion
csharp_style_var_when_type_is_apparent = true:suggestion
dotnet_style_parentheses_in_arithmetic_binary_operators = never_if_unnecessary:none
dotnet_style_parentheses_in_other_binary_operators = never_if_unnecessary:none
dotnet_style_parentheses_in_relational_binary_operators = never_if_unnecessary:none
dotnet_style_predefined_type_for_locals_parameters_members = true:suggestion
dotnet_style_predefined_type_for_member_access = true:suggestion
dotnet_style_qualification_for_event = false:suggestion
dotnet_style_qualification_for_field = false:suggestion
dotnet_style_qualification_for_method = false:suggestion
dotnet_style_qualification_for_property = false:suggestion
dotnet_style_require_accessibility_modifiers = for_non_interface_members:suggestion

# ReSharper inspection severities
resharper_arrange_redundant_parentheses_highlighting = hint
resharper_arrange_this_qualifier_highlighting = hint
resharper_arrange_type_member_modifiers_highlighting = hint
resharper_arrange_type_modifiers_highlighting = hint
resharper_built_in_type_reference_style_for_member_access_highlighting = hint
resharper_built_in_type_reference_style_highlighting = hint
resharper_partial_type_with_single_part_highlighting = hint
resharper_redundant_base_qualifier_highlighting = warning
resharper_suggest_var_or_type_built_in_types_highlighting = hint
resharper_suggest_var_or_type_elsewhere_highlighting = hint
resharper_suggest_var_or_type_simple_types_highlighting = hint
resharper_unused_member_global_highlighting = hint
```

`ClassNeverInstantiated.Global`と`UnusedType.Global`を抑制したいので、指摘レベルをサジェスチョンからヒントに変更します。ReSharperのオプション画面から設定してもよいのですが、画面からだと指摘を見つけ出しづらいのでこのあたりを参考に、.editorconfigを直に修正するほうが個人的には簡単です。

- [.NET ソース コード分析の概要](https://docs.microsoft.com/ja-jp/dotnet/fundamentals/code-analysis/overview)
- [インスペクションの一覧ページ](https://pleiades.io/help/resharper/Reference__Code_Inspections_CSHARP.html)
- [EditorConfigプロパティー](https://pleiades.io/help/resharper/EditorConfig_Properties.html)

``` ini:.editorconfig追加した行
resharper_class_never_instantiated_global_highlighting = hint
resharper_unused_type_global_highlighting = hint
```

改めて下記のコマンドを実行すると、

``` sh
jb inspectcode ConsoleApp3.sln -o=c:\temp\out.xml
```

指摘レベルをヒントにした項目(7行目と31行目)が指摘されなくなったことがわかります。

``` xml:out.xml
<?xml version="1.0" encoding="utf-8"?>
<!-- Generated by JetBrains Inspect Code 2020.3.2 -->
<Report ToolsVersion="203.0.20201229.113241">
  <Information>
    <Solution>ConsoleApp3.sln</Solution>
    <InspectionScope>
      <Element>Solution</Element>
    </InspectionScope>
  </Information>
  <IssueTypes>
    <IssueType Id="InconsistentNaming" Category="Constraints Violations" CategoryId="ConstraintViolation" Description="Inconsistent Naming" Severity="WARNING" WikiUrl="https://www.jetbrains.com/resharperplatform/help?Keyword=InconsistentNaming" />
    <IssueType Id="InvalidXmlDocComment" Category="Potential Code Quality Issues" CategoryId="CodeSmell" Description="Invalid XML documentation comment" Severity="WARNING" />
    <IssueType Id="NotAccessedVariable.Compiler" Category="Compiler Warnings" CategoryId="CompilerWarnings" Description="Non-accessed local variable" Severity="WARNING" />
    <IssueType Id="PossibleMultipleEnumeration" Category="Potential Code Quality Issues" CategoryId="CodeSmell" Description="Possible multiple enumeration" Severity="WARNING" WikiUrl="https://www.jetbrains.com/resharperplatform/help?Keyword=PossibleMultipleEnumeration" />
    <IssueType Id="RedundantUsingDirective" Category="Redundancies in Code" CategoryId="CodeRedundancy" Description="Redundant using directive" Severity="WARNING" WikiUrl="https://www.jetbrains.com/resharperplatform/help?Keyword=RedundantUsingDirective" />
    <IssueType Id="ReplaceWithSingleCallToFirstOrDefault" Category="Common Practices and Code Improvements" CategoryId="BestPractice" SubCategory="Replace with single call to FirstOrDefault(..)" Description="Replace with single call to FirstOrDefault(..): Replace with single call to FirstOrDefault(..)" Severity="SUGGESTION" />
    <IssueType Id="UnusedParameter.Local" Category="Redundancies in Symbol Declarations" CategoryId="DeclarationRedundancy" SubCategory="Unused parameter" Description="Unused parameter: Private accessibility" Severity="WARNING" />
  </IssueTypes>
  <Issues>
    <Project Name="ConsoleApp3">
      <Issue TypeId="RedundantUsingDirective" File="ConsoleApp3\Program.cs" Offset="35-53" Line="3" Message="Using directive is not required by the code and can be safely removed" />
      <Issue TypeId="InvalidXmlDocComment" File="ConsoleApp3\Program.cs" Offset="198-201" Line="12" Message="Cannot resolve parameter 'arg'" />
      <Issue TypeId="InvalidXmlDocComment" File="ConsoleApp3\Program.cs" Offset="247-251" Line="13" Message="Parameter 'args' has no matching param tag in the XML comment for ConsoleApp3.Program.Main (but other parameters do)" />
      <Issue TypeId="UnusedParameter.Local" File="ConsoleApp3\Program.cs" Offset="247-251" Line="13" Message="Parameter 'args' is never used" />
      <Issue TypeId="InconsistentNaming" File="ConsoleApp3\Program.cs" Offset="346-359" Line="16" Message="Name 'UpperCamelVar' does not match rule 'Local variables'. Suggested name is 'upperCamelVar'." />
      <Issue TypeId="NotAccessedVariable.Compiler" File="ConsoleApp3\Program.cs" Offset="392-396" Line="17" Message="Local variable 'str1' is only assigned but its value is never used" />
      <Issue TypeId="ReplaceWithSingleCallToFirstOrDefault" File="ConsoleApp3\Program.cs" Offset="435-485" Line="18" Message="Replace with single call to FirstOrDefault(..)" />
      <Issue TypeId="PossibleMultipleEnumeration" File="ConsoleApp3\Program.cs" Offset="778-790" Line="24" Message="Possible multiple enumeration" />
      <Issue TypeId="PossibleMultipleEnumeration" File="ConsoleApp3\Program.cs" Offset="832-844" Line="25" Message="Possible multiple enumeration" />
    </Project>
  </Issues>
</Report>
```

諸々修正して、こんなコードになりました。スクロールバーも指摘がないのできれいになっています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/36535/5a28cb74-055f-2081-b02a-b03dd2460a8d.png)

改めてコードインスペクションを実行すると、、、

``` xml:out.xml
<?xml version="1.0" encoding="utf-8"?>
<!-- Generated by JetBrains Inspect Code 2020.3.2 -->
<Report ToolsVersion="203.0.20201229.113241">
  <Information>
    <Solution>ConsoleApp3.sln</Solution>
    <InspectionScope>
      <Element>Solution</Element>
    </InspectionScope>
  </Information>
  <IssueTypes />
  <Issues />
</Report>
```

指摘がなくなりましたね！

# まとめ
C#では昔からStyleCopなどを取り入れることが多いですが、ReSharperに慣れている身としてはReSharperと同じようなルールで指摘してくれるのは大変ありがたいです。コードレビュー時にフォーマットなどでいちいち指摘をするので、ツールで代替えできるものはどんどんツールに頼っていきましょう。無料だし！！