根据本页的说明,你可以使用 Infer 尝试检查一些简单的例子。你会看到 Infer 报告了一些问题,修复这些问题,再进行一次检测,Infer 便不再报告问题。这将使得我们对 Infer 如何工作有一些初步的认识,更近一步的使用,可以参考用户使用参考

以下所有的例子都可以在 infer/examples 中找到。

Hello world Java

以下是一个简答的 Java 例子。

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

通过以下命令在 Hello.java 同级目录执行 Infer。

infer -- javac Hello.java

你将会看到以下的报告输出:

Hello.java:5: error: NULL_DEREFERENCE
  object s last assigned on line 4 could be null and is dereferenced at line 5  

编辑文件,加入为空检查:

  int test() {
    String s = null;
    return s == null ? 0 : s.length();
  }

再次运行 Infer,这次,运行结果显示 No issues found,未发现错误。

Hello world Objective-C

以下是一个简单的 Objective-C 例子:

// Hello.m
#import <Foundation/Foundation.h>

@interface Hello: NSObject
@property NSString* s;
@end

@implementation Hello
NSString* m() {
    Hello* hello = nil;
    return hello->_s;
}
@end

Hello.m 同级目录,运行:

infer -- clang -c Hello.m

以下是错误报告输出:

Hello.m:10 NULL_DEREFERENCE
  pointer hello last assigned on line 9 could be null and is dereferenced at line 10, column 12

编辑,如下修正:

NSString* m() {
    Hello* hello = nil;
    return hello.s;
}

再次运行,No issues found, 没有报错。

Hello world C

一个简单的 C 例子:

// hello.c
#include <stdlib.h>

void test() {
  int *s = NULL;
  *s = 42;
}

hello.c 同级目录运行:

infer -- gcc -c hello.c

报错输出:

hello.c:5: error: NULL_DEREFERENCE
  pointer s last assigned on line 4 could be null and is dereferenced at line 5, column 10

编辑,修正:

void test() {
  int *s = NULL;
  if (s != NULL) {
    *s = 42;
  }
}

再次运行,不在汇报错误,问题修复。

在进行 C 文件分析时,Infer 使用 gcc 命令并在内部运行 clang 来解析。

因此,你可能会得到一些和 gcc 不一样的编译器警告以及错误。以下命令等效的:

infer -- gcc -c hello.c
infer -- clang -c hello.c

Hello world Android

为了能够分析 Android 应用,请确保已经安装最新的 Android SDK 22,并保持最新。当然还有 Android SDK Build-toolsAndroid Support Repository

Android 的示例在 infer/examples/android_hello 。 编辑 local.properties 并设定本地 SDK 路径。

Android 示例使用 gradle 构建。不过你不需要下载和安装 gradle。 项目中的脚本 gradlew 会自动下载 gradle以及相关的项目依赖。

之后,运行:

infer -- ./gradlew build

Infer 会输出一系列问题:

MainActivity.java:20: error: NULL_DEREFERENCE
   object s last assigned on line 19 could be null and is dereferenced at line 20

MainActivity.java:37: error: RESOURCE_LEAK
   resource acquired by call to FileOutputStream(...) at line 34 is not released after line 37

增量分析

如果你没有改变任何文件,运行 Infer 进行检测,你会注意到,这次并没有进行分析。

这时因为 gradle 是增量的编译的。所有已经编译过的文件没有变动的文件将不会再编译。Infer 根据编译命令获取需要分析的文件,因此,这样的情况下,获取不到任何需要编译分析和分析的文件。

以下有两种解决方案:

  1. 在两次分析之间运行 gradlew clean

    ./gradlew clean
    

这将会使得每次都重新进行编译,所以 Infer 能获取到所有要分析的文件。

  1. 在增量模式下运行 Infer

    infer --incremental -- ./gradlew build
    

    这会使得 Infer 保留之前的编译结果。如果没有 --incremental (短命令是 -i ),Infer 会移除存储这些结果的文件夹: infer-out

你可以在 Infer workflow 页面中,详细了解这两种方案的细节。

Hello world iOS

iOS 的示例代码在这里 infer/examples/ios_hello,使用 Infer 检测:

infer -- xcodebuild -target HelloWorldApp -configuration Debug -sdk iphonesimulator

将会有以下问题输出:

AppDelegate.m:20: error: MEMORY_LEAK
   memory dynamically allocated to shadowPath by call to CGPathCreateWithRect() at line 20, column 28 is not reachable after line 20, column 5

AppDelegate.m:25: error: RESOURCE_LEAK
   resource acquired to fp by call to fopen() at line 25, column 8 is not released after line 25, column 5

AppDelegate.m:29: warning: PARAMETER_NOT_NULL_CHECKED
   Parameter callback is not checked for null, there could be a null pointer dereference: pointer callback could be null and is dereferenced at line 29, column 5

AppDelegate.m:34: error: NULL_DEREFERENCE
   pointer str last assigned on line 33 could be null and is dereferenced at line 34, column 12

AppDelegate.m:39: error: PREMATURE_NIL_TERMINATION_ARGUMENT
   pointer str last assigned on line 38 could be nil which results in a call to arrayWithObjects: with 1 arguments instead of 3 (nil indicates that the last argument of this variadic method has been reached) at line 39, column 12

Hello.m:20: error: NULL_DEREFERENCE
   pointer hello last assigned on line 19 could be null and is dereferenced at line 20, column 12

Hello.m:25: warning: IVAR_NOT_NULL_CHECKED
   Instance variable hello -> _hello is not checked for null, there could be a null pointer dereference: pointer ret_hello last assigned on line 24 could be null and is dereferenced at line 25, column 12

Hello.m:30: warning: PARAMETER_NOT_NULL_CHECKED
   Parameter hello is not checked for null, there could be a null pointer dereference: pointer ret_hello last assigned on line 29 could be null and is dereferenced at line 30, column 12

gradle 相似,需要使用 --incremental (或 -i) 使得增量编译有结果输出。

infer --incremental -- xcodebuild -target HelloWorldApp -configuration Debug -sdk iphonesimulator

或者在编译前清理:

xcodebuild -target HelloWorldApp -configuration Debug -sdk iphonesimulator clean

Hello world Make

使用 Infer 检测 C 代码,示例在 infer/examples/c_hello

infer -- make

将会输出以下问题:

example.c:22: error: NULL_DEREFERENCE
   pointer max last assigned on line 21 could be null and is dereferenced at line 22, column 10

example.c:36: error: NULL_DEREFERENCE
   pointer joe last assigned on line 35 could be null and is dereferenced by call to get_age() at line 36, column 10

example.c:45: error: RESOURCE_LEAK
   resource acquired to fd by call to open() at line 41, column 12 is not released after line 45, column 5

example.c:51: error: MEMORY_LEAK
   memory dynamically allocated to p by call to malloc() at line 51, column 14 is not reachable after line 51, column 3

example.c:57: error: MEMORY_LEAK
   memory dynamically allocated to p by call to malloc() at line 56, column 14 is not reachable after line 57, column 3

同样,和gradle 类似,为了使得增量编译下有结果输出,需要使用 --incremental (或 -i)

infer --incremental -- make

或者清理:

make clean