Inferを試してみる

Inferは、Facebook社が開発を続けている静的解析ツールです。
元々はMonoidics社で開発が続けられていましたが、2013年にFacebook社に買収をされ、その後2015年にオープンソースとなりました。
JavaやC、C++、Objective-Cといった言語のバグの可能性のあるコードを検出でき、JavaであればNullポインタの参照や、リソースリークなどが検出できます。

公式サイト
https://fbinfer.com/
GitHub
https://github.com/facebook/infer

現在のプロジェクトでもSpotBugsなどの静的解析ツールは導入しているのですが、他のツールを探していたところ見つけたツールで、本記事はこちらを試してみた記事になります。

## 実行環境
今回は以下の環境で実行した結果になります。

- OS: macOS
- 対象言語: Java

## インストール
macであればbrewコマンドでインストールできます。

```
brew install infer
```

LinuxやWindowsでのインストール方法は[公式サイト](https://fbinfer.com/docs/getting-started)にその方法が記載されているので、参考にして下さい。

## Inferの試用
オープンソースで公開されているInferのリポジトリには、サンプルとして使用できるコードも含まれています。

https://github.com/facebook/infer/tree/master/examples

今回はまずこちらをダウンロードして試してみました。
ダウンロード後はexampleディレクトリに移動します。ここにはHello.javaやhello.cといった単一のソースコードファイルや、複数のソースコードをリンクしてコンパイルするファイルが格納されたjava_helloやc_helloといったディレクトリが存在します。

Hello.javaの内容は以下のような内容になっています。

```Java
/*
 * Copyright (c) Facebook, Inc. and its affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

class Hello {
  int test() {
    String s = null;
    return s.length();
  }
}
```

オブジェクトにnullを代入して、それを参照しています。

このファイルに対してinferを使って解析を行います。
inferを実行するときは、`--`の後ろに、その言語のコンパイルコマンドを繋げて実行します。

```
infer -- javac Hello.java
```

これを実行すると、以下のような結果が出力されます。

```
Capturing in javac mode...
Found 1 source file to analyze in ＜ダウンロードしたディレクトリ＞/infer/examples/infer-out


Analysis finished in 1.24ss

Found 1 issue

Hello.java:11: error: NULL_DEREFERENCE
  object `s` last assigned on line 10 could be null and is dereferenced at line 11.
  9.     int test() {
  10.       String s = null;
  11. >     return s.length();
  12.     }
  13.   }


Summary of the reports

  NULL_DEREFERENCE: 1

```
画面にはNULLを参照しているバグを検出した結果が出力されます。
解析結果は、コマンドを実行したディレクトリに`infer-out`というディレクトリが作成され、その下のbugs.txtやreport.jsonといったファイルにも出力されています。

サンプルの中にある`java_hello`下でinferを実行した場合は、以下のような結果が出力されます。

```
infer -- javac Pointers.java Resources.java Hello.java
```

実行結果

```
Capturing in javac mode...
Found 3 source files to analyze in ＜ダウンロードしたディレクトリ＞/infer/examples/java_hello/infer-out


Analysis finished in 1.366ss

Found 3 issues

Hello.java:28: error: NULL_DEREFERENCE
  object `a` last assigned on line 26 could be null and is dereferenced at line 28.
  26.       Pointers.A a = Pointers.mayReturnNull(rng.nextInt());
  27.       // FIXME: should check for null before calling method()
  28. >     a.method();
  29.     }
  30.

Hello.java:38: error: RESOURCE_LEAK
  resource of type `java.io.FileOutputStream` acquired by call to `allocateResource()` at line 32 is not released after line 38.
  36.
  37.       try {
  38. >       stream.write(12);
  39.       } finally {
  40.         // FIXME: should close the stream

Hello.java:63: error: RESOURCE_LEAK
  resource of type `java.io.FileOutputStream` acquired to `fos` by call to `FileOutputStream(...)` at line 53 is not released after line 63.
**Note**: potential exception at line 57
  61.         }
  62.       }
  63. >   }
  64.   }


Summary of the reports

     RESOURCE_LEAK: 2
  NULL_DEREFERENCE: 1
```

こちらではリソースリークなども検出しています。

inferは`javac`でコンパイルする以外でも、mavenやgradleで構成されたプロジェクトでも使用できます。
その場合は、以下のように`--`の後にmvnコマンドやgradleコマンドを繋げる形になります。

#### Gradle

```
infer run -- gradle <gradle task, e.g. "build">
infer run -- ./gradlew <gradle task, e.g. "build">
```

####  Maven

```
infer run -- mvn <maven target>
```

プロジェクト単位で実行した場合は、タスクの終了後に解析が実行されます。ファイル数によってはかなり時間がかかるので注意して下さい。
解析結果は先と同様に`infer-out`の下に出力されます。

また、Mavenプロジェクトで実行をした際はpom.xmlに一部設定の追加が行われて実行がされていました。（実行後は元に戻ります。）
解析中は他のmvnコマンドは実行しない方が良さそうです。