├── .gitignore ├── CNAME ├── README.md ├── _config.yml ├── _data ├── authors.yml ├── features.yml ├── nav.yml ├── nav_docs.yml └── promo.yml ├── _docs ├── 00-getting-started.md ├── 00-hello-world.md ├── 01-adding-models.md ├── 01-advanced-features.md ├── 01-analyzing-apps-or-projects.md ├── 01-checkers.md ├── 01-eradicate.md ├── 01-infer-workflow.md ├── 01-install-from-sources.md ├── 02-about-infer.md ├── 02-limitations.md ├── 02-separation-logic-and-biabduction.md ├── 03-checker-bug-types.md ├── 03-eradicate-warnings.md └── 03-infer-bug-types.md ├── _includes ├── blog_pagination.html ├── content │ ├── gridblocks.html │ └── items │ │ └── gridblock.html ├── doc.html ├── doc_paging.html ├── footer.html ├── head.html ├── header.html ├── hero.html ├── katex_import.html ├── katex_render.html ├── nav.html ├── nav_blog.html ├── nav_docs.html ├── plugins │ ├── all_share.html │ ├── ascii_cinema.html │ ├── button.html │ ├── fb_pagelike.html │ ├── github_star.html │ ├── github_watch.html │ ├── google_share.html │ ├── group_join.html │ ├── like_button.html │ ├── post_social_plugins.html │ ├── slideshow.html │ ├── twitter_follow.html │ └── twitter_share.html ├── post.html ├── social_plugins.html └── ui │ └── button.html ├── _layouts ├── blog.html ├── default.html ├── doc_page.html ├── docs.html ├── home.html ├── page.html ├── plain.html ├── post.html ├── redirect.html └── support.html ├── _posts └── 2015-05-22-Infer-on-open-source-android-apps.md ├── _sass ├── _base.scss ├── _reset.scss ├── _slideshow.scss └── _syntax-highlighting.scss ├── blog ├── all.html └── index.html ├── css └── main.scss ├── docs └── index.html ├── downloads ├── InferAndroidExample.tar.gz ├── InferCExample.tar.gz └── InferiOSExample.tar.gz ├── feed.xml ├── index.md ├── static ├── favicon.png ├── images │ ├── Infer-landing.jpg │ ├── Infer-landing.png │ └── SepSplit.jpg ├── logo.png ├── og_image.png ├── oss_logo.png ├── pygments.css └── react-with-addons-0.13.1.js └── support.md /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_STORE 2 | _site/ 3 | Gemfile 4 | Gemfile.lock 5 | *.swo 6 | *.swp 7 | _site 8 | .sass-cache 9 | *.psd 10 | *~ 11 | 12 | -------------------------------------------------------------------------------- /CNAME: -------------------------------------------------------------------------------- 1 | fbinfer.com 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | How to run the website: 2 | 3 | ``` 4 | jekyll serve -w 5 | ``` 6 | -------------------------------------------------------------------------------- /_config.yml: -------------------------------------------------------------------------------- 1 | # Site settings 2 | permalink: /blog/:year/:month/:day/:title.html 3 | title: Infer 4 | tagline: 在发布前,检测 Android 和 iOS 应用的问题 5 | fbappid: "1615782811974223" 6 | gacode: "UA-43024238-10" 7 | description: 8 | baseurl: "" # the subpath of your site, e.g. /blog/ 9 | url: "" # the base hostname & protocol for your site 10 | ghrepo: "facebook/infer" 11 | 12 | # Build settings 13 | markdown: redcarpet 14 | highlighter: pygments 15 | 16 | collections: 17 | docs: 18 | output: true 19 | permalink: /docs/:name/ 20 | support: 21 | output: true 22 | permalink: /support.html 23 | 24 | sass: 25 | style: :compressed 26 | 27 | redcarpet: 28 | extensions: [with_toc_data] 29 | 30 | color: 31 | # Provides colour for background of top header of homepage 32 | primary: "#6223b0" 33 | # Provides colour for background of elsewhere on site 34 | secondary: "#e4e4e4" 35 | # A color that will work for buttons laid on top of primary colour 36 | light: "#2e0067" 37 | # Color that will work for text on top of light color 38 | lighttext: "#fff" 39 | # Color of text in the top header of homepage, must be legible on top of primary color 40 | headertext: "#fff" 41 | # Color of text on top of the secondary color background 42 | bodytext: "#151515" 43 | # Background of fixed nav header - headertext color is used for mini logo text 44 | nav: "#7d29e4" 45 | # Text of links in the nav 46 | navtext: "#c79aff" 47 | # Color of link underlines in the main body, and hover background color for links 48 | link: "#6223b0" 49 | -------------------------------------------------------------------------------- /_data/authors.yml: -------------------------------------------------------------------------------- 1 | 2 | peteroh: 3 | full_name: Peter O'Hearn 4 | fbid: 100006115543749 5 | cristianoc: 6 | full_name: Cristiano Calcagno 7 | fbid: 1184023671 8 | ddino: 9 | full_name: Dino Distefano 10 | fbid: 100003081070147 11 | shb: 12 | full_name: Sam Blackshear 13 | fbid: 3904246 14 | jrm: 15 | full_name: Jeremy Dubreil 16 | fbid: 535764144 17 | akotulski: 18 | full_name: Andrzej Kotulski 19 | fbid: 100000253156179 20 | martinoluca: 21 | full_name: Martino Luca 22 | fbid: 1006790910 23 | irp: 24 | full_name: Irene Papakonstantinou 25 | fbid: 100003814237180 26 | dulmarod: 27 | full_name: Dulma Rodriguez 28 | fbid: 1355558869 29 | jul: 30 | full_name: Jules Villard 31 | fbid: 100007156126884 32 | -------------------------------------------------------------------------------- /_data/features.yml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liaohuqiu/infer-docs-cn/4084585dd239a53339c3818aff938e18a3ed205b/_data/features.yml -------------------------------------------------------------------------------- /_data/nav.yml: -------------------------------------------------------------------------------- 1 | - title: 文档 2 | href: docs/getting-started.html 3 | category: docs 4 | 5 | - title: 帮助和支持 6 | href: support.html 7 | category: support 8 | 9 | - title: 博客 10 | href: blog/ 11 | category: blog 12 | 13 | - title: Github 14 | href: http://github.com/facebook/infer 15 | category: github 16 | -------------------------------------------------------------------------------- /_data/nav_docs.yml: -------------------------------------------------------------------------------- 1 | - title: 开始使用 2 | items: 3 | - id: getting-started 4 | - id: hello-world 5 | - title: 用户使用参考 6 | items: 7 | - id: infer-workflow 8 | - id: analyzing-apps-or-projects 9 | - id: checkers 10 | - id: eradicate 11 | - id: install-from-source 12 | - id: advanced-features 13 | - id: adding-models 14 | - title: Foundations 15 | items: 16 | - id: about-Infer 17 | - id: separation-logic-and-bi-abduction 18 | - id: limitations 19 | - title: Bug 类型参考 20 | items: 21 | - id: infer-bug-types 22 | - id: checkers-bug-types 23 | - id: eradicate-warnings 24 | -------------------------------------------------------------------------------- /_data/promo.yml: -------------------------------------------------------------------------------- 1 | - type: ascii_cinema 2 | href: 5uh7nmuvwsbdok61t2fh1iiob 3 | 4 | - type: button 5 | href: docs/getting-started.html 6 | text: Get Started 7 | 8 | - type: github_star -------------------------------------------------------------------------------- /_docs/00-getting-started.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: getting-started 3 | title: 开始使用 4 | layout: docs 5 | permalink: /docs/getting-started.html 6 | section: Quick Start 7 | section_order: 00 8 | order: 01 9 | --- 10 | ## 依赖 11 | 12 | Python >= 2.7. 13 | 14 | ## 下载 15 | 16 | 我们提供了一个预编译的二进制包,目前有 Mac 和 Linux 64 位版本。 17 | 18 | - Mac OS X: [https://github.com/facebook/infer/releases/download/v0.1.0/infer-osx-v0.1.0.tar.xz](https://github.com/facebook/infer/releases/download/v0.1.0/infer-osx-v0.1.0.tar.xz) 19 | - Linux: [https://github.com/facebook/infer/releases/download/v0.1.0/infer-linux64-v0.1.0.tar.xz](https://github.com/facebook/infer/releases/download/v0.1.0/infer-linux64-v0.1.0.tar.xz) 20 | 21 | 22 | ## 安装 23 | 24 | 在命令行,到下载目录,解压: 25 | 26 | - Mac OS X 27 | 28 | ```bash 29 | tar xf infer-osx-v0.1.0.tar.xz 30 | ``` 31 | 32 | - Linux 33 | 34 | ```bash 35 | tar xf infer-linux64-v0.1.0.tar.xz 36 | ``` 37 | 38 | 解压后会有一个 ```infer-osx-v0.1.0/``` 目录 (或者 39 | ```infer-linux64-v0.1.0/``` 目录), Infer 的主执行目录是 ```infer-osx-v0.1.0/infer/infer/bin/```. 40 | 41 | ### 设置 PATH 变量 42 | 43 | 我们建议把 Infer 的执行目录加入到环境变量中,这样使用起来会简便一些。当然,你也可以用绝对路径。本文档后续默认执行路径已加入到环境变量中。 44 | 45 | 如果你使用 Bask, 你可以使用以下命令设置环境变量。 46 | 47 | ```bash 48 | cd infer-*v0\.\1\.0 && 49 | echo "export PATH=\"\$PATH:`pwd`/infer/infer/bin\"" \ >> ~/.bash_profile && 50 | source ~/.bash_profile 51 | ``` 52 | 53 | 你可用通过在命令行执行 ```echo $SHELL``` 确定所使用的 shell。根据具体的情况调整上面的命令。 54 | 55 | 如果你是在 linux 中,如果 `~/.bash_profile` 不存在的话,你也许需要把 `~/.bash_profile` 替换成 `~/.bashrc`。 56 | -------------------------------------------------------------------------------- /_docs/00-hello-world.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: hello-world 3 | title: Hello, World! 4 | layout: docs 5 | permalink: /docs/hello-world.html 6 | section: Quick Start 7 | section_order: 00 8 | order: 02 9 | --- 10 | 11 | 根据本页的说明,你可以使用 Infer 尝试检查一些简单的例子。你会看到 Infer 报告了一些问题,修复这些问题,再进行一次检测,Infer 便不再报告问题。这将使得我们对 Infer 如何工作有一些初步的认识,更近一步的使用,可以参考[用户使用参考](docs/infer-workflow.html)。 12 | 13 | 14 | 以下所有的例子都可以在 [`infer/examples`](https://github.com/facebook/infer/tree/master/examples) 中找到。 15 | 16 | - [Hello world Java](docs/hello-world.html#hello-world-java) 17 | - [Hello world Objective-C](docs/hello-world.html#hello-world-objective-c) 18 | - [Hello world C](docs/hello-world.html#hello-world-c) 19 | - [Hello world Android](docs/hello-world.html#hello-world-android) 20 | - [Hello world iOS](docs/hello-world.html#hello-world-ios) 21 | - [Hello world Make](docs/hello-world.html#hello-world-make) 22 | 23 | ## Hello world Java 24 | 25 | 以下是一个简答的 Java 例子。 26 | 27 | ```java 28 | // Hello.java 29 | class Hello { 30 | int test() { 31 | String s = null; 32 | return s.length(); 33 | } 34 | } 35 | ``` 36 | 37 | 通过以下命令在 [`Hello.java`](https://github.com/facebook/infer/tree/master/examples/Hello.java) 同级目录执行 Infer。 38 | 39 | ```bash 40 | infer -- javac Hello.java 41 | ``` 42 | 43 | 你将会看到以下的报告输出: 44 | 45 | ```bash 46 | Hello.java:5: error: NULL_DEREFERENCE 47 | object s last assigned on line 4 could be null and is dereferenced at line 5 48 | ``` 49 | 50 | 编辑文件,加入为空检查: 51 | 52 | ```java 53 | int test() { 54 | String s = null; 55 | return s == null ? 0 : s.length(); 56 | } 57 | ``` 58 | 59 | 再次运行 Infer,这次,运行结果显示 `No issues found`,未发现错误。 60 | 61 | ## Hello world Objective-C 62 | 63 | 以下是一个简单的 Objective-C 例子: 64 | 65 | ```Objective-C 66 | // Hello.m 67 | #import 68 | 69 | @interface Hello: NSObject 70 | @property NSString* s; 71 | @end 72 | 73 | @implementation Hello 74 | NSString* m() { 75 | Hello* hello = nil; 76 | return hello->_s; 77 | } 78 | @end 79 | ``` 80 | 81 | 在 [`Hello.m`](https://github.com/facebook/infer/tree/master/examples/Hello.m) 同级目录,运行: 82 | 83 | ```bash 84 | infer -- clang -c Hello.m 85 | ``` 86 | 87 | 以下是错误报告输出: 88 | 89 | ``` 90 | Hello.m:10 NULL_DEREFERENCE 91 | pointer hello last assigned on line 9 could be null and is dereferenced at line 10, column 12 92 | ``` 93 | 94 | 编辑,如下修正: 95 | 96 | ```Objective-C 97 | NSString* m() { 98 | Hello* hello = nil; 99 | return hello.s; 100 | } 101 | ``` 102 | 103 | 再次运行,```No issues found```, 没有报错。 104 | 105 | ## Hello world C 106 | 107 | 一个简单的 C 例子: 108 | 109 | ```c 110 | // hello.c 111 | #include 112 | 113 | void test() { 114 | int *s = NULL; 115 | *s = 42; 116 | } 117 | ``` 118 | 119 | 在 [`hello.c`](https://github.com/facebook/infer/tree/master/examples/hello.c) 同级目录运行: 120 | 121 | ```bash 122 | infer -- gcc -c hello.c 123 | ``` 124 | 125 | 报错输出: 126 | 127 | ``` 128 | hello.c:5: error: NULL_DEREFERENCE 129 | pointer s last assigned on line 4 could be null and is dereferenced at line 5, column 10 130 | ``` 131 | 132 | 编辑,修正: 133 | 134 | ```c 135 | void test() { 136 | int *s = NULL; 137 | if (s != NULL) { 138 | *s = 42; 139 | } 140 | } 141 | ``` 142 | 143 | 再次运行,不在汇报错误,问题修复。 144 | 145 | 在进行 C 文件分析时,Infer 使用 gcc 命令并在内部运行 clang 来解析。 146 | 147 | 因此,你可能会得到一些和 gcc 不一样的编译器警告以及错误。以下命令等效的: 148 | 149 | ```bash 150 | infer -- gcc -c hello.c 151 | infer -- clang -c hello.c 152 | ``` 153 | 154 | ## Hello world Android 155 | 156 | 为了能够分析 Android 应用,请确保已经安装最新的 [Android 157 | SDK](https://developer.android.com/sdk/installing/index.html) 22,并保持最新。当然还有 `Android SDK Build-tools` 和 `Android Support Repository`。 158 | 159 | Android 的示例在 [`infer/examples/android_hello`](https://github.com/facebook/infer/tree/master/examples/android_hello/) 。 160 | 编辑 `local.properties` 并设定本地 SDK 路径。 161 | 162 | Android 示例使用 [gradle](https://gradle.org/) 构建。不过你不需要下载和安装 gradle。 163 | 项目中的脚本 [`gradlew`](https://docs.gradle.org/current/userguide/gradle_wrapper.html) 会自动下载 gradle以及相关的项目依赖。 164 | 165 | 之后,运行: 166 | 167 | ```bash 168 | infer -- ./gradlew build 169 | ``` 170 | 171 | Infer 会输出一系列问题: 172 | 173 | ```bash 174 | MainActivity.java:20: error: NULL_DEREFERENCE 175 | object s last assigned on line 19 could be null and is dereferenced at line 20 176 | 177 | MainActivity.java:37: error: RESOURCE_LEAK 178 | resource acquired by call to FileOutputStream(...) at line 34 is not released after line 37 179 | ``` 180 | 181 | ### 增量分析 182 | 183 | 如果你没有改变任何文件,运行 Infer 进行检测,你会注意到,这次并没有进行分析。 184 | 185 | 这时因为 gradle 是增量的编译的。所有已经编译过的文件没有变动的文件将不会再编译。Infer 根据编译命令获取需要分析的文件,因此,这样的情况下,获取不到任何需要编译分析和分析的文件。 186 | 187 | 以下有两种解决方案: 188 | 189 | 1. 在两次分析之间运行 `gradlew clean` 190 | 191 | ```bash 192 | ./gradlew clean 193 | ``` 194 | 195 | 这将会使得每次都重新进行编译,所以 Infer 能获取到所有要分析的文件。 196 | 197 | 2. 在增量模式下运行 Infer 198 | 199 | ```bash 200 | infer --incremental -- ./gradlew build 201 | ``` 202 | 203 | 这会使得 Infer 保留之前的编译结果。如果没有 `--incremental` (短命令是 `-i` ),Infer 会移除存储这些结果的文件夹: `infer-out`。 204 | 205 | 你可以在 [Infer workflow](docs/infer-workflow.html) 页面中,详细了解这两种方案的细节。 206 | 207 | 208 | ## Hello world iOS 209 | 210 | iOS 的示例代码在这里 [`infer/examples/ios_hello`](https://github.com/facebook/infer/tree/master/examples/ios_hello/),使用 Infer 检测: 211 | 212 | ```bash 213 | infer -- xcodebuild -target HelloWorldApp -configuration Debug -sdk iphonesimulator 214 | ``` 215 | 216 | 将会有以下问题输出: 217 | 218 | ```bash 219 | AppDelegate.m:20: error: MEMORY_LEAK 220 | memory dynamically allocated to shadowPath by call to CGPathCreateWithRect() at line 20, column 28 is not reachable after line 20, column 5 221 | 222 | AppDelegate.m:25: error: RESOURCE_LEAK 223 | resource acquired to fp by call to fopen() at line 25, column 8 is not released after line 25, column 5 224 | 225 | AppDelegate.m:29: warning: PARAMETER_NOT_NULL_CHECKED 226 | 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 227 | 228 | AppDelegate.m:34: error: NULL_DEREFERENCE 229 | pointer str last assigned on line 33 could be null and is dereferenced at line 34, column 12 230 | 231 | AppDelegate.m:39: error: PREMATURE_NIL_TERMINATION_ARGUMENT 232 | 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 233 | 234 | Hello.m:20: error: NULL_DEREFERENCE 235 | pointer hello last assigned on line 19 could be null and is dereferenced at line 20, column 12 236 | 237 | Hello.m:25: warning: IVAR_NOT_NULL_CHECKED 238 | 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 239 | 240 | Hello.m:30: warning: PARAMETER_NOT_NULL_CHECKED 241 | 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 242 | ``` 243 | 244 | 和[gradle](docs/hello-world.html#incremental-analysis) 相似,需要使用 `--incremental` (或 `-i`) 使得增量编译有结果输出。 245 | 246 | ```bash 247 | infer --incremental -- xcodebuild -target HelloWorldApp -configuration Debug -sdk iphonesimulator 248 | ``` 249 | 或者在编译前清理: 250 | 251 | ```bash 252 | xcodebuild -target HelloWorldApp -configuration Debug -sdk iphonesimulator clean 253 | ``` 254 | 255 | 256 | ## Hello world Make 257 | 258 | 使用 Infer 检测 C 代码,示例在 [`infer/examples/c_hello`](https://github.com/facebook/infer/tree/master/examples/c_hello/)。 259 | 260 | ```bash 261 | infer -- make 262 | ``` 263 | 将会输出以下问题: 264 | 265 | ```bash 266 | example.c:22: error: NULL_DEREFERENCE 267 | pointer max last assigned on line 21 could be null and is dereferenced at line 22, column 10 268 | 269 | example.c:36: error: NULL_DEREFERENCE 270 | pointer joe last assigned on line 35 could be null and is dereferenced by call to get_age() at line 36, column 10 271 | 272 | example.c:45: error: RESOURCE_LEAK 273 | resource acquired to fd by call to open() at line 41, column 12 is not released after line 45, column 5 274 | 275 | example.c:51: error: MEMORY_LEAK 276 | memory dynamically allocated to p by call to malloc() at line 51, column 14 is not reachable after line 51, column 3 277 | 278 | example.c:57: error: MEMORY_LEAK 279 | memory dynamically allocated to p by call to malloc() at line 56, column 14 is not reachable after line 57, column 3 280 | ``` 281 | 282 | 同样,和[gradle](docs/hello-world.html#incremental-analysis) 类似,为了使得增量编译下有结果输出,需要使用 `--incremental` (或 `-i`) 283 | 284 | ```bash 285 | infer --incremental -- make 286 | ``` 287 | 288 | 或者清理: 289 | 290 | ```bash 291 | make clean 292 | ``` 293 | -------------------------------------------------------------------------------- /_docs/01-adding-models.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: adding-models 3 | title: Adding models 4 | layout: docs 5 | permalink: /docs/adding-models.html 6 | section: User Guide 7 | section_order: 01 8 | order: 07 9 | --- 10 | 11 | ## Why do we need models 12 | 13 | When analyzing projects with call dependencies between functions, Infer follows the call graph to decide in which order analyze these functions. The main goal is to use the analysis summary of a function wherever this function is called. On the following example: 14 | 15 | ```C 16 | int foo(int x) { 17 | if (x < 42) { 18 | return x; 19 | } else { 20 | return 0; 21 | } 22 | } 23 | 24 | int bar() { 25 | return foo(24); 26 | } 27 | 28 | int baz() { 29 | return foo(54); 30 | } 31 | ``` 32 | 33 | Infer starts with the analysis on `foo` and detect that this function either returns `0` if the argument is greater than or equal to `42`, or returns the value of the argument otherwise. With this information, Infer detects that `bar` always returns `24` and `baz` always returns `0`. 34 | 35 | Now, it may happen that the code of some function is not available during the analysis. For example, this happens when a project uses pre-compiled libraries. The most typical case is the use of the standard library like in the following example: 36 | 37 | ```C 38 | #include 39 | 40 | int* create() { 41 | int *p = malloc(sizeof(int)); 42 | if (p == NULL) exit(1); 43 | return p; 44 | } 45 | 46 | void assign(int x, int *p) { 47 | *p = x; 48 | } 49 | 50 | int* my_function() { 51 | int *p = create(); 52 | assign(42, p); 53 | return p; 54 | } 55 | ``` 56 | 57 | Here, Infer will start with the analysis of `create` but will not find the source code for `malloc`. To deal with this situation, Infer relies on models of the missing functions to proceed with the analysis. The function `malloc` is internally modeled as either returning `NULL`, or returning a valid and allocated pointer. Similarly, the function `exit` is modeled as terminating the execution. Using these two models, Infer detects that `create` always returns an allocated pointer and that `my_function` is safe. 58 | 59 | At this point, it is important to note that missing source code and missing models do not make the analysis fail. Missing functions are treated as having no effect. However, even though skipping these missing functions is fine in most cases, there can be cases where it affects the quality of the analysis. For example, missing models can lead to incorrect bug reports. 60 | 61 | Consider the case of a function `lib_exit` having the same semantic as `exit` but defined in an pre-compiled library not part of the project being analyzed: 62 | 63 | ```C 64 | void lib_exit(int); 65 | 66 | int* create() { 67 | int *p = malloc(sizeof(int)); 68 | if (p == NULL) lib_exit(1); 69 | return p; 70 | } 71 | ``` 72 | 73 | In this case, Infer will not be able to know that the return statement is only possible in the case where `p` is not null. When analyzing `my_function`, Infer will consider the null case and report a null dereference error in the call to `assign(42, p)`. 74 | 75 | Similarly, considering a function `lib_alloc` equivalent to `malloc`, and the function `create` now defined as: 76 | 77 | ```C 78 | int* lib_alloc(int); 79 | 80 | int* create() { 81 | int *p = lib_alloc(sizeof(int)); 82 | return p; 83 | } 84 | ``` 85 | 86 | Then Infer will not report any null dereference in `my_function`. 87 | 88 | ## Examples of models 89 | 90 | ### Some models for C 91 | 92 | Adding new models is easy. The models for C can be found in [`infer/models/c/src/`](https://github.com/facebook/infer/tree/master/infer/models/c/src). The file [`libc_basic.c`](https://github.com/facebook/infer/blob/master/infer/models/c/src/libc_basic.c) contains models for some of the most commonly encountered functions from the C standard library. For example, the function `xmalloc`, which is essentially the same function as `create` defined above, is modeled by: 93 | 94 | ```C 95 | void *xmalloc(size_t size) { 96 | void *ret = malloc(size); 97 | INFER_EXCLUDE_CONDITION(ret == NULL); 98 | return ret; 99 | } 100 | ``` 101 | 102 | The function `xmalloc` is modeled using `malloc` to create an allocated object and the macro `INFER_EXCLUDE_CONDITION` used to eliminate the case where `malloc` can return null. The list of helper functions and macros for writing models can be found in [`infer_builtins.c`](https://github.com/facebook/infer/blob/master/infer/models/c/src/infer_builtins.c). 103 | 104 | For a slightly more complex example, `realloc` is modeled as: 105 | 106 | ```C 107 | void *realloc(void *ptr, size_t size) { 108 | if(ptr==0) { // if ptr in NULL, behave as malloc 109 | return malloc(size); 110 | } 111 | int old_size; 112 | int can_enlarge; 113 | old_size = __get_array_size(ptr); // force ptr to be an array 114 | can_enlarge = __infer_nondet_int(); // nondeterministically choose whether the current block can be enlarged 115 | if(can_enlarge) { 116 | __set_array_size(ptr, size); // enlarge the block 117 | return ptr; 118 | } 119 | int *newblock = malloc(size); 120 | if(newblock) { 121 | free(ptr); 122 | return newblock; 123 | } 124 | else { // if new allocation fails, do not free the old block 125 | return newblock; 126 | } 127 | } 128 | ``` 129 | 130 | This model is based on existing models for `malloc` and `free` and three helper functions: 131 | 132 | - `__get_array_size(ptr)` which allows to manipulate with a model what Infer knows about the size of the allocated memory 133 | - `__set_array_size(ptr, size)` to modify the information about the size of the allocated memory 134 | - `__infer_nondet_int()` to create a variable which can have any possible integer value 135 | 136 | ### For Java 137 | 138 | The models for Java are following the same approach and the list of helper functions is in: 139 | 140 | [`infer/models/java/src/com/facebook/infer/models/InferBuiltins.java`](https://github.com/facebook/infer/blob/master/infer/models/java/src/com/facebook/infer/models/InferBuiltins.java) 141 | [`infer/models/java/src/com/facebook/infer/models/InferUndefined.java`](https://github.com/facebook/infer/blob/master/infer/models/java/src/com/facebook/infer/models/InferUndefined.java) 142 | 143 | For example, Infer treats Java hash maps using a recency abstraction model: Infer remembers the last two keys being added by `put` and checked by `containsKey`, which can be used to make sure that no null pointer exceptions are coming from the fact that `get(key)` returns null when `key` is not not in the map. This behavior can just be implemented via a model written in Java with the help of few helper functions understood by Infer. These models can be found in: 144 | 145 | [`infer/models/java/src/java/util/HashMap.java`](https://github.com/facebook/infer/blob/master/infer/models/java/src/java/util/HashMap.java) 146 | 147 | and just rely on these two methods: 148 | 149 | - `InferUndefined.boolean_undefined()` to create a non-deterministic choice 150 | - `(V)InferUndefined.object_undefined()` to create a non null undefined object of type `V` 151 | 152 | ## How to add new models 153 | 154 | Let's look at a toy example in Java. As explained above, models for C, Objective-C and Java are all following the same approach. 155 | 156 | ```Java 157 | import lib.Server; 158 | 159 | public class Test { 160 | 161 | enum Status { 162 | SUCCESS, FAILURE, PING_FAILURE, CONNECTION_FAILURE 163 | } 164 | 165 | Status convertStatus(Server s) { 166 | switch (s.getStatus()) { 167 | case 0: 168 | return Status.SUCCESS; 169 | case 1: 170 | return Status.FAILURE; 171 | case 2: 172 | return Status.FAILURE; 173 | default: // should not happen 174 | return null; 175 | } 176 | } 177 | 178 | String statusName(Server s) { 179 | Status status = convertStatus(s); 180 | return status.name(); 181 | } 182 | 183 | } 184 | ``` 185 | 186 | Assuming that the class `lib.Server` is part of a pre-compiled library, Infer will report a null pointer exception in `statusName`. This happens whenever `s.getStatus()` returns a value greater that `3`, in which case the default branch of the switch statement is taken and `convertStatus` returns `null`. However, we know from the documentation that the method `lib.Server.getStatus` can only return `0`, `1`, or `2`. A possible approach would be to use an assertion like the Guava `Preconditions.checkState` to inform Infer about the invariant: 187 | 188 | ```Java 189 | Status convertStatus(Server s) { 190 | int serverStatus = s.getStatus(); 191 | Preconditions.checkState(serverStatus >= 0 && serverStatus < 3); 192 | switch (s.getStatus()) { 193 | ... 194 | } 195 | } 196 | ``` 197 | 198 | However, in the case where adding preconditions is not possible, we can then write a model for `getStatus()` in order to make the analysis more precise. 199 | 200 | To create a model for `getStatus()`, we need to add a class with the name and the same package as for the original method. In this example: 201 | 202 | - create a file `infer/models/java/src/infer/models/Server.java` with the following content: 203 | 204 | ```Java 205 | package infer.models; 206 | 207 | import com.facebook.infer.models.InferBuiltins; 208 | import com.facebook.infer.models.InferUndefined; 209 | 210 | public class Server { 211 | 212 | public int getStatus() { 213 | int status = InferUndefined.int_undefined(); 214 | InferBuiltins.assume(status >= 0 && status < 3); 215 | return status; 216 | } 217 | } 218 | ``` 219 | 220 | - recompile infer: 221 | 222 | ```bash 223 | make -C infer 224 | ``` 225 | 226 | - run the analysis again: 227 | 228 | ```bash 229 | infer -- javac Test.java 230 | ``` 231 | 232 | Now it should no longer report a null pointer exception. 233 | -------------------------------------------------------------------------------- /_docs/01-advanced-features.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: advanced-features 3 | title: Advanced usage 4 | layout: docs 5 | permalink: /docs/advanced-features.html 6 | section: User Guide 7 | section_order: 01 8 | order: 06 9 | --- 10 | 11 | In this section we discuss how to use Infer if you wish to make contributions to it or just look under the hood 12 | to learn more about how it is working. 13 | There are, for instance, debug options and ways to obtain the specs from the methods. 14 | 15 | ## Structure of the results folder 16 | 17 | After a successful Infer run, a directory is created to store the 18 | results of the analysis. By default this directory is called 19 | `infer-out`. 20 | 21 | ``` 22 | infer-out 23 | ├── captured/ 24 | ├── log/ 25 | ├── multicore/ 26 | ├── sources/ 27 | ├── specs/ 28 | ├── bugs.txt 29 | ├── procs.csv 30 | ├── report.csv 31 | ├── report.json 32 | └── stats.json 33 | ``` 34 | 35 | - `captured/` contains information for each file analyzed by Infer. See [below](docs/advanced-features.html#captured-folder) for more information. 36 | - The `log/`, `multicore/`, and `sources/` folders are used internally to drive the analyzer. 37 | - `specs/` contains the [specs](docs/advanced-features.html#print-the-specs) of each function that was analyzed, as inferred by Infer. 38 | - `bugs.txt`, `report.csv`, and `report.json` contain the Infer reports in three different formats. 39 | - `procs.csv` and `stats.json` contain debug information about the run. 40 | 41 | 42 | ### Captured folder 43 | 44 | Inside the folder `infer-out/captured` there is a folder for each captured file. Assume we captured a file called `example.c`. Then, Infer creates the following files inside the folder `infer-out/captured/example.c/`: 45 | 46 | - `example.c.cfg` 47 | - `example.c.cg` 48 | - `example.c.tenv` 49 | 50 | The files `.cfg`, `.cg` and `.tenv` contain the intermediate representation of that file. This data is passed to the backend of Infer, which then performs the analysis. The files contain serialized OCaml data structures. The `.cfg` file contains a control flow graph for each function or method implemented in the file. The file `.cg` contains the call graph of the functions defined or called from that file. Finally, the file `.tenv` contains all the types that are defined or used in the file. 51 | 52 | 53 | 54 | ## Debug mode 55 | 56 | With the debug option enabled `infer --debug -- `, Infer outputs debug information. When using `make` or `clang`, one needs an extra debug argument for the frontend: 57 | 58 | ```bash 59 | infer --frontend_debug --debug -- make example.c 60 | ``` 61 | 62 | In each captured folder, we obtain the file `icfg.dot`, which is the graphical representation of the file `.cfg` and the file 63 | `call_graph.dot`, that is the graphical representation of the call graph. 64 | 65 | 66 | Moreover, we obtain an html page for each captured file inside `infer-out/captured`. This html file contains the source file. In each line of the file there are links to the nodes of the control flow graph that correspond to that line of code. So one can see what the translation looks like. Moreover, when you click on those links you can see details of the symbolic execution of that particular node. If the option `--no_test` is also passed to `infer`, then the page pointed to from the nodes contains the printout of the whole symbolic execution. 67 | 68 | ## Print the specs 69 | 70 | It is also possible to print the specs created by Infer using the command `InferPrint`. You can print one particular spec that corresponds to one method, or you can print all the specs in the results directory. Let us look at an example: 71 | 72 | ```java 73 | class Hello { 74 | int x; 75 | void setX(int newX) { 76 | this.x = newX; 77 | } 78 | } 79 | ``` 80 | 81 | We run Infer on this example with: 82 | 83 | ```bash 84 | infer -- javac Hello.java 85 | ``` 86 | 87 | Infer saves the spec for the method `setX` in `infer-out/specs` and we can print it with the command: 88 | 89 | ```bash 90 | InferPrint infer-out/specs/Hello.setX{98B5}:void.specs 91 | ``` 92 | 93 | The convention for method names in Java is `.`. This outputs the following: 94 | 95 | ```bash 96 | Procedure: void Hello.setX(int) 97 | void void Hello.setX(int)(class Hello *this, int newX) 98 | Timestamp: 1 99 | Status: INACTIVE 100 | Phase: RE_EXECUTION 101 | Dependency_map: 102 | TIME:0.006893 s TIMEOUT:N SYMOPS:34 CALLS:0,0 103 | ERRORS: 104 | --------------------------- 1 of 1 [nvisited: 4 5 6] --------------------------- 105 | PRE: 106 | this = val$1: ; 107 | newX = val$3: ; 108 | this|->{Hello.x:val$2}: 109 | POST 1 of 1: 110 | this = val$1: ; 111 | return = val$4: ; 112 | newX = val$3: ; 113 | this|->{Hello.x:newX}: 114 | ---------------------------------------------------------------- 115 | ``` 116 | 117 | which expresses the fact that `this` needs to be allocated at the beginning of the method, and that at the end of the method the field `x` is equal to `newX`. 118 | 119 | 120 | Moreover, you can print all the specs in the results directory with the command: 121 | 122 | ```bash 123 | InferPrint -results_dir infer-out 124 | ``` 125 | 126 | 127 | ## Run internal tests 128 | 129 | There are many tests in the Infer code base that check that Infer behaves correctly on small program examples. The tests use [Buck](http://buckbuild.com/), another Facebook's open source tool for building projects. We provide the script `inferTest` to run the tests, which requires buck to be in your PATH. 130 | 131 | ```bash 132 | inferTest java # Run the tests about Java analysis 133 | inferTest clang # Run the tests about C and Objective-C analysis 134 | inferTest c # Run the tests about C analysis 135 | inferTest objc # Run the tests about Objective-C analysis 136 | ``` 137 | -------------------------------------------------------------------------------- /_docs/01-analyzing-apps-or-projects.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: analyzing-apps-or-projects 3 | title: Analyzing apps or projects 4 | layout: docs 5 | permalink: /docs/analyzing-apps-or-projects.html 6 | section: User Guide 7 | section_order: 01 8 | order: 02 9 | --- 10 | 11 | To analyze files with Infer you can use the compilers `javac` and `clang`. You can also use Infer with `gcc`, however, internally Infer will use `clang` to compile your code. So, it may not work if your code does not compile with `clang`. 12 | 13 | Moreover, you can run Infer with a variety of build systems. Notice that you can run infer faster by running the compilation command in parallel, e.g. `infer -- make -j8`. 14 | Please also take into account that if you wish to analyze a project, you should probably do `clean` beforehand so that the compiler compiles all the files and so Infer also analyses all the files (see the [previous section](docs/infer-workflow.html)). 15 | 16 | Here is an overview of the build systems supported by Infer. You can 17 | get more information about how a particular build system is supported 18 | by running `infer --help -- `, for instance `infer 19 | --help -- gradle`. 20 | 21 | ### Gradle 22 | 23 | ```bash 24 | infer -- gradle 25 | infer -- ./gradlew 26 | ``` 27 | 28 | ### Buck 29 | 30 | ```bash 31 | infer -- buck 32 | ``` 33 | 34 | ### Maven 35 | ```bash 36 | infer -- mvn 37 | ``` 38 | 39 | ### Xcodebuild 40 | 41 | Infer can analyze apps built using `xcodebuild`. Only `.m` and `.c` 42 | files will be analyzed; `.cpp`, `.cc` and `.mm` files will be 43 | ignored. For instance, for an iOS app: 44 | 45 | ```bash 46 | infer -- xcodebuild -target -configuration -sdk iphonesimulator 47 | ``` 48 | 49 | ### Make 50 | 51 | Infer can analyze projects that compile with `make`. If there are C++ files in the project, they will be ignored. 52 | 53 | ```bash 54 | infer -- make 55 | ``` 56 | -------------------------------------------------------------------------------- /_docs/01-checkers.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: checkers 3 | title: "Infer : Checkers" 4 | layout: docs 5 | permalink: /docs/checkers.html 6 | section: User Guide 7 | section_order: 01 8 | order: 03 9 | --- 10 | 11 | The Infer analyzer performs sophisticated interprocedural static 12 | analysis. When this power is not needed, such as for analyses of the 13 | kind usually found in so-called *linters*, we have a framework called 14 | Infer:Checkers. 15 | 16 | Infer:Checkers can check a given property in each method of a given 17 | project, but *intra-procedurally*, not inter-procedurally. 18 | 19 | The checkers can be run by adding the option `-a checkers` to the analysis command as in this example: 20 | 21 | ```bash 22 | infer -a checkers -- javac Test.java 23 | ``` 24 | 25 | At the moment, we have the checker 26 | [immutable cast](docs/checkers-bug-types.html#CHECKERS_IMMUTABLE_CAST). 27 | -------------------------------------------------------------------------------- /_docs/01-eradicate.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: eradicate 3 | title: "Infer : Eradicate" 4 | layout: docs 5 | permalink: /docs/eradicate.html 6 | section: User Guide 7 | section_order: 01 8 | order: 04 9 | --- 10 | 11 | 12 | > "I call it my billion-dollar mistake. It was the invention of the null reference in 1965." 13 | > 14 | > [Tony Hoare](http://en.wikipedia.org/wiki/Tony_Hoare) 15 | 16 | 17 | ### What is Infer:Eradicate? 18 | 19 | Infer:Eradicate is a type checker for @Nullable annotations for Java. It is part of the Infer static analysis suite of tools. 20 | The goal is to eradicate null pointer exceptions. 21 | 22 | @Nullable 23 | annotations denote that a parameter, field or the return value of a method can be null. 24 | When decorating a parameter, this denotes that the parameter can legitimately be null and the method will need to deal with it. When decorating a method, this denotes the method might legitimately return null. 25 | 26 | Starting from @Nullable-annotated programs, the checker performs a flow sensitive analysis 27 | to propagate the nullability through assignments and calls, and flags errors for 28 | unprotected accesses to nullable values or inconsistent/missing annotations. 29 | It can also be used to add annotations to a previously un-annotated program. 30 | 31 | ### What is the @Nullable convention? 32 | 33 | If you say nothing, you're saying that the value cannot be null. This is the recommended option when possible: 34 | 35 | Program safely, annotate nothing! 36 | 37 | When this cannot be done, add a @Nullable annotation before the type to indicate that the value can be null. 38 | 39 | ### What is annotated? 40 | 41 | Annotations are placed at the interface of method calls and field accesses: 42 | 43 | - Parameters and return type of a method declaration. 44 | - Field declarations. 45 | 46 | Local variable declarations are not annotated: their nullability is inferred. 47 | 48 | ### How is Infer:Eradicate invoked? 49 | 50 | Eradicate can be invoked by adding the option `-a eradicate` to the analysis command as in this example: 51 | 52 | ```bash 53 | infer -a eradicate -- javac Test.java 54 | ``` 55 | 56 | The checker will report an error on the following program that accesses a nullable value without null check: 57 | 58 | ```java 59 | class C { 60 | int getLength(@Nullable String s) { 61 | return s.length(); 62 | } 63 | } 64 | ``` 65 | 66 | But it will not report an error on this guarded dereference: 67 | 68 | ```java 69 | class C { 70 | int getLength(@Nullable String s) { 71 | if (s != null) { 72 | return s.length(); 73 | } else { 74 | return -1; 75 | } 76 | } 77 | } 78 | ``` 79 | 80 | Eradicate reports the following [warnings](/docs/eradicate-warnings.html). 81 | -------------------------------------------------------------------------------- /_docs/01-infer-workflow.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: infer-workflow 3 | title: Infer 的工作机制 4 | layout: docs 5 | permalink: /docs/infer-workflow.html 6 | section: User Guide 7 | section_order: 01 8 | order: 01 9 | --- 10 | 11 | 本页文档说明 Infer 的几种运行方式,你可根据你自己的项目具体情况选用。 12 | 13 | **摘要** 14 | 15 | 1. 初次运行时,确保项目是清理过的。可以通过 (`make clean`,`gradle clean` 16 | 等等) 17 | 2. 两次运行之间,记得清理项目,或者通过 `--incremental` 18 | 选项,方式因为增量编译而无结果输出。 19 | 3. 如果你使用的是非增量编译系统,则无需如此,比如:`infer -- javac Hello.java`,编译 Java 文件。 20 | 4. 成功运行之后,在同一目录下,你可以通过 `inferTraceBugs` 浏览更加详细的报告。 21 | 22 | ## Infer 运行的两个阶段 23 | 24 | 不管是哪种语言,Infer 运行时,分为两个主要阶段: 25 | 26 | ### 1. 捕获阶段 27 | 28 | Infer 捕获编译命令,将文件翻译成 Infer 内部的中间语言。 29 | 30 | 这种翻译和编译类似,Infer 从编译过程获取信息,并进行翻译。这就是我们调用 Infer 31 | 时带上一个编译命令的原因了,比如: `infer -- clang -c file.c`, `infer -- javac 32 | File.java`。结果就是文件照常编译,同时被 Infer 33 | 翻译成中间语言,留作第二阶段处理。特别注意的就是,如果没有文件被编译,那么也没有任何文件会被分析。 34 | 35 | Infer 把中间文件存储在结果文件夹中,一般来说,这个文件夹会在运行 `infer` 36 | 的目录下创建,命名是 `infer-out/`。当然,你也可以通过 `-o` 37 | 选项来自定义文件夹名字: 38 | 39 | ```bash 40 | infer -o /tmp/out -- javac Test.java 41 | ``` 42 | 43 | ### 2. 分析阶段 44 | 45 | In this phase, the files in `infer-out/` are analyzed by Infer. Infer 46 | analyzes each function and method separately. If Infer encounters an 47 | error when analyzing a method or function, it stops there for that 48 | method or function, but will continue the analysis of other methods 49 | and functions. So, a possible workflow would be to run Infer on your 50 | code, fix the errors generated, and run it again to find possibly more 51 | errors or to check that all the errors have been fixed. 52 | 53 | The errors will be displayed in the standard output and also in a file 54 | `infer-out/bugs.txt`. We filter the bugs and show the ones that are 55 | most likely to be real. In the results directory (`infer-out/`), 56 | however, we also save a file `report.csv` that contains all the 57 | errors, warnings and infos reported by Infer in csv format. 58 | 59 | 60 | ## Incremental and non-incremental workflows 61 | 62 | By default, running Infer will delete the previous `infer-out/` 63 | directory if it exists. This leads to a *non-incremental* 64 | workflow. Passing `--incremental` (or `-i`) to Infer prevents it from 65 | deleting `infer-out/`, leading to an *incremental* workflow. 66 | 67 | There are exceptions to this. In particular, you can run only one of 68 | the phases above. For instance, `infer -- javac Hello.java` is 69 | equivalent to running these two commands: 70 | 71 | ```bash 72 | infer -a capture -- javac Hello.java 73 | infer -- analyze 74 | ``` 75 | 76 | Notice that the second command does not erase `infer-out/`, as the 77 | files it needs to analyze live there! 78 | 79 | You can learn more about the various modes of operations of Infer by 80 | running `infer --help`. 81 | 82 | Let us highlight when you may need non-incremental and incremental 83 | workflows. 84 | 85 | 86 | ### Non-incremental workflow 87 | 88 | Non-incremental workflow is well suited to running Infer repeatedly 89 | with a single compiler command, e.g. 90 | 91 | ```bash 92 | infer -- javac Hello.java 93 | edit Hello.java 94 | # make some changes to Hello.java, e.g. fix a bug reported by Infer 95 | infer -- javac Hello.java 96 | ``` 97 | 98 | To start a fresh analysis, you have to 99 | 100 | 1. delete the results directory: 101 | 102 | ```bash 103 | rm -fr infer-out 104 | ``` 105 | 106 | 2. clean the build products, for instance with `make clean` for a make-based project. 107 | 108 | 109 | ### Incremental workflow 110 | 111 | Software projects such as mobile apps use *incremental* build systems. 112 | Infer understands several such build systems, detailed in the [next 113 | section](docs/analyzing-apps-or-projects.html). To analyze your 114 | project using Infer, it has to use one of these build systems. 115 | 116 | Running Infer on your project is as simple as running `infer -- ` where the build command is the one you would 118 | normally use to compile your source code. Infer should be first run on 119 | a *clean* version of the project, to capture all the compilation 120 | commands in its capture phase. 121 | 122 | For instance, for a project compiled using gradle, 123 | 124 | ```bash 125 | gradle clean 126 | infer -- gradle build 127 | ``` 128 | 129 | Next, if you change some files in your project, for instance in 130 | response to an Infer report, you can either repeat the commands above, 131 | that is clean and reanalyze the entire project, or else tell Infer 132 | that you are using an incremental toolchain: 133 | 134 | ```bash 135 | edit some/File.java 136 | # make some changes to some/File.java 137 | infer --incremental -- gradle build 138 | ``` 139 | 140 | Note that you can run Infer with the `--incremental` flag the first 141 | time around as well. 142 | 143 | 144 | ## Exploring Infer reports 145 | 146 | You can get more information about the reports generated by Infer by 147 | running `inferTraceBugs` in the same directory. For instance 148 | 149 | ```bash 150 | infer -- gradle build 151 | inferTraceBugs 152 | ``` 153 | 154 | This tool allows you to see error traces leading to each bug reported 155 | by Infer, which can be helpful in tracking down the precise cause of 156 | each bug. See the output of `inferTraceBugs --help` for more 157 | information. 158 | -------------------------------------------------------------------------------- /_docs/01-install-from-sources.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: install-from-source 3 | title: Install from source 4 | layout: docs 5 | permalink: /docs/install-from-source.html 6 | section: User Guide 7 | section_order: 01 8 | order: 05 9 | --- 10 | 11 | Follow the instructions [here](https://github.com/facebook/infer/blob/master/INSTALL.md#install-infer-from-source) to compile Infer on different platforms. If you just wish to use Infer, and are not interested in making contributions to it, you may skip these instructions and just download the binaries provided in the section [Getting started with Infer](docs/getting-started.html). 12 | -------------------------------------------------------------------------------- /_docs/02-about-infer.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: about-Infer 3 | title: About Infer 4 | layout: docs 5 | permalink: /docs/about-Infer.html 6 | section: Foundations 7 | section_order: 02 8 | order: 01 9 | --- 10 | 11 | Infer is a static program analyzer for Java, C, and Objective-C, written in [OCaml](https://ocaml.org/). 12 | Infer is deployed within Facebook and it is running continuously to verify select properties of every code modification for the main Facebook apps for Android and iOS, Facebook Messenger, Instagram, and other apps. 13 | It can be used for other code too: Infer can analyze also C code and Java code that is not Android. 14 | At present Infer is tracking problems caused by null pointer dereferences and resource and memory leaks, which cause some of the more important problems on mobile. 15 | 16 | 17 | Infer came to Facebook with the acquisition of the verification startup Monoidics in 2013. 18 | Monoidics was itself based on recent 19 | academic research, particularly on separation logic and bi-abduction. 20 | Within Facebook, Infer has been undergoing iterative development and changing in response to feedback from developers. 21 | We are continuing development of Infer as open-source so that others can benefit from using it, and so that we 22 | can partner with the community on a journey aimed at making program verification technology more broadly practical. 23 | 24 | -------------------------------------------------------------------------------- /_docs/02-limitations.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: limitations 3 | title: Limitations, etc 4 | layout: docs 5 | permalink: /docs/limitations.html 6 | section: Foundations 7 | section_order: 02 8 | order: 03 9 | --- 10 | 11 | 12 | 13 | ## Expectations 14 | 15 | We want to be clear that if you run Infer on your project you might get very good results, but it is also possible that you don't. 16 | Although we have had good fix rates working with Facebook mobile codebases, 17 | we are not making strong claims about rates of false alarms or similar when applied to arbitrary 18 | codebases. For example, we have had some success [getting bugs fixed 19 | in the DuckDuckGo Android App](blog/2015/05/22/Infer-on-open-source-android-apps.html), but we encountered many false alarms when running Infer on GNU coreutils. 20 | It typical of program verification and static analysis tools that their results vary, 21 | and that is to be expected, e.g., because 22 | they are tackling undecidable problems and because different codebases they are applied to will have been coded differently. 23 | 24 | 25 | 26 | The good thing, though, is that you might get useful results! And, where the results are imperfect, 27 | this can be taken as input for improvement. 28 | 29 | 30 | Apart from these general remarks, Infer has a number of specific technical limitations, which we describe in terms 31 | of bug types and language features. 32 | 33 | 34 | 35 | ## Bug types 36 | 37 | At present Infer is reporting on a restricted collection of 38 | [bug types](/docs/infer-bug-types.html), 39 | typically involving null pointers and memory or resource leaks. 40 | The intitial set of bug types 41 | Infer has focused on was driven by the most pressing needs for serving the Facebook 42 | mobile developers. Our approach has been to report less initially, to iterate with developers and provide value to them, 43 | and gradually expand what we can do while still providing value. 44 | 45 | 46 | Some bug types we don't report as of yet include 47 | 48 | - Array bounds errors 49 | - Cast exceptions 50 | - Leaking of tainted data 51 | - Concurrency race conditions 52 | 53 | and more. In the first three cases we have partial treatments inside of Infer, but we have not surfaced these capabilities yet; the reports are not of sufficient quality to present to developers. 54 | For example, Infer can 55 | find some potential array bounds errors, but many of its reports are false alarms and it misses still more. 56 | 57 | Put another way: there is more work to do! 58 | 59 | 60 | ## Language Features 61 | 62 | 63 | 64 | A different dimension in which Infer is limited concerns language features. 65 | Infer either does not understand or has a weak treatment of 66 | 67 | - Concurrency, including Java's Concurrency Utilities and iOS's Grand Central Dispatch 68 | - Dynamic dispatch 69 | - Reflection 70 | - Android lifecycles 71 | - Arithmetic 72 | - and more 73 | 74 | Some of these problems are fundamental, largely open, problems in program analysis 75 | (especially concurrency), while for others there is much prior and successful work to draw upon 76 | (e.g., arithmetic) 77 | and are simply on our todo list awaiting work. 78 | 79 | 80 | Thus, 81 | Infer's core algorithms can be understood as being sound with 82 | respect to an idealized model (that is 83 | all soundness can ever be), but this idealized model is some distance from 84 | real execution models for programs where Infer is deployed. 85 | One consequence of this is that we cannot claim that Infer reasons about all flows through an application, 86 | but only some flows. 87 | 88 | In approaching these limitations going forward we must consider solutions that take into account our use case: to comment in minutes on 89 | modifications to large codebases. Methods based on whole program analysis are challenging to consider 90 | when approaching these problems for our deployment model. 91 | 92 | 93 | These limitations can be seen positively as opportunities for improvement, 94 | to do more static analysis and program verification for the benefit of programmers everywhere! 95 | We will 96 | be delighted if people from the static analysis and program verification communities 97 | join us in working on these problems. 98 | -------------------------------------------------------------------------------- /_docs/02-separation-logic-and-biabduction.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: separation-logic-and-bi-abduction 3 | title: Separation logic and bi-abduction 4 | layout: docs 5 | permalink: /docs/separation-logic-and-bi-abduction.html 6 | section: Foundations 7 | section_order: 02 8 | order: 02 9 | --- 10 | {% include katex_import.html %} 11 | 12 | - [Separation logic](docs/separation-logic-and-bi-abduction.html#separation-logic) 13 | - [Bi-abduction](docs/separation-logic-and-bi-abduction.html#bi-abduction) 14 | - [Technical papers](docs/separation-logic-and-bi-abduction.html#technical-papers) 15 | 16 | ## Separation logic 17 | 18 | Separation logic is a novel kind of mathematical logic which facilitates reasoning about 19 | mutations to computer memory. It enables scalability by breaking reasoning into chunks 20 | corresponding to local operations on memory, and then composing 21 | the reasoning chunks together. 22 | 23 | 24 | Separation logic is based on a logical connective \\( * \\) called the *separating conjunction* and pronounced "and separately". Separation logic formulae are interpreted over program allocated heaps. The logical formula 25 | \\( A*B \\) holds of a piece of program heap (a heaplet) when it can be divided into two sub-heaplets described by \\(A\\) and \\(B\\). 26 | For example, the formula 27 | $$x \mapsto y * y \mapsto x $$ can be read "\\(x\\) points to \\(y\\) and separately \\(y\\) points to \\(x\\)". This formula describes precisely two allocated memory cells. The first cell is allocated at the address denoted by the pointer \\(x\\) and the content of this cell is the value of \\(y\\). 28 | The second cell is 29 | allocated at the address denoted by the pointer \\(y\\) and the content of this second cell is the value of \\(x\\). Crucially, we know that there are precisely two cells because \\( * \\) stipulates that they are separated and therefore the cells are allocated in two different parts of memory. In other words, \\( * \\) 30 | says that \\(x\\) and \\(y\\) do not hold the same value (i.e., these pointers are not aliased). 31 | The heaplet partitioning defined by the formula above can visualized like so: 32 | 33 | ![static/images/SepSplit.jpg](static/images/SepSplit.jpg) 34 | 35 | 36 | The important thing about separating conjunction is 37 | the way that it fits together with mutation to computer memory; reasoning about program commands 38 | tends to work by updating \\(*\\)-conjuncts in-place, mimicking the operational in-place update of RAM. 39 | 40 | Separation logic uses Hoare triples of the form \\( \lbrace pre \rbrace prog \lbrace post \rbrace \\) where \\(pre\\) is the precondition, \\(prog\\) a program part, and \\(post\\) 41 | the postcondition. Triples are abstract specifications of the behavior of the program. For example, we could take 42 | 43 | $$ \lbrace r \mapsto open\rbrace \, closeResource(r)\, \lbrace r \mapsto closed\rbrace \;\;\; (spec)$$ 44 | 45 | as a specification for a method which closes a resource given to it as a parameter. 46 | 47 | 48 | Now, suppose we have two resources \\( r\_1 \\) and \\( r\_2 \\), described by \\(r\_1 \mapsto open * r\_2 \mapsto open\\) 49 | and we close the first of them. We think operationally in terms of updating the memory in place, leaving \\(r\_2 \mapsto open\\) alone, 50 | as described by this triple: 51 | 52 | $$ \lbrace r\_1 \mapsto open * r\_2 \mapsto open\rbrace closeResource(r\_1) \lbrace r\_1 \mapsto closed * r\_2 \mapsto open \rbrace \;\;\; (use)$$ 53 | 54 | What we have here is the that specification (spec) described how \\(closeResource()\\) works by mentioning only one 55 | piece of state, what is sometimes called a small specification, 56 | and in (use) we use that specification to update a larger precondition in place. 57 | 58 | This is an instance of a general pattern. 59 | There is a rule that lets you go from smaller to bigger specifications 60 | $$ \frac{\lbrace pre \rbrace prog \lbrace post \rbrace}{\lbrace pre * frame \rbrace prog \lbrace post * frame \rbrace}$$ 61 | Our passage from (spec) to (use) is obtained by taking 62 | 63 | - \\(pre\\) to be \\(r\_1 \mapsto open\\) 64 | - \\(post\\) to be \\(r\_1 \mapsto closed \\), and 65 | - \\(frame\\) to be \\(r\_2 \mapsto open \\) 66 | 67 | This rule is called the *frame rule* of separation logic. It is named after the frame problem, a classic problem in artificial intelligence. 68 | Generally, the \\(frame\\) describes state that remains unchanged; the terminology comes from the analogy of 69 | a background scene in an animation as unchanging while the objects and characters within the scene change. 70 | 71 | 72 | The frame rule is the key to the principle of local reasoning in separation logic: reasoning and specifications 73 | should concentrate on the resources that a program accesses (the footprint), without mentioning what 74 | doesn't change. 75 | 76 | 77 | 78 | ## Bi-abduction 79 | 80 | Bi-abduction is a form of logical inference for separation logic which automates the key ideas about local 81 | reasoning. 82 | 83 | Usually, logic works with validity or entailment statements like 84 | $$ 85 | A \vdash B 86 | $$ 87 | which says that \\(A\\) implies \\(B\\). Infer uses an extension of this inference question in an internal 88 | theorem prover while it runs over program statements. 89 | Infer's question 90 | $$ 91 | A * ?antiframe \vdash B * ?frame 92 | $$ 93 | is called *bi-abduction*. The problem here is for the theorem prover to discover a pair of frame and antiframe formulae that make the entailment statement valid. 94 | 95 | Global analyses of large programs are normally computational untractable. However, 96 | bi-abduction allows to break the large analysis of a large program in small independent analyses of its procedures. This gives Infer the ability to scale independently of the size of the analyzed code. Moreover, by breaking the analysis in small 97 | independent parts, when the full program is analyzed again because 98 | of a code change the analysis results of the unchanged part of the 99 | code can be reused and only the code change needs to be re-analyzed. This process is called incremental analysis and it 100 | is very powerful when integrating a static analysis tool like infer in a development environment. 101 | 102 | 103 | 104 | In order to be able to decompose a global analysis in small independent analyses, let's first consider how a function 105 | call is analyzed in separation logic. Assume we have the following spec for a function \\( f() \\): 106 | $$ \lbrace pre\_f \rbrace \;\; f() \;\; \lbrace post\_f \rbrace $$ 107 | and by analyzing the caller function, we compute that before 108 | the call of \\( f \\), the formula \\( CallingState \\) hold. Then 109 | to utilize the specification of \\( f \\) the following implication must holds: 110 | 111 | $$ CallingState \vdash pre\_f \;\;\;\;\;\;\;\;\;\;\;\; (Function Call)$$ 112 | 113 | 114 | Given that, 115 | bi-abduction is used at procedure call sites for two reasons: to discover missing state that is needed for the above implication to hold and allow the analysis 116 | to proceed (the antiframe) as well as state that the procedure leaves unchanged (the frame). 117 | 118 | To see how this works suppose we have some bare code 119 | $$ 120 | closeResource(r1); \, closeResource(r2) 121 | $$ 122 | but no overall specification; 123 | we are going to describe how to discover a pre/post spec for it. 124 | Considering the first statement and the (spec) above, the human might say: if only we had 125 | \\(r1 \mapsto open\\) in the precondition then we could proceed. 126 | Technically, 127 | we ask a bi-abduction question 128 | $$ 129 | emp * ?antiframe \vdash r1 \mapsto open * ?frame 130 | $$ 131 | and we can fill this in easily by picking \\(antiframe = r1 \mapsto open\\) and \\(frame = emp\\), 132 | where emp means the empty state. The emp is recording that at the start we presume nothing. So we obtain the trivially true implication: 133 | $$ 134 | emp * r1 \mapsto open \vdash r1 \mapsto open * emp 135 | $$ 136 | which, by applying logical rules, can be re-written equivalently to: 137 | $$ 138 | r1 \mapsto open \vdash r1 \mapsto open 139 | $$ 140 | Notice that this satisfy the (Function Call) requirement to correctly make the call. 141 | So let's add that information in the pre, and while we are at it 142 | record the information in the post of the first statement that comes from (spec). 143 |
144 | \\( \lbrace r1 \mapsto open \rbrace \\) 145 | \\( closeResource(r1) \\) 146 | \\( \lbrace r1 \mapsto closed \rbrace \\) 147 | \\( closeResource(r2) \\) 148 |
149 | Now, let's move to the second statement. Its precondition \\(r1 \mapsto closed\\) in the partial symbolic execution trace just given 150 | does not have the information needed by \\(closeResource(r2)\\), so we can fill that in and continue by 151 | putting \\(r2 \mapsto open\\) in the pre. While we are at it we can thread this assertion back to the beginning. 152 |
153 | \\( \lbrace r1 \mapsto open * r2 \mapsto open \rbrace \\) 154 | \\( closeResource(r1) \\) 155 | \\( \lbrace r1 \mapsto closed * r2 \mapsto open\rbrace \\) 156 | \\( closeResource(r2) \\) 157 |
158 | This information on what to threat backwards can be obtained as the antiframe part of the bi-abduction question 159 | $$ 160 | r1 \mapsto closed * ?antiframe \vdash r2 \mapsto open * ?frame 161 | $$ 162 | where the solution picks 163 | \\(antiframe = r2 \mapsto open\\) and \\(frame = r1 \mapsto closed\\). 164 | Note that the antiframe is precisely the information missing from the precondition in order for \\(closeResource(r2)\\) to proceed. On the other hand, the frame \\(r1 \mapsto closed\\) is the portion of state not changed by \\(closeResource(r2)\\); 165 | we can thread that trough to the overall postconditon 166 | ( as justified by the frame rule), giving us 167 |
168 | \\( \lbrace r1 \mapsto open * r2 \mapsto open \rbrace \\) 169 | \\( closeResource(r1) \\) 170 | \\( \lbrace r1 \mapsto closed * r2 \mapsto open\rbrace \\) 171 | \\( closeResource(r2) \\) 172 | \\( \lbrace r1 \mapsto closed * r2 \mapsto closed \rbrace\\) 173 |
174 | Thus, we have obtained a pre and post for this code by symbolically executing it, using bi-abduction 175 | to discover preconditions (abduction of antiframes) as well as untouched portions of memory (frames) as we go along. 176 | 177 | In general, bi-abduction 178 | provides a way to infer a pre/post specs from bare code, as long as we know specs for the primitives at the base level of the code. The human does not need to write preconditions and postconditions for all the procedures, 179 | which is the key to having a high level of automation. 180 | This is the basis for how Infer works, why it can scale, and how it can analyze code changes incrementally. 181 | 182 | Context: The logical terminology we have been using here comes from AI and philosophy of science. 183 | Abductive inference was introduced by the philosopher Charles Peirce, and described as the mechanism 184 | underpinning hypothesis formation (or, guessing what might be true about the world), the most 185 | creative part of the scientific process. 186 | Abduction and the frame problem have both attracted significant attention in AI. 187 | Infer uses an automated form of abduction to generate 188 | preconditions describing the memory that a program touches (the antiframe part above), and frame inference to 189 | discover what isn't touched. 190 | Infer then uses deductive reasoning to 191 | calculate a formula describing the effect of a program, starting from the preconditions. 192 | In a sense, Infer approaches automated reasoning about programs by mimicking what a human might do when trying to understand a program: it abduces what the program needs, and deduces conclusions of that. 193 | It is when the reasoning goes wrong that Infer reports a potential bug. 194 | 195 | This description is by necessity simplified compared to what Infer actually does. 196 | More technical information can be found in the following papers. The descriptions in the papers are 197 | precise, but still simplified; there are many engineering decisions not recorded there. Finally, beyond the papers, 198 | you can read the source code if you wish! 199 | 200 | 201 | ## Technical papers 202 | 203 | 204 | The following papers contain some of the technical background on Infer and information on how it is used inside Facebook. 205 | 206 | - Local Reasoning about Programs that Alter Data Structures. An early separation logic paper which advanced ideas about local reasoning and the frame rule. 207 | - Smallfoot: Modular Automatic Assertion Checking with Separation Logic. First separation logic verification tool, introduced frame inference 208 | - A Local Shape Analysis Based on Separation Logic. Separation logic meets abstract interpretation; calculating loop invariants via a fixed-point computation. 209 | - Compositional Shape Analysis by Means of Bi-Abduction. 210 | The bi-abduction paper. 211 | - Moving Fast with Software Verification. A paper about the way we use Infer at Facebook. 212 | 213 | {% include katex_render.html %} 214 | -------------------------------------------------------------------------------- /_docs/03-checker-bug-types.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: checkers-bug-types 3 | title: Checkers bug types 4 | layout: docs 5 | permalink: /docs/checkers-bug-types.html 6 | section: Bug Types Reference 7 | section_order: 03 8 | order: 02 9 | --- 10 | 11 | ## Checker immutable cast 12 | 13 | This error type is reported in Java. It fires when an immutable collection is returned from a method whose type is mutable. 14 | 15 | ```java 16 | public List getSomeList() { 17 | ImmutableList l = foo(...); 18 | return l; 19 | } 20 | ``` 21 | 22 | This can lead to a runtime error if users of ` getSomeList` try to modify the list e.g. by adding elements. 23 | 24 | Action: you can change the return type to be immutable, or make a copy of the collection so that it can be modified. 25 | -------------------------------------------------------------------------------- /_docs/03-eradicate-warnings.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: eradicate-warnings 3 | title: Eradicate warnings 4 | layout: docs 5 | permalink: /docs/eradicate-warnings.html 6 | section: Bug Types Reference 7 | section_order: 03 8 | order: 03 9 | --- 10 | 11 | Below you will find a description of all the warnings reported by 12 | [Eradicate](/docs/eradicate.html). 13 | 14 | - [Eradicate null field access](/docs/eradicate-warnings.html#ERADICATE_NULL_FIELD_ACCESS) 15 | - [Eradicate null method call](/docs/eradicate-warnings.html#ERADICATE_NULL_METHOD_CALL) 16 | - [Eradicate field not nullable](/docs/eradicate-warnings.html#ERADICATE_FIELD_NOT_NULLABLE) 17 | - [Eradicate field not initialized](/docs/eradicate-warnings.html#ERADICATE_FIELD_NOT_INITIALIZED) 18 | - [Eradicate parameter not nullable](/docs/eradicate-warnings.html#ERADICATE_PARAMETER_NOT_NULLABLE) 19 | - [Eradicate return not nullable](/docs/eradicate-warnings.html#ERADICATE_RETURN_NOT_NULLABLE) 20 | - [Eradicate condition redundant](/docs/eradicate-warnings.html#ERADICATE_CONDITION_REDUNDANT) 21 | - [Eradicate return over annotated](/docs/eradicate-warnings.html#ERADICATE_RETURN_OVER_ANNOTATED) 22 | - [Eradicate inconsistent subclass return annotation](/docs/eradicate-warnings.html#ERADICATE_INCONSISTENT_SUBCLASS_RETURN_ANNOTATION) 23 | - [Eradicate inconsistent subclass parameter annotation](/docs/eradicate-warnings.html#ERADICATE_INCONSISTENT_SUBCLASS_PARAMETER_ANNOTATION) 24 | 25 | 26 | ## Eradicate null field access 27 | 28 | A field access of the form x.field where x could be null. 29 | 30 | Example: 31 | 32 | ```java 33 | class C { 34 | void foo(@Nullable C x) { 35 | x.field = 3; 36 | } 37 | } 38 | ``` 39 | 40 | Action: 41 | Make sure that x cannot be null by changing the code or changing annotations. 42 | If this cannot be done, the only choice is to use defensive programming: 43 | if (x != null) { ... x.field ... } 44 | else { ... you need to decide what to do when x is null ... } 45 | The general recommendation is to push null checks up the call chain as much as possible in order 46 | to detect the place where null values originate and deal with them at that point. 47 | When a null value is propagated down the call chain it is often difficult to determine 48 | its origin without global knowledge of what the program does. 49 | For example, a null value could originate in third party libraries which are not under your 50 | control, and the best place to check for null is typically immediately after calling these 51 | library functions. 52 | 53 | ## Eradicate null method call 54 | 55 | A method call x.m(...) where x could be null. 56 | 57 | Example: 58 | 59 | ```java 60 | class C { 61 | void foo(@Nullable C x) { 62 | String s = x.toString(); 63 | } 64 | } 65 | ``` 66 | Action: 67 | Same as for Null field access. 68 | 69 | ## Eradicate field not nullable 70 | 71 | 72 | An assignment x.f = v where v could be null and field f is not annotated with @Nullable. 73 | 74 | Example: 75 | 76 | ```java 77 | class C { 78 | String f; 79 | 80 | void foo(@Nullable String s) { 81 | f = s; 82 | } 83 | } 84 | ``` 85 | 86 | Action: 87 | The preferred action is to ensure that a null value is never stored in the field, 88 | by changing the code or changing annotations. 89 | If this cannot be done, add a @Nullable annotation to the field. This annotation might trigger 90 | more warnings in other code that uses the field, as that code must now deal with null values. 91 | 92 | ## Eradicate field not initialized 93 | 94 | 95 | The constructor does not initialize a field f which is not annotated with @Nullable 96 | 97 | Example: 98 | 99 | ```java 100 | class C { 101 | String f; 102 | 103 | C () { // field f not initialized and not annotated @Nullable 104 | } 105 | } 106 | ``` 107 | 108 | Action: 109 | The preferred action is to initialize the field with a value that is not null. 110 | If, by design, null is a valid value for the field, then it should be annotated with @Nullable. 111 | 112 | 113 | 114 | ## Eradicate parameter not nullable 115 | 116 | Method call x.m(..., v, ...) where v can be null and the corresponding parameter in method m is not annotated with @Nullable 117 | 118 | Example: 119 | 120 | ```java 121 | class C { 122 | void m(C x) { 123 | String s = x.toString() 124 | } 125 | 126 | void test(@Nullable C x) { 127 | m(x); 128 | } 129 | } 130 | ``` 131 | 132 | Action: 133 | The preferred action is to ensure that a null value is never passed to the method, 134 | by changing the code or changing annotations. 135 | If this cannot be done, add a @Nullable annotation to the relevant parameter in the method declaration. 136 | This annotation might trigger more warnings in the implementation of method m, as that code must now deal with null values. 137 | 138 | 139 | ## Eradicate return not nullable 140 | 141 | Method m can return null, but the method's return type is not annotated with @Nullable 142 | 143 | Example: 144 | 145 | ```java 146 | class C { 147 | String m() { 148 | return null; 149 | } 150 | } 151 | ``` 152 | 153 | Action: 154 | The preferred action is to ensure that a null value is never returned by the method, 155 | by changing the code or changing annotations. 156 | If this cannot be done, add a @Nullable annotation to the the method declaration. 157 | This annotation might trigger more warnings in the callers of method m, as the callers must now deal with null values. 158 | 159 | ## Eradicate condition redundant 160 | 161 | 162 | This report is inactive by default. Condition (x != null) or (x == null) when x cannot be null: the first condition is always true and the second is always false 163 | 164 | Example: 165 | 166 | ```java 167 | class C { 168 | void m() { 169 | String s = new String("abc"); 170 | if (s != null) { 171 | int n = s.length(); 172 | } 173 | } 174 | } 175 | ``` 176 | 177 | Action: 178 | Make sure that the annotations are correct, as the condition is considered redundant based on the existing annotations. 179 | In particular, check the annotation of any input parameters and fields of the current method, as well 180 | as the annotations of any method called directly by the current method, if relevant. 181 | If the annotations are correct, you can remove the redundant case. 182 | 183 | 184 | 185 | ## Eradicate return overannotated 186 | 187 | This report is inactive by default. Method m is annotated with @Nullable but the method cannot return null 188 | 189 | Example: 190 | 191 | ```java 192 | class C { 193 | @Nullable String m() { 194 | String s = new String("abc"); 195 | return s; 196 | } 197 | } 198 | ``` 199 | 200 | Action: 201 | Make sure that the annotations are correct, as the return annotation is considered redundant based on the existing annotations. 202 | In particular, check the annotation of any input parameters and fields of the current method, as well 203 | as the annotations of any method called directly by the current method, if relevant. 204 | If the annotations are correct, you can remove the @Nullable annotation. 205 | 206 | 207 | ## Eradicate inconsistent subclass return annotation 208 | 209 | The return type of the overridden method is annotated @Nullable, but the corresponding method in the superclass is not. 210 | 211 | Action: choose a consistent annotation based on the desired invariant. 212 | 213 | Example: 214 | 215 | ```java 216 | class A { 217 | String create() { 218 | return new String("abc"); 219 | } 220 | } 221 | 222 | class B extends A { 223 | @Nullable String create() { // Inconsistent @Nullable annotation. 224 | return null; 225 | } 226 | } 227 | ``` 228 | A consistent use of @Nullable on the return type across subtyping should prevent runtime issue like in: 229 | 230 | ````java 231 | class Main { 232 | 233 | int foo(A a) { 234 | String s = a.create(); 235 | return s.length(); 236 | } 237 | 238 | void main(String[] args) { 239 | A a = new B(); 240 | foo(a); 241 | } 242 | 243 | } 244 | ``` 245 | 246 | ## Inconsistent subclass parameter annotation 247 | 248 | A parameter of the overridden method is missing a @Nullable annotation present in the superclass. 249 | 250 | Action: choose a consistent annotation based on the desired invariant. 251 | 252 | Example: 253 | 254 | ````java 255 | class A { 256 | 257 | int len(@Nullable String s) { 258 | if (s != null) { 259 | return s.length(); 260 | } else { 261 | return 0; 262 | } 263 | } 264 | } 265 | 266 | class B extends A { 267 | 268 | int len(String s) { // @Nullable missing. 269 | return s.length(); 270 | } 271 | } 272 | ``` 273 | 274 | A consistent use of @Nullable on parameters across subtyping should prevent runtime issue like in: 275 | 276 | ````java 277 | public class Main { 278 | 279 | String s; 280 | 281 | int foo() { 282 | A a = new B(); 283 | return a.len(s); 284 | } 285 | } 286 | ``` 287 | 288 | -------------------------------------------------------------------------------- /_docs/03-infer-bug-types.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: infer-bug-types 3 | title: Infer bug types 4 | layout: docs 5 | permalink: /docs/infer-bug-types.html 6 | section: Bug Types Reference 7 | section_order: 03 8 | order: 01 9 | --- 10 | 11 | Here is an overview of the types of bugs currently reported by Infer. 12 | 13 | - Bugs reported in Java 14 | - [Resource leak](/docs/infer-bug-types.html#RESOURCE_LEAK) 15 | - [Null dereference](/docs/infer-bug-types.html#NULL_DEREFERENCE) 16 | 17 | - Bugs reported in C and Objective-C 18 | - [Resource leak](/docs/infer-bug-types.html#RESOURCE_LEAK) 19 | - [Memory leak](/docs/infer-bug-types.html#MEMORY_LEAK) 20 | - [Null dereference](/docs/infer-bug-types.html#NULL_DEREFERENCE) 21 | - [Parameter not null checked](/docs/infer-bug-types.html#PARAMETER_NOT_NULL_CHECKED) 22 | - [Ivar not null checked](/docs/infer-bug-types.html#IVAR_NOT_NULL_CHECKED) 23 | - [Premature nil termination argument](/docs/infer-bug-types.html#PREMATURE_NIL_TERMINATION_ARGUMENT) 24 | 25 | - Bugs reported only in Objective-C 26 | - [Retain cycle](/docs/infer-bug-types.html#RETAIN_CYCLE) 27 | 28 | ## Resource leak 29 | 30 | Infer reports resource leaks in C, Objective-C and Java. In general, resources are entities such as files, sockets, connections, etc, that need to be closed after being used. 31 | 32 | ### Resource leak in C 33 | 34 | This is an example of a resource leak in C code: 35 | 36 | ```c 37 | -(void) resource_leak_bug { 38 | FILE *fp; 39 | fp=fopen("c:\\test.txt", "r"); // file opened and not closed. 40 | } 41 | ``` 42 | 43 | ### Resource leak in Java 44 | 45 | For the remaining of this section, we will consider examples of resource leaks in Java code. 46 | 47 | TIP: A common source of bugs is exceptions skipping past close() statements. That is the first thing to look for if INFER reports a potential resource leak. 48 | 49 | ### Basics and Standard Idiom 50 | 51 | Some objects in Java, the resources, are supposed to be closed when you stop using them, and failure to close is a resource leak. Resources include 52 | input streams, output streams, readers, writers, sockets, http connections, cursors, and json parsers. 53 | 54 | The standard idiom is 55 | 56 | ```java 57 | // Standard idiom 58 | Allocate resource 59 | try { 60 | do some stuff 61 | } finally { 62 | close resource 63 | } 64 | ``` 65 | 66 | or more for example, 67 | 68 | ```java 69 | // Standard Idiom 70 | public static void foo () throws IOException{ 71 | FileOutputStream fos = new FileOutputStream(new File("whatever.txt")); 72 | try { 73 | fos.write(7); 74 | } finally { 75 | fos.close(); 76 | } 77 | } 78 | ``` 79 | and you should use the standard idiom for the most part, when you don't want to return the resource to the surrounding context. 80 | 81 | Sometimes people just leave out close(), and that is a bug, but more typically exceptional paths are the root of the problem, as in 82 | 83 | ```java 84 | // leak because of exception 85 | public static void foo () throws IOException { 86 | FileOutputStream fos = new FileOutputStream(new File("whatever.txt")); 87 | fos.write(7); //DOH! What if exception? 88 | fos.close(); 89 | } 90 | ``` 91 | 92 | where an exception in fos.write will cause execution to skip past the close() statement. 93 | 94 | #### Multiple Resources Bugs 95 | 96 | We can deal with multiple resources correctly and simply just by nesting the standard idiom. 97 | 98 | ```java 99 | // Two Resources nested 100 | public static void foo() throws IOException { 101 | FileInputStream fis = new FileInputStream(new File("whatever.txt")); 102 | try { 103 | FileOutputStream fos = new FileOutputStream(new File("everwhat.txt")); 104 | try { 105 | fos.write(fis.read()); 106 | } finally { 107 | fos.close(); 108 | } 109 | } finally { 110 | fis.close(); 111 | } 112 | } 113 | ``` 114 | 115 | Bugs often occur when using multiple resources in other ways because of exceptions in close() methods. For example, 116 | 117 | ```java 118 | // Classic Two Resources Bug 119 | public static void foo() throws IOException { 120 | FileInputStream fis = null; 121 | FileOutputStream fos = null; 122 | try { 123 | fis = new FileInputStream(new File("whatever.txt")); 124 | fos = new FileOutputStream(new File("everwhat.txt")); 125 | fos.write(fis.read()); 126 | } finally { 127 | if (fis!=null) fis.close(); 128 | if (fos!=null) fos.close(); 129 | } 130 | } 131 | ``` 132 | 133 | Here, if there is an exception in the call to fis.close() execution will skip past fos.close(); a leak. 134 | 135 | Another way, besides the standard idiom, to deal with this problem is to swallow exceptions. 136 | 137 | ```java 138 | // Two Resources Fix 1 139 | public static void foo() throws IOException { 140 | FileInputStream fis = null; 141 | FileOutputStream fos = null; 142 | try { 143 | fis = new FileInputStream(new File("whatever.txt")); 144 | fos = new FileOutputStream(new File("everwhat.txt")); 145 | fos.write(fis.read()); 146 | } finally { 147 | try { 148 | if (fis!=null) fis.close(); 149 | } catch (Exception e) {}; // Exception swallowing 150 | if (fos!=null) fos.close(); 151 | } 152 | } 153 | ``` 154 | 155 | You can also swallow the exception on the output stream. Some people prefer not to swallow output stream exceptions, and also flush before closing. 156 | http://code.google.com/p/guava-libraries/issues/detail?id=1118 157 | 158 | Notice that the nested standard idiom does not need the checks for null, which are in there in this case to protect against the case when one of the allocations throws an exception, in which case one would get a NullPointerException. 159 | 160 | ### Nested_Allocations 161 | 162 | When a resource allocation is included as an argument to a constructor, 163 | if the constructor fails it can leave an an unreachable resource that no one can close. 164 | 165 | For example 166 | gzipOutputStream = new GZIPOutputStream(new FileOutputStream(out)); 167 | is bad in case the outer constructor, GZIPOutputStream, throws an exception. In that case, no one will have a hold of the FileOutputStream and so no one will be able to close it. 168 | 169 | In such a case you need to move the allocation the FileOutputStream out of the nested position and name it, so you are able to close if anything goes wrong during execution of the GZIPOutputStream constructor. 170 | 171 | Here are resources that can throw exceptions i their constructor(s). 172 | 173 | - ObjectInputStream , ObjectOutputStream, PipedInputStream, PipedOutputStream, PipedReader, PipedWriter, 174 | JarInputStream, JarOutputStream, GZIPInputStream, GZIPOutputStream , ZipFile 175 | all throw IOException 176 | - PrintStream throws UnsupportedEncodingException 177 | 178 | The constructors for FileInputStream, FileOutputStream and RandomAccessFile throw FileNotFoundException, but these cases are not problematic in the sense that their arguments are not resources and so they do not cause the nested resource leak. 179 | 180 | ### Allocation of JSonParser and Cursor resources 181 | 182 | Some resources are created inside libraries instead of by "new". 183 | 184 | Cursor is an interface, the actual resources are something like SQLiteCursor. So, every time you call a function that returns a Cursor object, there is an allocation. 185 | 186 | For instance, in the functions from SQLiteDatabase 187 | query(…) and rawQuery(…) 188 | allocate a cursor resource. For 189 | SQLiteQueryBuilder, ContentProviderClient, ContentResolver. MediaStore and DownloadManager it is only 190 | query(…) 191 | Cursor objects cursor created by these functions need to be closed (i.e., cursor.close()). 192 | 193 | Similarly, JsonParser is an abstract class, and create a resource in functions from the class JsonFactory 194 | createParser(byte[] data) 195 | createParser(byte[] data, int offset, int len) 196 | createParser(String content) 197 | createParser(URL url) 198 | createParser(File f) 199 | JsonParser objects js created by these functions need to be closed (jp.close()). On the other hand . JasonParsers 200 | gotten from 201 | createParser(InputStream in) and 202 | createParser(Reader r) 203 | give you JsonParsers that don’t need to be closed. This is because they receive the resource from somewhere that will maintain the responsibility to close it. 204 | 205 | ### Escaping resources and exceptions 206 | 207 | Sometimes you want to return a resource to the outside, in which case you should not close it, but you still need to be careful of exceptions in 208 | case control skips past the return leaving no one to close. Here is a simple example of a positive use of escaping resources. 209 | 210 | ```java 211 | // An escaping resource, shouldn't close 212 | public BugReportAttachment createAttachment(File reportDirectory, String fileName) 213 | throws FileNotFoundException { 214 | File file = new File(reportDirectory, fileName); 215 | OutputStream stream = new FileOutputStream(file); 216 | return new BugReportAttachment(Uri.fromFile(file), stream); 217 | } 218 | ``` 219 | In this case it is intended that an object that wraps `stream` is passed to the caller of `createAttachment`. 220 | You should certainly not close stream here, because it is being passed to the outside. 221 | 222 | But for escaping resources like this you still need to be careful of exceptions. For example, in 223 | 224 | ```java 225 | // An escaping resource, and a leak 226 | public BugReportAttachment createAttachment(File reportDirectory, String fileName) 227 | throws FileNotFoundException { 228 | File file = new File(reportDirectory, fileName); 229 | OutputStream stream = new FileOutputStream(file); 230 | stream.write(7); 231 | return new BugReportAttachment(Uri.fromFile(file), stream); 232 | } 233 | ``` 234 | 235 | if stream.write(7) throws an exception, then no one will have a hold of stream, and no one will be able to close it; a leak. 236 | 237 | ### Java 7's try-with-resources 238 | 239 | **(For use only if you have are using Java 7)** 240 | 241 | Clearly, accounting for the ramifications of all the exceptional cases is complicated, and there is a better way in Java 7. 242 | 243 | ```java 244 | // Two Resources Fix 2; via try-with-resources 245 | public static void foo() throws IOException { 246 | try ( 247 | FileInputStream fis = new FileInputStream(new File("whatever.txt")); 248 | FileOutputStream fos = new FileOutputStream(new File("everwhat.txt")) 249 | ) { 250 | fos.write(fis.read()); 251 | } 252 | } 253 | ``` 254 | 255 | All the complicated exceptional cases above are (apparently) covered by this construct, and the result is much simpler. 256 | 257 | So, if you are trying to fix a potential leak in code with multiples resources you can go ahead and try to understand whether the potential leak is real. 258 | Or, if the code is complex and it is hard to figure out, it would be perfectly legitimate to simply convert the code over to 259 | try-with-resources if you have access to Java 7, so as to save yourself some brain-cycles. You will also end up with cleaner code. 260 | 261 | If try-with-resources is so great you should always use it. But you shouldn't… Try-with-resources gives resources static scoping, and works via a stack discipline. Sometimes, you want a resource to persist beyond scope, 262 | as in the escaping example above. 263 | In an escaping example maybe you could refactor lots of code so that try-with-resources applies, and maybe you cannot in a sensible way. 264 | This just illustrates that, though you might hear people say that try-with-resources "solves" the resource problem, it does not. It is very useful, but you cannot use it blindly 265 | when you see a resource-allocation site. 266 | 267 | ## Memory leak 268 | 269 | 270 | ###Memory leak in C 271 | 272 | This error type is only reported in C and Objective-C code. In Java we do not report memory leaks because it is a garbage collected language. 273 | 274 | In C, Infer reports memory leaks when objects are created with `malloc` and not freed. For example: 275 | 276 | ```c 277 | -(void) memory_leak_bug { 278 | struct Person *p = malloc(sizeof(struct Person)); 279 | } 280 | ``` 281 | 282 | ### Memory leak in Objective-C 283 | 284 | Additionally, in Objective-C, Infer reports memory leaks that happen when objects from Core Foundation or Core Graphics don't get released. 285 | 286 | ```objc 287 | -(void) memory_leak_bug_cf { 288 | CGPathRef shadowPath = CGPathCreateWithRect(self.inputView.bounds, NULL); //object created and not released. 289 | } 290 | ``` 291 | ## Retain cycle 292 | 293 | 294 | A retain cycle is a situation when object A retains object B, and object B retains object A at the same time. Here is an example: 295 | 296 | ```objc 297 | @class Child; 298 | @interface Parent : NSObject { 299 | Child *child; // Instance variables are implicitly __strong 300 | } 301 | @end 302 | @interface Child : NSObject { 303 | Parent *parent; 304 | } 305 | @end 306 | ``` 307 | 308 | You can fix a retain cycle in ARC by using __weak variables or weak properties for your "back links", i.e. links to direct or indirect parents in an object hierarchy: 309 | 310 | ```objc 311 | @class Child; 312 | @interface Parent : NSObject { 313 | Child *child; 314 | } 315 | @end 316 | @interface Child : NSObject { 317 | __weak Parent *parent; 318 | } 319 | @end 320 | ``` 321 | ## Null Dereference 322 | 323 | 324 | Infer reports null dereference bugs in C, Objective-C and Java. The issue is about a pointer that can be `null` 325 | and it is dereferenced. This leads to a crash in all the above languages. 326 | 327 | ### Null dereference in C 328 | 329 | Here is an example of an inter-procedural null dereference bug in C: 330 | 331 | ```c 332 | struct Person { 333 | int age; 334 | int height; 335 | int weight; 336 | }; 337 | int get_age(struct Person *who) { 338 | return who->age; 339 | } 340 | int null_pointer_interproc() { 341 | struct Person *joe = 0; 342 | return get_age(joe); 343 | } 344 | ``` 345 | 346 | ### Null dereference in Objective-C 347 | 348 | In Objective-C, null dereferences are less common than in Java, but they still happen and their cause can be hidden. 349 | In general, passing a message to nil does not cause a crash and returns `nil`, but dereferencing a pointer directly 350 | does cause a crash as well as calling a `nil` block. 351 | 352 | ```objc 353 | -(void) foo:(void (^)())callback { 354 | callback(); 355 | } 356 | 357 | -(void) bar { 358 | [self foo:nil]; //crash 359 | } 360 | ``` 361 | 362 | Moreover, there are functions from the libraries that do not allow `nil` to 363 | be passed as argument. Here are some examples: 364 | 365 | 366 | ```objc 367 | -(void) foo { 368 | NSString *str = nil; 369 | NSArray *animals = @[@"horse", str, @"dolphin"]; //crash 370 | } 371 | 372 | -(void) bar { 373 | CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); //can return NULL 374 | ... 375 | CFRelease(colorSpace); //crashes if called with NULL 376 | } 377 | ``` 378 | 379 | ### Null dereference in Java 380 | 381 | Many of Infer's reports of potential NPE's come from code of the form 382 | 383 | ```java 384 | p = foo(); // foo() might return null 385 | stuff(); 386 | p.goo(); // dereferencing p, potential NPE 387 | ``` 388 | 389 | If you see code of this form, then you have several options. 390 | 391 | If you are unsure whether or not foo() will return null , you should ideally 392 | i. Change the code to ensure that foo() can not return null 393 | ii. Add a check for whether p is null, and do something other than dereferencing p when it is null. 394 | 395 | Sometimes, in case ii it is not obvious what you should do when p is null. One possibility (a last option) is to throw an exception, failing early. 396 | This can be done using checkNotNull as in the following code: 397 | 398 | ```java 399 | // code idiom for failing early 400 | 401 | import static com.google.common.base.Preconditions.checkNotNull; 402 | 403 | //... intervening code 404 | 405 | p = checkNotNull(foo()); // foo() might return null 406 | stuff(); 407 | p.goo(); // dereferencing p, potential NPE 408 | ``` 409 | 410 | The call checkNotNull(foo()) will never return null; in case foo() returns null it fails early by throwing an NPE. 411 | 412 | If you are absolutely sure that foo() will not be null , then if you land your diff this case will no longer be reported after your diff makes it to master. In the future we might include analysis directives (hey, analyzer, p is not null!) like in Hack that tell the analyzer 413 | the information that you know, but that is for later. 414 | 415 | ## Parameter not null checked 416 | 417 | This error type is reported only in Objective-C. It is similar to Null dereference, but Infer hasn't found a whole trace where the error can happen, but only found that a null dereference can happen if you call a method with nil as an argument. Therefore it is only a warning. For example: 418 | 419 | ```objc 420 | -(int) foo:(A* a) { 421 | B b* = [a foo]; // sending a message with receiver nil returns nil 422 | return b->x; // dereferencing b, potential NPE if you pass nil as the argument a. 423 | } 424 | ``` 425 | 426 | or when the parameter is a block: 427 | 428 | ```objc 429 | -(void) foo:(void (^)(BOOL))block { 430 | block(YES); // calling a nil block will cause a crash. 431 | } 432 | ``` 433 | 434 | Possible solutions are adding a check for `nil`, or making sure that the method is not called with `nil`. 435 | 436 | ## Ivar not null checked 437 | 438 | This error type is only reported in Objective-C. This is similar to Null dereference, but Infer hasn't found a whole trace where the error can happen, but only found that a null dereference can happen if an instance variable of a parameter is `nil`. For example: 439 | 440 | ```objc 441 | -(int) foo { 442 | B b* = [self->_a foo]; // sending a message with receiver nil returns nil 443 | return b->x; // dereferencing b, potential NPE if you pass nil as the argument a. 444 | } 445 | ``` 446 | 447 | Possible solutions are adding a check for `nil`, or making sure that the method is not called with `nil`. 448 | 449 | ## Premature nil termination argument 450 | 451 | This error type is reported in C and Objective-C. In many variadic methods, `nil` is used to signify the end of the list of input objects. This is similar to nil-termination of C strings. If one of the arguments that is not the last argument to the method is `nil` as well, Infer reports an error because that may lead to unexpected behavior. 452 | 453 | An example of such variadic methods is [arrayWithObjects](https://developer.apple.com/library/prerelease/ios/documentation/Cocoa/Reference/Foundation/Classes/NSArray_Class/index.html#//apple_ref/occ/clm/NSArray/arrayWithObjects) 454 | 455 | ```objc 456 | NSArray *foo = [NSArray arrayWithObjects: @"aaa", str, @"bbb", nil]; 457 | ``` 458 | 459 | In this example, if `str` is `nil` then an array `@[@"aaa"]` of size 1 will be created, and not an array `@[@"aaa", str, @"bbb"]` of size 3 as expected. 460 | 461 | -------------------------------------------------------------------------------- /_includes/blog_pagination.html: -------------------------------------------------------------------------------- 1 | 2 | {% if paginator.total_pages > 1 %} 3 |
4 | 27 |
28 | {% endif %} 29 | 30 | -------------------------------------------------------------------------------- /_includes/content/gridblocks.html: -------------------------------------------------------------------------------- 1 |
2 | {% for item in {{include.data_source}} %} 3 | {% include content/items/gridblock.html item=item gridtype=include.grid_type %} 4 | {% cycle '', '
' %} 5 | {% endfor %} 6 |
-------------------------------------------------------------------------------- /_includes/content/items/gridblock.html: -------------------------------------------------------------------------------- 1 |
2 | {% if item.image %} 3 | {{ item.title }} 4 | {% endif %} 5 |

{{ item.title }}

6 | {{ item.text | markdownify }} 7 |
-------------------------------------------------------------------------------- /_includes/doc.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |

{% if include.truncate %}{{ page.title }}{% else %}{{ page.title }}{% endif %}

4 |
5 | 6 |
7 | {% if include.truncate %} 8 | {% if page.content contains '' %} 9 | {{ page.content | split:'' | first }} 10 | 15 | {% else %} 16 | {{ page.content }} 17 | {% endif %} 18 | {% else %} 19 | {{ content }} 20 | {% endif %} 21 |
22 | {% include doc_paging.html %} 23 |
-------------------------------------------------------------------------------- /_includes/doc_paging.html: -------------------------------------------------------------------------------- 1 | {% assign doc_groups = site.docs | sort: "section_order" | group_by: "section" %} 2 | {% assign ordered_docs = site.docs | sort: "section_order" | group_by: "section" %} 3 | {% for group in doc_groups %} 4 | {% assign gindex = forloop.index0 %} 5 | {% assign gitems = group.items | sort: "order" %} 6 | {% for doc_hash in gitems %} 7 | {% if doc_hash.id == page.id %} 8 | {% assign index = forloop.index0 %} 9 | {% assign groupindex = gindex %} 10 | {% endif %} 11 | {% endfor %} 12 | {% endfor %} 13 | {% if index %} 14 |
15 | {% assign next = index | plus: 1 %}{% assign prev = index | minus: 1 %} 16 | {% assign nextgroup = groupindex | plus: 1 %}{% assign prevgroup = groupindex | minus: 1 %} 17 | {% assign groupitems = doc_groups[groupindex].items | sort: "order" %} 18 | {% if doc_groups[prevgroup].items %} 19 | {% assign prevgroupitems = doc_groups[prevgroup].items | sort: "order" %} 20 | {% endif %} 21 | {% if doc_groups[nextgroup].items %} 22 | {% assign nextgroupitems = doc_groups[nextgroup].items | sort: "order" %} 23 | {% endif %} 24 | {% assign prevdoc = groupitems[prev] %}{% assign nextdoc = groupitems[next] %} 25 | {% if prevdoc and prev >= 0 %} 26 | 27 | {% elsif prevgroupitems and prevgroup >= 0 %} 28 | {% assign prevdoc = prevgroupitems.last %} 29 | 30 | {% endif %} 31 | {% if nextdoc %} 32 | 33 | {% elsif nextgroupitems %} 34 | {% assign nextdoc = nextgroupitems.first %} 35 | 36 | {% endif %} 37 |
38 | {% endif %} -------------------------------------------------------------------------------- /_includes/footer.html: -------------------------------------------------------------------------------- 1 |
2 | 8 |
9 | 18 | 19 | -------------------------------------------------------------------------------- /_includes/head.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | {% if page.title %}{{ page.title }}{% else %}{{ site.title }}{% endif %} 25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /_includes/header.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 | 5 |

{{ site.title }}

6 |

{{ site.tagline }}

7 | 8 |
9 |

{% if page.excerpt %}{{ page.excerpt | strip_html }}{% else %}{{ site.description }}{% endif %}

10 |
11 |
12 | {% for promo in site.data.promo %} 13 | {% include plugins/{{promo.type}}.html button_href=promo.href button_text=promo.text %} 14 |
15 | {% endfor %} 16 |
17 |
18 |
19 |
-------------------------------------------------------------------------------- /_includes/hero.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |

An image management library

5 |
6 | Get Started 7 |
8 |
9 |
10 | -------------------------------------------------------------------------------- /_includes/katex_import.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /_includes/katex_render.html: -------------------------------------------------------------------------------- 1 | 211 | -------------------------------------------------------------------------------- /_includes/nav.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |

{{ site.title }}

4 | 15 | 16 |
17 | 32 | 100 |
101 | -------------------------------------------------------------------------------- /_includes/nav_blog.html: -------------------------------------------------------------------------------- 1 | 2 | 14 | -------------------------------------------------------------------------------- /_includes/nav_docs.html: -------------------------------------------------------------------------------- 1 |
2 |
3 | 4 | 27 | -------------------------------------------------------------------------------- /_includes/plugins/all_share.html: -------------------------------------------------------------------------------- 1 |
2 | {% include plugins/like_button.html %}{% include plugins/twitter_share.html %}{% include plugins/google_share.html %} 3 |
-------------------------------------------------------------------------------- /_includes/plugins/ascii_cinema.html: -------------------------------------------------------------------------------- 1 |
2 | -------------------------------------------------------------------------------- /_includes/plugins/button.html: -------------------------------------------------------------------------------- 1 | {{ include.button_text }} -------------------------------------------------------------------------------- /_includes/plugins/fb_pagelike.html: -------------------------------------------------------------------------------- 1 | 4 | -------------------------------------------------------------------------------- /_includes/plugins/github_star.html: -------------------------------------------------------------------------------- 1 |
2 | Star 3 |
4 | -------------------------------------------------------------------------------- /_includes/plugins/github_watch.html: -------------------------------------------------------------------------------- 1 |
2 | Watch 3 |
4 | -------------------------------------------------------------------------------- /_includes/plugins/google_share.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 | 5 | 6 | -------------------------------------------------------------------------------- /_includes/plugins/group_join.html: -------------------------------------------------------------------------------- 1 | {{ include.button_text }} -------------------------------------------------------------------------------- /_includes/plugins/like_button.html: -------------------------------------------------------------------------------- 1 |
2 | -------------------------------------------------------------------------------- /_includes/plugins/post_social_plugins.html: -------------------------------------------------------------------------------- 1 |
2 | 8 |
15 |
16 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /_includes/plugins/slideshow.html: -------------------------------------------------------------------------------- 1 |
2 | 14 | -------------------------------------------------------------------------------- /_includes/plugins/twitter_follow.html: -------------------------------------------------------------------------------- 1 | 4 | 5 | -------------------------------------------------------------------------------- /_includes/plugins/twitter_share.html: -------------------------------------------------------------------------------- 1 |
2 | 3 |
4 | -------------------------------------------------------------------------------- /_includes/post.html: -------------------------------------------------------------------------------- 1 |
2 | {% assign author = site.data.authors[page.author] %} 3 |
4 | {% if author.fbid %} 5 |
6 | {{ author.fullname }} 7 |
8 | {% endif %} 9 | {% if author.full_name %} 10 | 11 | {% endif %} 12 |

{% if include.truncate %}{{ page.title }}{% else %}{{ page.title }}{% endif %}

13 | 14 |
15 | 16 |
17 | {% if include.truncate %} 18 | {% if page.content contains '' %} 19 | {{ page.content | split:'' | first }} 20 | 25 | {% else %} 26 | {{ page.content }} 27 | {% endif %} 28 | {% else %} 29 | {{ content }} 30 | {% endif %} 31 |
32 | {% include plugins/all_share.html %} 33 |
-------------------------------------------------------------------------------- /_includes/social_plugins.html: -------------------------------------------------------------------------------- 1 | 7 |
14 | 15 |
16 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /_includes/ui/button.html: -------------------------------------------------------------------------------- 1 | {{ include.button_text }} -------------------------------------------------------------------------------- /_layouts/blog.html: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | sectionid: blog 4 | --- 5 |
6 |
7 | {% include nav_blog.html %} 8 | {{ content }} 9 |
10 |
11 | 12 | -------------------------------------------------------------------------------- /_layouts/default.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | {% include head.html %} 4 | 5 | {% include nav.html alwayson=true %} 6 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /_layouts/doc_page.html: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | --- 4 | 5 |
6 |
7 | {% include nav_docs.html %} 8 | {{ content }} 9 |
10 |
11 | 12 | -------------------------------------------------------------------------------- /_layouts/docs.html: -------------------------------------------------------------------------------- 1 | --- 2 | layout: doc_page 3 | --- 4 | 5 | {% include doc.html %} -------------------------------------------------------------------------------- /_layouts/home.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | {% include head.html %} 4 | 5 | {% include nav.html alwayson=true %} 6 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /_layouts/page.html: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | --- 4 |
5 |
6 | {% include nav_blog.html %} 7 | {{ content }} 8 |
9 |
10 | 11 | -------------------------------------------------------------------------------- /_layouts/plain.html: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | --- 4 | 5 |
6 |
7 | {{ content }} 8 |
9 |
10 | 11 | -------------------------------------------------------------------------------- /_layouts/post.html: -------------------------------------------------------------------------------- 1 | --- 2 | layout: page 3 | --- 4 | 5 | {% include post.html %} 6 | 7 | -------------------------------------------------------------------------------- /_layouts/redirect.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /_layouts/support.html: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | --- 4 | 5 |
6 |
7 | {{ content }} 8 |
9 |
10 | 11 | -------------------------------------------------------------------------------- /_posts/2015-05-22-Infer-on-open-source-android-apps.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Infer on Open Source Android Apps 3 | layout: post 4 | author: dulmarod 5 | category: blog 6 | --- 7 | We ran Infer on a few open source Android apps with the aim of finding some bugs 8 | and getting them fixed. Some of those reports got indeed fixed. 9 | 10 | One of the apps analyzed was the search engine [DuckDuckGo](https://github.com/duckduckgo/android). We found that many database cursors were not closed. Soon after we reported the issue, a developer [fixed it](https://github.com/duckduckgo/android/commit/2c2d79f990dde0e44cdbecb1925b73c63bf9141d). 11 | 12 | We also analyzed the popular email client [k-9](https://github.com/k9mail/k-9). We found a file not closed leak and reported it. Interestingly, a developer [fixed it](https://github.com/k9mail/k-9/commit/d538278be62687758c956af62ee47c53637d67d8) by not writing some logging info to the file at all. So Infer helped them to simplify their code. 13 | 14 | [Conversations](https://github.com/siacs/Conversations) is an open source XMPP/Jabber client for Android smart phones. We analyzed it as well and found a file not closed leak, which was also [fixed](https://github.com/Flowdalic/MemorizingTrustManager/commit/190c57a9a8385f4726c817924b123438af6adc2f). 15 | -------------------------------------------------------------------------------- /_sass/_base.scss: -------------------------------------------------------------------------------- 1 | body { 2 | background: $footer-bg; 3 | color: $text; 4 | font: 300 #{$base-font-size}/#{$base-line-height} $base-font-family; 5 | text-align: center; 6 | text-rendering: optimizeLegibility; 7 | } 8 | 9 | img { 10 | max-width: 100%; 11 | } 12 | 13 | a { 14 | border-bottom: 1px dotted $primary-bg; 15 | color: $text; 16 | text-decoration: none; 17 | -webkit-transition: background 0.3s, color 0.3s; 18 | transition: background 0.3s, color 0.3s; 19 | } 20 | 21 | #fb_oss a { 22 | border: 0; 23 | } 24 | 25 | h1, h2, h3, h4 { 26 | font-family: $header-font-family; 27 | } 28 | 29 | .navPusher { 30 | ol { 31 | list-style: decimal; 32 | } 33 | 34 | ul { 35 | list-style: disc; 36 | } 37 | 38 | ol, ul { 39 | padding-left: 24px; 40 | 41 | li { 42 | padding-bottom: 4px; 43 | padding-left: 6px; 44 | } 45 | } 46 | 47 | strong { 48 | font-weight: bold; 49 | } 50 | } 51 | 52 | .fixedHeaderContainer { 53 | background: $nav-bg; 54 | color: $header-text; 55 | height: $header-height; 56 | opacity: 0.0; 57 | padding: $header-ptop 0 $header-pbot; 58 | position: fixed; 59 | -webkit-transition: opacity 0.2s ease-in-out; 60 | transition: opacity 0.2s ease-in-out; 61 | width: 100%; 62 | z-index: 9999; 63 | 64 | &.visible { 65 | opacity: 1.0; 66 | } 67 | 68 | a { 69 | border: 0; 70 | color: $header-text; 71 | } 72 | 73 | header { 74 | height: $header-height; 75 | margin: 0 auto; 76 | max-width: $content-width; 77 | padding: 0 10px; 78 | position: relative; 79 | text-align: left; 80 | 81 | img { 82 | height: 30px; 83 | margin-right: 10px; 84 | } 85 | 86 | h2 { 87 | display: inline; 88 | font-family: $header-font-family; 89 | font-size: 16px; 90 | letter-spacing: -0.02em; 91 | line-height: 18px; 92 | position: relative; 93 | top: -9px; 94 | } 95 | } 96 | 97 | .navigationWrapper { 98 | display: inline-block; 99 | font-family: $header-font-family; 100 | 101 | &.navigationFull { 102 | display: none; 103 | } 104 | 105 | &.navigationSlider { 106 | position: absolute; 107 | right: 12px; 108 | 109 | .navSlideout { 110 | cursor: pointer; 111 | font-size: 24px; 112 | padding-top: 2px; 113 | -webkit-transition: -webkit-transform 0.3s; 114 | transition: transform 0.3s; 115 | 116 | &.navSlideoutActive { 117 | transform: rotate(90deg); 118 | -webkit-transform: rotate(90deg); 119 | } 120 | } 121 | 122 | .slidingNav { 123 | background: #222; 124 | box-sizing: border-box; 125 | height: 100vh; 126 | opacity: 0; 127 | overflow: hidden; 128 | padding: 0; 129 | position: absolute; 130 | right: -12px; 131 | top: $header-height + $header-pbot; 132 | -webkit-transition: opacity 0.5s, width 0.5s; 133 | transition: opacity 0.5s, width 0.5s; 134 | width: 0; 135 | 136 | &.slidingNavActive { 137 | opacity: 1; 138 | width: 75vw; 139 | } 140 | 141 | ul { 142 | list-style: none; 143 | padding-left: 0; 144 | 145 | li { 146 | padding: 0; 147 | a { 148 | border-bottom: 1px solid #111; 149 | display:block; 150 | padding: 4vw 12px; 151 | -webkit-transition: background-color 0.3s; 152 | transition: background-color 0.3s; 153 | 154 | &:focus, 155 | &:hover { 156 | background: $primary-bg; 157 | } 158 | } 159 | } 160 | } 161 | } 162 | } 163 | } 164 | } 165 | 166 | .navPusher { 167 | border-top: $header-height + $header-ptop + $header-pbot solid $primary-bg; 168 | position: relative; 169 | left: 0; 170 | z-index: 99; 171 | height: 100%; 172 | 173 | &::after { 174 | position: absolute; 175 | top: 0; 176 | right: 0; 177 | width: 0; 178 | height: 0; 179 | background: rgba(0,0,0,0.4); 180 | content: ''; 181 | opacity: 0; 182 | -webkit-transition: opacity 0.5s, width 0.1s 0.5s, height 0.1s 0.5s; 183 | transition: opacity 0.5s, width 0.1s 0.5s, height 0.1s 0.5s; 184 | } 185 | 186 | .sliderActive &::after { 187 | width: 100%; 188 | height: 100%; 189 | opacity: 1; 190 | -webkit-transition: opacity 0.5s; 191 | transition: opacity 0.5s; 192 | } 193 | } 194 | 195 | .headerContainer { 196 | background: $primary-bg; 197 | color: $header-text; 198 | 199 | a { 200 | color: $header-text; 201 | } 202 | 203 | .headerWrapper { 204 | padding-top: 2em; 205 | 206 | h1#project_title { 207 | font-family: $header-font-family; 208 | font-size: 300%; 209 | letter-spacing: -0.08em; 210 | line-height: 1em; 211 | margin-bottom: 80px; 212 | } 213 | 214 | h2#project_tagline { 215 | font-family: $header-font-family; 216 | font-size: 200%; 217 | letter-spacing: -0.04em; 218 | line-height: 1em; 219 | } 220 | } 221 | } 222 | 223 | .wrapper { 224 | margin: 0px auto; 225 | max-width: $content-width; 226 | padding: 0 20px; 227 | } 228 | 229 | .footerContainer { 230 | background: $footer-bg; 231 | 232 | .footerWrapper { 233 | padding-top: 4vh; 234 | padding-bottom: 4vh; 235 | } 236 | } 237 | 238 | img.projectLogo { 239 | height: 100px; 240 | margin-bottom: 0px; 241 | } 242 | 243 | section#intro { 244 | margin: 70px 0; 245 | } 246 | 247 | .fbossFontLight { 248 | font-family: $base-font-family; 249 | font-weight: 300; 250 | font-style: normal; 251 | } 252 | 253 | .fb-like { 254 | display: block; 255 | margin-bottom: 50px; 256 | width: 100%; 257 | } 258 | 259 | a.blockButton { 260 | background: $primary-bg; 261 | border-radius: 4px; 262 | border: 2px solid transparent; 263 | clear: both; 264 | color: $header-text; 265 | display: inline-block; 266 | font-family: $header-font-family; 267 | font-size: 120%; 268 | padding: 10px 20px; 269 | position: relative; 270 | -webkit-transition: background-color 0.2s, color 0.2s, border 0.2s; 271 | transition: background-color 0.2s, color 0.2s, border 0.2s; 272 | 273 | .mainContainer .mainWrapper &:hover, 274 | .mainContainer .mainWrapper &:focus { 275 | background: $secondary-bg; 276 | border: 2px solid $primary-bg; 277 | color: $primary-bg; 278 | } 279 | 280 | .headerContainer &{ 281 | background: $light-color; 282 | color: $light-text-color; 283 | 284 | &:hover, 285 | &:focus { 286 | background: $primary-bg; 287 | border: 2px solid $light-color; 288 | color: $light-text-color; 289 | } 290 | } 291 | } 292 | 293 | 294 | .promoSection { 295 | margin: -25px 0 0; 296 | text-align: center; 297 | 298 | .pluginBlock { 299 | display: inline-block; 300 | margin: 25px 0; 301 | 302 | &.fb_iframe_widget_fluid { 303 | display: inline-block; 304 | } 305 | 306 | &:last-of-type:not(.slideshowBlock) { 307 | margin-bottom: 50px; 308 | } 309 | 310 | &.allShareBlock { 311 | .pluginBlock { 312 | margin: 0 12px; 313 | width: auto; 314 | 315 | &.fb-like { 316 | top: -5px; 317 | } 318 | } 319 | } 320 | } 321 | } 322 | 323 | .center { 324 | display: block; 325 | text-align: center; 326 | } 327 | 328 | .mainContainer { 329 | background: $secondary-bg; 330 | overflow: auto; 331 | padding: 0 4vw; 332 | 333 | .mainWrapper { 334 | padding-bottom: 4vh; 335 | padding-top: 4vh; 336 | text-align: left; 337 | 338 | &.homeWrapper { 339 | font-size: 120%; 340 | } 341 | 342 | .blockButton { 343 | margin: 4vh 0; 344 | 345 | &.marginsmall { 346 | margin: 1vh 0; 347 | } 348 | } 349 | 350 | .allShareBlock { 351 | margin: -12px; 352 | padding: 1vh 0; 353 | 354 | .pluginBlock { 355 | margin: 12px; 356 | padding: 0; 357 | } 358 | } 359 | 360 | a { 361 | &:hover, 362 | &:focus { 363 | background: $primary-bg; 364 | color: $header-text; 365 | } 366 | } 367 | 368 | em { 369 | font-style: italic; 370 | } 371 | 372 | strong, b { 373 | font-weight: bold; 374 | } 375 | 376 | h1 { 377 | font-size: 300%; 378 | line-height: 1em; 379 | padding: 1.4em 0 1em; 380 | text-align: center; 381 | } 382 | 383 | h2 { 384 | font-size: 250%; 385 | line-height: 1em; 386 | padding: 1.4em 0 1em; 387 | text-align: left; 388 | } 389 | 390 | h3 { 391 | font-size: 150%; 392 | line-height: 1em; 393 | padding: 1em 0 0.8em; 394 | } 395 | 396 | p { 397 | padding: 0.8em 0; 398 | } 399 | 400 | nav.toc { 401 | position: relative; 402 | section { 403 | background: $primary-bg; 404 | color: $header-text; 405 | margin-bottom: 2vh; 406 | padding: 12px 24px; 407 | 408 | .navGroup { 409 | h3 { 410 | display: none; 411 | } 412 | 413 | &:not(.navGroupActive) { 414 | display: none; 415 | } 416 | } 417 | } 418 | 419 | .toggleNav { 420 | position: relative; 421 | } 422 | 423 | .navToggle { 424 | background: $primary-bg; 425 | color: $header-text; 426 | height: 24px; 427 | position: absolute; 428 | right: 24px; 429 | text-align: center; 430 | top: 20px; 431 | width: 24px; 432 | 433 | i { 434 | cursor: pointer; 435 | -webkit-transition: -webkit-transform 0.5s; 436 | transition: transform 0.5s; 437 | } 438 | 439 | &.navToggleActive i { 440 | transform: rotate(180deg); 441 | -webkit-transform: rotate(180deg); 442 | } 443 | } 444 | 445 | .toggleNavActive { 446 | section { 447 | .navGroup { 448 | display: block; 449 | 450 | h3 { 451 | display: block; 452 | } 453 | 454 | ul li { 455 | display: block; 456 | } 457 | } 458 | 459 | ul li { 460 | display: block; 461 | padding-bottom: 4px; 462 | } 463 | } 464 | } 465 | 466 | h3 { 467 | font-size: 125%; 468 | padding-right: 24px; 469 | padding-top: 0.4em; 470 | } 471 | 472 | ul { 473 | padding-left: 0; 474 | padding-right: 24px; 475 | 476 | li { 477 | list-style-type: none; 478 | padding-bottom: 0; 479 | padding-left: 0; 480 | 481 | &:not(.navListItemActive) { 482 | display: none; 483 | } 484 | 485 | a { 486 | border-bottom: none; 487 | border-left: 4px solid transparent; 488 | color: $header-text; 489 | display: inline-block; 490 | margin-bottom: 0.6vh; 491 | padding: 1vh 0 0.4vh 8px; 492 | -webkit-transition: border-color 0.3s; 493 | transition: border-color 0.3s; 494 | 495 | &:hover, 496 | &:focus { 497 | border-left: 4px solid $nav-text; 498 | } 499 | 500 | &.navItemActive { 501 | border-left: 4px solid $header-text; 502 | } 503 | } 504 | } 505 | } 506 | } 507 | 508 | .post { 509 | background: #fff; 510 | padding: 6vw 6vw 8vh; 511 | position: relative; 512 | 513 | a { 514 | color: $link-color; 515 | 516 | &:hover, 517 | &:focus { 518 | color: #fff; 519 | } 520 | } 521 | 522 | h2 { 523 | border-bottom: 4px solid $primary-bg; 524 | font-size: 130%; 525 | } 526 | 527 | h3 { 528 | border-bottom: 1px solid $primary-bg; 529 | font-size: 110%; 530 | } 531 | 532 | .authorPhoto { 533 | border-radius: 50%; 534 | height: 50px; 535 | left: 50%; 536 | margin-left: -25px; 537 | overflow: hidden; 538 | position: absolute; 539 | top: -25px; 540 | width: 50px; 541 | } 542 | 543 | .post-header { 544 | padding: 0 0 1em; 545 | text-align: center; 546 | 547 | h1 { 548 | font-size: 150%; 549 | line-height: 1em; 550 | padding: 0.4em 0 0; 551 | 552 | a { 553 | border: none; 554 | } 555 | } 556 | 557 | .post-authorName { 558 | color: $primary-bg; 559 | font-family: $header-font-family; 560 | margin-top: -2vw; 561 | text-align: center; 562 | } 563 | 564 | .post-meta { 565 | color: $primary-bg; 566 | font-family: $header-font-family; 567 | text-align: center; 568 | } 569 | } 570 | 571 | .postSocialPlugins { 572 | padding-top: 1em; 573 | } 574 | 575 | .docPagination { 576 | background: $primary-bg; 577 | bottom: 0px; 578 | left: 0px; 579 | position: absolute; 580 | right: 0px; 581 | 582 | .pager { 583 | display: inline-block; 584 | width: 50%; 585 | } 586 | 587 | .pagingNext { 588 | float: right; 589 | text-align: right; 590 | } 591 | 592 | a { 593 | border: none; 594 | color: $header-text; 595 | display: block; 596 | padding: 4px 12px; 597 | 598 | &:hover { 599 | background-color: $secondary-bg; 600 | color: $text; 601 | } 602 | 603 | .pagerLabel { 604 | display: inline; 605 | } 606 | 607 | .pagerTitle { 608 | display: none; 609 | } 610 | } 611 | } 612 | } 613 | 614 | .posts { 615 | .post { 616 | margin-bottom: 6vh; 617 | } 618 | } 619 | } 620 | } 621 | 622 | .gridBlock { 623 | margin: 20px 0; 624 | padding: 2vh 0; 625 | 626 | .twoByGridBlock { 627 | padding: 0; 628 | 629 | img { 630 | margin-top: 6vh; 631 | max-width: 100%; 632 | } 633 | 634 | &.featureBlock h3 { 635 | font-size: 150%; 636 | margin: 20px 0; 637 | padding-bottom: 0; 638 | } 639 | } 640 | 641 | .gridClear { 642 | clear: both; 643 | } 644 | 645 | .leftBlock { 646 | padding: 40px 0; 647 | text-align: left; 648 | } 649 | } 650 | 651 | #integrations_title { 652 | font-size: 250%; 653 | margin: 80px 0; 654 | } 655 | 656 | .ytVideo { 657 | height: 0; 658 | overflow: hidden; 659 | padding-bottom: 53.4%; /* 16:9 */ 660 | padding-top: 25px; 661 | position: relative; 662 | } 663 | 664 | .ytVideo iframe, 665 | .ytVideo object, 666 | .ytVideo embed { 667 | height: 100%; 668 | left: 0; 669 | position: absolute; 670 | top: 0; 671 | width: 100%; 672 | } 673 | 674 | @media only screen and (min-width: 480px) { 675 | h1#project_title { 676 | font-size: 500%; 677 | } 678 | 679 | h2#project_tagline { 680 | font-size: 250%; 681 | } 682 | 683 | img.projectLogo { 684 | margin-bottom: 10px; 685 | height: 200px; 686 | } 687 | 688 | .fixedHeaderContainer { 689 | .navigationWrapper { 690 | &.navigationSlider { 691 | 692 | .slidingNav { 693 | right: -22px; 694 | 695 | &.slidingNavActive { 696 | width: 50vw; 697 | } 698 | 699 | ul { 700 | li { 701 | a { 702 | padding: 1vw 12px; 703 | } 704 | } 705 | } 706 | } 707 | } 708 | } 709 | } 710 | 711 | .headerWrapper { 712 | padding-top: 8em; 713 | } 714 | 715 | .gridBlock { 716 | margin: -20px; 717 | overflow: auto; 718 | padding: 1vh 0; 719 | 720 | .twoByGridBlock { 721 | box-sizing: border-box; 722 | float: left; 723 | padding: 20px; 724 | text-align: center; 725 | width: 50%; 726 | } 727 | 728 | .leftBlock { 729 | padding: 40px 20px; 730 | } 731 | } 732 | 733 | .mainContainer { 734 | .mainWrapper { 735 | nav.toc { 736 | h3 { 737 | padding-top: 0.4em; 738 | } 739 | 740 | ul li a { 741 | margin-bottom: 0; 742 | padding-bottom: 0.2vh; 743 | padding-top: 0; 744 | } 745 | 746 | .navToggle { 747 | top: 12px; 748 | } 749 | } 750 | 751 | .post { 752 | padding: 4vw 4vw 4em; 753 | 754 | h2 { 755 | font-size: 180%; 756 | } 757 | 758 | h3 { 759 | font-size: 120%; 760 | } 761 | 762 | .post-header { 763 | padding: 1em 0; 764 | } 765 | 766 | .docPagination { 767 | a { 768 | .pagerLabel { 769 | display: none; 770 | } 771 | .pagerTitle { 772 | display: inline; 773 | } 774 | } 775 | } 776 | } 777 | } 778 | } 779 | } 780 | 781 | @media only screen and (min-width: 900px) { 782 | .fixedHeaderContainer { 783 | .navigationWrapper { 784 | 785 | nav { 786 | padding: 0 1em; 787 | position: relative; 788 | top: -9px; 789 | 790 | ul { 791 | margin: 0 -0.4em; 792 | li { 793 | padding: 0 0.4em; 794 | display: inline-block; 795 | 796 | a { 797 | border: 0; 798 | color: $nav-text; 799 | 800 | &:hover { 801 | color: $header-text; 802 | } 803 | } 804 | 805 | &.navItemActive { 806 | a { 807 | color: $header-text; 808 | } 809 | } 810 | } 811 | } 812 | } 813 | 814 | &.navigationFull { 815 | display: inline-block; 816 | } 817 | 818 | &.navigationSlider { 819 | display: none; 820 | } 821 | } 822 | } 823 | 824 | .navPusher::after { 825 | display: none; 826 | } 827 | } 828 | 829 | @media only screen and (min-width: 1024px) { 830 | .mainContainer { 831 | .mainWrapper { 832 | nav.toc { 833 | box-sizing: border-box; 834 | display: inline-block; 835 | border-right: 12px solid $secondary-bg; 836 | width: 285px; 837 | vertical-align: top; 838 | 839 | .navToggle { 840 | display: none; 841 | } 842 | 843 | .toggleNav { 844 | section { 845 | .navGroup { 846 | display: block; 847 | 848 | h3 { 849 | display: block; 850 | } 851 | 852 | ul li { 853 | display: block; 854 | } 855 | } 856 | 857 | ul li { 858 | display: block; 859 | padding-bottom: 4px; 860 | } 861 | } 862 | } 863 | ul li a { 864 | font-size: 14px; 865 | padding-bottom: 0.1vh; 866 | } 867 | } 868 | 869 | .post { 870 | box-sizing: border-box; 871 | display: inline-block; 872 | padding: 2vw 24px 4em; 873 | width: 590px; 874 | 875 | .post-header { 876 | h1 { 877 | font-size: 250%; 878 | } 879 | } 880 | } 881 | 882 | .posts { 883 | display: inline-block; 884 | width: 590px; 885 | 886 | .post { 887 | margin-bottom: 4vh; 888 | width: 100%; 889 | } 890 | } 891 | } 892 | } 893 | } 894 | 895 | @media only screen and (min-width: 1040px) { 896 | .mainContainer { 897 | .mainWrapper { 898 | nav.toc { 899 | width: 300px; 900 | } 901 | } 902 | } 903 | } 904 | 905 | @media only screen and (min-width: 1200px) { 906 | .fixedHeaderContainer { 907 | header { 908 | max-width: 1100px; 909 | } 910 | } 911 | 912 | .wrapper { 913 | max-width: 1100px; 914 | } 915 | 916 | .homeWrapper { 917 | max-width: 900px; 918 | } 919 | 920 | .headerWrapper { 921 | max-width: 900px; 922 | } 923 | 924 | .mainContainer .mainWrapper { 925 | .post, .posts { 926 | width: 760px; 927 | } 928 | } 929 | } 930 | 931 | @media only screen and (min-width: 1500px) { 932 | .fixedHeaderContainer { 933 | header { 934 | max-width: 1400px; 935 | } 936 | } 937 | 938 | .wrapper { 939 | max-width: 1400px; 940 | } 941 | 942 | .homeWrapper { 943 | max-width: 900px; 944 | } 945 | 946 | .headerWrapper { 947 | max-width: 900px; 948 | } 949 | 950 | .mainContainer .mainWrapper { 951 | .post, .posts { 952 | width: 1035px; 953 | } 954 | } 955 | } 956 | 957 | -------------------------------------------------------------------------------- /_sass/_reset.scss: -------------------------------------------------------------------------------- 1 | html, body, div, span, applet, object, iframe, 2 | h1, h2, h3, h4, h5, h6, p, blockquote, pre, 3 | a, abbr, acronym, address, big, cite, code, 4 | del, dfn, em, img, ins, kbd, q, s, samp, 5 | small, strike, strong, sub, sup, tt, var, 6 | b, u, i, center, 7 | dl, dt, dd, ol, ul, li, 8 | fieldset, form, label, legend, 9 | table, caption, tbody, tfoot, thead, tr, th, td, 10 | article, aside, canvas, details, embed, 11 | figure, figcaption, footer, header, hgroup, 12 | menu, nav, output, ruby, section, summary, 13 | time, mark, audio, video { 14 | margin: 0; 15 | padding: 0; 16 | border: 0; 17 | font-size: 100%; 18 | font: inherit; 19 | vertical-align: baseline; 20 | } 21 | /* HTML5 display-role reset for older browsers */ 22 | article, aside, details, figcaption, figure, 23 | footer, header, hgroup, menu, nav, section { 24 | display: block; 25 | } 26 | body { 27 | line-height: 1; 28 | } 29 | ol, ul { 30 | list-style: none; 31 | } 32 | blockquote, q { 33 | quotes: none; 34 | } 35 | blockquote:before, blockquote:after, 36 | q:before, q:after { 37 | content: ''; 38 | content: none; 39 | } 40 | table { 41 | border-collapse: collapse; 42 | border-spacing: 0; 43 | } -------------------------------------------------------------------------------- /_sass/_slideshow.scss: -------------------------------------------------------------------------------- 1 | .slideshow { 2 | position: relative; 3 | 4 | .slide { 5 | display: none; 6 | 7 | img { 8 | display: block; 9 | margin: 0 auto; 10 | } 11 | 12 | &.slideActive { 13 | display: block; 14 | } 15 | 16 | a { 17 | border: none; 18 | display: block; 19 | } 20 | } 21 | 22 | .pagination { 23 | display: block; 24 | margin: -10px; 25 | padding: 1em 0; 26 | text-align: center; 27 | width: 100%; 28 | 29 | .pager { 30 | background: transparent; 31 | border: 2px solid rgba(255, 255, 255, 0.5); 32 | border-radius: 50%; 33 | cursor: pointer; 34 | display: inline-block; 35 | height: 12px; 36 | margin: 10px; 37 | transition: background-color 0.3s, border-color 0.3s; 38 | width: 12px; 39 | 40 | &.pagerActive { 41 | background: rgba(255, 255, 255, 0.5); 42 | border-width: 4px; 43 | height: 8px; 44 | width: 8px; 45 | } 46 | } 47 | } 48 | } -------------------------------------------------------------------------------- /_sass/_syntax-highlighting.scss: -------------------------------------------------------------------------------- 1 | /** 2 | * Syntax highlighting styles 3 | */ 4 | .highlight { 5 | background: #f0f0f0; 6 | font-family: monospace; 7 | font-size: 12px; 8 | 9 | .c { color: #998; font-style: italic } // Comment 10 | .err { color: #a61717; background-color: #e3d2d2 } // Error 11 | .k { font-weight: bold } // Keyword 12 | .o { font-weight: bold } // Operator 13 | .cm { color: #998; font-style: italic } // Comment.Multiline 14 | .cp { color: #999; font-weight: bold } // Comment.Preproc 15 | .c1 { color: #998; font-style: italic } // Comment.Single 16 | .cs { color: #999; font-weight: bold; font-style: italic } // Comment.Special 17 | .gd { color: #000; background-color: #fdd } // Generic.Deleted 18 | .gd .x { color: #000; background-color: #faa } // Generic.Deleted.Specific 19 | .ge { font-style: italic } // Generic.Emph 20 | .gr { color: #a00 } // Generic.Error 21 | .gh { color: #999 } // Generic.Heading 22 | .gi { color: #000; background-color: #dfd } // Generic.Inserted 23 | .gi .x { color: #000; background-color: #afa } // Generic.Inserted.Specific 24 | .go { color: #888 } // Generic.Output 25 | .gp { color: #555 } // Generic.Prompt 26 | .gs { font-weight: bold } // Generic.Strong 27 | .gu { color: #aaa } // Generic.Subheading 28 | .gt { color: #a00 } // Generic.Traceback 29 | .kc { font-weight: bold } // Keyword.Constant 30 | .kd { font-weight: bold } // Keyword.Declaration 31 | .kp { font-weight: bold } // Keyword.Pseudo 32 | .kr { font-weight: bold } // Keyword.Reserved 33 | .kt { color: #458; font-weight: bold } // Keyword.Type 34 | .m { color: #099 } // Literal.Number 35 | .s { color: #d14 } // Literal.String 36 | .na { color: #008080 } // Name.Attribute 37 | .nb { color: #0086B3 } // Name.Builtin 38 | .nc { color: #458; font-weight: bold } // Name.Class 39 | .no { color: #008080 } // Name.Constant 40 | .ni { color: #800080 } // Name.Entity 41 | .ne { color: #900; font-weight: bold } // Name.Exception 42 | .nf { color: #900; font-weight: bold } // Name.Function 43 | .nn { color: #555 } // Name.Namespace 44 | .nt { color: #000080 } // Name.Tag 45 | .nv { color: #008080 } // Name.Variable 46 | .ow { font-weight: bold } // Operator.Word 47 | .w { color: #bbb } // Text.Whitespace 48 | .mf { color: #099 } // Literal.Number.Float 49 | .mh { color: #099 } // Literal.Number.Hex 50 | .mi { color: #099 } // Literal.Number.Integer 51 | .mo { color: #099 } // Literal.Number.Oct 52 | .sb { color: #d14 } // Literal.String.Backtick 53 | .sc { color: #d14 } // Literal.String.Char 54 | .sd { color: #d14 } // Literal.String.Doc 55 | .s2 { color: #d14 } // Literal.String.Double 56 | .se { color: #d14 } // Literal.String.Escape 57 | .sh { color: #d14 } // Literal.String.Heredoc 58 | .si { color: #d14 } // Literal.String.Interpol 59 | .sx { color: #d14 } // Literal.String.Other 60 | .sr { color: #009926 } // Literal.String.Regex 61 | .s1 { color: #d14 } // Literal.String.Single 62 | .ss { color: #990073 } // Literal.String.Symbol 63 | .bp { color: #999 } // Name.Builtin.Pseudo 64 | .vc { color: #008080 } // Name.Variable.Class 65 | .vg { color: #008080 } // Name.Variable.Global 66 | .vi { color: #008080 } // Name.Variable.Instance 67 | .il { color: #099 } // Literal.Number.Integer.Long 68 | } 69 | 70 | .mainWrapper > .highlight pre code, 71 | section > .highlight pre code, 72 | table.highlighttable { 73 | background: #f0f0f0; 74 | border-left: 4px solid $primary-bg; 75 | color: #000; 76 | display: block; 77 | font-family: monospace; 78 | font-size: 12px; 79 | margin: 1em 0; 80 | overflow-x: auto; 81 | padding: 12px; 82 | text-align: left; 83 | } 84 | 85 | .highlighttable { 86 | display: block; 87 | box-sizing: border-box; 88 | 89 | .linenos .hljs { 90 | padding: 0 1em 0 0; 91 | } 92 | } 93 | 94 | code { 95 | color: $primary-bg; 96 | font-family: monospace; 97 | } 98 | -------------------------------------------------------------------------------- /blog/all.html: -------------------------------------------------------------------------------- 1 | --- 2 | id: all 3 | layout: blog 4 | category: blog 5 | --- 6 | 7 |
8 |
9 |

All Posts

10 | {% for post in site.posts %} 11 | {% assign author = site.data.authors[post.author] %} 12 |

13 | 14 | {{ post.title }} 15 | 16 | on {{ post.date | date: "%B %e, %Y" }} by {{ author.display_name }} 17 |

18 | {% endfor %} 19 |
20 |
21 | -------------------------------------------------------------------------------- /blog/index.html: -------------------------------------------------------------------------------- 1 | --- 2 | id: blog 3 | title: Blog 4 | layout: blog 5 | category: blog 6 | --- 7 | 8 |
9 | {% for page in site.posts %} 10 | {% include post.html truncate=true %} 11 | {% endfor %} 12 |
13 | -------------------------------------------------------------------------------- /css/main.scss: -------------------------------------------------------------------------------- 1 | --- 2 | # Only the main Sass file needs front matter (the dashes are enough) 3 | --- 4 | @charset "utf-8"; 5 | 6 | 7 | 8 | // Our variables 9 | $base-font-family: "Segoe UI", Helvetica, sans-serif; 10 | $header-font-family: "Gotham Rounded A", "Gotham Rounded B", 'Helvetica Neue', Arial, sans-serif; 11 | $base-font-size: 16px; 12 | $small-font-size: $base-font-size * 0.875; 13 | $base-line-height: 1.4em; 14 | 15 | $spacing-unit: 12px; 16 | 17 | $text: {{ site.color.bodytext }}; 18 | $header-text: {{ site.color.headertext }}; 19 | $primary-bg: {{ site.color.primary }}; 20 | $secondary-bg: {{ site.color.secondary }}; 21 | $light-color: {{ site.color.light }}; 22 | $light-text-color: {{ site.color.lighttext }}; 23 | $nav-bg: {{ site.color.nav }}; 24 | $nav-text: {{ site.color.navtext }}; 25 | $footer-bg: #1F242A; 26 | $link-color: {{ site.color.link }}; 27 | 28 | $header-height: 34px; 29 | $header-ptop: 10px; 30 | $header-pbot: 8px; 31 | 32 | // Width of the content area 33 | $content-width: 900px; 34 | 35 | // Using media queries with like this: 36 | // @include media-query($on-palm) { 37 | // .wrapper { 38 | // padding-right: $spacing-unit / 2; 39 | // padding-left: $spacing-unit / 2; 40 | // } 41 | // } 42 | @mixin media-query($device) { 43 | @media screen and (max-width: $device) { 44 | @content; 45 | } 46 | } 47 | 48 | 49 | 50 | // Import partials from `sass_dir` (defaults to `_sass`) 51 | @import 52 | "reset", 53 | "base", 54 | "slideshow", 55 | "syntax-highlighting" 56 | ; 57 | -------------------------------------------------------------------------------- /docs/index.html: -------------------------------------------------------------------------------- 1 | --- 2 | id: docs 3 | title: Docs 4 | layout: redirect 5 | destination: getting-started.html 6 | --- 7 | -------------------------------------------------------------------------------- /downloads/InferAndroidExample.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liaohuqiu/infer-docs-cn/4084585dd239a53339c3818aff938e18a3ed205b/downloads/InferAndroidExample.tar.gz -------------------------------------------------------------------------------- /downloads/InferCExample.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liaohuqiu/infer-docs-cn/4084585dd239a53339c3818aff938e18a3ed205b/downloads/InferCExample.tar.gz -------------------------------------------------------------------------------- /downloads/InferiOSExample.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liaohuqiu/infer-docs-cn/4084585dd239a53339c3818aff938e18a3ed205b/downloads/InferiOSExample.tar.gz -------------------------------------------------------------------------------- /feed.xml: -------------------------------------------------------------------------------- 1 | --- 2 | layout: null 3 | --- 4 | 5 | 6 | 7 | {{ site.title | xml_escape }} 8 | {{ site.description | xml_escape }} 9 | {{ site.url }}{{ site.baseurl }}/ 10 | 11 | {{ site.time | date_to_rfc822 }} 12 | {{ site.time | date_to_rfc822 }} 13 | Jekyll v{{ jekyll.version }} 14 | {% for post in site.posts limit:10 %} 15 | 16 | {{ post.title | xml_escape }} 17 | {{ post.content | xml_escape }} 18 | {{ post.date | date_to_rfc822 }} 19 | {{ post.url | prepend: site.baseurl | prepend: site.url }} 20 | {{ post.url | prepend: site.baseurl | prepend: site.url }} 21 | {% for tag in post.tags %} 22 | {{ tag | xml_escape }} 23 | {% endfor %} 24 | {% for cat in post.categories %} 25 | {{ cat | xml_escape }} 26 | {% endfor %} 27 | 28 | {% endfor %} 29 | 30 | 31 | -------------------------------------------------------------------------------- /index.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: home 3 | title: Infer 中文 | APP 的静态分析工具 4 | id: home 5 | --- 6 | 7 | ## 什么是 Infer? 8 | 9 | Facebook 的 Infer 是一个静态分析工具。Infer 可以分析 Objective-C, Java 或者 C 代码,报告潜在的问题。 10 | 11 | 任何人都可以使用 Infer 检测应用,这可以将那些严重的 bug 扼杀在发布之前,同时防止应用崩溃和性能低下。 12 | 13 | ![在开发流程中引入 Infer](static/images/Infer-landing.png) 14 | 15 | ## 特性 16 | 17 | ### Android 和 Java 18 | 19 | Infer 可检查 Android 和 Java 代码中的 NullPointException 和 资源泄露。 20 | 21 | ### iOS 22 | 23 | 除了以上,Infer 还可发现 iOS 和 C 代码中的内存泄露。 24 | 25 | ## 谁在使用 Infer? 26 | 27 | Infer 已经成为 Facebook 开发流程的一个环节,包括 Facebook Android 和 iOS 主客户端,Facebook Messenger, Instagram 在内的,以及其他影响亿万用户的手机应用,每次代码变更,都要经过 Infer 的检测。 28 | 29 | ## 使用 Infer 30 | 31 | 从 [开始使用](docs/getting-started.html) 指南和我们的其他 [文档](docs/) 开始,下载并尝试使用 Infer 吧。 32 | 33 | Infer 还在持续改进,我们将会开源状态下,持续开发。 34 | 35 | 我们希望 Infer 对其他项目有所帮助,所以我们希望你可以试着使用它,或者给这个项目添砖加瓦,加入我们的技术社区,给我们反馈吧! 36 | -------------------------------------------------------------------------------- /static/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liaohuqiu/infer-docs-cn/4084585dd239a53339c3818aff938e18a3ed205b/static/favicon.png -------------------------------------------------------------------------------- /static/images/Infer-landing.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liaohuqiu/infer-docs-cn/4084585dd239a53339c3818aff938e18a3ed205b/static/images/Infer-landing.jpg -------------------------------------------------------------------------------- /static/images/Infer-landing.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liaohuqiu/infer-docs-cn/4084585dd239a53339c3818aff938e18a3ed205b/static/images/Infer-landing.png -------------------------------------------------------------------------------- /static/images/SepSplit.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liaohuqiu/infer-docs-cn/4084585dd239a53339c3818aff938e18a3ed205b/static/images/SepSplit.jpg -------------------------------------------------------------------------------- /static/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liaohuqiu/infer-docs-cn/4084585dd239a53339c3818aff938e18a3ed205b/static/logo.png -------------------------------------------------------------------------------- /static/og_image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liaohuqiu/infer-docs-cn/4084585dd239a53339c3818aff938e18a3ed205b/static/og_image.png -------------------------------------------------------------------------------- /static/oss_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liaohuqiu/infer-docs-cn/4084585dd239a53339c3818aff938e18a3ed205b/static/oss_logo.png -------------------------------------------------------------------------------- /static/pygments.css: -------------------------------------------------------------------------------- 1 | .hll { background-color: #ffffcc } 2 | .c { color: #999988; font-style: italic } /* Comment */ 3 | .err { color: #a61717; background-color: #e3d2d2 } /* Error */ 4 | .k { color: #000000; font-weight: bold } /* Keyword */ 5 | .o { color: #000000; } /* Operator */ 6 | .cm { color: #666666; font-weight: bold; font-style: italic } /* Comment.Multiline */ 7 | .cp { color: #666666; font-weight: bold; font-style: italic } /* Comment.Preproc */ 8 | .c1 { color: #666666; font-weight: bold; font-style: italic } /* Comment.Single */ 9 | .cs { color: #666666; font-weight: bold; font-style: italic } /* Comment.Special */ 10 | .gd { color: #000000; background-color: #ffdddd } /* Generic.Deleted */ 11 | .ge { color: #000000; font-style: italic } /* Generic.Emph */ 12 | .gr { color: #aa0000 } /* Generic.Error */ 13 | .gh { color: #999999 } /* Generic.Heading */ 14 | .gi { color: #000000; background-color: #ddffdd } /* Generic.Inserted */ 15 | .go { color: #888888 } /* Generic.Output */ 16 | .gp { color: #555555 } /* Generic.Prompt */ 17 | .gs { font-weight: bold } /* Generic.Strong */ 18 | .gu { color: #aaaaaa } /* Generic.Subheading */ 19 | .gt { color: #aa0000 } /* Generic.Traceback */ 20 | .kc { color: #000000; font-weight: bold } /* Keyword.Constant */ 21 | .kd { color: #000000; font-weight: bold } /* Keyword.Declaration */ 22 | .kn { color: #000000; font-weight: bold } /* Keyword.Namespace */ 23 | .kp { color: #000000; font-weight: bold } /* Keyword.Pseudo */ 24 | .kr { color: #000000; font-weight: bold } /* Keyword.Reserved */ 25 | .kt { color: #445588; font-weight: bold } /* Keyword.Type */ 26 | .m { color: #009999 } /* Literal.Number */ 27 | .s { color: #d01040 } /* Literal.String */ 28 | .na { color: #008080 } /* Name.Attribute */ 29 | .nb { } /* Name.Builtin */ 30 | .nc { color: #445588; font-weight: bold } /* Name.Class */ 31 | .no { color: #008080 } /* Name.Constant */ 32 | .nd { color: #3c5d5d; font-weight: bold } /* Name.Decorator */ 33 | .ni { color: #800080 } /* Name.Entity */ 34 | .ne { color: #990000; font-weight: bold } /* Name.Exception */ 35 | .nf { color: #990000; font-weight: bold } /* Name.Function */ 36 | .nl { color: #990000; font-weight: bold } /* Name.Label */ 37 | .nn { color: #555555 } /* Name.Namespace */ 38 | .nt { color: #000080 } /* Name.Tag */ 39 | .nv { color: #008080 } /* Name.Variable */ 40 | .ow { color: #000000; font-weight: bold } /* Operator.Word */ 41 | .w { color: #bbbbbb } /* Text.Whitespace */ 42 | .mf { color: #009999 } /* Literal.Number.Float */ 43 | .mh { color: #009999 } /* Literal.Number.Hex */ 44 | .mi { color: #009999 } /* Literal.Number.Integer */ 45 | .mo { color: #009999 } /* Literal.Number.Oct */ 46 | .sb { color: #d01040 } /* Literal.String.Backtick */ 47 | .sc { color: #d01040 } /* Literal.String.Char */ 48 | .sd { color: #d01040 } /* Literal.String.Doc */ 49 | .s2 { color: #d01040 } /* Literal.String.Double */ 50 | .se { color: #d01040 } /* Literal.String.Escape */ 51 | .sh { color: #d01040 } /* Literal.String.Heredoc */ 52 | .si { color: #d01040 } /* Literal.String.Interpol */ 53 | .sx { color: #d01040 } /* Literal.String.Other */ 54 | .sr { color: #009926 } /* Literal.String.Regex */ 55 | .s1 { color: #d01040 } /* Literal.String.Single */ 56 | .ss { color: #990073 } /* Literal.String.Symbol */ 57 | .bp { color: #999999 } /* Name.Builtin.Pseudo */ 58 | .vc { color: #008080 } /* Name.Variable.Class */ 59 | .vg { color: #008080 } /* Name.Variable.Global */ 60 | .vi { color: #008080 } /* Name.Variable.Instance */ 61 | .il { color: #009999 } /* Literal.Number.Integer.Long */ 62 | -------------------------------------------------------------------------------- /support.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: support 3 | title: Infer | Need help? 4 | id: support 5 | category: support 6 | --- 7 | 8 | ## Need help? 9 | 10 | Do not hesitate to ask questions using the following channels, or to 11 | submit pull request! 12 | 13 | ### GitHub issues 14 | 15 | The [GitHub issues](https://github.com/facebook/Infer/issues) page is 16 | a good place to ask questions, find answers, and report issues. 17 | 18 | ### Twitter 19 | 20 | Keep up to date with the latest Infer news on 21 | [@fbinfer](https://twitter.com/fbinfer). 22 | 23 | ### IRC 24 | 25 | Our IRC channel is [#infer](irc://chat.freenode.net/infer) on 26 | Freenode.net. 27 | 28 | 29 | ## Troubleshooting 30 | 31 | ### Running "infer -- \" fails. 32 | 33 | Please make sure that: 34 | 35 | - \ runs successfully on its own. 36 | - `infer` is in your `$PATH` (try `which infer`, it should show where `infer` is located) 37 | 38 | ### Running Infer fails with "ImportError: No module named xml.etree.ElementTree" 39 | 40 | Make sure that the `xml` Python package is installed. For instance, on 41 | OpenSuse 13.1, it is provided by the 42 | [`python-xmldiff`](http://software.opensuse.org/download.html?project=XML&package=python-xmldiff) 43 | package. 44 | 45 | ### My problem is not listed here 46 | 47 | Do not hesitate to [contact us](support.html#need-help?). 48 | 49 | 50 | ## FAQ 51 | 52 | Here are some frequently asked questions. More to come. 53 | 54 | ### Is Infer supported for Windows? 55 | 56 | Infer is not supported on Windows at the moment. You may try 57 | installing Infer on a Linux virtual machine. 58 | --------------------------------------------------------------------------------