はじめに

FacebookのOSSにInferという static analysis tool for Java, Objective-C and C がある. リリースされたのは2016年6月らしい.

第2言語的にJavaを使ってはいるが,こういうのは使ったことがないので試してみた.

注意:今回はJavaでの使い方等についてのみ言及しますが,C++,C,Objective-Cでも可能です.

本題

Installation

Homebrewを使っている場合:

brew update
brew install infer

あとInfer自体はOCamlだが,Python2系に依存がある.

Linux:

Getting started with Inferを見てください.

使い方

公式のInferの使い方はこちら.

自作のword2vecに使ってみる. こんな感じのファイル構成. もちろん gradle runで実行できる状態でコンパイルエラーは出ません.

├── README.md
├── build.gradle
├── gradlew
├── gradlew.bat
├── settings.gradle
└── src
    ├── main
    │   └── java
    │       ├── models
    │       │   ├── CBoW.java
    │       │   ├── SkipGram.java
    │       │   └── Word2Vec.java
    │       └── utils
    │           ├── AliasNegativeSampler.java
    │           ├── AliasSampler.java
    │           ├── ArrayNegativeSampler.java
    │           ├── NegativeSampler.java
    │           ├── Vocab.java
    │           └── Word.java
    └── test
        └── java
            ├── models
            │   └── Word2VecTest.java
            └── utils
                ├── AliasSamplerTest.java
                ├── NegativeSamplerTest.java
                ├── VocabTest.java
                └── WordTest.java

Inferでは,解析するために独自の言語に変換する(コンパイルする感じ). これをcapture phaseという. このコマンドは infer capture -- javac HOGEHOGE.java なのだが, capture と後述する analyze と一緒にやってくれる infer run をここでは使用する.

とりあえず Word.java ファイルを例に実行.

$ infer run -- javac src/main/java/utils/Word.java
Capturing in javac mode...
Found 1 source file to analyze in /Users/nzw/Dropbox/td/word2vec/infer-out
Starting analysis...

legend:
  "F" analyzing a file
  "." analyzing a procedure

F....

  No issues found

これを実行すると,同じディレクトリに infer-out/ が生成される (ここまでが capture). infer-out/の構成とかはこのページで言及されている.

次は analysis phaseinfer runを使わない場合は,$ infer captureを実行後に$ infer analyzeを実行する. バグ等があると表示してくれる.

先ほど実行したファイルではバグが検知されないので,別ファイルVocab.javaで試す.

$ infer run -- javac src/main/java/utils/Vocab.java
Capturing in javac mode...
Found 1 source file to analyze in /Users/nzw/word2vec/infer-out
Starting analysis...

legend:
  "F" analyzing a file
  "." analyzing a procedure

F...............

Found 1 issue

src/main/java/utils/Vocab.java:48: error: RESOURCE_LEAK
   resource of type `java.io.PushbackReader` acquired to `reader` by call to `new()` at line 32 is not released after line 48
**Note**: potential exception at line 39
  46.           System.out.println("The number of vocab is " + numVocab);
  47.           System.out.println("The number of words is " + numTrainWords);
  48. >     }
  49.
  50.       public Vocab(String fname, int minCount, double sample) throws IllegalArgumentException, IOException {

Summary of the reports

infer-out/bugs.txtにInferが標準出力したバグが記録される.また,report.csvreport.jsonにそれぞれの形式で記録される.


javac以外を使う

--の後ろは,javac だけでなく,gradlemvnでも可能.

$ gradle clean
$ infer run -- gradle build

runの解析オプション

run にはいくつかバリエーションがある. 例えば Eradicateは,Javaの@Nullableのアノテーションをチェックする.

$ infer run -a checkers --eradicate -- gradle run
Found 3 issues

src/main/java/utils/AliasSampler.java:19: error: ERADICATE_FIELD_NOT_INITIALIZED
  Field `AliasSampler.A` is not initialized in the constructor and is not declared `@Nullable`
  17.       private final Random rand = new Random();
  18.
  19. >     public AliasSampler(int numSampledClass){
  20.           this.numSampledClass = numSampledClass;
  21.           rand.setSeed(7);

src/main/java/utils/AliasSampler.java:19: error: ERADICATE_FIELD_NOT_INITIALIZED
  Field `AliasSampler.S` is not initialized in the constructor and is not declared `@Nullable`
  17.       private final Random rand = new Random();
  18.
  19. >     public AliasSampler(int numSampledClass){
  20.           this.numSampledClass = numSampledClass;
  21.           rand.setSeed(7);

src/main/java/utils/Vocab.java:157: error: ERADICATE_RETURN_NOT_NULLABLE
  Method `getWord(...)` may return null but it is not annotated with `@Nullable`. (Origin: null constant at line 161)
  155.       }
  156.
  157. >     public String getWord(int wordId){
  158.           if(wordId >= 0 && wordId < word2index.size()){
  159.               return index2word.get(wordId).getWord();

Summary of the reports

  ERADICATE_FIELD_NOT_INITIALIZED: 2
    ERADICATE_RETURN_NOT_NULLABLE: 1

諸注意とか

Limitations, etcInfer bug types を見ると,

  • Context leak
  • Null dereference
  • Resource leak
  • Unsafe_GuardedBy_Access

に対応しており,Array bounds errorsなどは対象外となっている(部分的に対応しているっぽい).

理解が及ばず,言及していない機能もありますが,以上です.