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でのインストール方法は公式サイトにその方法が記載されているので、参考にして下さい。
Inferの試用
オープンソースで公開されているInferのリポジトリには、サンプルとして使用できるコードも含まれています。
今回はまずこちらをダウンロードして試してみました。
ダウンロード後はexampleディレクトリに移動します。ここにはHello.javaやhello.cといった単一のソースコードファイルや、複数のソースコードをリンクしてコンパイルするファイルが格納されたjava_helloやc_helloといったディレクトリが存在します。
Hello.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コマンドは実行しない方が良さそうです。