diff --git a/settings.gradle b/settings.gradle
index afd285d56f972e27d7f51a7a66a8b502dbe4df75..aba0955c8a958936beded53e73cf0147d398ee27 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -11,6 +11,7 @@ rootProject.name = 'sle19-impl'
 
 include 'statemachine'
 include 'dg'
+include 'simplecfg'
 include 'extendj'
 include 'extendj:java4'
 include 'extendj:java5'
diff --git a/simplecfg/.gitignore b/simplecfg/.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..e6c14cd07d2acf3256143cb39267fee74d2265c3
--- /dev/null
+++ b/simplecfg/.gitignore
@@ -0,0 +1,20 @@
+# gradle build
+/build/
+/.gradle/
+
+# generated files
+/*.jar
+/src/gen/
+
+# generated graphs
+/testdata/*.png
+
+# vim
+*.swp
+
+# eclipse
+/.project
+/.classpath
+/.settings/
+/bin/
+
diff --git a/simplecfg/CONTRIBUTING.md b/simplecfg/CONTRIBUTING.md
new file mode 100644
index 0000000000000000000000000000000000000000..1ba853922fb804fd5265e877f3e8a4e1d6615448
--- /dev/null
+++ b/simplecfg/CONTRIBUTING.md
@@ -0,0 +1,24 @@
+Want to contribute? Great! First, read this page (including the small print at the end).
+
+### Before you contribute
+Before we can use your code, you must sign the
+[Google Individual Contributor License Agreement](https://developers.google.com/open-source/cla/individual?csw=1)
+(CLA), which you can do online. The CLA is necessary mainly because you own the
+copyright to your changes, even after your contribution becomes part of our
+codebase, so we need your permission to use and distribute your code. We also
+need to be sure of various other things—for instance that you'll tell us if you
+know that your code infringes on other people's patents. You don't have to sign
+the CLA until after you've submitted your code for review and a member has
+approved it, but you must do it before we can put your code into our codebase.
+Before you start working on a larger contribution, you should get in touch with
+us first through the issue tracker with your idea so that we can help out and
+possibly guide you. Coordinating up front makes it much easier to avoid
+frustration later on.
+
+### Code reviews
+All submissions, including submissions by project members, require review. We
+use Github pull requests for this purpose.
+
+### The small print
+Contributions made by corporations are covered by a different agreement than
+the one above, the Software Grant and Corporate Contributor License Agreement.
diff --git a/simplecfg/LICENSE b/simplecfg/LICENSE
new file mode 100644
index 0000000000000000000000000000000000000000..d645695673349e3947e8e5ae42332d0ac3164cd7
--- /dev/null
+++ b/simplecfg/LICENSE
@@ -0,0 +1,202 @@
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
diff --git a/simplecfg/README.md b/simplecfg/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..bb0a66bcccf5b897fe276bbe28e7cb99814c06cd
--- /dev/null
+++ b/simplecfg/README.md
@@ -0,0 +1,70 @@
+Simple CFG module for ExtendJ
+=============================
+
+This is a Control Flow Graph (CFG) module for the ExtendJ compiler for building
+simplified CFGs. This module builds CFGs for Java methods where only branches
+and method calls have are included.  These simple CFGs provide enough
+information to perform intraprocedural static analyses on Java code.
+
+This repository also includes two sample Java static analyzers based on the this
+CFG module.  One analyzer checks for additional calls to a
+java.io.Reader/java.io.Writer after `close()` was called on the same instance.
+The other analyzer checks for potential `null` dereferences on paramters
+annotated with javax.annotation.Nullable.
+
+Disclaimer
+----------
+
+This is not an official Google product (experimental or otherwise), it is just
+code that happens to be owned by Google.
+
+Shipshape Module
+----------------
+
+The demo analyzers can be plugged into the [Shipshape][1] pipeline. The
+Shipshape integration is currently experimental.
+
+Dependencies
+------------
+
+To build the Simplified CFG generator you need the following dependencies:
+
+* Git
+* Gradle 2.4
+* ExtendJ
+
+This repository has a submodule for the ExtendJ compiler. If you did not clone
+this repository with the `--recursive` flag you will have to run `git submodule
+init` followed by `git submodule update`, this will clone a specific commit from
+the ExtendJ repository into the `third_party/extendj/` directory.
+
+Building
+--------
+
+Note that you must have the Git submodule `third_party/extendj/git` in
+order to build SimpleCFG. To download the submodule, use the following commands:
+
+    git submodule init
+    git submodule update
+
+
+Build the Simplified CFG generator Jar file by running the following Gradle command:
+
+    gradle jar
+
+
+Testing
+-------
+
+The tests may be run by issuing the following command:
+
+    gradle test
+
+Most tests check that a well-formed SimpleCFG is built for each Java file in the
+testdata directory. The tests are structured so that they test the successors of
+each node in the resulting CFG for the single block/method in each of the Java files.
+
+You can generate images for the CFGs in each test file by running the `graph.sh`
+shell script.
+
+[1]: https://github.com/google/shipshape
diff --git a/simplecfg/build.gradle b/simplecfg/build.gradle
new file mode 100644
index 0000000000000000000000000000000000000000..45deaad9613999f038146ab8300b717967db3d1b
--- /dev/null
+++ b/simplecfg/build.gradle
@@ -0,0 +1,107 @@
+buildscript {
+	repositories.mavenLocal()
+	repositories.mavenCentral()
+	dependencies {
+		classpath 'org.jastadd:jastaddgradle:1.13.3'
+	}
+}
+
+apply plugin: 'java'
+apply plugin: 'application'
+apply plugin: 'jastadd'
+apply plugin: 'idea'
+
+repositories {
+	mavenLocal()
+}
+
+idea {
+	module {
+		generatedSourceDirs += file('./src/gen/java')
+	}
+}
+
+//sourceSets {
+//	main {
+//		java.srcDirs "src/gen/java"
+//		java.srcDirs "../extendj/src/frontend"
+//	}
+//}
+sourceSets.main {
+	java {
+
+		srcDirs "src/gen/java"
+		srcDirs '../extendj/src/frontend'
+	}
+	resources {
+		srcDir '../extendj/src/res'
+		srcDir jastadd.buildInfoDir
+	}
+}
+
+dependencies {
+//	compile group: 'com.google.guava', name: 'guava', version: '18.0'
+	testCompile group: 'com.google.truth', name: 'truth', version: '0.27'
+}
+
+jastadd {
+	configureModuleBuild()
+	modules  {
+
+		include("../extendj/jastadd_modules")
+
+		module "simplecfg", {
+
+			imports "java8 frontend"
+
+			jastadd {
+				basedir "src/main/jastadd/"
+				include "**/*.ast"
+				include "**/*.jadd"
+				include "**/*.jrag"
+			}
+
+			java {
+				basedir "src/main/java/"
+				include "**/*.java"
+			}
+
+		}
+
+	}
+
+
+	// Target module to build:
+	module = 'simplecfg'
+
+	astPackage = 'org.extendj.ast'
+	parser.name = 'JavaParser'
+	scanner.name = 'OriginalScanner'
+//
+	genDir = 'src/gen/java'
+
+
+	parser.genDir = 'src/gen/java/org/extendj/parser'
+	scanner.genDir = 'src/gen/java/org/extendj/scanner'
+//
+//	if (project.hasProperty('extraJastAddOptions')) {
+//		extraJastAddOptions += project.extraJastAddOptions.split(',') as List
+//		print("options: ${extraJastAddOptions}")
+//	}
+
+}
+
+
+test {
+	inputs.dir file('testdata')
+}
+
+mainClassName = 'com.google.simplecfg.PrintCfg'
+jar.manifest.attributes 'Main-Class': mainClassName
+jar.destinationDir = projectDir
+
+sourceCompatibility = '1.8'
+targetCompatibility = '1.8'
+
+// fix broken scanner dependencies
+//scanner.outputs.upToDateWhen {false}
diff --git a/simplecfg/gentest.sh b/simplecfg/gentest.sh
new file mode 100755
index 0000000000000000000000000000000000000000..388f72d85e009b96014ee3dba6a59f5824445d65
--- /dev/null
+++ b/simplecfg/gentest.sh
@@ -0,0 +1,29 @@
+#!/bin/bash
+
+# Copyright 2014 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+set -e
+
+if [ $# -lt "1" ]; then
+  echo "Error: no input program specified."
+  exit 1
+else
+  gradle jar
+  for src in "$@"; do
+    java -cp simplecfg.jar com.google.simplecfg.TestGenerator "$src"
+  done
+fi
+
+echo "done"
diff --git a/simplecfg/gradle/wrapper/gradle-wrapper.jar b/simplecfg/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 0000000000000000000000000000000000000000..3ebf88af3b642f4f202c85c16a3815fce3e9590d
Binary files /dev/null and b/simplecfg/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/simplecfg/gradle/wrapper/gradle-wrapper.properties b/simplecfg/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000000000000000000000000000000000000..019065d1d650ce87992f9d60b7a162f7f2f8caf9
--- /dev/null
+++ b/simplecfg/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,5 @@
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.2-all.zip
diff --git a/simplecfg/graph.sh b/simplecfg/graph.sh
new file mode 100755
index 0000000000000000000000000000000000000000..c15fd3c9bbc80e6999f9fa8792cf5d9b885549b9
--- /dev/null
+++ b/simplecfg/graph.sh
@@ -0,0 +1,36 @@
+#!/bin/bash
+
+# Copyright 2015 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+set -e
+
+gradle jar
+
+gengraph()
+{
+    OUT="${1%%\.javax}.png"
+    echo "$1 -> $OUT"
+    java -jar simplecfg.jar "$1" | dot -Tpng > "$OUT"
+}
+
+if [ $# -lt "1" ]; then
+  for f in testdata/*.javax; do
+    gengraph $f
+  done
+else
+  gengraph $1
+fi
+
+echo "done"
diff --git a/simplecfg/modules b/simplecfg/modules
new file mode 100644
index 0000000000000000000000000000000000000000..52ac3fcb925cde71aa8354d5ce0b51c98404e0b2
--- /dev/null
+++ b/simplecfg/modules
@@ -0,0 +1,83 @@
+/**
+ * Copyright 2015 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+
+// // Custom version of the Java 8 frontend without JavaScanner (compile error due to moved class).
+// module("java8 frontend mod") {
+// 	moduleName "Java SE 8"
+// 	moduleVariant "frontend"
+//
+// 	def java8 = "../extendj/java8"
+//
+// 	imports "java7 frontend"
+//
+// 	jastadd {
+// 		basedir java8
+// 		include "grammar/*.ast"
+// 		include "frontend/*.jadd"
+// 		include "frontend/*.jrag"
+//
+// 		excludeFrom "java7 frontend", "frontend/JavaVersion.jrag"
+//
+// 		exclude "frontend/FrontendMain.jrag"
+// 		excludeFrom "java5 frontend", "frontend/BytecodeReader.jrag"
+// 		excludeFrom "java7 frontend", "frontend/Variable.jadd"
+//
+// 	}
+//
+// 	scanner {
+// 		basedir java8
+// 		include "scanner/Separators.flex"
+// 		include "scanner/Operators.flex"
+//
+// 		excludeFrom "java4 frontend", "scanner/Preamble.flex"
+// 	}
+//
+// 	parser {
+// 	    basedir java8
+// 		include "parser/*"
+// 	}
+// }
+
+module("simplecfg") {
+
+	imports "java8 frontend"
+
+	jastadd {
+		basedir "src/main/jastadd/"
+		include "**/*.ast"
+		include "**/*.jadd"
+		include "**/*.jrag"
+	}
+
+	java {
+		basedir "src/main/java/"
+		include "**/*.java"
+	}
+
+	scanner {
+		include "src/main/scanner/Header.flex", [-4]
+		excludeFrom "java4 frontend", "scanner/Header.flex"
+	}
+
+	parser {
+		// Replace parser package declaration.
+		include "src/main/parser/Header.parser", [-2]
+		excludeFrom "java4 frontend", "parser/Header.parser"
+	}
+}
+
diff --git a/simplecfg/src/main/jastadd/AlreadyClosedAnalysis.jrag b/simplecfg/src/main/jastadd/AlreadyClosedAnalysis.jrag
new file mode 100644
index 0000000000000000000000000000000000000000..47d49883d9f55261f2966855698bc59195d3a5cd
--- /dev/null
+++ b/simplecfg/src/main/jastadd/AlreadyClosedAnalysis.jrag
@@ -0,0 +1,90 @@
+/**
+ * Copyright 2015 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Adds an analysis checking for calls to an instance of java.io.Writer or java.io.Reader after
+ * {@code close()} has been called on the same instance.
+ *
+ * <p>For each method call to an effectively final variable of type java.io.Closeable,
+ * we do a breadth-first search on the reverse CFG from the location of that call to see if a
+ * call to {@code close()} was made on the same instance previously.
+ */
+aspect AlreadyClosedAnalysis {
+
+  MethodAccess contributes alreadyClosedFinding()
+      when alreadyClosedStream()
+      to CompilationUnit.findings()
+      for compilationUnit();
+
+  /** Allow MethodAccess to lookup the enclosing compilation unit.  */
+  inh CompilationUnit MethodAccess.compilationUnit();
+
+  /** Generate a finding for method call after close() call.  */
+  syn lazy ExtendJFinding MethodAccess.alreadyClosedFinding() =
+      finding("AlreadyClosed", String.format(
+          "close() may have already been called on %s at this point",
+          prevExpr().prettyPrint()));
+
+  /** Check if the reciever of this method access was already closed. */
+  syn boolean MethodAccess.alreadyClosedStream() {
+    // The receiver must be an effectively final local variable (or parameter).
+    if (name().equals("close") // Don't check for repeated close() calls.
+        || name().equals("toString") || name().equals("toByteArray")
+        || !hasPrevExpr() // Does not have a variable or parameter receiver.
+        || prevExpr().varDecl() == null // Receiver is not a variable/parameter.
+        || !(prevExpr().varDecl().isFinal()
+            || prevExpr().varDecl().isEffectivelyFinal()) // Receiver might have changed.
+        || !prevExpr().type().isCloseable()) { // Receiver is not instance of java.io.Closeable.
+      return false;
+    }
+    final Variable receiver = prevExpr().varDecl();
+    return null != call().reverseBfs(new CfgVisitor() {
+      @Override
+      public SearchAction processEdge(CfgNode pred, CfgNode succ) {
+        if (succ.isCloseCall(receiver)) {
+          if (pred.isException()) {
+            // This call is interrupted by an exception. Don't continue the search from this node.
+            return SearchAction.SKIP;
+          } else {
+            return SearchAction.SUCCESSOR_MATCH;
+          }
+        }
+        if (succ.isCall(receiver)) {
+          // Already analyzed from this point.
+          return SearchAction.SKIP;
+        }
+        if (succ.isDeclarationOf(receiver)) {
+          // Skip this node and don't reconsider any other path that encounters the node.
+          return SearchAction.IGNORE;
+        }
+        return SearchAction.CONTINUE;
+      }
+    });
+  }
+
+  /** Test if the CFG node is a call node with the given variable as receiver. */
+  syn boolean CfgNode.isCall(Variable receiver) = false;
+  eq CfgMethodCall.isCall(Variable receiver) = methodAccess().hasReceiver(receiver);
+
+  /** Test if the CFG node is a call node for receiver.close(). */
+  syn boolean CfgNode.isCloseCall(Variable receiver) = false;
+  eq CfgMethodCall.isCloseCall(Variable receiver) =
+      methodAccess().hasReceiver(receiver) && methodAccess().getID().equals("close");
+
+  /** Check if this is a Reader or Writer.  */
+  syn boolean TypeDecl.isCloseable() =
+      !isUnknown() && instanceOf(lookupType("java.io", "Closeable"));
+}
diff --git a/simplecfg/src/main/jastadd/CFG.ast b/simplecfg/src/main/jastadd/CFG.ast
new file mode 100644
index 0000000000000000000000000000000000000000..b1e7cbe5349021cc8790f4a6ee2613ee6b1eb168
--- /dev/null
+++ b/simplecfg/src/main/jastadd/CFG.ast
@@ -0,0 +1,36 @@
+/**
+ * Copyright 2015 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/** A node in a Control Flow Graph (CFG). */
+abstract CfgNode;
+
+/** The CFG entry node. */
+CfgEntry : CfgNode ::= <Succ:CfgNode>;
+
+/** The CFG exit node. */
+CfgExit : CfgNode;
+
+/** A method call in the CFG. */
+CfgMethodCall : CfgNode;
+
+/** A conditional branch in the CFG.  */
+CfgBranch : CfgNode;
+
+/** A branch in the CFG caused by potential thrown exceptions.  */
+CfgException : CfgNode;
+
+/** A marker node used to mark try block entry points or the end of if-statement branches.  */
+CfgMarker : CfgNode;
diff --git a/simplecfg/src/main/jastadd/CfgSearch.jrag b/simplecfg/src/main/jastadd/CfgSearch.jrag
new file mode 100644
index 0000000000000000000000000000000000000000..e950ffe2f24654847136b0fd7f5b8f8c90a804cd
--- /dev/null
+++ b/simplecfg/src/main/jastadd/CfgSearch.jrag
@@ -0,0 +1,149 @@
+/**
+ * Copyright 2015 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * This aspect adds an API for searching for nodes matching some property in a Control Flow Graph.
+ */
+aspect CfgSearch {
+  enum SearchAction {
+    /**
+     * The search is complete because the currently processed edge successor matches the search.
+     */
+    SUCCESSOR_MATCH,
+    /**
+     * The search is complete because the currently processed edge predecessor matches the search.
+     */
+    PREDECESSOR_MATCH,
+    /**
+     * The search should skip adding the successors from the successor of the current edge.
+     */
+    SKIP,
+    /**
+     * The search continues as normal, adding successors from the target of the current edge.
+     */
+    CONTINUE,
+    /**
+     * The search does not process additional edges to the target of the current edge, and the
+     * successors of the target node are not added to the work queue.
+     */
+    IGNORE
+  }
+
+  /** A CFG visitor decides which nodes to process based on the current edge (pred, succ). */
+  interface CfgVisitor {
+    /**
+     * Returns the action a Breadth-First search should take for this edge.
+     * @param pred Edge source.
+     * @param succ Edge destination.
+     */
+    public SearchAction processEdge(CfgNode pred, CfgNode succ);
+  }
+
+  /**
+   * Performs a Breadth-First Search over the CFG successors starting from this node.
+   * @param visitor a visitor that decides when the search is terminated and which
+   * successors to process
+   * @return {@code null} if no match was found
+   */
+  public CfgNode CfgNode.bfs(CfgVisitor visitor) {
+    Set<CfgNode> visited = Collections.newSetFromMap(
+        new IdentityHashMap<CfgNode, Boolean>());
+    Queue<CfgNode> work = new LinkedList<CfgNode>();
+    work.add(this);
+    while (!work.isEmpty()) {
+      CfgNode node = work.poll();
+      for (CfgNode succ : node.successors()) {
+        if (!visited.contains(succ)) {
+          switch (visitor.processEdge(node, succ)) {
+            case SUCCESSOR_MATCH:
+              return succ;
+            case PREDECESSOR_MATCH:
+              return node;
+            case SKIP:
+              continue;
+            case CONTINUE:
+              work.add(succ);
+              visited.add(succ);
+              break;
+            case IGNORE:
+              visited.add(succ);
+              break;
+          }
+        }
+      }
+    }
+    // The search matched nothing and we exhausted all successors.
+    return null;
+  }
+
+  inh lazy CfgEntry CfgNode.cfg();
+
+  /**
+   * Performs a Breadth-First Search over the CFG predecessors starting from this node.
+   * @param visitor a visitor that decides when the search is terminated and which
+   * predecessors to process
+   * @return {@code null} if no match was found
+   */
+  public CfgNode CfgNode.reverseBfs(CfgVisitor visitor) {
+    Set<CfgNode> visited = Collections.newSetFromMap(
+        new IdentityHashMap<CfgNode, Boolean>());
+    cfg().initPredecessors();
+    Queue<CfgNode> work = new LinkedList<CfgNode>();
+    work.add(this);
+    while (!work.isEmpty()) {
+      CfgNode node = work.poll();
+      for (CfgNode pred : node.predecessors) {
+        if (!visited.contains(pred)) {
+          switch (visitor.processEdge(node, pred)) {
+            case SUCCESSOR_MATCH:
+              return pred;
+            case PREDECESSOR_MATCH:
+              return node;
+            case SKIP:
+              continue;
+            case CONTINUE:
+              work.add(pred);
+              visited.add(pred);
+              break;
+            case IGNORE:
+              visited.add(pred);
+              break;
+          }
+        }
+      }
+    }
+    // The search matched nothing and we exhausted all predecessors.
+    return null;
+  }
+
+  /** A matcher used to search for particular nodes in a CFG. */
+  interface CfgMatcher {
+    public boolean match(CfgNode node);
+  }
+
+  /** Search for a previous node matching the matcher, starting from the current CFG node. */
+  public CfgNode CfgNode.findPreviousNode(final CfgMatcher matcher) {
+    return reverseBfs(new CfgVisitor() {
+      @Override
+      public SearchAction processEdge(CfgNode pred, CfgNode succ) {
+        return matcher.match(succ) ? SearchAction.SUCCESSOR_MATCH : SearchAction.CONTINUE;
+      }
+    });
+  }
+
+  /** Check if there is a previous node matching the matcher, starting from the current CFG node. */
+  syn boolean CfgNode.hasPreviousNode(CfgMatcher matcher) = null != findPreviousNode(matcher);
+}
diff --git a/simplecfg/src/main/jastadd/ClassPathFilter.ast b/simplecfg/src/main/jastadd/ClassPathFilter.ast
new file mode 100644
index 0000000000000000000000000000000000000000..0acfbecbf4c23a749f940a1d7f682e930ef939f8
--- /dev/null
+++ b/simplecfg/src/main/jastadd/ClassPathFilter.ast
@@ -0,0 +1,22 @@
+/**
+ * Copyright 2015 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * PlaceholderTypeDecl is used to avoid locating and parsing source or class files when
+ * analyzing a single file.
+ * Represents a type that is known to exist but which we don't know anything else about.
+ */
+PlaceholderTypeDecl : ClassDecl;
diff --git a/simplecfg/src/main/jastadd/ClassPathFilter.jrag b/simplecfg/src/main/jastadd/ClassPathFilter.jrag
new file mode 100644
index 0000000000000000000000000000000000000000..33e9b33fd79d0e27f80816cf444966374a6998c3
--- /dev/null
+++ b/simplecfg/src/main/jastadd/ClassPathFilter.jrag
@@ -0,0 +1,235 @@
+/**
+ * Copyright 2015 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Replace type lookup to avoid parsing extra sources.
+ * All sources other than the one that is being analyzed is replaced
+ * by the Unknown type, or a placeholder type - a TypeDecl with just
+ * a name and package.
+ */
+aspect ClassPathFilter {
+
+  /** The type lookup filter to use during type lookups.  */
+  private TypeLookupFilter Program.typeLookupFilter = NO_TYPE_FILTER;
+
+  /** Changes the type lookup filter.  */
+  public void Program.setTypeLookupFilter(TypeLookupFilter filter) {
+    typeLookupFilter = filter;
+  }
+
+  public interface TypeLookupFilter {
+    /**
+     * This allows extra source type map initialization. It is run after the default source type map
+     * initialization code.
+     */
+    void initializeSourceTypeMap(Program program);
+    /**
+     * This allows extra library type map initialization. It is run after the default library type
+     * map initialization code.
+     */
+    void initializeLibraryTypeMap(Program program);
+
+    /**
+     * Returns the TypeDecl for the requested type. Returns UnknownType if the type was not
+     * found, or if the type was filtered out by this filter.
+     */
+    TypeDecl lookupSourceType(Program program, String packageName, String typeName);
+
+    /**
+     * Returns the TypeDecl for the requested type. Returns UnknownType if the type was not
+     * found, or if the type was filtered out by this filter.
+     */
+    TypeDecl lookupLibraryType(Program program, String packageName, String typeName);
+  }
+
+  /** This type filter does not perform any filtering. */
+  public static final TypeLookupFilter Program.NO_TYPE_FILTER = new TypeLookupFilter() {
+    @Override
+    public void initializeSourceTypeMap(Program program) {
+    }
+
+    @Override
+    public void initializeLibraryTypeMap(Program program) {
+    }
+
+    @Override
+    public TypeDecl lookupSourceType(Program program, String packageName, String typeName) {
+      return program.lookupSourceType(packageName, typeName);
+    }
+
+    @Override
+    public TypeDecl lookupLibraryType(Program program, String packageName, String typeName) {
+      return program.lookupLibraryType(packageName, typeName);
+    }
+  };
+
+  /** This type filter filters out library types. */
+  public static final TypeLookupFilter Program.BASE_LIBRARY_FILTER = new TypeLookupFilter() {
+    @Override
+    public void initializeSourceTypeMap(Program program) {
+    }
+
+    @Override
+    public void initializeLibraryTypeMap(Program program) {
+      // All types that need to be distinguishable in the code being analyzed
+      // should be added as placeholders here.
+      // This list contains all types which are looked up explicitly in the
+      // ExtendJ frontend code with lookupType(pkg, name) All type lookups that
+      // don't match a placeholder type get mapped to the Unknown type.
+      program.addPlaceholderType("java.lang", "Object");
+      program.addPlaceholderType("java.lang", "AutoCloseable");
+      program.addPlaceholderType("java.lang", "Class");
+      program.addPlaceholderType("java.lang", "Cloneable");
+      program.addPlaceholderType("java.lang", "Error");
+      program.addPlaceholderType("java.lang", "Exception");
+      program.addPlaceholderType("java.lang", "FunctionalInterface");
+      program.addPlaceholderType("java.lang", "NullPointerException");
+      program.addPlaceholderType("java.lang", "Throwable");
+      program.addPlaceholderType("java.lang", "Enum");
+      program.addPlaceholderType("java.lang", "Iterable");
+      program.addPlaceholderType("java.lang", "Iterator");
+      program.addPlaceholderType("java.lang", "RuntimeException");
+
+      // Annotations and boxed primitive types are required
+      // to do some simple type analysis.
+
+      // Add annotation types.
+      program.addPlaceholderType("java.lang.annotation", "Target");
+      program.addPlaceholderType("java.lang.annotation", "Retention");
+      program.addPlaceholderType("java.lang.annotation", "Inherited");
+      program.addPlaceholderType("java.lang", "SuppressWarnings");
+      program.addPlaceholderType("java.lang", "Override");
+      program.addPlaceholderType("java.lang", "Serializable");
+
+      // Boxed primitive types.
+      program.addPlaceholderType("java.lang", "Integer");
+      program.addPlaceholderType("java.lang", "Float");
+      program.addPlaceholderType("java.lang", "Short");
+      program.addPlaceholderType("java.lang", "Byte");
+      program.addPlaceholderType("java.lang", "Character");
+      program.addPlaceholderType("java.lang", "Long");
+      program.addPlaceholderType("java.lang", "Double");
+      program.addPlaceholderType("java.lang", "String");
+      program.addPlaceholderType("java.lang", "Boolean");
+      program.addPlaceholderType("java.lang", "Void");
+    }
+
+    @Override
+    public TypeDecl lookupSourceType(Program program, String packageName, String typeName) {
+      return program.lookupSourceType(packageName, typeName);
+    }
+
+    @Override
+    public TypeDecl lookupLibraryType(Program program, String packageName, String typeName) {
+      String fullName = packageName.isEmpty() ? typeName : packageName + "." + typeName;
+      if (program.libraryTypeMap.containsKey(fullName)) {
+        return program.libraryTypeMap.get(fullName);
+      } else {
+        program.libraryTypeMap.put(fullName, program.unknownType());
+        return program.unknownType();
+      }
+    }
+  };
+
+  public static final TypeLookupFilter Program.ANALYZER_TYPE_FILTER = new TypeLookupFilter() {
+    @Override
+    public void initializeSourceTypeMap(Program program) {
+      BASE_LIBRARY_FILTER.initializeSourceTypeMap(program);
+    }
+
+    @Override
+    public void initializeLibraryTypeMap(Program program) {
+      BASE_LIBRARY_FILTER.initializeLibraryTypeMap(program);
+
+      // Types needed for read/write after close analysis.
+      program.addPlaceholderType("java.io", "Writer");
+      program.addPlaceholderType("java.io", "Reader");
+
+      // Types needed for Nullable Dereference analysis.
+      program.addPlaceholderType("javax.annotation", "Nullable");
+    }
+
+    @Override
+    public TypeDecl lookupSourceType(Program program, String packageName, String typeName) {
+      return BASE_LIBRARY_FILTER.lookupSourceType(program, packageName, typeName);
+    }
+
+    @Override
+    public TypeDecl lookupLibraryType(Program program, String packageName, String typeName) {
+      return BASE_LIBRARY_FILTER.lookupLibraryType(program, packageName, typeName);
+    }
+  };
+
+  refine LookupFullyQualifiedTypes
+  protected void Program.initializeSourceTypeMap() {
+    refined();
+    typeLookupFilter.initializeSourceTypeMap(this);
+  }
+
+  refine LookupFullyQualifiedTypes
+  protected void Program.initializeLibraryTypeMap() {
+    refined();
+    typeLookupFilter.initializeLibraryTypeMap(this);
+  }
+
+  refine LookupFullyQualifiedTypes
+  eq Program.lookupType(String packageName, String typeName) {
+    TypeDecl sourceType = typeLookupFilter.lookupSourceType(this, packageName, typeName);
+    if (!sourceType.isUnknown()) {
+      return sourceType;
+    }
+    if (!libraryTypeMapInitialized) {
+      initializeLibraryTypeMap();
+      libraryTypeMapInitialized = true;
+    }
+    return typeLookupFilter.lookupLibraryType(this, packageName, typeName);
+  }
+
+  /**
+   * Add a placeholder type declaration to the library type map.
+   * This triggers evaluation of a placeholder type NTA and a placeholder
+   * compilation unit NTA.
+   */
+  public void Program.addPlaceholderType(String packageName, String typeName) {
+    String fullName = packageName.equals("") ? typeName : packageName + "." + typeName;
+    CompilationUnit cu = placeholderCompilationUnit(packageName);
+    cu.setFromSource(false);
+    cu.setClassSource(ClassSource.NONE);
+    TypeDecl placeholder = cu.placeholderTypeDecl(typeName);
+    libraryTypeMap.put(fullName, placeholder);
+  }
+
+  /**
+   * Build a placeholder compilation unit for types in a package.
+   */
+  syn nta CompilationUnit Program.placeholderCompilationUnit(String packageName) {
+    CompilationUnit u = new CompilationUnit();
+    u.setPackageDecl(packageName);
+    return u;
+  }
+
+  /**
+   * Build placeholder type declaration.
+   */
+  syn nta TypeDecl CompilationUnit.placeholderTypeDecl(String typeName) {
+    PlaceholderTypeDecl decl = new PlaceholderTypeDecl();
+    decl.setModifiers(new Modifiers(new List().add(new Modifier("public"))));
+    decl.setID(typeName);
+    return decl;
+  }
+
+}
+
diff --git a/simplecfg/src/main/jastadd/Findings.jrag b/simplecfg/src/main/jastadd/Findings.jrag
new file mode 100644
index 0000000000000000000000000000000000000000..104905bf4d3267d9d135e467465c2991c31dba01
--- /dev/null
+++ b/simplecfg/src/main/jastadd/Findings.jrag
@@ -0,0 +1,119 @@
+/**
+ * Copyright 2015 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+import java.util.Collection;
+import java.util.LinkedList;
+
+/**
+ * This aspect provides the general attributes to collect findings for the
+ * available analyses.
+ */
+aspect Findings {
+  /** Collection of API usage findings.  */
+  coll Collection<ExtendJFinding> CompilationUnit.findings()
+      [new LinkedList<ExtendJFinding>()]
+      with add
+      root CompilationUnit;
+
+  /** Build a new finding with the given subcategory and message.  */
+  syn ExtendJFinding ASTNode.finding(String subcategory, String message) {
+    ASTNode location = locationNode();
+    return new ExtendJFinding(sourceFile(), subcategory, message,
+        getLine(location.getStart()), getColumn(location.getStart()),
+        getLine(location.getEnd()), getColumn(location.getEnd()));
+  }
+
+  /** Find the closest AST node with source location information.  */
+  syn ASTNode ASTNode.locationNode() {
+    ASTNode node = this;
+    while (node.getParent() != null && node.getStart() == 0) {
+      node = node.getParent();
+    }
+    return node;
+  }
+
+  /** Find the indentation for the current statement.  */
+  inh String Stmt.indentation();
+
+  /** Find the indentation for the current expression.  */
+  inh String Expr.indentation();
+
+  /** Find the indentation for the current type declaration.  */
+  inh String TypeDecl.indentation();
+
+  eq Block.getChild().indentation() = indentation() + "  ";
+  eq TypeDecl.getChild().indentation() = indentation() + "  ";
+
+  eq CompilationUnit.getChild().indentation() = "";
+  eq Program.getChild().indentation() = "";
+
+  /** A finding produced by an ExtendJ analyzer.  */
+  public class ExtendJFinding {
+
+    public final String sourcePath;
+    public final String subcategory;
+    public final String message;
+    public final int startLine;
+    public final int startColumn;
+    public final int endLine;
+    public final int endColumn;
+    public final Collection<ExtendJFix> fixes = new ArrayList<>();
+
+    /**
+     * Describes a suggested fix. The suggested fix has a description which does not seem to
+     * show up in Critique. The new text should end with a newline.
+     */
+    public static class ExtendJFix {
+      public final String description;
+      public final int startLine;
+      public final int endLine;
+      public final String newText;
+
+      ExtendJFix(String description, int startLine, int endLine, String newText) {
+        this.description = description;
+        this.startLine = startLine;
+        this.endLine = endLine;
+        this.newText = newText;
+      }
+    }
+
+    public ExtendJFinding(String sourcePath, String subcategory, String message,
+        int startLine, int startColumn, int endLine, int endColumn) {
+      this.sourcePath = sourcePath;
+      this.subcategory = subcategory;
+      this.message = message;
+      this.startLine = startLine;
+      this.startColumn = startColumn;
+      this.endLine = endLine;
+      this.endColumn = endColumn;
+    }
+
+    /**
+     * Set a new suggested fix for this finding.
+     */
+    public ExtendJFinding addFix(String description, int startLine, int endLine, String newText) {
+      fixes.add(new ExtendJFix(description, startLine, endLine, newText));
+      return this;
+    }
+
+    @Override
+    public String toString() {
+      // This message is printed for each finding when running the CLI.
+      return String.format("%s:%d:%d: %s", sourcePath, startLine, startColumn, message);
+    }
+  }
+}
diff --git a/simplecfg/src/main/jastadd/NullableDereferenceAnalysis.jrag b/simplecfg/src/main/jastadd/NullableDereferenceAnalysis.jrag
new file mode 100644
index 0000000000000000000000000000000000000000..db9afd1b5bab4b1dbc43107c23dd6ac4ff13b69c
--- /dev/null
+++ b/simplecfg/src/main/jastadd/NullableDereferenceAnalysis.jrag
@@ -0,0 +1,504 @@
+/**
+ * Copyright 2015 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Adds an analysis that checks for dereferences of a parameter declared nullable.
+ *
+ * <p>When a method or constructor parameter is annotated with javax.annotation.Nullable,
+ * we check that all dereferences of that parameter are guarded by a null-check. The
+ * analysis is control-flow sensitive in that it will recognize if all control flow paths
+ * to the dereference are effectively guarded by a null check, for example: {@code
+ * if (p == null) return;
+ * p.x(); // Guarded by null check above.
+ * }
+ *
+ * <p>The analysis is not intraprocedural, so in order to avoid false positives
+ * where a method call guards against nullness the analyzer assumes that
+ * calling a method with an argument x results in an exception if x is null and
+ * thus works like an effective null guard for x.
+ *
+ * <p>To find potential null dereferences on nullable parameters, the analysis does a forward CFG
+ * traversal from the entry-point of the method. Note that this is only done whenever a
+ * {@code @Nullable} parameter has been encountered in the method. The traversal explores all paths
+ * from the method entry until it finds null guard statements such as {@code if (p != null)}. The
+ * branches that are protected from nullness get pruned in the search and the search continues on
+ * other branches. The search is performed by a {@code NullDereferenceLocator}, which implements the
+ * {@code CfgSearch} interface. This visitor is invoked for each node in the CFG search, and it
+ * decides if the search will continue from that node, or if the current edge should be skipped
+ * (i.e., pruned).
+ *
+ * <p>In order to find the position in the CFG where a potential null dereference occurs, CFG marker
+ * nodes are inserted. This is done by adding a synthesized non-terminal attribute (NTA) for
+ * a CfgMarker on Dot (which represents Java dot expressions). This marker node appears
+ * in the CFG as "nullable access" because it is inserted whenever a nullable variable is
+ * dereferenced.
+ *
+ * <p>Dataflow analysis is not used, so in order to analyze a parameter it is required to be
+ * effectively final, i.e. it is not assigned anywhere in side the body of the method/constructor.
+ */
+aspect NullableDereferenceAnalysis {
+
+  // Give ParameterDeclaration access to the inherited compilationUnit attribute.
+  inh CompilationUnit ParameterDeclaration.compilationUnit();
+
+  ParameterDeclaration contributes nullableDereferenceFinding()
+      when nullableDereferenceFinding() != null
+      to CompilationUnit.findings()
+      for compilationUnit();
+
+  /**
+   * Generate a NullableDereference finding for this dot expression,
+   * if no finding should be reported this attribute returns {@code null}.
+   */
+  syn lazy ExtendJFinding ParameterDeclaration.nullableDereferenceFinding() {
+    if (!getModifiers().hasNullableAnnotation()) {
+      return null;
+    }
+    if (!isFinal() && !isEffectivelyFinal()) {
+      // Do not analyze non-effectively final parameters.
+      return null;
+    }
+    Expr location = findNullableDereference(this);
+    if (location == null) {
+      return null;
+    }
+    ExtendJFinding finding = location.finding("NullableDereference", String.format(
+        "Dereferencing %s, which was declared @Nullable.", name()));
+    if (compilationUnit().fromSource()) {
+      ASTNode modifierLocation = nullableModifierLocation();
+      int line = getLine(modifierLocation.getStart());
+      int startCol = getColumn(modifierLocation.getStart());
+      int endCol = getColumn(modifierLocation.getEnd());
+      if (startCol < endCol && line == getLine(modifierLocation.getEnd())) {
+        try {
+          InputStream data = compilationUnit().getClassSource().openInputStream();
+          java.util.Scanner scanner = new java.util.Scanner(data);
+          for (int i = 1; i < line && scanner.hasNextLine(); ++i) {
+            scanner.nextLine();
+          }
+          if (scanner.hasNextLine()) {
+            String text = scanner.nextLine();
+            finding.addFix("Remove the @Nullable annotation.",
+                line, line,
+                text.substring(0,startCol-1) + text.substring(endCol+1) + "\n");
+          }
+        } catch (IOException e) {
+          // Failed to unparse the current line.
+          // This is not a serious problem; we just don't give a fix suggestion.
+        }
+      }
+    }
+    return finding;
+  }
+
+  // Exclude variable arity parameters from Nullable dereference analysis.
+  // When a variable arity parameter is annotated @Nullable, that will most likely be intended as a
+  // @Nullable annotation for the individual parameters, not the containing argument array.
+  eq VariableArityParameterDeclaration.nullableDereferenceFinding() = null;
+
+  /**
+   * Find the location node for the javax.annotation.Nullable annotation in the modifier list.
+   * Returns {@code null} if the location of the modifier was not found.
+   */
+  syn ASTNode ParameterDeclaration.nullableModifierLocation() =
+      getModifiers().nullableModifierLocation();
+
+  syn ASTNode Modifiers.nullableModifierLocation() {
+    for (Modifier modifier : getModifierList()) {
+      if (modifier.isAnnotation("javax.annotation", "Nullable")) {
+        return modifier.locationNode();
+      }
+    }
+    return null;
+  }
+
+  /**
+   * Find a location, not necessarily the first location, in the host method/constructor where the
+   * parameter is accessed without a null guard.
+   */
+  inh Expr ParameterDeclaration.findNullableDereference(Variable var);
+
+  eq Program.getChild().findNullableDereference(Variable var) = null;
+  eq BodyDecl.getChild().findNullableDereference(Variable var) = null;
+
+  eq MethodDecl.getParameter().findNullableDereference(Variable var) {
+    if (!hasBlock()) {
+      return null;
+    }
+    CfgNode cfgNode = entry().bfs(new NullDereferenceLocator(var));
+    return cfgNode == null ? null : cfgNode.receiverExpr();
+  }
+
+  eq ConstructorDecl.getParameter().findNullableDereference(Variable var) {
+    CfgNode cfgNode = entry().bfs(new NullDereferenceLocator(var));
+    return cfgNode == null ? null : cfgNode.receiverExpr();
+  }
+
+  /**
+   * A CFG visitor that searches in the forward CFG for a nullable dereference.
+   *
+   * <p>The search stops at parts of the search tree guarded by a null check
+   * on the receiver variable.
+   */
+  class NullDereferenceLocator implements CfgVisitor {
+    private final Variable var;
+
+    public NullDereferenceLocator(Variable var) {
+      this.var = var;
+    }
+
+    @Override public SearchAction processEdge(CfgNode pred, CfgNode succ) {
+      if (pred.isNullGuard(var, succ)) {
+        return SearchAction.SKIP;
+      }
+      Expr receiver = succ.receiverExpr();
+      if (receiver != null && receiver.isVariable(var) && !receiver.hasNullGuard(var)) {
+        return SearchAction.SUCCESSOR_MATCH;
+      }
+      return SearchAction.CONTINUE;
+    }
+  }
+
+  /**
+   * Returns the receiver expression if the CFG node is the child of a dereference expression.
+   * Returns {@code null} otherwise.
+   */
+  inh Expr CfgNode.receiverExpr();
+  eq Program.getChild().receiverExpr() = null;
+  eq BodyDecl.getChild().receiverExpr() = null;
+  eq BodyDecl.exit().receiverExpr() = null;
+  eq TryStmt.tryEntryMarker().receiverExpr() = null;
+  eq BreakStmt.marker().receiverExpr() = null;
+  eq ContinueStmt.marker().receiverExpr() = null;
+  eq ReturnStmt.marker().receiverExpr() = null;
+  eq MethodAccess.exceptionNode().receiverExpr() = null;
+  eq MethodAccess.call().receiverExpr() =
+      hasPrevExpr()
+      ? prevExpr()
+      : null;
+  eq ThrowStmt.exceptionNode().receiverExpr() = null;
+  eq TryStmt.exceptionNode().receiverExpr() = null;
+  eq ConditionalExpr.branch().receiverExpr() = null;
+  eq ConditionalExpr.thenEndMarker().receiverExpr() = null;
+  eq ConditionalExpr.elseEndMarker().receiverExpr() = null;
+  eq IfStmt.branch().receiverExpr() = null;
+  eq IfStmt.thenEndMarker().receiverExpr() = null;
+  eq IfStmt.elseEndMarker().receiverExpr() = null;
+  eq ForStmt.branch().receiverExpr() = null;
+  eq EnhancedForStmt.branch().receiverExpr() = null;
+  eq WhileStmt.branch().receiverExpr() = null;
+  eq DoStmt.branch().receiverExpr() = null;
+  eq SwitchStmt.branch().receiverExpr() = null;
+  eq LambdaBody.exit().receiverExpr() = null;
+  eq Dot.nullableDereferenceMarker().receiverExpr() = getLeft();
+
+  /** Marker node used to find location of a nullable dereference in the CFG.  */
+  syn nta CfgMarker Dot.nullableDereferenceMarker() = new CfgMarker();
+
+  /** Insert nullable dereference marker in the CFG.  */
+  refine SimpleCFG
+  eq Dot.getLeft().follow() =
+      getRight().isMethodAccess()
+      ? refined()
+      : nullableDereferenceMarker();
+
+  eq Dot.nullableDereferenceMarker().succ() = Collections.singleton(getRight().entry());
+
+  syn boolean CfgNode.isNullGuard(Variable var, CfgNode succ) = false;
+
+  /**
+   * We assume that calling a method with the variable var as an argument
+   * results in an exception thrown by the method call if var is null. This is
+   * not true for many methods, but it should reduce the false positive rate
+   * for the NullableDereference analyzer.
+   */
+  eq CfgMethodCall.isNullGuard(Variable var, CfgNode succ) {
+    if (succ instanceof CfgException) {
+      return false;
+    }
+    MethodAccess access = methodAccess();
+    for (Expr arg : access.getArgList()) {
+      if (arg.isVariable(var)) {
+        return true;
+      }
+    }
+    return false;
+  }
+
+  /** Check if this branch has a null-guarding condition.  */
+  eq CfgBranch.isNullGuard(Variable var, CfgNode succ) = inNullGuard(var, succ);
+
+  inh boolean CfgBranch.inNullGuard(Variable var, CfgNode succ);
+
+  eq IfStmt.branch().inNullGuard(Variable var, CfgNode succ) =
+      succ == getThen().entry()
+      ? getCondition().isNonNullWhenTrue(var)
+      : getCondition().isNonNullWhenFalse(var);
+
+  eq ConditionalExpr.branch().inNullGuard(Variable var, CfgNode succ) =
+      succ == getTrueExpr().entry()
+      ? getCondition().isNonNullWhenTrue(var)
+      : getCondition().isNonNullWhenFalse(var);
+
+  eq ForStmt.branch().inNullGuard(Variable var, CfgNode succ) =
+      succ == getStmt().entry()
+      ? getCondition().isNonNullWhenTrue(var)
+      : getCondition().isNonNullWhenFalse(var);
+
+  eq WhileStmt.branch().inNullGuard(Variable var, CfgNode succ) =
+      succ == getStmt().entry()
+      ? getCondition().isNonNullWhenTrue(var)
+      : getCondition().isNonNullWhenFalse(var);
+
+  eq EnhancedForStmt.branch().inNullGuard(Variable var, CfgNode succ) = false;
+  eq DoStmt.branch().inNullGuard(Variable var, CfgNode succ) = false;
+  eq SwitchStmt.branch().inNullGuard(Variable var, CfgNode succ) = false;
+
+  /** Returns {@code true} if this set of modifiers includes {@code javax.annotation.Nullable}.  */
+  syn boolean Modifiers.hasNullableAnnotation() = hasAnnotation("javax.annotation", "Nullable");
+
+  /** Return {@code true} if this expression is guarded by a != null check for var.  */
+  inh boolean Expr.hasNullGuard(Variable var);
+  eq Program.getChild().hasNullGuard(Variable var) = false;
+  eq IfStmt.getThen().hasNullGuard(Variable var) = getCondition().isNonNullWhenTrue(var);
+  eq IfStmt.getElse().hasNullGuard(Variable var) = getCondition().isNonNullWhenFalse(var);
+  eq WhileStmt.getStmt().hasNullGuard(Variable var) = getCondition().isNonNullWhenTrue(var);
+  eq ForStmt.getStmt().hasNullGuard(Variable var) = getCondition().isNonNullWhenTrue(var);
+  eq ConditionalExpr.getTrueExpr().hasNullGuard(Variable var) =
+      getCondition().isNonNullWhenTrue(var) || hasNullGuard(var);
+  eq ConditionalExpr.getFalseExpr().hasNullGuard(Variable var) =
+      getCondition().isNonNullWhenFalse(var) || hasNullGuard(var);
+  eq AndLogicalExpr.getRightOperand().hasNullGuard(Variable var) =
+      getLeftOperand().isNonNullWhenTrue(var) || hasNullGuard(var);
+  eq AndBitwiseExpr.getRightOperand().hasNullGuard(Variable var) =
+      getLeftOperand().isNonNullWhenTrue(var) || hasNullGuard(var);
+  eq OrLogicalExpr.getRightOperand().hasNullGuard(Variable var) =
+      getLeftOperand().isNonNullWhenFalse(var) || hasNullGuard(var);
+
+  /** @return {@code true} if the variable var is null when this expression is true. */
+  syn boolean Expr.isNullWhenTrue(Variable var) = false;
+
+  eq NEExpr.isNullWhenTrue(Variable var) =
+      getLeftOperand().isTrue() && getRightOperand().isNullWhenFalse(var)
+      || getRightOperand().isTrue() && getLeftOperand().isNullWhenFalse(var)
+      || getLeftOperand().isFalse() && getRightOperand().isNullWhenTrue(var)
+      || getRightOperand().isFalse() && getLeftOperand().isNullWhenTrue(var);
+
+  eq EQExpr.isNullWhenTrue(Variable var) =
+      getLeftOperand().isNull() && getRightOperand().varDecl() == var
+      || getRightOperand().isNull() && getLeftOperand().varDecl() == var
+      || getLeftOperand().isTrue() && getRightOperand().isNullWhenTrue(var)
+      || getRightOperand().isTrue() && getLeftOperand().isNullWhenTrue(var)
+      || getLeftOperand().isFalse() && getRightOperand().isNullWhenFalse(var)
+      || getRightOperand().isFalse() && getLeftOperand().isNullWhenFalse(var);
+
+  eq LogNotExpr.isNullWhenTrue(Variable var) = getOperand().isNullWhenFalse(var);
+
+  eq ParExpr.isNullWhenTrue(Variable var) = getExpr().isNullWhenTrue(var);
+
+  eq AndLogicalExpr.isNullWhenTrue(Variable var) =
+      getLeftOperand().isNullWhenTrue(var) || getRightOperand().isNullWhenTrue(var);
+
+  eq AndBitwiseExpr.isNullWhenTrue(Variable var) =
+      getLeftOperand().isNullWhenTrue(var) || getRightOperand().isNullWhenTrue(var);
+
+  eq OrLogicalExpr.isNullWhenTrue(Variable var) =
+      getLeftOperand().isFalse() && getRightOperand().isNullWhenTrue(var)
+      || getRightOperand().isFalse() && getLeftOperand().isNullWhenTrue(var);
+
+  eq Dot.isNullWhenTrue(Variable var) =
+      !getLeft().isVariable(var) && getRight().isNullWhenTrue(var);
+
+  // Assume that a method call to X.isNull_(var) is equivalent to a null test on var.
+  eq MethodAccess.isNullWhenTrue(Variable var) =
+      name().startsWith("isNull") && getNumArg() == 1 && getArg(0).isVariable(var);
+
+  eq VarAccess.isNullWhenTrue(Variable var) = decl().isNullWhenTrue(var);
+
+  syn boolean Variable.isNullWhenTrue(Variable var);
+  eq EnumConstant.isNullWhenTrue(Variable var) = false;
+  eq ParameterDeclaration.isNullWhenTrue(Variable var) = false;
+  eq FieldDeclarator.isNullWhenTrue(Variable var) = false;
+  eq CatchParameterDeclaration.isNullWhenTrue(Variable var) = false;
+  eq InferredParameterDeclaration.isNullWhenTrue(Variable var) = false;
+  eq VariableDeclarator.isNullWhenTrue(Variable var) =
+      type().isBoolean() && hasInit() && isEffectivelyFinal()
+      ? getInit().isNullWhenTrue(var)
+      : false;
+
+  /** @return {@code true} if the variable var is null when this expression is false. */
+  syn boolean Expr.isNullWhenFalse(Variable var) = false;
+
+  eq NEExpr.isNullWhenFalse(Variable var) =
+      getLeftOperand().isNull() && getRightOperand().varDecl() == var
+      || getRightOperand().isNull() && getLeftOperand().varDecl() == var
+      || getLeftOperand().isTrue() && getRightOperand().isNullWhenTrue(var)
+      || getRightOperand().isTrue() && getLeftOperand().isNullWhenTrue(var)
+      || getLeftOperand().isFalse() && getRightOperand().isNullWhenFalse(var)
+      || getRightOperand().isFalse() && getLeftOperand().isNullWhenFalse(var);
+
+  eq EQExpr.isNullWhenFalse(Variable var) =
+      getLeftOperand().isTrue() && getRightOperand().isNullWhenFalse(var)
+      || getRightOperand().isTrue() && getLeftOperand().isNullWhenFalse(var)
+      || getLeftOperand().isFalse() && getRightOperand().isNullWhenTrue(var)
+      || getRightOperand().isFalse() && getLeftOperand().isNullWhenTrue(var);
+
+  eq LogNotExpr.isNullWhenFalse(Variable var) = getOperand().isNullWhenTrue(var);
+
+  eq ParExpr.isNullWhenFalse(Variable var) = getExpr().isNullWhenFalse(var);
+
+  eq AndLogicalExpr.isNullWhenFalse(Variable var) =
+      getLeftOperand().isTrue() && getRightOperand().isNullWhenFalse(var)
+      || getRightOperand().isTrue() && getLeftOperand().isNullWhenFalse(var);
+
+  eq AndBitwiseExpr.isNullWhenFalse(Variable var) =
+      getLeftOperand().isTrue() && getRightOperand().isNullWhenFalse(var)
+      || getRightOperand().isTrue() && getLeftOperand().isNullWhenFalse(var);
+
+  eq OrLogicalExpr.isNullWhenFalse(Variable var) =
+      getLeftOperand().isNullWhenFalse(var) && getRightOperand().isNullWhenFalse(var);
+
+  eq Dot.isNullWhenFalse(Variable var) =
+      !getLeft().isVariable(var) && getRight().isNullWhenFalse(var);
+
+  // Assume that a method call to X.isNo{t,n}Null_(var) is equivalent to a non-null test on var.
+  eq MethodAccess.isNullWhenFalse(Variable var) =
+      (name().startsWith("isNotNull") || name().startsWith("isNonNull"))
+      && getNumArg() == 1 && getArg(0).isVariable(var);
+
+  eq VarAccess.isNullWhenFalse(Variable var) = decl().isNullWhenFalse(var);
+
+  syn boolean Variable.isNullWhenFalse(Variable var);
+  eq EnumConstant.isNullWhenFalse(Variable var) = false;
+  eq ParameterDeclaration.isNullWhenFalse(Variable var) = false;
+  eq FieldDeclarator.isNullWhenFalse(Variable var) = false;
+  eq CatchParameterDeclaration.isNullWhenFalse(Variable var) = false;
+  eq InferredParameterDeclaration.isNullWhenFalse(Variable var) = false;
+  eq VariableDeclarator.isNullWhenFalse(Variable var) =
+      type().isBoolean() && hasInit() && isEffectivelyFinal()
+      ? getInit().isNullWhenFalse(var)
+      : false;
+
+  /** @return {@code true} if the variable var is non-null when this expression is true. */
+  syn boolean Expr.isNonNullWhenTrue(Variable var) = false;
+
+  eq NEExpr.isNonNullWhenTrue(Variable var) =
+      getLeftOperand().isNull() && getRightOperand().varDecl() == var
+      || getRightOperand().isNull() && getLeftOperand().varDecl() == var
+      || getLeftOperand().isTrue() && getRightOperand().isNonNullWhenFalse(var)
+      || getRightOperand().isTrue() && getLeftOperand().isNonNullWhenFalse(var)
+      || getLeftOperand().isFalse() && getRightOperand().isNonNullWhenTrue(var)
+      || getRightOperand().isFalse() && getLeftOperand().isNonNullWhenTrue(var);
+
+  eq EQExpr.isNonNullWhenTrue(Variable var) =
+      getLeftOperand().isTrue() && getRightOperand().isNonNullWhenTrue(var)
+      || getRightOperand().isTrue() && getLeftOperand().isNonNullWhenTrue(var)
+      || getLeftOperand().isFalse() && getRightOperand().isNonNullWhenFalse(var)
+      || getRightOperand().isFalse() && getLeftOperand().isNonNullWhenFalse(var);
+
+  eq LogNotExpr.isNonNullWhenTrue(Variable var) = getOperand().isNullWhenTrue(var);
+
+  eq ParExpr.isNonNullWhenTrue(Variable var) = getExpr().isNonNullWhenTrue(var);
+
+  eq AndLogicalExpr.isNonNullWhenTrue(Variable var) =
+      getLeftOperand().isNonNullWhenTrue(var) || getRightOperand().isNonNullWhenTrue(var);
+
+  eq AndBitwiseExpr.isNonNullWhenTrue(Variable var) =
+      getLeftOperand().isNonNullWhenTrue(var) || getRightOperand().isNonNullWhenTrue(var);
+
+  eq OrLogicalExpr.isNonNullWhenTrue(Variable var) =
+      getLeftOperand().isFalse() && getRightOperand().isNonNullWhenTrue(var)
+      || getRightOperand().isFalse() && getLeftOperand().isNonNullWhenTrue(var);
+
+  eq Dot.isNonNullWhenTrue(Variable var) =
+      !getLeft().isVariable(var) && getRight().isNonNullWhenTrue(var);
+
+  // Assume that a method call to X.isNo{t,n}Null_(var) is equivalent to a non-null test on var.
+  eq MethodAccess.isNonNullWhenTrue(Variable var) =
+      (name().startsWith("isNotNull") || name().startsWith("isNonNull"))
+      && getNumArg() == 1 && getArg(0).isVariable(var);
+
+  eq VarAccess.isNonNullWhenTrue(Variable var) = decl().isNonNullWhenTrue(var);
+
+  syn boolean Variable.isNonNullWhenTrue(Variable var);
+  eq EnumConstant.isNonNullWhenTrue(Variable var) = false;
+  eq ParameterDeclaration.isNonNullWhenTrue(Variable var) = false;
+  eq FieldDeclarator.isNonNullWhenTrue(Variable var) = false;
+  eq CatchParameterDeclaration.isNonNullWhenTrue(Variable var) = false;
+  eq InferredParameterDeclaration.isNonNullWhenTrue(Variable var) = false;
+  eq VariableDeclarator.isNonNullWhenTrue(Variable var) =
+      type().isBoolean() && hasInit() && isEffectivelyFinal()
+      ? getInit().isNonNullWhenTrue(var)
+      : false;
+
+  // An instanceof check guards against the variable being null.
+  eq InstanceOfExpr.isNonNullWhenTrue(Variable var) = getExpr().isVariable(var);
+
+  /** @return {@code true} if the variable var is non-null when this expression is false. */
+  syn boolean Expr.isNonNullWhenFalse(Variable var) = false;
+
+  eq NEExpr.isNonNullWhenFalse(Variable var) =
+      getLeftOperand().isTrue() && getRightOperand().isNonNullWhenTrue(var)
+      || getRightOperand().isTrue() && getLeftOperand().isNonNullWhenTrue(var)
+      || getLeftOperand().isFalse() && getRightOperand().isNonNullWhenFalse(var)
+      || getRightOperand().isFalse() && getLeftOperand().isNonNullWhenFalse(var);
+
+  eq EQExpr.isNonNullWhenFalse(Variable var) =
+      getLeftOperand().isNull() && getRightOperand().varDecl() == var
+      || getRightOperand().isNull() && getLeftOperand().varDecl() == var
+      || getLeftOperand().isTrue() && getRightOperand().isNonNullWhenFalse(var)
+      || getRightOperand().isTrue() && getLeftOperand().isNonNullWhenFalse(var)
+      || getLeftOperand().isFalse() && getRightOperand().isNonNullWhenTrue(var)
+      || getRightOperand().isFalse() && getLeftOperand().isNonNullWhenTrue(var);
+
+  eq LogNotExpr.isNonNullWhenFalse(Variable var) = getOperand().isNonNullWhenTrue(var);
+
+  eq ParExpr.isNonNullWhenFalse(Variable var) = getExpr().isNonNullWhenFalse(var);
+
+  eq AndLogicalExpr.isNonNullWhenFalse(Variable var) =
+      getLeftOperand().isTrue() && getRightOperand().isNonNullWhenFalse(var)
+      || getRightOperand().isTrue() && getLeftOperand().isNonNullWhenFalse(var);
+
+  eq AndBitwiseExpr.isNonNullWhenFalse(Variable var) =
+      getLeftOperand().isTrue() && getRightOperand().isNonNullWhenFalse(var)
+      || getRightOperand().isTrue() && getLeftOperand().isNonNullWhenFalse(var);
+
+  eq OrLogicalExpr.isNonNullWhenFalse(Variable var) =
+      getLeftOperand().isNonNullWhenFalse(var) || getRightOperand().isNonNullWhenFalse(var);
+
+  eq Dot.isNonNullWhenFalse(Variable var) =
+      !getLeft().isVariable(var) && getRight().isNonNullWhenFalse(var);
+
+  // Assume that a method call to X.isNull_(var) is equivalent to a null test on var.
+  eq MethodAccess.isNonNullWhenFalse(Variable var) =
+      name().startsWith("isNull") && getNumArg() == 1 && getArg(0).isVariable(var);
+
+  eq VarAccess.isNonNullWhenFalse(Variable var) = decl().isNonNullWhenFalse(var);
+
+  syn boolean Variable.isNonNullWhenFalse(Variable var);
+  eq EnumConstant.isNonNullWhenFalse(Variable var) = false;
+  eq ParameterDeclaration.isNonNullWhenFalse(Variable var) = false;
+  eq FieldDeclarator.isNonNullWhenFalse(Variable var) = false;
+  eq CatchParameterDeclaration.isNonNullWhenFalse(Variable var) = false;
+  eq InferredParameterDeclaration.isNonNullWhenFalse(Variable var) = false;
+  eq VariableDeclarator.isNonNullWhenFalse(Variable var) =
+      type().isBoolean() && hasInit() && isEffectivelyFinal()
+      ? getInit().isNonNullWhenFalse(var)
+      : false;
+
+  syn boolean Expr.isNull() = type().isNull();
+  eq NullLiteral.isNull() = true;
+}
diff --git a/simplecfg/src/main/jastadd/PrintCfg.jrag b/simplecfg/src/main/jastadd/PrintCfg.jrag
new file mode 100644
index 0000000000000000000000000000000000000000..35b7f87b20941a63f9140fb13ba2e5fa7f21c244
--- /dev/null
+++ b/simplecfg/src/main/jastadd/PrintCfg.jrag
@@ -0,0 +1,145 @@
+/**
+ * Copyright 2015 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.IdentityHashMap;
+import java.util.LinkedList;
+import java.util.Set;
+import java.util.Queue;
+
+/** Helper attributes used to print a CFG in dot graph format. */
+aspect PrintCfg {
+
+  @Override
+  public String CfgNode.toString() {
+    return name();
+  }
+
+  public void BodyDecl.printReverseCfg() {
+    entry().initPredecessors();
+    System.out.println("digraph " + graphName() + " {");
+    exit().printReverseCfg();
+    System.out.println("}");
+  }
+
+  public void CfgNode.printReverseCfg() {
+    Set<CfgNode> visited = Collections.newSetFromMap(new IdentityHashMap<CfgNode, Boolean>());
+    Queue<CfgNode> queue = new LinkedList<CfgNode>();
+
+    // Enqueue this node.
+    visited.add(this);
+    queue.add(this);
+
+    while (!queue.isEmpty()) {
+      CfgNode work = queue.poll();
+
+      System.out.format("  %s%s;\n", work.dotId(), work.dotAttributes());
+
+      // Add all out-edges for this node.
+      for (CfgNode succ : work.predecessors) {
+        System.out.format("  %s -> %s;\n", work.dotId(), succ.dotId());
+        if (!visited.contains(succ)) {
+          visited.add(succ);
+          queue.add(succ);
+        }
+      }
+    }
+  }
+
+  public void BodyDecl.printCfg() {
+    System.out.println("digraph " + graphName() + " {");
+    entry().printCfg();
+    System.out.println("}");
+  }
+
+  public void CfgNode.printCfg() {
+    Set<CfgNode> visited = Collections.newSetFromMap(new IdentityHashMap<CfgNode, Boolean>());
+    Queue<CfgNode> queue = new LinkedList<CfgNode>();
+
+    // Enqueue this node.
+    visited.add(this);
+    queue.add(this);
+
+    while (!queue.isEmpty()) {
+      CfgNode work = queue.poll();
+
+      System.out.format("  %s%s;\n", work.dotId(), work.dotAttributes());
+
+      // Add all out-edges for this node.
+      for (CfgNode succ : work.successors()) {
+        System.out.format("  %s -> %s;\n", work.dotId(), succ.dotId());
+        if (!visited.contains(succ)) {
+          visited.add(succ);
+          queue.add(succ);
+        }
+      }
+    }
+  }
+
+  syn String BodyDecl.graphName() = "";
+  eq MethodDecl.graphName() = name();
+
+  /**
+   * The ID for this node in a dot graph.
+   */
+  syn String CfgNode.dotId() = String.format("n%08X", hashCode());
+
+  // TODO(joqvist): escape string literals in generated labels.
+  syn String CfgNode.dotAttributes() = " [label=\"" + name() + "\"]";
+  eq CfgBranch.dotAttributes() = " [label=\"" + name() + "\",shape=diamond]";
+  eq CfgException.dotAttributes() = " [label=\"" + name() + "\",shape=box]";
+  eq CfgMarker.dotAttributes() = " [label=\"" + name() + "\",shape=box]";
+
+  syn String CfgNode.name();
+  eq CfgBranch.name() = branchLabel();
+  eq CfgEntry.name() = "entry";
+  eq CfgExit.name() = "exit";
+  eq CfgException.name() = "exception";
+  eq CfgMarker.name() = markerName();
+  eq CfgMethodCall.name() = callLabel();
+
+  inh String CfgMethodCall.callLabel();
+  eq MethodAccess.call().callLabel() = name() + "()";
+
+  inh String CfgBranch.branchLabel();
+  eq IfStmt.branch().branchLabel() = "if (" + getCondition().prettyPrint() + ")";
+  eq ConditionalExpr.branch().branchLabel() = "if (" + getCondition().prettyPrint() + ")";
+  eq ForStmt.branch().branchLabel() = "for (" + getCondition().prettyPrint() + ")";
+  eq WhileStmt.branch().branchLabel() = "while (" + getCondition().prettyPrint() + ")";
+  eq DoStmt.branch().branchLabel() = "do_while (" + getCondition().prettyPrint() + ")";
+  eq EnhancedForStmt.branch().branchLabel() = String.format("for (%s %s : %s)",
+      getVariableDecl().getTypeAccess().prettyPrint(),
+      getVariableDecl().getID(),
+      getExpr().prettyPrint());
+  eq SwitchStmt.branch().branchLabel() = "switch (" + getExpr().prettyPrint() + ")";
+
+  inh String CfgMarker.markerName();
+  eq BreakStmt.marker().markerName() = "break";
+  eq ContinueStmt.marker().markerName() = "continue";
+  eq ConditionalExpr.thenEndMarker().markerName() = "then-end";
+  eq ConditionalExpr.elseEndMarker().markerName() = "else-end";
+  eq IfStmt.thenEndMarker().markerName() = "then-end";
+  eq IfStmt.elseEndMarker().markerName() = "else-end";
+  eq ReturnStmt.marker().markerName() = "return";
+  eq TryStmt.tryEntryMarker().markerName() = "try";
+  eq Program.getChild().markerName() = "marker";
+  eq Dot.nullableDereferenceMarker().markerName() = "nullable access";
+  eq ForStmt.loopEndMarker().markerName() = "for-end";
+  eq EnhancedForStmt.loopEndMarker().markerName() = "for-end";
+  eq WhileStmt.loopEndMarker().markerName() = "while-end";
+  eq DoStmt.doEntryMarker().markerName() = "do-entry";
+}
diff --git a/simplecfg/src/main/jastadd/PrintCfgTest.jrag b/simplecfg/src/main/jastadd/PrintCfgTest.jrag
new file mode 100644
index 0000000000000000000000000000000000000000..cc881f8bc2e746752bd5a7e8ed88022c5a3c81da
--- /dev/null
+++ b/simplecfg/src/main/jastadd/PrintCfgTest.jrag
@@ -0,0 +1,179 @@
+/**
+ * Copyright 2015 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.util.Set;
+
+import java.util.Collections;
+import java.util.Queue;
+import java.util.HashMap;
+import java.util.IdentityHashMap;
+import java.util.Queue;
+import java.util.LinkedList;
+
+/** Attributes useful for generating a test case from a CFG. */
+aspect PrintCfgTest {
+
+  /** Generate a test case from the CFG of this body decl. */
+  public void BodyDecl.printCfgTest() {
+    CfgNode entry = entry();
+    String className = hostType().name();
+    String testName = className.substring(0, 1).toLowerCase()
+        + className.substring(1);
+    System.out.println("  @Test public void " + testName + "() {");
+    System.out.println("    CfgNode entry = parseCfg(\"" + className + "\");");
+    Queue<CfgNode> work = new LinkedList<CfgNode>();
+    Set<CfgNode> visited = Collections.newSetFromMap(
+        new IdentityHashMap<CfgNode, Boolean>());
+    visited.add(entry);
+    Map<CfgNode, String> vars = new HashMap<CfgNode, String>();
+    vars.put(entry, entry.toString());
+    Map<String, Integer> nextIds = new HashMap<String, Integer>();
+    nextIds.put("entry", 2);
+    work.add(entry);
+    while (!work.isEmpty()) {
+      CfgNode node = work.poll();
+      boolean testSuccs = node.printAssert(visited, vars, nextIds);
+      if (testSuccs) {
+        for (CfgNode succ : node.successors()) {
+          if (!visited.contains(succ)) {
+            visited.add(succ);
+            work.add(succ);
+          }
+        }
+      }
+    }
+    System.out.println("  }");
+  }
+
+  /**
+   * @return {@code true} if successors should be tested.
+   */
+  protected boolean CfgNode.printAssert(
+      Set<CfgNode> visited,
+      Map<CfgNode, String> vars,
+      Map<String, Integer> nextIds) {
+    Set<? extends CfgNode> successors = successors();
+    if (successors.size() == 1) {
+      CfgNode succ = successors.iterator().next();
+      String nodeName = nextVarName(succ.varName(), nextIds);
+      if (!visited.contains(succ)) {
+        vars.put(succ, nodeName);
+        System.out.println(String.format(
+            "    CfgNode %s = succ(%s, \"%s\");",
+            nodeName, vars.get(this), succ.name()));
+      } else {
+        System.out.println(String.format(
+            "    assertThat(succ(%s, \"%s\")).isSameAs(%s);",
+            vars.get(this), succ.name(), vars.get(succ)));
+      }
+    } else if (successors.size() > 1) {
+      boolean duplicates = false;
+      Set<String> dups = new HashSet<String>();
+      for (CfgNode succ : successors) {
+        if (dups.contains(succ.name())) {
+          duplicates = true;
+          break;
+        }
+        dups.add(succ.name());
+      }
+      if (duplicates) {
+        System.out.print(String.format(
+            "    assertThat(%s.successors()).containsExactly(\"",
+            vars.get(this)));
+        boolean first = true;
+        for (CfgNode succ : successors) {
+          if (!first) {
+            System.out.print("\", \"");
+          }
+          first = false;
+          System.out.print(succ.name());
+        }
+        System.out.println("\");");
+        System.out.println(String.format(
+            "    // NOTE Code to test the successors of %s was not "
+            + "auto-generated\n    // due to identical successor names.",
+            vars.get(this)));
+        return false;
+      } else {
+        String arrayName = nextVarName(this.varName() + "Succ", nextIds);
+        System.out.print(String.format(
+            "    CfgNode[] %s = succ(%s",
+            arrayName, vars.get(this)));
+        int index = 0;
+        Map<String, CfgNode> succMap = new HashMap<String, CfgNode>();
+        for (CfgNode succ : successors) {
+          System.out.format(", \"%s\"", succ.name());
+          succMap.put(arrayName + "[" + index + "]", succ);
+          index += 1;
+        }
+        System.out.println(");");
+        for (Map.Entry<String, CfgNode> succ : succMap.entrySet()) {
+          if (!visited.contains(succ.getValue())) {
+            vars.put(succ.getValue(), succ.getKey());
+          } else {
+            System.out.println(String.format(
+                "    assertThat(%s).isSameAs(%s);",
+                succ.getKey(), vars.get(succ.getValue())));
+          }
+        }
+      }
+    }
+    return true;
+  }
+
+  protected static String CfgNode.nextVarName(String name,
+      Map<String, Integer> nextIds) {
+    if (nextIds.containsKey(name)) {
+      int id = nextIds.get(name);
+      nextIds.put(name, id+1);
+      return name + id;
+    } else {
+      nextIds.put(name, 2);
+      return name;
+    }
+  }
+
+  syn String CfgNode.varName() = name();
+  eq CfgBranch.varName() = branchKind() + "Branch";
+  eq CfgException.varName() = "exception";
+  eq CfgMarker.varName() = markerVarName();
+  eq CfgMethodCall.varName() = methodAccess().getID();
+
+  inh String CfgBranch.branchKind();
+  eq IfStmt.branch().branchKind() = "if";
+  eq ConditionalExpr.branch().branchKind() = "if";
+  eq ForStmt.branch().branchKind() = "for";
+  eq WhileStmt.branch().branchKind() = "while";
+  eq DoStmt.branch().branchKind() = "doWhile";
+  eq EnhancedForStmt.branch().branchKind() = "for";
+  eq SwitchStmt.branch().branchKind() = "switch";
+
+  inh String CfgMarker.markerVarName();
+  eq BreakStmt.marker().markerVarName() = "breakMarker";
+  eq ContinueStmt.marker().markerVarName() = "continueMarker";
+  eq ConditionalExpr.thenEndMarker().markerVarName() = "thenEnd";
+  eq ConditionalExpr.elseEndMarker().markerVarName() = "elseEnd";
+  eq IfStmt.thenEndMarker().markerVarName() = "thenEnd";
+  eq IfStmt.elseEndMarker().markerVarName() = "elseEnd";
+  eq ReturnStmt.marker().markerVarName() = "returnMarker";
+  eq TryStmt.tryEntryMarker().markerVarName() = "tryEntry";
+  eq Program.getChild().markerVarName() = "marker";
+  eq Dot.nullableDereferenceMarker().markerVarName() = "nullable";
+  eq ForStmt.loopEndMarker().markerVarName() = "forEnd";
+  eq EnhancedForStmt.loopEndMarker().markerVarName() = "forEnd";
+  eq WhileStmt.loopEndMarker().markerVarName() = "whileEnd";
+  eq DoStmt.doEntryMarker().markerVarName() = "doEntry";
+}
diff --git a/simplecfg/src/main/jastadd/SimpleCFG.jrag b/simplecfg/src/main/jastadd/SimpleCFG.jrag
new file mode 100644
index 0000000000000000000000000000000000000000..162622bb10fa429e861076736a12e726958f7378
--- /dev/null
+++ b/simplecfg/src/main/jastadd/SimpleCFG.jrag
@@ -0,0 +1,791 @@
+/**
+ * Copyright 2015 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.IdentityHashMap;
+import java.util.Iterator;
+import java.util.Set;
+
+/**
+ * Attributes to build a simplified Control Flow Graph (CFG) for a methods,
+ * constructors, and initializers.
+ *
+ * <p>The simple CFG contains only method calls and branches. The simple CFG has
+ * a naive exception model - any exception is assumed to be throwable by any
+ * statement.
+ */
+aspect SimpleCFG {
+
+  /** @return {@code true} if this node corresponds to a method call. */
+  syn boolean CfgNode.isCall() = false;
+  eq CfgMethodCall.isCall() = true;
+
+  /** @return {@code true} if this node corresponds to an exception branch. */
+  syn boolean CfgNode.isException() = false;
+  eq CfgException.isException() = true;
+
+  /** @return {@code true} if this node corresponds to a branch. */
+  syn boolean CfgNode.isBranch() = false;
+  eq CfgBranch.isBranch() = true;
+
+  /**
+   * Successor nodes in the CFG.
+   */
+  syn lazy Set<? extends CfgNode> CfgNode.successors();
+
+  eq CfgEntry.successors() = Collections.singleton(getSucc());
+
+  eq CfgExit.successors() = Collections.emptySet();
+
+  eq CfgMethodCall.successors() = succ();
+
+  eq CfgBranch.successors() = succ();
+
+  eq CfgException.successors() = succ();
+
+  eq CfgMarker.successors() = succ();
+
+  /**
+   * Set that contains either one or two unique objects. The objects are
+   * compared with reference equality.
+   */
+  class IdentityTupleSet<E> implements Set<E> {
+    final E a, b;
+
+    public IdentityTupleSet(E a, E b) {
+      this.a = a;
+      this.b = b;
+    }
+
+    @Override
+    public boolean add(E e) {
+      throw new UnsupportedOperationException();
+    }
+    @Override
+    public boolean addAll(Collection<? extends E> e) {
+      throw new UnsupportedOperationException();
+    }
+    @Override
+    public void clear() {
+      throw new UnsupportedOperationException();
+    }
+    @Override
+    public boolean contains(Object o) {
+      return o == a || o == b;
+    }
+    @Override
+    public boolean containsAll(Collection<?> c) {
+      for (Object o : c) {
+        if (!contains(o)) {
+          return false;
+        }
+      }
+      return true;
+    }
+    @Override
+    public int hashCode() {
+      return a.hashCode() + b.hashCode();
+    }
+    @Override
+    public boolean isEmpty() {
+      return false;
+    }
+    @Override
+    public Iterator<E> iterator() {
+      return new Iterator<E>() {
+        int index = 0;
+        @Override
+        public boolean hasNext() {
+          return (a == b && index < 1)
+              || (a != b && index < 2);
+        }
+        @Override
+        public E next() {
+          return ++index == 1 ? a : b;
+        }
+        @Override
+        public void remove() {
+          throw new UnsupportedOperationException();
+        }
+      };
+    }
+    @Override
+    public boolean remove(Object o) {
+      throw new UnsupportedOperationException();
+    }
+    @Override
+    public boolean removeAll(Collection<?> c) {
+      throw new UnsupportedOperationException();
+    }
+    @Override
+    public boolean retainAll(Collection<?> c) {
+      throw new UnsupportedOperationException();
+    }
+    @Override
+    public int size() {
+      return a == b ? 1 : 2;
+    }
+    @Override
+    public Object[] toArray() {
+      return new Object[] { a, b };
+    }
+    @Override
+    public <T> T[] toArray(T[] array) {
+      array[0] = (T) a;
+      array[1] = (T) b;
+      return array;
+    }
+    @Override
+    public String toString() {
+      if (a == b) {
+        return "[" + a + "]";
+      } else {
+        return "[" + a + ", " + b + "]";
+      }
+    }
+  }
+
+  /** Successors to this branch node. */
+  inh Set<? extends CfgNode> CfgBranch.succ();
+
+  /** Successors to this method call node. */
+  inh Set<? extends CfgNode> CfgMethodCall.succ();
+
+  inh Set<? extends CfgNode> CfgException.succ();
+
+  inh Set<? extends CfgNode> CfgMarker.succ();
+
+  /** Build a small set with two elements. */
+  public <T> Set<T> ASTNode.smallSet(T a, T b) {
+    return new IdentityTupleSet<T>(a, b);
+  }
+
+  /**
+   * The entry CFG node of this statement. This is the next method call,
+   * branch, or exit node of the simplified CFG following the entry of
+   * this statement.
+   */
+  syn CfgNode Stmt.entry() = follow();
+
+  /**
+   * Find the next CFG node representing the next branch, or the next
+   * method access following this statement.
+   */
+  inh CfgNode Stmt.follow();
+
+  inh CfgNode Expr.follow();
+
+  // Needed for completeness, but never used by anything relevant.
+  eq Program.getChild().follow() = new CfgExit();
+
+  /**
+   * The entry node in a filtered CFG.
+   */
+  syn lazy CfgEntry BodyDecl.entry() = new CfgEntry(exit());
+
+  eq MethodDecl.entry() =
+      hasBlock()
+      ?  new CfgEntry(getBlock().entry())
+      : new CfgEntry(exit());
+
+  eq ConstructorDecl.entry() = new CfgEntry(getBlock().entry());
+
+  eq InstanceInitializer.entry() = new CfgEntry(getBlock().entry());
+
+  eq StaticInitializer.entry() = new CfgEntry(getBlock().entry());
+
+  /**
+   * The exit node in a filtered CFG.
+   */
+  syn nta CfgExit BodyDecl.exit() = new CfgExit();
+
+  eq Block.entry() {
+    if (getNumStmt() > 0) {
+      return getStmt(0).entry();
+    } else {
+      return follow();
+    }
+  }
+
+  eq LabeledStmt.entry() = getStmt().entry();
+
+  eq VarDeclStmt.entry() =
+      getNumDeclarator() > 0
+      ? getDeclarator(0).entry()
+      : follow();
+
+  eq VarDeclStmt.getDeclarator(int index).follow() =
+      index + 1 < getNumDeclarator()
+      ? getDeclarator(index + 1).entry()
+      : follow();
+
+  inh CfgNode VariableDeclarator.follow();
+
+  syn CfgNode VariableDeclarator.entry() =
+      hasInit()
+      ? getInit().entry()
+      : follow();
+
+  eq SynchronizedStmt.entry() = getExpr().entry();
+
+  eq SynchronizedStmt.getExpr().follow() = getBlock().entry();
+
+  // Note: catch-all clauses get special treatment!
+
+  eq TryStmt.entry() = tryEntryMarker();
+
+  eq TryWithResources.entry() =
+      getNumResource() > 0
+      ? getResource(0).entry()
+      : super.entry();
+
+  eq TryWithResources.getResource(int index).follow() =
+      index+1 < getNumResource()
+      ? getResource(index+1).entry()
+      : super.entry();
+
+  /**
+   * The entry of a try statement has a branch to the block and to each
+   * catch clause or finally block.
+   */
+  syn nta CfgMarker TryStmt.tryEntryMarker() = new CfgMarker();
+
+  eq TryStmt.tryEntryMarker().succ() =
+      joinSets(Collections.singleton(getBlock().entry()), catchBranches());
+
+  eq MethodAccess.exceptionNode().succ() = exceptionBranches();
+
+  eq ThrowStmt.exceptionNode().succ() = exceptionBranches();
+
+  /**
+   * Gives the set of CFG nodes targeted by an exception thrown at this
+   * statement. This includes the catch blocks of the enclosing try statement as
+   * well as the finally block. Does not include any targets following a
+   * catch of Throwable.
+   */
+  inh Set<? extends CfgNode> MethodAccess.exceptionBranches();
+  inh Set<? extends CfgNode> TryStmt.exceptionBranches();
+  inh Set<? extends CfgNode> ThrowStmt.exceptionBranches();
+
+  inh TypeDecl TryStmt.typeThrowable();
+
+  eq TryStmt.getBlock().exceptionBranches() = catchBranches();
+
+  eq TryWithResources.getResource().exceptionBranches() = catchBranches();
+
+  /**
+   * Gives the set of CFG nodes targeted by an exception thrown inside this try
+   * statement. This includes the catch blocks of this try statements as
+   * well as the finally block. Does not include any targets following a
+   * catch of Throwable.
+   */
+  syn Set<? extends CfgNode> TryStmt.catchBranches() {
+    // Check for catch-all clauses (catching java.lang.Throwable).
+    Set<CfgNode> set = Collections.newSetFromMap(new IdentityHashMap<CfgNode, Boolean>());
+    for (CatchClause clause : getCatchClauseList()) {
+      set.add(clause.getBlock().entry());
+      if (clause instanceof BasicCatch
+          && ((BasicCatch) clause).getParameter().type() == typeThrowable()) {
+        // This is a catch-all clause: no other clauses after this can catch an
+        // exception.
+        return set;
+      }
+    }
+    if (hasNonEmptyFinally()) {
+      set.add(getExceptionHandler().entry());
+    }
+    return set;
+  }
+
+  eq TryStmt.getExceptionHandler().follow() = exceptionNode();
+  eq TryStmt.exceptionNode().succ() = exceptionBranches();
+
+  eq BodyDecl.getChild().exceptionBranches() = Collections.singleton(exit());
+
+  eq CompilationUnit.getChild().exceptionBranches() = Collections.emptySet();
+  eq TypeDecl.getChild().exceptionBranches() = Collections.emptySet();
+
+  public <U, V extends U> Set<U> Stmt.joinSets(Set<U> a, Set<V> b) {
+    Set<U> set = Collections.newSetFromMap(
+        new IdentityHashMap<U, Boolean>());
+    set.addAll(a);
+    set.addAll(b);
+    return set;
+  }
+
+  eq TryStmt.getBlock().follow() =
+      hasNonEmptyFinally()
+      ? getFinally().entry()
+      : follow();
+
+  eq TryStmt.getCatchClause(int index).follow() =
+      hasNonEmptyFinally()
+      ? getFinally().entry()
+      : follow();
+
+  /** The CFG marker for this break statement.  */
+  syn nta CfgMarker BreakStmt.marker() = new CfgMarker();
+
+  eq BreakStmt.entry() = marker();
+  eq BreakStmt.marker().succ() =
+      hasFinally()
+      ? Collections.singleton(getFinally().entry())
+      : Collections.singleton(targetStmt().follow());
+  eq BreakStmt.marker().follow() =
+      hasFinally()
+      ? getFinally().entry()
+      : targetStmt().follow();
+
+  eq BreakStmt.getFinally().follow() = targetStmt().follow();
+
+  /** The CFG marker for this continue statement.  */
+  syn nta CfgMarker ContinueStmt.marker() = new CfgMarker();
+
+  eq ContinueStmt.entry() = marker();
+  eq ContinueStmt.marker().succ() =
+      hasFinally()
+      ? Collections.singleton(getFinally().entry())
+      : Collections.singleton(targetStmt().entry());
+  eq ContinueStmt.marker().follow() =
+      hasFinally()
+      ? getFinally().entry()
+      : targetStmt().entry();
+
+  eq ContinueStmt.getFinally().follow() = targetStmt().follow();
+
+  /** The CFG marker for this continue statement.  */
+  syn nta CfgMarker ReturnStmt.marker() = new CfgMarker();
+
+  eq ReturnStmt.entry() = marker();
+  eq ReturnStmt.marker().succ() =
+      hasResult()
+      ? Collections.singleton(getResult().entry())
+      : Collections.singleton(returnTarget());
+  eq ReturnStmt.marker().follow() =
+      hasResult()
+      ? getResult().entry()
+      : returnTarget();
+
+  eq ReturnStmt.getResult().follow() = returnTarget();
+
+  inh CompilationUnit ReturnStmt.compilationUnit();
+
+  syn CfgNode ReturnStmt.returnTarget() =
+      hasFinally()
+      ? getFinally().entry()
+      : methodExit();
+
+  eq ReturnStmt.getFinally().follow() = methodExit();
+
+  /**
+   * Finds the CFG exit node for the enclosing method, constructor, or
+   * initializer.
+   */
+  inh CfgNode ReturnStmt.methodExit();
+
+  eq BodyDecl.getChild().methodExit() = exit();
+
+  eq LambdaBody.getChild().methodExit() = exit();
+
+  eq ThrowStmt.entry() = getExpr().entry();
+
+  // Since we not have precise type lookups we need to approximate the possible
+  // exception branches.
+  eq ThrowStmt.getExpr().follow() = exceptionNode();
+
+  /**
+   * This node represents the control flow path taken when an exception
+   * interrupts the call.
+   */
+  syn nta CfgException MethodAccess.exceptionNode() = new CfgException();
+
+  /** This node represents the control flow following the exception.  */
+  syn nta CfgException ThrowStmt.exceptionNode() = new CfgException();
+
+  /**
+   * This node represents the control flow path taken if an exception
+   * is thrown in the start of a try statement.
+   */
+  syn nta CfgException TryStmt.exceptionNode() = new CfgException();
+
+  eq ExprStmt.entry() = getExpr().entry();
+
+  syn CfgNode Expr.entry();
+
+  eq ArrayAccess.entry() = getExpr().entry();
+
+  // All of these nodes are uninteresting for the simple CFG.
+  // Setting entry() = follow() makes sure the node is not included in the CFG.
+  eq AbstractWildcard.entry() = follow();
+  eq ClassAccess.entry() = follow();
+  eq DiamondAccess.entry() = follow();
+  eq PackageAccess.entry() = follow();
+  eq ParseName.entry() = follow(); // Always rewritten.
+  eq ParTypeAccess.entry() = follow();
+  eq SuperAccess.entry() = follow();
+  eq ThisAccess.entry() = follow();
+  eq VarAccess.entry() = follow();
+  eq TypeAccess.entry() = follow();
+  eq SyntheticTypeAccess.entry() = follow();
+  eq ClassReference.entry() = follow();
+  eq ArrayReference.entry() = follow();
+  eq LambdaExpr.entry() = follow();
+  eq TypeMethodReference.entry() = follow();
+  eq AmbiguousMethodReference.entry() = follow();
+
+  eq ParExpr.entry() = getExpr().entry();
+  eq CastExpr.entry() = getExpr().entry();
+  eq IntersectionCastExpr.entry() = getExpr().entry();
+  eq Unary.entry() = getOperand().entry();
+
+  eq ExprMethodReference.entry() = getExpr().entry();
+
+  /** The method call node for this method access. */
+  syn nta CfgMethodCall MethodAccess.call() = new CfgMethodCall();
+
+  eq MethodAccess.call().succ() =
+      isInsideTryBlockOrResource()
+      ? smallSet(exceptionNode(), follow())
+      : Collections.singleton(follow());
+
+  // If there are arguments control flow passes to the arguments first.
+  eq MethodAccess.entry() =
+      getNumArg() > 0
+      ? getArg(0).entry()
+      : call();
+
+  // If we have arguments the CfgMethodCall is placed after the last argument.
+  eq MethodAccess.getArg(int index).follow() =
+      index+1 < getNumArg()
+      ? getArg(index+1).entry()
+      : call();
+
+  // If there are arguments control flow passes to the arguments first.
+  eq ConstructorAccess.entry() =
+      getNumArg() > 0
+      ? getArg(0).entry()
+      : follow();
+
+  // If we have arguments the CfgMethodCall is placed after the last argument.
+  eq ConstructorAccess.getArg(int index).follow() =
+      index+1 < getNumArg()
+      ? getArg(index+1).entry()
+      : follow();
+
+  eq ClassInstanceExpr.entry() =
+      getNumArg() > 0
+      ? getArg(0).entry()
+      : getAccess().entry();
+
+  eq ClassInstanceExpr.getArg(int index).follow() =
+      index+1 < getNumArg()
+      ? getArg(index+1).entry()
+      : getAccess().entry();
+
+  eq ArrayInit.entry() =
+      getNumInit() > 0
+      ? getInit(0).entry()
+      : follow();
+
+  eq ArrayInit.getInit(int index).follow() =
+      index+1 < getNumInit()
+      ? getInit(index+1).entry()
+      : follow();
+
+  /** @return {@code true} if this statement is inside a try block. */
+  inh boolean MethodAccess.isInsideTryBlockOrResource();
+  inh boolean Stmt.isInsideTryBlockOrResource();
+
+  eq CompilationUnit.getChild().isInsideTryBlockOrResource() = false;
+  eq TypeDecl.getChild().isInsideTryBlockOrResource() = false;
+  eq BodyDecl.getChild().isInsideTryBlockOrResource() = false;
+  eq LambdaBody.getChild().isInsideTryBlockOrResource() = false;
+  eq TryStmt.getBlock().isInsideTryBlockOrResource() = true;
+  eq TryWithResources.getResource().isInsideTryBlockOrResource() = true;
+  eq NTAFinallyBlock.getChild().isInsideTryBlockOrResource() =
+      origin.getFinallyBlock().isInsideTryBlockOrResource();
+  eq Program.getChild().isInsideTryBlockOrResource() = false;
+
+  eq InstanceOfExpr.entry() = getExpr().entry();
+
+  eq AssignExpr.entry() = getSource().entry();
+
+  eq AssignExpr.getSource().follow() = getDest().entry();
+
+  eq Literal.entry() = follow();
+
+  eq ArrayCreationExpr.entry() =
+      hasArrayInit()
+      ? getArrayInit().entry()
+      : follow();
+
+  eq Binary.entry() = getLeftOperand().entry();
+  eq Binary.getLeftOperand().follow() = getRightOperand().entry();
+
+  eq Dot.entry() = getLeft().entry();
+  eq Dot.getLeft().follow() = getRight().entry();
+
+  /** The branch node for this conditional expression. */
+  syn nta CfgBranch ConditionalExpr.branch() = new CfgBranch();
+
+  /**
+   * The then-end node is a marker node marking the end of a then-branch in a conditional
+   * expression.
+   */
+  syn nta CfgMarker ConditionalExpr.thenEndMarker() = new CfgMarker();
+
+  /**
+   * The else-end node is a marker node marking the end of a else-branch in a conditional
+   * expression.
+   */
+  syn nta CfgMarker ConditionalExpr.elseEndMarker() = new CfgMarker();
+
+  eq ConditionalExpr.entry() = getCondition().entry();
+  eq ConditionalExpr.getCondition().follow() = branch();
+  eq ConditionalExpr.getTrueExpr().follow() = thenEndMarker();
+  eq ConditionalExpr.getFalseExpr().follow() = elseEndMarker();
+  eq ConditionalExpr.thenEndMarker().follow() = follow();
+  eq ConditionalExpr.elseEndMarker().follow() = follow();
+  eq ConditionalExpr.thenEndMarker().succ() = Collections.singleton(follow());
+  eq ConditionalExpr.elseEndMarker().succ() = Collections.singleton(follow());
+
+  eq ConditionalExpr.branch().succ() =
+      smallSet(getTrueExpr().entry(), getFalseExpr().entry());
+
+  /** The branch node for this statement. */
+  syn nta CfgBranch IfStmt.branch() = new CfgBranch();
+
+  /** The then-end node is a marker node marking the end of a then-branch in an if statement.  */
+  syn nta CfgMarker IfStmt.thenEndMarker() = new CfgMarker();
+
+  /** The else-end node is a marker node marking the end of a else-branch in an if statement.  */
+  syn nta CfgMarker IfStmt.elseEndMarker() = new CfgMarker();
+
+  eq IfStmt.entry() = getCondition().entry();
+
+  eq IfStmt.getCondition().follow() = branch();
+  eq IfStmt.getThen().follow() = thenEndMarker();
+  eq IfStmt.getElse().follow() = elseEndMarker();
+  eq IfStmt.thenEndMarker().follow() = follow();
+  eq IfStmt.elseEndMarker().follow() = follow();
+  eq IfStmt.thenEndMarker().succ() = Collections.singleton(follow());
+  eq IfStmt.elseEndMarker().succ() = Collections.singleton(follow());
+
+  eq IfStmt.branch().succ() =
+      hasElse()
+      ? smallSet(getThen().entry(), getElse().entry())
+      : smallSet(getThen().entry(), follow());
+
+  /** The branch node for this statement. */
+  syn nta CfgBranch ForStmt.branch() = new CfgBranch();
+
+  /** The CFG end marker for this loop. */
+  syn nta CfgMarker ForStmt.loopEndMarker() = new CfgMarker();
+
+  eq ForStmt.entry() =
+      getNumInitStmt() > 0
+      ? getInitStmt(0).entry()
+      : getCondition().entry();
+
+  eq ForStmt.getInitStmt(int index).follow() =
+      index+1 < getNumInitStmt()
+      ? getInitStmt(index+1).entry()
+      : getCondition().entry();
+
+  eq ForStmt.getCondition().follow() = branch();
+
+  eq ForStmt.getUpdateStmt(int index).follow() =
+      index+1 < getNumUpdateStmt()
+      ? getUpdateStmt(index+1).entry()
+      : getCondition().entry();
+
+  eq ForStmt.getStmt().follow() = loopEndMarker();
+
+  eq ForStmt.loopEndMarker().follow() =
+      getNumUpdateStmt() > 0
+      ? getUpdateStmt(0).entry()
+      : getCondition().entry();
+
+  eq ForStmt.loopEndMarker().succ() =
+      getNumUpdateStmt() > 0
+      ? Collections.singleton(getUpdateStmt(0).entry())
+      : Collections.singleton(getCondition().entry());
+
+  eq ForStmt.branch().succ() {
+    if (getCondition().isTrue()) {
+      return Collections.singleton(getStmt().entry());
+    } else if (getCondition().isFalse()) {
+      return Collections.singleton(follow());
+    } else {
+      return smallSet(getStmt().entry(), follow());
+    }
+  }
+
+  /** The branch node for this statement. */
+  syn nta CfgBranch EnhancedForStmt.branch() = new CfgBranch();
+
+  /** The CFG end marker for this loop. */
+  syn nta CfgMarker EnhancedForStmt.loopEndMarker() = new CfgMarker();
+
+  eq EnhancedForStmt.branch().succ() =
+      smallSet(getStmt().entry(), follow());
+
+  eq EnhancedForStmt.entry() = getExpr().entry();
+
+  eq EnhancedForStmt.getExpr().follow() = branch();
+
+  eq EnhancedForStmt.getStmt().follow() = loopEndMarker();
+
+  eq EnhancedForStmt.loopEndMarker().follow() = entry(); // Loop back.
+
+  eq EnhancedForStmt.loopEndMarker().succ() = Collections.singleton(entry());
+
+  /** The branch node for this statement. */
+  syn nta CfgBranch WhileStmt.branch() = new CfgBranch();
+
+  /** The CFG end marker for this loop. */
+  syn nta CfgMarker WhileStmt.loopEndMarker() = new CfgMarker();
+
+  eq WhileStmt.entry() = getCondition().entry();
+
+  eq WhileStmt.getCondition().follow() = branch();
+
+  eq WhileStmt.getStmt().follow() = loopEndMarker();
+
+  eq WhileStmt.loopEndMarker().follow() = entry(); // Loop back.
+
+  eq WhileStmt.loopEndMarker().succ() = Collections.singleton(entry());
+
+  eq WhileStmt.branch().succ() {
+    if (getCondition().isTrue()) {
+      return Collections.singleton(getStmt().entry());
+    } else if (getCondition().isFalse()) {
+      return Collections.singleton(follow());
+    } else {
+      return smallSet(getStmt().entry(), follow());
+    }
+  }
+
+  /** The branch node for this statement. */
+  syn nta CfgBranch DoStmt.branch() = new CfgBranch();
+
+  /** The CFG entry marker for this loop. */
+  syn nta CfgMarker DoStmt.doEntryMarker() = new CfgMarker();
+
+  eq DoStmt.entry() = doEntryMarker();
+
+  eq DoStmt.doEntryMarker().follow() = getStmt().entry();
+
+  eq DoStmt.doEntryMarker().succ() = Collections.singleton(getStmt().entry());
+
+  eq DoStmt.getStmt().follow() = getCondition().entry();
+
+  eq DoStmt.getCondition().follow() = branch();
+
+  // Loop back.
+  eq DoStmt.branch().succ() {
+    if (getCondition().isTrue()) {
+      return Collections.singleton(entry());
+    } else if (getCondition().isFalse()) {
+      return Collections.singleton(follow());
+    } else {
+      return smallSet(entry(), follow());
+    }
+  }
+
+  syn nta CfgBranch SwitchStmt.branch() = new CfgBranch();
+
+  eq SwitchStmt.entry() = getExpr().entry();
+
+  eq SwitchStmt.getExpr().follow() = branch();
+
+  eq SwitchStmt.branch().succ() {
+    Set<CfgNode> set = Collections.newSetFromMap(
+        new IdentityHashMap<CfgNode, Boolean>());
+    boolean hasDefault = false;
+    for (Stmt stmt : getBlock().getStmtList()) {
+      if (stmt instanceof Case) {
+        set.add(stmt.entry());
+        if (stmt instanceof DefaultCase) {
+          hasDefault = true;
+        }
+      }
+    }
+    if (!hasDefault) {
+      set.add(follow());
+    }
+    return set;
+  }
+
+  eq BodyDecl.getChild().follow() = exit();
+
+  eq Block.getStmt(int index).follow() =
+      index+1 < getNumStmt()
+      ? getStmt(index+1).entry()
+      : follow();
+
+  syn lazy CfgEntry LambdaBody.entry();
+  eq BlockLambdaBody.entry() = new CfgEntry(getBlock().entry());
+  eq ExprLambdaBody.entry() = new CfgEntry(getExpr().entry());
+
+  syn nta CfgExit LambdaBody.exit() = new CfgExit();
+
+  eq BlockLambdaBody.getBlock().follow() = exit();
+  eq ExprLambdaBody.getExpr().follow() = exit();
+
+  /** Find the method access which this call node is associated with. */
+  inh MethodAccess CfgMethodCall.methodAccess();
+  eq MethodAccess.call().methodAccess() = this;
+
+  /** Find the entry node to the CFG this statement belongs to.  */
+  inh lazy CfgEntry Stmt.cfg();
+  inh lazy CfgEntry Expr.cfg();
+
+  eq BodyDecl.getChild().cfg() = entry();
+  eq LambdaBody.getChild().cfg() = entry();
+
+  eq CompilationUnit.getChild().cfg() {
+    throw new Error("Not in a CFG.");
+  }
+
+  /**
+   * Predecessors of this node in the CFG.  Predecessors are filled in when
+   * accessing the CFG through the cfg() attribute.
+   */
+  protected Collection<CfgNode> CfgNode.predecessors =
+      new LinkedList<CfgNode>();
+
+  private boolean CfgEntry.initializedPredecessors = false;
+
+  /** Initializes the predecessor sets for each node in a CFG. */
+  protected void CfgEntry.initPredecessors() {
+    if (!initializedPredecessors) {
+      initializedPredecessors = true;
+      Queue<CfgNode> queue = new LinkedList<CfgNode>();
+      queue.add(this);
+      while (!queue.isEmpty()) {
+        CfgNode node = queue.poll();
+        for (CfgNode succ : node.successors()) {
+          if (succ.predecessors.isEmpty()) {
+            queue.add(succ);
+          }
+          succ.predecessors.add(node);
+        }
+      }
+    }
+  }
+}
diff --git a/simplecfg/src/main/jastadd/VariableDeclarationScope.jrag b/simplecfg/src/main/jastadd/VariableDeclarationScope.jrag
new file mode 100644
index 0000000000000000000000000000000000000000..653d51169e81577c0611a9970b4c60505790cf36
--- /dev/null
+++ b/simplecfg/src/main/jastadd/VariableDeclarationScope.jrag
@@ -0,0 +1,75 @@
+/**
+ * Copyright 2015 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * This aspect adds attributes to check if a variable was declared inside an a statement represented
+ * by a particular CFG node.
+ */
+aspect VariableDeclarationScope {
+
+  /** @return {@code true} if this method access has the given variable as receiver. */
+  syn boolean MethodAccess.hasReceiver(Variable receiver) =
+      hasPrevExpr() && prevExpr().isVariable(receiver);
+
+  /**
+   * Test if the CFG node is tied to a statement that declares the variable,
+   * or if the declaration of the variable is somewhere inside the statement this
+   * branch represents.
+   */
+  syn boolean CfgNode.isDeclarationOf(Variable var) = false;
+
+  eq CfgBranch.isDeclarationOf(Variable var) =
+      branchDeclaresVariable(var)
+      || variableDeclaredInsideStatement(var);
+
+  syn boolean CfgBranch.variableDeclaredInsideStatement(Variable var) {
+    Stmt stmt = hostStatement();
+    ASTNode node = (ASTNode) var;
+    while (node != stmt && node != null) {
+      node = node.getParent();
+    }
+    return node == stmt;
+  }
+
+  /** The statement this branch represents.  */
+  inh Stmt CfgBranch.hostStatement();
+
+  /** The statement this expression is part of.  */
+  inh Stmt Expr.hostStatement();
+  eq Stmt.getChild().hostStatement() = this;
+  eq Program.getChild().hostStatement() = null;
+
+  eq IfStmt.branch().hostStatement() = this;
+  eq ConditionalExpr.branch().hostStatement() = hostStatement();
+  eq ForStmt.branch().hostStatement() = this;
+  eq WhileStmt.branch().hostStatement() = this;
+  eq DoStmt.branch().hostStatement() = this;
+  eq EnhancedForStmt.branch().hostStatement() = this;
+  eq SwitchStmt.branch().hostStatement() = this;
+
+  /** Test if the CFG node is tied to a statement that declares the variable. */
+  inh boolean CfgBranch.branchDeclaresVariable(Variable var);
+
+  eq EnhancedForStmt.branch().branchDeclaresVariable(Variable var) =
+      getVariableDecl() == var;
+  eq IfStmt.branch().branchDeclaresVariable(Variable var) = false;
+  eq ConditionalExpr.branch().branchDeclaresVariable(Variable var) = false;
+  eq ForStmt.branch().branchDeclaresVariable(Variable var) = false;
+  eq WhileStmt.branch().branchDeclaresVariable(Variable var) = false;
+  eq DoStmt.branch().branchDeclaresVariable(Variable var) = false;
+  eq SwitchStmt.branch().branchDeclaresVariable(Variable var) = false;
+
+}
diff --git a/simplecfg/src/main/java/com/google/simplecfg/ExtendJAnalyzerFrontend.java b/simplecfg/src/main/java/com/google/simplecfg/ExtendJAnalyzerFrontend.java
new file mode 100644
index 0000000000000000000000000000000000000000..fb9ad9d1fbae37eb5cc851e2b5f80028bf53fa0e
--- /dev/null
+++ b/simplecfg/src/main/java/com/google/simplecfg/ExtendJAnalyzerFrontend.java
@@ -0,0 +1,155 @@
+/**
+ * Copyright 2015 Google Inc. All Rights Reserved.
+ * <p>
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.google.simplecfg;
+
+import org.extendj.ast.*;
+
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+
+/**
+ * Produces findings using analyzers implemented in the ExtendJ compiler.
+ */
+public class ExtendJAnalyzerFrontend extends Frontend {
+
+  private final JavaParser javaParser;
+  private final BytecodeReader bytecodeReader;
+  private final Collection<ExtendJFinding> findings = new ArrayList<ExtendJFinding>();
+
+  /** Create new analyzer instance.  */
+  public ExtendJAnalyzerFrontend() {
+    super("ExtendJ Analyzer", "v1.0");
+    javaParser = new JavaParser() {
+      @Override
+      public CompilationUnit parse(InputStream is, String fileName)
+          throws IOException, beaver.Parser.Exception {
+        return new org.extendj.parser.JavaParser().parse(is, fileName);
+      }
+    };
+    bytecodeReader = new BytecodeReader() {
+      @Override
+      public CompilationUnit read(InputStream is, String fullName, Program p)
+          throws FileNotFoundException, IOException {
+        return new BytecodeParser(is, fullName).parse(null, null, p);
+      }
+    };
+  }
+
+  /**
+   * Analyze a single file for findings and return the findings in a collection.
+   */
+  public static Collection<ExtendJFinding> analyzeFile(final String path) throws Error {
+    ExtendJAnalyzerFrontend checker = new ExtendJAnalyzerFrontend();
+    int result = checker.run(new String[]{path});
+    if (result != EXIT_SUCCESS) {
+      throw new Error("exit code: " + result);
+    }
+    return checker.findings;
+  }
+
+  /**
+   * Returns the list of findings from the analyzed source files.
+   *
+   * <p>Used by ExtendJAnalyzerMain to print the generated findings on stdout.
+   */
+  Collection<ExtendJFinding> getFindings() {
+    return findings;
+  }
+
+  /**
+   * Run the Java checker.
+   * @param args command-line arguments
+   * @return 0 on success, 1 on error, 2 on configuration error, 3 on system
+   */
+  public int run(String args[]) {
+    return run(args, bytecodeReader, javaParser);
+  }
+
+  @Override
+  protected int processCompilationUnit(CompilationUnit unit) {
+    if (unit.fromSource()) {
+      findings.addAll(unit.findings());
+    }
+    return EXIT_SUCCESS;
+  }
+
+  @Override
+  public int run(String[] args, BytecodeReader reader, JavaParser parser) {
+    program.resetStatistics();
+    program.setTypeLookupFilter(Program.ANALYZER_TYPE_FILTER);
+    program.initBytecodeReader(bytecodeReader);
+    program.initJavaParser(javaParser);
+
+    initOptions();
+    int argResult = processArgs(args);
+    if (argResult != 0) {
+      return argResult;
+    }
+
+    if (program.options().hasOption("-version")) {
+      printVersion();
+      return EXIT_SUCCESS;
+    }
+
+    Collection<String> files = program.options().files();
+    if (program.options().hasOption("-help") || files.isEmpty()) {
+      printUsage();
+      return EXIT_SUCCESS;
+    }
+
+    return run(files);
+  }
+
+  private int run(Collection<String> files) {
+    try {
+      for (String file : files) {
+        // Calling addSourceFile will parse the file and add it to the program AST.
+        program.addSourceFile(file);
+      }
+
+      // Process source compilation units.
+      int compileResult = EXIT_SUCCESS;
+
+      Iterator<CompilationUnit> iter = program.compilationUnitIterator();
+      while (iter.hasNext()) {
+        CompilationUnit unit = iter.next();
+        int result = processCompilationUnit(unit);
+        if (result != EXIT_SUCCESS) {
+          compileResult = result;
+          if (compileResult == EXIT_UNHANDLED_ERROR) {
+            // Stop immediately when an unhandled error is encountered.
+            return compileResult;
+          }
+        }
+      }
+
+      if (compileResult != EXIT_SUCCESS) {
+        return compileResult;
+      }
+    } catch (IOException e) {
+      throw new Error(e);
+    } finally {
+      if (program.options().hasOption("-profile")) {
+        program.printStatistics(System.out);
+      }
+    }
+    return EXIT_SUCCESS;
+  }
+}
diff --git a/simplecfg/src/main/java/com/google/simplecfg/ExtendJAnalyzerMain.java b/simplecfg/src/main/java/com/google/simplecfg/ExtendJAnalyzerMain.java
new file mode 100644
index 0000000000000000000000000000000000000000..78c7422d7134857755ddb5d67d85107cab81ddf7
--- /dev/null
+++ b/simplecfg/src/main/java/com/google/simplecfg/ExtendJAnalyzerMain.java
@@ -0,0 +1,40 @@
+/**
+ * Copyright 2015 Google Inc. All Rights Reserved.
+ * <p>
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.google.simplecfg;
+
+import org.extendj.ast.ExtendJFinding;
+
+/**
+ * Produces findings using analyzers implemented in the ExtendJ compiler.
+ */
+public class ExtendJAnalyzerMain {
+
+  /**
+   * Run the ExtendJ analyzer on the files supplied on the command line.
+   * @param args command-line arguments
+   */
+  public static void main(String[] args) {
+    ExtendJAnalyzerFrontend checker = new ExtendJAnalyzerFrontend();
+    int result = checker.run(args);
+    if (result != 0) {
+      System.exit(result);
+    }
+    System.out.println("Found " + checker.getFindings().size() + " findings.");
+    for (ExtendJFinding finding : checker.getFindings()) {
+      System.out.println(finding);
+    }
+  }
+}
diff --git a/simplecfg/src/main/java/com/google/simplecfg/PrintCfg.java b/simplecfg/src/main/java/com/google/simplecfg/PrintCfg.java
new file mode 100644
index 0000000000000000000000000000000000000000..31c69ffe3e6f2815fd33a1c121c48c5ce7e669d0
--- /dev/null
+++ b/simplecfg/src/main/java/com/google/simplecfg/PrintCfg.java
@@ -0,0 +1,75 @@
+/**
+ * Copyright 2015 Google Inc. All Rights Reserved.
+ * <p>
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.google.simplecfg;
+
+import org.extendj.ast.BodyDecl;
+import org.extendj.ast.CompilationUnit;
+import org.extendj.ast.Program;
+import org.extendj.ast.TypeDecl;
+import org.extendj.parser.JavaParser;
+
+import java.io.FileInputStream;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * Prints a Simplified Control Flow Graph for the first method in a Java program.
+ */
+public class PrintCfg {
+
+  public static void main(String args[]) {
+    int exitCode = new PrintCfg().run(args);
+    if (exitCode != 0) {
+      System.exit(exitCode);
+    }
+  }
+
+  private int run(String args[]) {
+    Set<String> argSet = new HashSet<>();
+    for (String arg : args) {
+      argSet.add(arg);
+    }
+    boolean reverse = argSet.contains("-reverse");
+    for (String path : args) {
+      if (!path.equals("-reverse")) {
+        try {
+          Program program = new Program();
+          program.setTypeLookupFilter(Program.BASE_LIBRARY_FILTER);
+          CompilationUnit unit = new JavaParser().parse(new FileInputStream(path), path);
+          // Attach the parsed unit to a program node so we have a healthy AST.
+          program.addCompilationUnit(unit);
+          // Ensure compilation unit is set to final. This is important to get
+          // caching to work right in the AST.
+          unit = program.getCompilationUnit(0);
+          for (TypeDecl type : unit.getTypeDeclList()) {
+            for (BodyDecl bd : type.getBodyDeclList()) {
+              if (reverse) {
+                bd.printReverseCfg();
+              } else {
+                bd.printCfg();
+              }
+            }
+          }
+        } catch (Exception e) {
+          System.err.println("Failed to parse input file: " + path);
+          e.printStackTrace();
+          return 1;
+        }
+      }
+    }
+    return 0;
+  }
+}
diff --git a/simplecfg/src/main/java/com/google/simplecfg/TestGenerator.java b/simplecfg/src/main/java/com/google/simplecfg/TestGenerator.java
new file mode 100644
index 0000000000000000000000000000000000000000..83172eaf4b0b76d19af80af6476ec4fabb661db7
--- /dev/null
+++ b/simplecfg/src/main/java/com/google/simplecfg/TestGenerator.java
@@ -0,0 +1,66 @@
+/**
+ * Copyright 2015 Google Inc. All Rights Reserved.
+ * <p>
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.google.simplecfg;
+
+import org.extendj.ast.BodyDecl;
+import org.extendj.ast.CompilationUnit;
+import org.extendj.ast.Program;
+import org.extendj.ast.TypeDecl;
+import org.extendj.parser.JavaParser;
+
+import java.io.FileInputStream;
+
+/** Generate test cases for the first CFG of each input class. */
+class TestGenerator {
+
+  public static void main(String args[]) {
+    int exitCode = new TestGenerator().run(args);
+    if (exitCode != 0) {
+      System.exit(exitCode);
+    }
+  }
+
+  private int run(String args[]) {
+    for (String path : args) {
+      try {
+        Program program = new Program();
+        program.setTypeLookupFilter(Program.BASE_LIBRARY_FILTER);
+        CompilationUnit unit = new JavaParser().parse(new FileInputStream(path), path);
+        // Attach the parsed unit to a program node so we have a healthy AST.
+        program.addCompilationUnit(unit);
+        // Ensure compilation unit is set to final. This is important to get
+        // caching to work right in the AST.
+        unit = program.getCompilationUnit(0);
+        if (unit.getNumTypeDecl() < 1) {
+          System.err.println("Error: no classes declared in file " + path);
+          return 1;
+        }
+        TypeDecl type = unit.getTypeDecl(0);
+        if (type.getNumBodyDecl() < 1) {
+          System.err.println("Error: first class has no body decls in file " + path);
+          return 1;
+        }
+        BodyDecl bd = type.getBodyDecl(0);
+        bd.printCfgTest();
+      } catch (Exception e) {
+        System.err.println("Failed to parse input file: " + path);
+        e.printStackTrace();
+        return 1;
+      }
+    }
+    return 0;
+  }
+}
diff --git a/simplecfg/src/test/java/com/google/simplecfg/AlreadyClosedTest.java b/simplecfg/src/test/java/com/google/simplecfg/AlreadyClosedTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..687f228b68c4c4335c15c8c051b8c1b9ccac326d
--- /dev/null
+++ b/simplecfg/src/test/java/com/google/simplecfg/AlreadyClosedTest.java
@@ -0,0 +1,64 @@
+/**
+ * Copyright 2015 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.google.simplecfg;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import org.extendj.ast.Program;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import java.util.Collection;
+
+/** Integration tests for the already-closed checker. */
+@RunWith(JUnit4.class)
+public class AlreadyClosedTest {
+
+  @Test public void test01() {
+    Collection<String> findings = StmtCfgTest.findings("Close01", Program.NO_TYPE_FILTER);
+    assertThat(findings).containsExactly(
+        "testdata/Close01.javax:23:5: close() may have already been called on writer at this point");
+  }
+
+  /**
+   * Test that an already-closed finding was generated on the correct line for a simple positive
+   * test case.
+   *
+   * <p>This test case effectively checks that the type analysis works because the type used is
+   * java.io.Writer, and the analyzer will check if that type is a subtype of java.io.Closeable.
+   */
+  @Test public void writer01() {
+    Collection<String> findings = StmtCfgTest.findings("AlreadyClosedWriter01",
+        Program.NO_TYPE_FILTER);
+    assertThat(findings).hasSize(1);
+    assertThat(findings).containsExactly(
+        "testdata/AlreadyClosedWriter01.javax:27:5: close() may have already been called on writer at this point");
+  }
+
+  @Test public void controlFlow01() {
+    Collection<Integer> lines = StmtCfgTest.findingLines("AlreadyClosedControlFlow01",
+        Program.NO_TYPE_FILTER);
+    assertThat(lines).containsExactly(34, 60, 68, 79, 84, 103, 118);
+  }
+
+  @Test public void negativeFindings01() {
+    Collection<String> findings = StmtCfgTest.findings("AlreadyClosedNegativeFindings01",
+        Program.NO_TYPE_FILTER);
+    assertThat(findings).isEmpty();
+  }
+}
diff --git a/simplecfg/src/test/java/com/google/simplecfg/NullableDereferenceTest.java b/simplecfg/src/test/java/com/google/simplecfg/NullableDereferenceTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..5d49861b32151b1edd3f9f983d51712fda724839
--- /dev/null
+++ b/simplecfg/src/test/java/com/google/simplecfg/NullableDereferenceTest.java
@@ -0,0 +1,145 @@
+/**
+ * Copyright 2016 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.google.simplecfg;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import org.extendj.ast.CompilationUnit;
+import org.extendj.ast.ExtendJFinding;
+import org.extendj.ast.Program;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import java.util.Collection;
+
+/**
+ * Integration tests for the nullable dereference checker.
+ *
+ * <p>Tests are grouped by the type of null guard used, and then split arbitrarily into separate
+ * tests/files in order to not have too many positive/negative finding tests in a single test file.
+ */
+@RunWith(JUnit4.class)
+public class NullableDereferenceTest {
+
+  @Test public void suggestedFixEndsWithNewline() {
+    CompilationUnit unit = StmtCfgTest.parseFile("NullableNullGuard01",
+        Program.ANALYZER_TYPE_FILTER);
+    Collection<ExtendJFinding> findings = unit.findings();
+    assertThat(findings).isNotEmpty();
+    ExtendJFinding finding = findings.iterator().next();
+    assertThat(finding.fixes).hasSize(1);
+    assertThat(finding.fixes.iterator().next().newText).endsWith("\n");
+  }
+
+  @Test public void nullGuards01() {
+    Collection<String> findings = StmtCfgTest.findings("NullableNullGuard01");
+    assertThat(findings).containsExactly(
+        "testdata/NullableNullGuard01.javax:42:25: Dereferencing p, which was declared @Nullable.",
+        "testdata/NullableNullGuard01.javax:49:12: Dereferencing p, which was declared @Nullable.",
+        "testdata/NullableNullGuard01.javax:62:12: Dereferencing p, which was declared @Nullable.",
+        "testdata/NullableNullGuard01.javax:93:12: Dereferencing q, which was declared @Nullable."
+        );
+  }
+
+  @Test public void nullGuards02() {
+    Collection<String> findings = StmtCfgTest.findings("NullableNullGuard02");
+    assertThat(findings).containsExactly(
+        "testdata/NullableNullGuard02.javax:49:7: Dereferencing p, which was declared @Nullable.",
+        "testdata/NullableNullGuard02.javax:54:7: Dereferencing p, which was declared @Nullable."
+        );
+  }
+
+  @Test public void nullGuards03() {
+    Collection<Integer> lines = StmtCfgTest.findingLines("NullableNullGuard03",
+        Program.ANALYZER_TYPE_FILTER);
+    assertThat(lines).containsExactly(28, 34, 41, 48, 113, 119);
+  }
+
+  @Test public void methodNullGuard01() {
+    Collection<String> findings = StmtCfgTest.findings("NullableMethodNullGuard01");
+    assertThat(findings).isEmpty();
+  }
+
+  @Test public void dataflow01() {
+    Collection<String> findings = StmtCfgTest.findings("NullableDataflow01");
+    assertThat(findings).containsExactly(
+        "testdata/NullableDataflow01.javax:27:7: Dereferencing p, which was declared @Nullable.",
+        "testdata/NullableDataflow01.javax:35:7: Dereferencing p, which was declared @Nullable."
+        );
+  }
+
+  @Test public void instanceOf() {
+    Collection<String> findings = StmtCfgTest.findings("NullableInstanceOf");
+    assertThat(findings).isEmpty();
+  }
+
+  @Test public void variableArity() {
+    Collection<String> findings = StmtCfgTest.findings("NullableVariableArity");
+    assertThat(findings).isEmpty();
+  }
+
+  @Test public void nullableDereference01() {
+    Collection<String> findings = StmtCfgTest.findings("NullableDereference01");
+    assertThat(findings).containsExactly(
+        "testdata/NullableDereference01.javax:27:12: Dereferencing p, which was declared @Nullable.",
+        "testdata/NullableDereference01.javax:31:12: Dereferencing p, which was declared @Nullable."
+        );
+  }
+
+  /** Test false positive for GitHub issue #10. */
+  @Test public void issue10() {
+    Collection<String> findings = StmtCfgTest.findings("NullableDereferenceIssue10");
+    assertThat(findings).containsExactly(
+        "testdata/NullableDereferenceIssue10.javax:34:31: Dereferencing y, which was declared @Nullable."
+        );
+  }
+
+  @Test public void issue11() {
+    Collection<String> findings = StmtCfgTest.findings("NullableDereferenceIssue11");
+    assertThat(findings).isEmpty();
+  }
+
+  @Test public void issue12() {
+    Collection<String> findings = StmtCfgTest.findings("NullableDereferenceIssue12");
+    assertThat(findings).isEmpty();
+  }
+
+  @Test public void eqExpr() {
+    Collection<String> findings = StmtCfgTest.findings("NullableDereferenceEqExpr");
+    assertThat(findings).isEmpty();
+  }
+
+  @Test public void neExpr() {
+    Collection<String> findings = StmtCfgTest.findings("NullableDereferenceNeExpr");
+    assertThat(findings).isEmpty();
+  }
+
+  @Test public void methodCall() {
+    Collection<String> findings = StmtCfgTest.findings("NullableDereferenceMethodCall");
+    assertThat(findings).containsExactly(
+        "testdata/NullableDereferenceMethodCall.javax:41:16: "
+        + "Dereferencing p, which was declared @Nullable.");
+  }
+
+  @Test public void issue13() {
+    Collection<String> findings = StmtCfgTest.findings("NullableDereferenceIssue13");
+    assertThat(findings).containsExactly(
+        "testdata/NullableDereferenceIssue13.javax:33:7: "
+        + "Dereferencing obj, which was declared @Nullable.");
+  }
+}
diff --git a/simplecfg/src/test/java/com/google/simplecfg/StmtCfgTest.java b/simplecfg/src/test/java/com/google/simplecfg/StmtCfgTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..974b17485fea1dffefea174725b1c4ea6303a9e4
--- /dev/null
+++ b/simplecfg/src/test/java/com/google/simplecfg/StmtCfgTest.java
@@ -0,0 +1,658 @@
+/**
+ * Copyright 2015 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.google.simplecfg;
+
+import static com.google.common.truth.Truth.assertThat;
+import static org.junit.Assert.fail;
+
+import org.extendj.ast.BytecodeParser;
+import org.extendj.ast.BytecodeReader;
+import org.extendj.ast.CfgNode;
+import org.extendj.ast.CompilationUnit;
+import org.extendj.ast.ExtendJFinding;
+import org.extendj.ast.FileClassSource;
+import org.extendj.ast.JavaParser;
+import org.extendj.ast.Program;
+import org.extendj.ast.SourceFolderPath;
+import org.extendj.ast.TypeLookupFilter;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.InputStream;
+import java.io.IOException;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.Map;
+import java.util.Set;
+
+/** Tests for simplified Control Flow Graphs built for methods/constructors/initializers.  */
+@RunWith(JUnit4.class)
+public class StmtCfgTest {
+
+  /** Helper method to parse an ExtendJ compilation unit from a file.  */
+  protected static CompilationUnit parseFile(String filename, TypeLookupFilter typeFilter) {
+    String path = "testdata/" + filename + ".javax";
+    try {
+      JavaParser javaParser = new JavaParser() {
+        @Override
+        public CompilationUnit parse(java.io.InputStream is, String fileName)
+            throws IOException, beaver.Parser.Exception {
+          return new org.extendj.parser.JavaParser().parse(is, fileName);
+        }
+      };
+      BytecodeReader bytecodeReader = new BytecodeReader() {
+        @Override
+        public CompilationUnit read(InputStream is, String fullName, Program p)
+        throws FileNotFoundException, IOException {
+          return new BytecodeParser(is, fullName).parse(null, null, p);
+        }
+      };
+      Program program = new Program();
+      program.initBytecodeReader(bytecodeReader);
+      program.initJavaParser(javaParser);
+      program.setTypeLookupFilter(typeFilter);
+      CompilationUnit unit = javaParser.parse(new FileInputStream(path), path);
+      // Attach the parsed unit to a program node so we have a healthy AST.
+      program.addCompilationUnit(unit);
+      // Ensure compilation unit is set to final. This is important to get
+      // caching to work right in the AST.
+      unit = program.getCompilationUnit(0);
+      unit.setClassSource(new FileClassSource(new SourceFolderPath("testdata"), path));
+      unit.setFromSource(true);
+      return unit;
+    } catch (Exception e) {
+      e.printStackTrace();
+      fail("failed to parse test input file: " + path);
+    }
+    // Failed.
+    return null;
+  }
+
+  /** Helper to get the findings for a given file. */
+  protected static Collection<String> findings(String filename) {
+    return findings(filename, Program.ANALYZER_TYPE_FILTER);
+  }
+
+  /** Helper to get the findings for a given file. */
+  protected static Collection<String> findings(String filename, TypeLookupFilter typeFilter) {
+    CompilationUnit unit = StmtCfgTest.parseFile(filename, typeFilter);
+    Collection<String> findings = new HashSet<String>();
+    for (ExtendJFinding finding : unit.findings()) {
+      findings.add(finding.toString());
+    }
+    return findings;
+  }
+
+  /** Helper to get the line numbers where findings were reported for a given file. */
+  protected static Collection<Integer> findingLines(String filename, TypeLookupFilter typeFilter) {
+    CompilationUnit unit = StmtCfgTest.parseFile(filename, typeFilter);
+    Collection<Integer> lines = new LinkedList<>();
+    for (ExtendJFinding finding : unit.findings()) {
+      lines.add(finding.startLine);
+    }
+    return lines;
+  }
+
+  private static CfgNode parseCfg(String filename) {
+    CompilationUnit unit = parseFile(filename, Program.BASE_LIBRARY_FILTER);
+    assertThat(unit.getTypeDeclList()).isNotEmpty();
+    assertThat(unit.getTypeDecl(0).getBodyDeclList()).isNotEmpty();
+    return unit.getTypeDecl(0).getBodyDecl(0).entry();
+  }
+
+  /**
+   * Assert and return a single successor of the node.
+   */
+  private static CfgNode succ(CfgNode node, String successor) {
+    assertThat(cfgNames(node.successors())).containsExactly(successor);
+    return node.successors().iterator().next();
+  }
+
+  /**
+   * Assert the successors of the node and return them in an array
+   * using the same ordering as the input array.
+   */
+  private static CfgNode[] succ(CfgNode node, String... successors) {
+    assertThat(cfgNames(node.successors())).containsExactly((Object[]) successors);
+
+    // Ensure no duplicate successor names.
+    Set<String> dups = new HashSet<String>();
+    for (String succ : successors) {
+      if (dups.contains(succ)) {
+        fail("can not assert successors with duplicate names");
+      }
+      dups.add(succ);
+    }
+
+    Map<String, CfgNode> successorMap = new HashMap<>();
+    for (CfgNode successor : node.successors()) {
+      successorMap.put(successor.toString(), successor);
+    }
+
+    CfgNode[] result = new CfgNode[successors.length];
+    for (int i = 0; i < successors.length; ++i) {
+      result[i] = successorMap.get(successors[i]);
+    }
+    return result;
+  }
+
+  /** Convert a collection of CfgNodes to a collection of of the node names.  */
+  private static Collection<String> cfgNames(Iterable<? extends CfgNode> cfgs) {
+    // Use a linked list because we need to preserve duplicate names.
+    // TODO(joqvist): Use Java 8 stream api to map CfgNode -> String.
+    Collection<String> names = new LinkedList<>();
+    for (CfgNode cfg : cfgs) {
+      names.add(cfg.toString());
+    }
+    return names;
+  }
+
+  @Test public void ifStmt01() {
+    CfgNode entry = parseCfg("IfStmt01");
+    CfgNode call1 = succ(entry, "call1()");
+    CfgNode branch = succ(call1, "if (call1())");
+    CfgNode[] targets = succ(branch, "onTrue()", "exit");
+    CfgNode thenEnd = succ(targets[0], "then-end");
+    assertThat(succ(thenEnd, "exit")).isSameAs(targets[1]);
+  }
+
+  @Test public void ifStmt02() {
+    CfgNode entry = parseCfg("IfStmt02");
+    CfgNode call1 = succ(entry, "call1()");
+    CfgNode branch = succ(call1, "if (call1())");
+    CfgNode[] targets = succ(branch, "onTrue()", "onFalse()");
+    CfgNode thenEnd = succ(targets[0], "then-end");
+    CfgNode elseEnd = succ(targets[1], "else-end");
+    assertThat(succ(thenEnd, "exit")).isSameAs(succ(elseEnd, "exit"));
+  }
+
+  @Test public void ifStmt03() {
+    CfgNode entry = parseCfg("IfStmt03");
+    CfgNode call1 = succ(entry, "call1()");
+    CfgNode branch = succ(call1, "if (call1())");
+    CfgNode[] targets = succ(branch, "onTrue()", "onFalse()");
+    succ(succ(targets[0], "then-end"), "exit");
+    succ(targets[1], "call2()");
+  }
+
+  @Test public void ifStmt04() {
+    CfgNode entry = parseCfg("IfStmt04");
+    CfgNode call1 = succ(entry, "call1()");
+    CfgNode branch = succ(call1, "if (call1())");
+    CfgNode[] targets = succ(branch, "onTrue()", "onFalse()");
+    succ(targets[0], "call2()");
+    succ(succ(targets[1], "else-end"), "exit");
+  }
+
+  @Test public void forStmt01() {
+    CfgNode entry = parseCfg("ForStmt01");
+    CfgNode call1 = succ(entry, "call1()");
+    CfgNode branch = succ(call1, "for (call1())");
+    CfgNode[] targets = succ(branch, "for-end", "exit");
+    assertThat(succ(targets[0], "call1()")).isSameAs(call1);
+  }
+
+  @Test public void forStmt02() {
+    CfgNode entry = parseCfg("ForStmt02");
+    CfgNode call1 = succ(entry, "call1()");
+    CfgNode branch = succ(call1, "for (call1())");
+    CfgNode[] targets = succ(branch, "call2()", "exit");
+    CfgNode forEnd = succ(targets[0], "for-end");
+    assertThat(succ(forEnd, "call1()")).isSameAs(call1);
+  }
+
+  @Test public void forStmt03() {
+    CfgNode entry = parseCfg("ForStmt03");
+    CfgNode init = succ(entry, "init()");
+    CfgNode cond = succ(init, "cond()");
+    CfgNode branch = succ(cond, "for (cond())");
+    CfgNode[] targets = succ(branch, "stmt()", "exit");
+    CfgNode stmt = targets[0];
+    CfgNode forEnd = succ(stmt, "for-end");
+    CfgNode update = succ(forEnd, "update()");
+    assertThat(succ(update, "cond()")).isSameAs(cond);
+  }
+
+  @Test public void forStmt04() {
+    CfgNode entry = parseCfg("ForStmt04");
+    CfgNode i = succ(entry, "i()");
+    CfgNode j = succ(i, "j()");
+    CfgNode cond = succ(j, "cond()");
+    CfgNode branch = succ(cond, "for (cond())");
+    CfgNode[] targets = succ(branch, "stmt()", "exit");
+    CfgNode stmt = targets[0];
+    CfgNode forEnd = succ(stmt, "for-end");
+    CfgNode u1 = succ(forEnd, "u1()");
+    CfgNode u2 = succ(u1, "u2()");
+    CfgNode u3 = succ(u2, "u3()");
+    assertThat(succ(u3, "cond()")).isSameAs(cond);
+  }
+
+  @Test public void forStmt05() {
+    CfgNode entry = parseCfg("ForStmt05");
+    CfgNode i = succ(entry, "i()");
+    CfgNode branch = succ(i, "for (false)");
+    CfgNode y = succ(branch, "y()");
+    succ(y, "exit");
+  }
+
+  @Test public void whileStmt01() {
+    CfgNode entry = parseCfg("WhileStmt01");
+    CfgNode branch = succ(entry, "while (true)");
+    CfgNode whileEnd = succ(branch, "while-end");
+    assertThat(succ(whileEnd, "while (true)")).isSameAs(branch);
+  }
+
+  @Test public void whileStmt02() {
+    CfgNode entry = parseCfg("WhileStmt02");
+    CfgNode cond = succ(entry, "cond()");
+    CfgNode branch = succ(cond, "while (cond())");
+    CfgNode[] targets = succ(branch, "while-end", "exit");
+    assertThat(succ(targets[0], "cond()")).isSameAs(cond);
+  }
+
+  @Test public void whileStmt03() {
+    CfgNode entry = parseCfg("WhileStmt03");
+    CfgNode whileBranch = succ(entry, "while (true)");
+    CfgNode cond = succ(whileBranch, "cond()");
+    CfgNode ifBranch = succ(cond, "if (cond())");
+    CfgNode[] whileSucc = succ(ifBranch, "while-end", "break");
+    assertThat(succ(whileSucc[0], "while (true)")).isSameAs(whileBranch);
+    succ(succ(whileSucc[1], "tail()"), "exit");
+  }
+
+  @Test public void whileStmt04() {
+    CfgNode entry = parseCfg("WhileStmt04");
+    CfgNode whileBranch = succ(entry, "while (true)");
+    CfgNode cond = succ(whileBranch, "cond()");
+    CfgNode ifBranch = succ(cond, "if (cond())");
+    CfgNode[] ifSucc = succ(ifBranch, "continue", "x()");
+    assertThat(succ(ifSucc[0], "while (true)")).isSameAs(whileBranch);
+    CfgNode y = succ(succ(ifSucc[1], "break"), "y()");
+    succ(y, "exit");
+  }
+
+  @Test public void whileStmt05() {
+    CfgNode entry = parseCfg("WhileStmt05");
+    CfgNode branch = succ(entry, "while (false)");
+    CfgNode y = succ(branch, "y()");
+    succ(y, "exit");
+  }
+
+  @Test public void doStmt01() {
+    CfgNode entry = parseCfg("DoStmt01");
+    CfgNode doEntry = succ(entry, "do-entry");
+    CfgNode x = succ(doEntry, "x()");
+    CfgNode y = succ(x, "y()");
+    CfgNode branch = succ(y, "do_while (y())");
+    CfgNode[] targets = succ(branch, "z()", "do-entry");
+    succ(targets[0], "exit");
+    assertThat(succ(targets[1], "x()")).isSameAs(x);
+  }
+
+  @Test public void doStmt02() {
+    CfgNode entry = parseCfg("DoStmt02");
+    CfgNode doEntry = succ(entry, "do-entry");
+    CfgNode x = succ(doEntry, "x()");
+    CfgNode branch = succ(x, "do_while (false)");
+    CfgNode y = succ(branch, "y()");
+    succ(y, "exit");
+  }
+
+  @Test public void enhancedFor01() {
+    CfgNode entry = parseCfg("EnhancedFor01");
+    CfgNode aList = succ(entry, "aList()");
+    CfgNode branch = succ(aList, "for (int a : aList())");
+    CfgNode[] targets = succ(branch, "x()", "exit");
+    CfgNode forEnd = succ(targets[0], "for-end");
+    assertThat(succ(forEnd, "aList()")).isSameAs(aList);
+  }
+
+  @Test public void methodAccess01() {
+    CfgNode entry = parseCfg("MethodAccess01");
+    CfgNode p1 = succ(entry, "p1()");
+    CfgNode p2 = succ(p1, "p2()");
+    CfgNode p3 = succ(p2, "p3()");
+    CfgNode x = succ(p3, "x()");
+    succ(x, "exit");
+  }
+
+  @Test public void conditionalExpr01() {
+    CfgNode entry = parseCfg("ConditionalExpr01");
+    CfgNode x = succ(entry, "x()");
+    CfgNode branch = succ(x, "if (x())");
+    CfgNode[] targets = succ(branch, "y()", "z()");
+    CfgNode thenEnd = succ(targets[0], "then-end");
+    CfgNode elseEnd = succ(targets[1], "else-end");
+    // Assert that the branches converge on exit.
+    assertThat(succ(thenEnd, "exit")).isSameAs(succ(elseEnd, "exit"));
+  }
+
+  @Test public void switchStmt01() {
+    CfgNode entry = parseCfg("SwitchStmt01");
+    CfgNode expr = succ(entry, "expr()");
+    CfgNode branch = succ(expr, "switch (expr())");
+    CfgNode[] targets = succ(branch, "x()", "y()", "z()", "d()");
+    assertThat(succ(targets[0], "y()")).isSameAs(targets[1]);
+    CfgNode exit = succ(succ(targets[1], "break"), "exit");
+    assertThat(succ(targets[2], "d()")).isSameAs(targets[3]);
+    assertThat(succ(targets[3], "exit")).isSameAs(exit);
+  }
+
+  @Test public void switchStmt02() {
+    CfgNode entry = parseCfg("SwitchStmt02");
+    CfgNode expr = succ(entry, "expr()");
+    CfgNode branch = succ(expr, "switch (expr())");
+    CfgNode[] targets = succ(branch, "x()", "y()", "z()", "exit");
+    assertThat(succ(targets[0], "y()")).isSameAs(targets[1]);
+    assertThat(succ(succ(targets[1], "break"), "exit")).isSameAs(targets[3]);
+    assertThat(succ(targets[2], "exit")).isSameAs(targets[3]);
+  }
+
+  @Test public void tryStmt01() {
+    CfgNode entry = parseCfg("TryStmt01");
+    CfgNode tryBranch = succ(entry, "try");
+    CfgNode[] trySucc = succ(tryBranch, "cond()", "x()");
+    CfgNode[] condSucc = succ(trySucc[0], "exception", "if (cond())");
+    CfgNode x = succ(condSucc[0], "x()");
+    assertThat(x).isSameAs(trySucc[1]);
+    CfgNode exit = succ(succ(x, "exception"), "exit");
+    CfgNode[] ifSucc = succ(condSucc[1], "a()", "return");
+    CfgNode[] aSucc = succ(ifSucc[0], "exception", "x()");
+    assertThat(succ(aSucc[0], "x()")).isSameAs(x);
+    assertThat(aSucc[1]).isNotSameAs(x);
+    assertThat(succ(succ(aSucc[1], "y()"), "exit")).isSameAs(exit);
+    CfgNode x2 = succ(ifSucc[1], "x()");
+    assertThat(x2).isNotSameAs(x);
+    assertThat(succ(x2, "exit")).isSameAs(exit);
+  }
+
+  @Test public void tryStmt02() {
+    CfgNode entry = parseCfg("TryStmt02");
+    CfgNode tryBranch = succ(entry, "try");
+    CfgNode[] tryTargets = succ(tryBranch, "c1()", "c2()", "exit");
+    assertThat(succ(tryTargets[0], "exit")).isSameAs(tryTargets[2]);
+    assertThat(succ(tryTargets[1], "exit")).isSameAs(tryTargets[2]);
+  }
+
+  @Test public void tryStmt03() {
+    CfgNode entry = parseCfg("TryStmt03");
+    CfgNode tryEntry = succ(entry, "try");
+    CfgNode[] targets = succ(tryEntry, "if (condition)", "x()");
+
+    CfgNode exception = succ(targets[1], "exception");
+    CfgNode exit = succ(exception, "exit");
+
+    CfgNode[] ifSucc = succ(targets[0], "return", "x()");
+    assertThat(succ(succ(ifSucc[0], "x()"), "exit")).isSameAs(exit);
+    assertThat(succ(succ(ifSucc[1], "y()"), "exit")).isSameAs(exit);
+  }
+
+  @Test public void filtering01() {
+    CfgNode entry = parseCfg("Filtering01");
+    CfgNode i = succ(entry, "i()");
+    CfgNode j = succ(i, "j()");
+    CfgNode cond = succ(j, "cond()");
+    CfgNode forBranch = succ(cond, "for (cond() && c == 3)");
+    CfgNode[] forSucc = succ(forBranch, "stmt()", "return");
+    CfgNode stmt = forSucc[0];
+    CfgNode u1 = succ(succ(stmt, "for-end"), "u1()");
+    CfgNode u2 = succ(u1, "u2()");
+    CfgNode u3 = succ(u2, "u3()");
+    assertThat(succ(u3, "cond()")).isSameAs(cond);
+  }
+
+  @Test public void throwStmt01() {
+    CfgNode entry = parseCfg("ThrowStmt01");
+    CfgNode x = succ(entry, "x()");
+    CfgNode exception = succ(x, "exception");
+    succ(exception, "exit");
+  }
+
+  // Note: tests should be designed so that there is no need to test duplicate
+  // successors. Simply insert an extra call at the start of one of the
+  // branches.
+
+  // Generated tests below here.
+  // Be extra careful when generating a test: you must manually verify that
+  // it tests the graph correctly and that the generated graph matches your
+  // expectations. Watch out for nodes that should be identical but appear to
+  // be separate: in some cases this is okay but it could also indicate faulty
+  // caching for NTAs.
+
+  @Test public void genTryStmt01() {
+    CfgNode entry = parseCfg("GenTryStmt01");
+    CfgNode tryEntry = succ(entry, "try");
+    CfgNode[] targets = succ(tryEntry, "cond()", "c1()", "c2()");
+    CfgNode[] targets2 = succ(targets[0], "exception", "if (cond())");
+    CfgNode exit = succ(targets[1], "exit");
+    assertThat(succ(targets[2], "exit")).isSameAs(exit);
+    CfgNode[] targets3 = succ(targets2[0], "c1()", "c2()");
+    assertThat(targets3[0]).isSameAs(targets[1]);
+    assertThat(targets3[1]).isSameAs(targets[2]);
+    CfgNode[] targets4 = succ(targets2[1], "exception", "x()");
+    CfgNode[] targets5 = succ(targets4[0], "c1()", "c2()");
+    assertThat(targets5[1]).isSameAs(targets[2]);
+    assertThat(targets5[0]).isSameAs(targets[1]);
+    CfgNode[] targets6 = succ(targets4[1], "exception", "exit");
+    assertThat(targets6[1]).isSameAs(exit);
+    CfgNode[] targets7 = succ(targets6[0], "c1()", "c2()");
+    assertThat(targets7[0]).isSameAs(targets[1]);
+    assertThat(targets7[1]).isSameAs(targets[2]);
+  }
+
+  @Test public void genTryStmt02() {
+    CfgNode entry = parseCfg("GenTryStmt02");
+    CfgNode tryEntry = succ(entry, "try");
+    CfgNode[] targets = succ(tryEntry, "tryBlock()", "c2()", "c1()");
+    CfgNode[] targets2 = succ(targets[0], "exception", "exit");
+    assertThat(succ(targets[1], "exit")).isSameAs(targets2[1]);
+    assertThat(succ(targets[2], "exit")).isSameAs(targets2[1]);
+    CfgNode[] targets3 = succ(targets2[0], "c2()", "c1()");
+    assertThat(targets3[0]).isSameAs(targets[1]);
+    assertThat(targets3[1]).isSameAs(targets[2]);
+  }
+
+  @Test public void genTryStmt03() {
+    CfgNode entry = parseCfg("GenTryStmt03");
+    CfgNode tryEntry = succ(entry, "try");
+    CfgNode[] targets = succ(tryEntry, "c1()", "x()");
+    CfgNode f = succ(targets[0], "f()");
+    CfgNode[] targets2 = succ(targets[1], "exception", "f()");
+    assertThat(targets2[1]).isSameAs(f);
+    CfgNode exit = succ(f, "exit");
+    assertThat(succ(targets2[0], "c1()")).isSameAs(targets[0]);
+  }
+
+
+  @Test public void genTryStmt04() {
+    CfgNode entry = parseCfg("GenTryStmt04");
+    CfgNode tryEntry = succ(entry, "try");
+    CfgNode[] tryEntrySucc = succ(tryEntry, "if (condition)", "x()");
+    CfgNode[] ifBranchSucc = succ(tryEntrySucc[0], "a()", "x()");
+    CfgNode exception = succ(tryEntrySucc[1], "exception");
+    CfgNode[] aSucc = succ(ifBranchSucc[0], "exception", "return");
+    CfgNode y = succ(ifBranchSucc[1], "y()");
+    CfgNode exit = succ(exception, "exit");
+    assertThat(succ(aSucc[0], "x()")).isSameAs(tryEntrySucc[1]);
+    CfgNode x2 = succ(aSucc[1], "x()");
+    assertThat(succ(y, "exit")).isSameAs(exit);
+    assertThat(succ(x2, "exit")).isSameAs(exit);
+  }
+
+  @Test public void genTryStmt05() {
+    CfgNode entry = parseCfg("GenTryStmt05");
+    CfgNode tryEntry = succ(entry, "try");
+    CfgNode[] tryEntrySucc = succ(tryEntry, "f2()", "try");
+    CfgNode exception = succ(tryEntrySucc[0], "exception");
+    CfgNode[] tryEntrySucc2 = succ(tryEntrySucc[1], "if (condition)", "f1()");
+    CfgNode exit = succ(exception, "exit");
+    CfgNode[] ifBranchSucc = succ(tryEntrySucc2[0], "exception", "f1()");
+    CfgNode[] f1Succ = succ(tryEntrySucc2[1], "exception", "return");
+    assertThat(succ(ifBranchSucc[0], "f1()")).isSameAs(tryEntrySucc2[1]);
+    CfgNode[] f1Succ2 = succ(ifBranchSucc[1], "exception", "return");
+    assertThat(succ(f1Succ[0], "f2()")).isSameAs(tryEntrySucc[0]);
+    CfgNode f22 = succ(f1Succ[1], "f2()");
+    assertThat(succ(f1Succ2[0], "f2()")).isSameAs(tryEntrySucc[0]);
+    CfgNode f24 = succ(f1Succ2[1], "f2()");
+    assertThat(succ(f22, "exit")).isSameAs(exit);
+    assertThat(succ(f24, "exit")).isSameAs(exit);
+  }
+
+  @Test public void genTryStmt06() {
+    CfgNode entry = parseCfg("GenTryStmt06");
+    CfgNode tryEntry = succ(entry, "try");
+    CfgNode[] tryEntrySucc = succ(tryEntry, "f2()", "try");
+    CfgNode exception = succ(tryEntrySucc[0], "exception");
+    CfgNode[] tryEntrySucc2 = succ(tryEntrySucc[1], "s()", "f1()");
+    CfgNode exit = succ(exception, "exit");
+    CfgNode[] sSucc = succ(tryEntrySucc2[0], "exception", "f1()");
+    CfgNode[] f1Succ = succ(tryEntrySucc2[1], "exception", "return");
+    assertThat(succ(sSucc[0], "f1()")).isSameAs(tryEntrySucc2[1]);
+    CfgNode[] f1Succ2 = succ(sSucc[1], "exception", "return");
+    assertThat(succ(f1Succ[0], "f2()")).isSameAs(tryEntrySucc[0]);
+    CfgNode f22 = succ(f1Succ[1], "f2()");
+    assertThat(succ(f1Succ2[0], "f2()")).isSameAs(tryEntrySucc[0]);
+    CfgNode f24 = succ(f1Succ2[1], "f2()");
+    assertThat(succ(f22, "exit")).isSameAs(exit);
+    assertThat(succ(f24, "exit")).isSameAs(exit);
+  }
+
+  @Test public void genTryStmt07() {
+    CfgNode entry = parseCfg("GenTryStmt07");
+    CfgNode tryEntry = succ(entry, "try");
+    CfgNode[] tryEntrySucc = succ(tryEntry, "c1()", "x()");
+    CfgNode returnMarker = succ(tryEntrySucc[0], "return");
+    CfgNode[] xSucc = succ(tryEntrySucc[1], "exception", "f()");
+    CfgNode f = succ(returnMarker, "f()");
+    assertThat(succ(xSucc[0], "c1()")).isSameAs(tryEntrySucc[0]);
+    CfgNode y = succ(xSucc[1], "y()");
+    CfgNode exit = succ(f, "exit");
+    assertThat(succ(y, "exit")).isSameAs(exit);
+  }
+
+  @Test public void genForStmt01() {
+    CfgNode entry = parseCfg("GenForStmt01");
+    CfgNode forBranch = succ(entry, "for (i < 100)");
+    CfgNode[] forBranchSucc = succ(forBranch, "c()", "fin()");
+    CfgNode whileBranch = succ(forBranchSucc[0], "while (c())");
+    CfgNode exit = succ(forBranchSucc[1], "exit");
+    CfgNode[] whileBranchSucc = succ(whileBranch, "if (i >= 40)", "for-end");
+    CfgNode[] ifBranchSucc = succ(whileBranchSucc[0], "break", "while-end");
+    CfgNode u = succ(whileBranchSucc[1], "u()");
+    assertThat(succ(ifBranchSucc[0], "fin()")).isSameAs(forBranchSucc[1]);
+    assertThat(succ(ifBranchSucc[1], "c()")).isSameAs(forBranchSucc[0]);
+    assertThat(succ(u, "for (i < 100)")).isSameAs(forBranch);
+  }
+
+  @Test public void genForStmt02() {
+    CfgNode entry = parseCfg("GenForStmt02");
+    CfgNode forBranch = succ(entry, "for (i < 100)");
+    CfgNode[] forBranchSucc = succ(forBranch, "c()", "fin()");
+    CfgNode whileBranch = succ(forBranchSucc[0], "while (c())");
+    CfgNode exit = succ(forBranchSucc[1], "exit");
+    CfgNode[] whileBranchSucc = succ(whileBranch, "if (i >= 40)", "for-end");
+    CfgNode[] ifBranchSucc = succ(whileBranchSucc[0], "continue", "while-end");
+    CfgNode u = succ(whileBranchSucc[1], "u()");
+    assertThat(succ(ifBranchSucc[0], "for (i < 100)")).isSameAs(forBranch);
+    assertThat(succ(ifBranchSucc[1], "c()")).isSameAs(forBranchSucc[0]);
+    assertThat(succ(u, "for (i < 100)")).isSameAs(forBranch);
+  }
+
+  @Test public void genClassInstance01() {
+    CfgNode entry = parseCfg("GenClassInstance01");
+    CfgNode p1 = succ(entry, "p1()");
+    CfgNode p2 = succ(p1, "p2()");
+    CfgNode p3 = succ(p2, "p3()");
+    CfgNode p4 = succ(p3, "p4()");
+    CfgNode exit = succ(p4, "exit");
+  }
+
+  @Test public void genSwitchStmt01() {
+    CfgNode entry = parseCfg("GenSwitchStmt01");
+    CfgNode whileBranch = succ(entry, "while (x + y == 400 - z)");
+    CfgNode[] whileBranchSucc = succ(whileBranch, "switch (x)", "exit");
+    CfgNode[] switchBranchSucc = succ(whileBranchSucc[0], "c6()", "c3()", "break", "c5()", "c1()", "c4()", "while-end", "c2()");
+    CfgNode breakMarker = succ(switchBranchSucc[0], "break");
+    assertThat(succ(switchBranchSucc[1], "c2()")).isSameAs(switchBranchSucc[7]);
+    assertThat(succ(switchBranchSucc[2], "while-end")).isSameAs(switchBranchSucc[6]);
+    CfgNode returnMarker = succ(switchBranchSucc[3], "return");
+    CfgNode breakMarker2 = succ(switchBranchSucc[4], "break");
+    CfgNode breakMarker3 = succ(switchBranchSucc[5], "break");
+    assertThat(succ(switchBranchSucc[6], "while (x + y == 400 - z)")).isSameAs(whileBranch);
+    CfgNode continueMarker = succ(switchBranchSucc[7], "continue");
+    assertThat(succ(breakMarker, "while-end")).isSameAs(switchBranchSucc[6]);
+    assertThat(succ(returnMarker, "exit")).isSameAs(whileBranchSucc[1]);
+    assertThat(succ(breakMarker2, "while-end")).isSameAs(switchBranchSucc[6]);
+    assertThat(succ(breakMarker3, "while-end")).isSameAs(switchBranchSucc[6]);
+    assertThat(succ(continueMarker, "while (x + y == 400 - z)")).isSameAs(whileBranch);
+  }
+
+  @Test public void genClassInstance02() {
+    CfgNode entry = parseCfg("GenClassInstance02");
+    CfgNode toString = succ(entry, "toString()");
+    CfgNode exit = succ(toString, "exit");
+  }
+
+  @Test public void genTryWithResources01() {
+    CfgNode entry = parseCfg("GenTryWithResources01");
+    CfgNode openStream = succ(entry, "openStream()");
+    CfgNode[] openStreamSucc = succ(openStream, "exception", "try");
+    CfgNode[] exceptionSucc = succ(openStreamSucc[0], "c()", "f()");
+    CfgNode[] tryEntrySucc = succ(openStreamSucc[1], "c()", "stmt()", "f()");
+    assertThat(tryEntrySucc[2]).isSameAs(exceptionSucc[1]);
+    assertThat(tryEntrySucc[0]).isSameAs(exceptionSucc[0]);
+    CfgNode f = succ(exceptionSucc[0], "f()");
+    CfgNode exception = succ(exceptionSucc[1], "exception");
+    CfgNode[] stmtSucc = succ(tryEntrySucc[1], "exception", "f()");
+    assertThat(stmtSucc[1]).isSameAs(f);
+    CfgNode exit = succ(f, "exit");
+    assertThat(succ(exception, "exit")).isSameAs(exit);
+    CfgNode[] exceptionSucc2 = succ(stmtSucc[0], "c()", "f()");
+    assertThat(exceptionSucc2[0]).isSameAs(exceptionSucc[0]);
+    assertThat(exceptionSucc2[1]).isSameAs(exceptionSucc[1]);
+  }
+
+  @Test public void genTryWithResources02() {
+    CfgNode entry = parseCfg("GenTryWithResources02");
+    CfgNode o1 = succ(entry, "o1()");
+    CfgNode[] o1Succ = succ(o1, "exception", "o2()");
+    CfgNode[] exceptionSucc = succ(o1Succ[0], "c()", "f()");
+    CfgNode[] o2Succ = succ(o1Succ[1], "exception", "try");
+    CfgNode f = succ(exceptionSucc[0], "f()");
+    CfgNode exception = succ(exceptionSucc[1], "exception");
+    CfgNode[] exceptionSucc2 = succ(o2Succ[0], "c()", "f()");
+    assertThat(exceptionSucc2[0]).isSameAs(exceptionSucc[0]);
+    assertThat(exceptionSucc2[1]).isSameAs(exceptionSucc[1]);
+    CfgNode[] tryEntrySucc = succ(o2Succ[1], "c()", "stmt()", "f()");
+    assertThat(tryEntrySucc[2]).isSameAs(exceptionSucc[1]);
+    assertThat(tryEntrySucc[0]).isSameAs(exceptionSucc[0]);
+    CfgNode exit = succ(f, "exit");
+    assertThat(succ(exception, "exit")).isSameAs(exit);
+    CfgNode[] stmtSucc = succ(tryEntrySucc[1], "exception", "f()");
+    assertThat(stmtSucc[1]).isSameAs(f);
+    CfgNode[] exceptionSucc3 = succ(stmtSucc[0], "c()", "f()");
+    assertThat(exceptionSucc3[1]).isSameAs(exceptionSucc[1]);
+    assertThat(exceptionSucc3[0]).isSameAs(exceptionSucc[0]);
+  }
+
+}
diff --git a/simplecfg/src/test/java/org/extendj/ast/IdentityTupleSetTest.java b/simplecfg/src/test/java/org/extendj/ast/IdentityTupleSetTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..417eeb8d6ce2e13e06e3085279c61871eff478fa
--- /dev/null
+++ b/simplecfg/src/test/java/org/extendj/ast/IdentityTupleSetTest.java
@@ -0,0 +1,58 @@
+/**
+ * Copyright 2015 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.extendj.ast;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import java.util.Set;
+
+import org.extendj.ast.IdentityTupleSet;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/** Unit tests for {@link IdentityTupleSet}. */
+@RunWith(JUnit4.class)
+public class IdentityTupleSetTest {
+
+  // Two unique objects used for testing.
+  private Object a = new Object();
+  private Object b = new Object();
+
+  @Test public void testSize() {
+    assertThat(new IdentityTupleSet<>(null, null)).hasSize(1);
+    assertThat(new IdentityTupleSet<>(a, a)).hasSize(1);
+    assertThat(new IdentityTupleSet<>(a, b)).hasSize(2);
+    assertThat(new IdentityTupleSet<>(null, b)).hasSize(2);
+  }
+
+  @Test public void testIsEmpty() {
+    assertThat(new IdentityTupleSet<>(null, null)).isNotEmpty();
+    assertThat(new IdentityTupleSet<>(a, a)).isNotEmpty();
+    assertThat(new IdentityTupleSet<>(a, b)).isNotEmpty();
+    assertThat(new IdentityTupleSet<>(null, b)).isNotEmpty();
+  }
+
+  @Test public void testDuplicateContains() {
+    assertThat(new IdentityTupleSet<>(null, null)).containsExactly((Object) null);
+    assertThat(new IdentityTupleSet<>(a, a)).containsExactly(a);
+  }
+
+  @Test public void testContains() {
+    assertThat(new IdentityTupleSet<>(a, b)).containsExactly(a, b);
+  }
+}
diff --git a/simplecfg/testdata/AlreadyClosedControlFlow01.javax b/simplecfg/testdata/AlreadyClosedControlFlow01.javax
new file mode 100644
index 0000000000000000000000000000000000000000..2463b37f65b00a7ff3f37d6ea2a606dabb23dee8
--- /dev/null
+++ b/simplecfg/testdata/AlreadyClosedControlFlow01.javax
@@ -0,0 +1,140 @@
+/**
+ * Copyright 2015 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ * This is test data, not real code! This file is parsed by the Java API analyzer tests
+ * to check that the expected findings are reported for this file.
+ */
+import java.io.BufferedInputStream;
+import java.io.Closeable;
+import java.io.FileInputStream;
+
+/* This code is used to check that the already-closed analysis handles different control flow
+ * structures appropriately.
+ */
+class AlreadyClosedControlFlow01 {
+  void for01(FileInputStream in) {
+    for (int i = 0; i < 10; ++i) {
+      in.close();
+    }
+    // The analyzer does not know how many loop iterations are executed, assumes 0+
+    // iterations are possible.
+    in.skip(0); // Positive finding.
+  }
+
+  void for02() {
+    for (int i = 0; i < 10; ++i) {
+      FileInputStream in = new FileInputStream("somefile");
+      in.skip(0);
+      in.close(); // Negative finding: new instance each iteration.
+    }
+  }
+
+  void if01() {
+    Closeable in = new FileInputStream("somefile");
+    if (System.currentTimeMillis() & 1 == 0) {
+      in.read();
+      in.close();
+    } else {
+      in.read();
+      in.close();
+    }
+  }
+
+  void if02() {
+    Closeable in = new FileInputStream("somefile");
+    in.close();
+    if (System.currentTimeMillis() & 1 == 0) {
+      in.read(); // Positive finding.
+    }
+  }
+
+  void if03() {
+    Closeable in = new FileInputStream("foo");
+    in.close();
+    // Check that the if condition is analyzed.
+    if (in.read() == -1) { // Positive finding.
+    }
+  }
+
+  void while01() {
+    Closeable in = new FileInputStream("bar");
+    while (true) {
+      in.read();
+      in.close();
+      break;
+    }
+    in.read(); // Positive finding.
+  }
+
+  void while02() {
+    Closeable in = new FileInputStream("bar");
+    while (in.read() != -1) { // Positive finding.
+      in.close();
+    }
+  }
+
+  void try01(BufferedInputStream in) {
+    try {
+      in.close();
+    } catch (Throwable t) {
+      in.read(); // Negative finding: preceding close call interrupted by exception.
+    }
+  }
+
+  void try02(BufferedInputStream in) {
+    try {
+      in.close();
+    } catch (Throwable t) {
+      in.read();
+    } finally {
+      in.read(); // Positive finding: can be reached after an uninterrupted close call.
+    }
+  }
+
+  void switch01(BufferedInputStream in, int i) {
+    switch (i) {
+      case 1:
+        in.close();
+        break;
+      case 2:
+        in.read();
+        break;
+      case 3:
+        in.close();
+      case 4:
+        in.read(); // Positive finding.
+        break;
+    }
+  }
+
+  void switch02(BufferedInputStream in, int i) {
+    switch (i) {
+      case 1:
+        in.close();
+        break;
+      case 2:
+        in.read();
+        break;
+      case 3:
+        in.close();
+        return;
+      case 4:
+        in.read();
+      default:
+        in.close();
+    }
+  }
+}
diff --git a/simplecfg/testdata/AlreadyClosedNegativeFindings01.javax b/simplecfg/testdata/AlreadyClosedNegativeFindings01.javax
new file mode 100644
index 0000000000000000000000000000000000000000..f59ecd19af455d4b9ea08b870f183917199e57bf
--- /dev/null
+++ b/simplecfg/testdata/AlreadyClosedNegativeFindings01.javax
@@ -0,0 +1,87 @@
+/**
+ * Copyright 2015 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ * This is test data, not real code! This file is parsed by the Java API analyzer tests
+ * to check that no findings are reported for this file.
+ */
+import java.io.Closeable;
+import java.io.FileInputStream;
+
+/* Negative findings for the already-closed analyzer. */
+class AlreadyClosedNegativeFindings01 {
+
+  static class MyCloseable {
+    public void close() {
+    }
+    public void write() {
+    }
+  }
+
+  void f1() {
+    // MyCloseable does not implement java.io.Closeable, so it should not cause findings.
+    MyCloseable closeable = new MyCloseable();
+    closeable.close();
+    closeable.write();
+  }
+
+  void f2() {
+    while (true) {
+      // This is a new instance each iteration, so close is only called once per instance.
+      Closeable in = new FileInputStream("x");
+      in.read();
+      in.close();
+      if (System.currentTimeMillis() % 2 == 1) {
+        break;
+      }
+  }
+
+  void f3() {
+    // This local variable is not effectively final, so it should not be analyzed.
+    Closeable in = new FileInputStream("x");
+    in.read();
+    in.close();
+    in = new FileInputStream("y");
+    in.read();
+  }
+
+  void f4() {
+    // This local variable is not effectively final, so it should not be analyzed.
+    Closeable in = new FileInputStream("x");
+    in.read();
+    in.close();
+    in = new FileInputStream("y");
+    in.read();
+  }
+
+  void f5(Closeable in) {
+    // Repeated calls to close() are allowed and do not produce findings.
+    in.close();
+    in.close();
+    in.close();
+    in.close();
+    in.close();
+  }
+
+  String f6(ByteArrayOutputStream out) {
+    out.close();
+    return out.toString(); // Negative finding: calling toString() after close() is okay.
+  }
+
+  byte[] f7(ByteArrayOutputStream out) {
+    out.close();
+    return out.toByteArray(); // Negative finding: calling toByteArray() after close() is okay.
+  }
+}
diff --git a/simplecfg/testdata/AlreadyClosedWriter01.javax b/simplecfg/testdata/AlreadyClosedWriter01.javax
new file mode 100644
index 0000000000000000000000000000000000000000..2b7769c0f5333ade6ce2d9aafc9482be9225f68a
--- /dev/null
+++ b/simplecfg/testdata/AlreadyClosedWriter01.javax
@@ -0,0 +1,29 @@
+/**
+ * Copyright 2015 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ * This is test data, not real code! This file is parsed by the Java API analyzer tests
+ * to check that the expected findings are reported for this file.
+ */
+
+/* This code is used to check that the already-closed analyzer can identify java.io.Writer
+ * as a subtype of java.io.Closeable and report a finding for a simple call after close.
+ */
+class AlreadyClosedWriter01 {
+  void f(java.io.Writer writer) {
+    writer.close();
+    writer.flush(); // Finding: Calling flush() after close().
+  }
+}
diff --git a/simplecfg/testdata/Close01.javax b/simplecfg/testdata/Close01.javax
new file mode 100644
index 0000000000000000000000000000000000000000..025fc45599d3724fbc211d78e48717aa4e191f7c
--- /dev/null
+++ b/simplecfg/testdata/Close01.javax
@@ -0,0 +1,25 @@
+/**
+ * Copyright 2015 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ * This is test data, not real source code!
+ * StmtCfgTest builds and tests the CFG for the first block/method in this class.
+ */
+public class Close01 {
+  void f(java.io.Writer writer) {
+    writer.close();
+    writer.write(new byte[10]);
+  }
+}
diff --git a/simplecfg/testdata/ConditionalExpr01.javax b/simplecfg/testdata/ConditionalExpr01.javax
new file mode 100644
index 0000000000000000000000000000000000000000..de8385b6bf9b2144b19711bccb0c8045c0e4cfc5
--- /dev/null
+++ b/simplecfg/testdata/ConditionalExpr01.javax
@@ -0,0 +1,25 @@
+/**
+ * Copyright 2015 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ * This is test data, not real source code!
+ * StmtCfgTest builds and tests the CFG for the first block/method in this class.
+ */
+class ConditionalExpr01 {
+  static {
+    // Conditional expressions work like if statements.
+    int unused = x() ? y() : z();
+  }
+}
diff --git a/simplecfg/testdata/DoStmt01.javax b/simplecfg/testdata/DoStmt01.javax
new file mode 100644
index 0000000000000000000000000000000000000000..d2a6d832c3a6db205f19b2c0570aa2cb1bc4c5d2
--- /dev/null
+++ b/simplecfg/testdata/DoStmt01.javax
@@ -0,0 +1,27 @@
+/**
+ * Copyright 2015 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ * This is test data, not real source code!
+ * StmtCfgTest builds and tests the CFG for the first block/method in this class.
+ */
+class DoStmt01 {
+  {
+    do {
+      x();
+    } while (y());
+    z();
+  }
+}
diff --git a/simplecfg/testdata/DoStmt02.javax b/simplecfg/testdata/DoStmt02.javax
new file mode 100644
index 0000000000000000000000000000000000000000..2e1fb9eb949f7797c03fd0e385c8a1e83e024bba
--- /dev/null
+++ b/simplecfg/testdata/DoStmt02.javax
@@ -0,0 +1,28 @@
+/**
+ * Copyright 2015 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ * This is test data, not real source code!
+ * StmtCfgTest builds and tests the CFG for the first block/method in this class.
+ */
+class DoStmt02 {
+  {
+    // Test do statement with constant false condition.
+    do {
+      x();
+    } while (false);
+    y();
+  }
+}
diff --git a/simplecfg/testdata/EnhancedFor01.javax b/simplecfg/testdata/EnhancedFor01.javax
new file mode 100644
index 0000000000000000000000000000000000000000..f1532b23592ed6f6f6b22fe9dcf641409ca852c7
--- /dev/null
+++ b/simplecfg/testdata/EnhancedFor01.javax
@@ -0,0 +1,26 @@
+/**
+ * Copyright 2015 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ * This is test data, not real source code!
+ * StmtCfgTest builds and tests the CFG for the first block/method in this class.
+ */
+class EnhancedFor01 {
+  {
+    for (int a : aList()) {
+      x();
+    }
+  }
+}
diff --git a/simplecfg/testdata/Filtering01.javax b/simplecfg/testdata/Filtering01.javax
new file mode 100644
index 0000000000000000000000000000000000000000..8f83cee0544b48b338ef50ba730ebc49e405839f
--- /dev/null
+++ b/simplecfg/testdata/Filtering01.javax
@@ -0,0 +1,31 @@
+/**
+ * Copyright 2015 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ * This is test data, not real source code!
+ * StmtCfgTest builds and tests the CFG for the first block/method in this class.
+ */
+class Filtering01 {
+  int x(int c) {
+    // Test that uninteresting expressions are not included in the CFG.
+    int a = 4;
+    int b = 3;
+    for (int i = i(1, 4 * 5), j = j(); cond() && c == 3; u1(), j += c / 2, u2(), u3(), i++) {
+      stmt();
+      a += (--b) * c;
+    }
+    return a + b;
+  }
+}
diff --git a/simplecfg/testdata/ForStmt01.javax b/simplecfg/testdata/ForStmt01.javax
new file mode 100644
index 0000000000000000000000000000000000000000..ce58d63becab96d0b5fa5fb498817e97854f8518
--- /dev/null
+++ b/simplecfg/testdata/ForStmt01.javax
@@ -0,0 +1,25 @@
+/**
+ * Copyright 2015 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ * This is test data, not real source code!
+ * StmtCfgTest builds and tests the CFG for the first block/method in this class.
+ */
+class ForStmt01 {
+  void x() {
+    for (; call1(); ) {
+    }
+  }
+}
diff --git a/simplecfg/testdata/ForStmt02.javax b/simplecfg/testdata/ForStmt02.javax
new file mode 100644
index 0000000000000000000000000000000000000000..f204489cd686042e0bacffe8b5368af12ff207f9
--- /dev/null
+++ b/simplecfg/testdata/ForStmt02.javax
@@ -0,0 +1,26 @@
+/**
+ * Copyright 2015 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ * This is test data, not real source code!
+ * StmtCfgTest builds and tests the CFG for the first block/method in this class.
+ */
+class ForStmt02 {
+  void x() {
+    for (; call1(); ) {
+      call2();
+    }
+  }
+}
diff --git a/simplecfg/testdata/ForStmt03.javax b/simplecfg/testdata/ForStmt03.javax
new file mode 100644
index 0000000000000000000000000000000000000000..720fefd193cc581a23ac7aa8c3f0b794d2c82701
--- /dev/null
+++ b/simplecfg/testdata/ForStmt03.javax
@@ -0,0 +1,25 @@
+/**
+ * Copyright 2015 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ * This is test data, not real source code!
+ * StmtCfgTest builds and tests the CFG for the first block/method in this class.
+ */
+class ForStmt03 {
+  void x() {
+    for (int i = init(); cond(); update())
+      stmt();
+  }
+}
diff --git a/simplecfg/testdata/ForStmt04.javax b/simplecfg/testdata/ForStmt04.javax
new file mode 100644
index 0000000000000000000000000000000000000000..29b036334033967540d8c8a3f71b078b8238d22e
--- /dev/null
+++ b/simplecfg/testdata/ForStmt04.javax
@@ -0,0 +1,27 @@
+/**
+ * Copyright 2015 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ * This is test data, not real source code!
+ * StmtCfgTest builds and tests the CFG for the first block/method in this class.
+ */
+class ForStmt04 {
+  {
+    // Initializer expressions and update statements are evaluated in the order they are listed.
+    for (int i = i(), j = j(); cond(); u1(), u2(), u3()) {
+      stmt();
+    }
+  }
+}
diff --git a/simplecfg/testdata/ForStmt05.javax b/simplecfg/testdata/ForStmt05.javax
new file mode 100644
index 0000000000000000000000000000000000000000..7cea144800964ac35735329cc6f0803023cb30b0
--- /dev/null
+++ b/simplecfg/testdata/ForStmt05.javax
@@ -0,0 +1,28 @@
+/**
+ * Copyright 2015 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ * This is test data, not real source code!
+ * StmtCfgTest builds and tests the CFG for the first block/method in this class.
+ */
+class ForStmt05 {
+  {
+    // Test for statement with constant false condition.
+    for (Object o = i(); false; u()) {
+      x();
+    }
+    y();
+  }
+}
diff --git a/simplecfg/testdata/GenClassInstance01.javax b/simplecfg/testdata/GenClassInstance01.javax
new file mode 100644
index 0000000000000000000000000000000000000000..26e7baeeee6582ca92fddd82f3272e65b823c3dd
--- /dev/null
+++ b/simplecfg/testdata/GenClassInstance01.javax
@@ -0,0 +1,24 @@
+/**
+ * Copyright 2015 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ * This is test data, not real source code!
+ * StmtCfgTest builds and tests the CFG for the first block/method in this class.
+ */
+class GenClassInstance01 {
+  GenClassInstance01() {
+    new C(p1(), p2(), new D(p3() + p4()));
+  }
+}
diff --git a/simplecfg/testdata/GenClassInstance02.javax b/simplecfg/testdata/GenClassInstance02.javax
new file mode 100644
index 0000000000000000000000000000000000000000..a73a972e46c5ae098041f3528a701e7d54302d6e
--- /dev/null
+++ b/simplecfg/testdata/GenClassInstance02.javax
@@ -0,0 +1,28 @@
+/**
+ * Copyright 2015 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ * This is test data, not real source code!
+ * StmtCfgTest builds and tests the CFG for the first block/method in this class.
+ */
+class GenClassInstance02 {
+  {
+    new Object() {
+      void f() {
+        notInParentCfg();
+      }
+    }.toString();
+  }
+}
diff --git a/simplecfg/testdata/GenForStmt01.javax b/simplecfg/testdata/GenForStmt01.javax
new file mode 100644
index 0000000000000000000000000000000000000000..a1dab9ad9e9689d31297dde0950bfcb22ee48c09
--- /dev/null
+++ b/simplecfg/testdata/GenForStmt01.javax
@@ -0,0 +1,33 @@
+/**
+ * Copyright 2015 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ * This is test data, not real source code!
+ * StmtCfgTest builds and tests the CFG for the first block/method in this class.
+ */
+class GenForStmt01 {
+  {
+    // Test a labeled break.
+    loop1:
+    for (int i = 3 * x; i < 100; ++i, u()) {
+      while (c()) {
+        if (i >= 40) {
+          break loop1;
+        }
+      }
+    }
+    fin();
+  }
+}
diff --git a/simplecfg/testdata/GenForStmt02.javax b/simplecfg/testdata/GenForStmt02.javax
new file mode 100644
index 0000000000000000000000000000000000000000..23535baf67376b1a0d3ee5e7d69f5a906575dfa8
--- /dev/null
+++ b/simplecfg/testdata/GenForStmt02.javax
@@ -0,0 +1,33 @@
+/**
+ * Copyright 2015 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ * This is test data, not real source code!
+ * StmtCfgTest builds and tests the CFG for the first block/method in this class.
+ */
+class GenForStmt02 {
+  {
+    // Test a labeled continue.
+    loop1:
+    for (int i = 3 * x; i < 100; ++i, u()) {
+      while (c()) {
+        if (i >= 40) {
+          continue loop1;
+        }
+      }
+    }
+    fin();
+  }
+}
diff --git a/simplecfg/testdata/GenSwitchStmt01.javax b/simplecfg/testdata/GenSwitchStmt01.javax
new file mode 100644
index 0000000000000000000000000000000000000000..1ef81a62a8217e678e0dec43256448b5d44a0492
--- /dev/null
+++ b/simplecfg/testdata/GenSwitchStmt01.javax
@@ -0,0 +1,46 @@
+/**
+ * Copyright 2015 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ * This is test data, not real source code!
+ * StmtCfgTest builds and tests the CFG for the first block/method in this class.
+ */
+class GenSwitchStmt01 {
+  GenSwitchStmt01() {
+    while (x + y == 400 - z) {
+      switch (x) {
+        case -1:
+          c1();
+          break;
+        case 1:
+          break;
+        case 3:
+          c3();
+        case 2:
+          c2();
+          continue;
+        case 4:
+          c4();
+          break;
+        case 5:
+          c5();
+          return;
+        case 6:
+          c6();
+          break;
+      }
+    }
+  }
+}
diff --git a/simplecfg/testdata/GenTryStmt01.javax b/simplecfg/testdata/GenTryStmt01.javax
new file mode 100644
index 0000000000000000000000000000000000000000..63975d3b5f46a503bbe353af2216a20e3f19de7e
--- /dev/null
+++ b/simplecfg/testdata/GenTryStmt01.javax
@@ -0,0 +1,34 @@
+/**
+ * Copyright 2015 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ * This is test data, not real source code!
+ * StmtCfgTest builds and tests the CFG for the first block/method in this class.
+ */
+class GenTryStmt01 {
+  void f(Exception exception) {
+    // Test that branches from throw statement go to both all catch clauses.
+    try {
+      if (cond()) {
+        throw exception;
+      }
+      x();
+    } catch (Exception1 e) {
+      c1();
+    } catch (Exception2 e) {
+      c2();
+    }
+  }
+}
diff --git a/simplecfg/testdata/GenTryStmt02.javax b/simplecfg/testdata/GenTryStmt02.javax
new file mode 100644
index 0000000000000000000000000000000000000000..3fb2f476c9c7917d1f5108a02bb4bf9c0804b9ee
--- /dev/null
+++ b/simplecfg/testdata/GenTryStmt02.javax
@@ -0,0 +1,30 @@
+/**
+ * Copyright 2015 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ * This is test data, not real source code!
+ * StmtCfgTest builds and tests the CFG for the first block/method in this class.
+ */
+class GenTryStmt02 {
+  {
+    try {
+      tryBlock();
+    } catch (Exception unused) {
+      c1();
+    } catch (Throwable unused) {
+      c2();
+    }
+  }
+}
diff --git a/simplecfg/testdata/GenTryStmt03.javax b/simplecfg/testdata/GenTryStmt03.javax
new file mode 100644
index 0000000000000000000000000000000000000000..3d0cf1fb671fb71b91f1c107f1f8446246b4eca5
--- /dev/null
+++ b/simplecfg/testdata/GenTryStmt03.javax
@@ -0,0 +1,33 @@
+/**
+ * Copyright 2015 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ * This is test data, not real source code!
+ * StmtCfgTest builds and tests the CFG for the first block/method in this class.
+ */
+class GenTryStmt03 {
+  {
+    try {
+      x();
+    } catch (Throwable unused) {
+      c1();
+    } catch (Exception unused) {
+      // Never reached.
+      c2();
+    } finally {
+      f();
+    }
+  }
+}
diff --git a/simplecfg/testdata/GenTryStmt04.javax b/simplecfg/testdata/GenTryStmt04.javax
new file mode 100644
index 0000000000000000000000000000000000000000..66f3e7d2f0b4adbdfff36d17505d6526008a939c
--- /dev/null
+++ b/simplecfg/testdata/GenTryStmt04.javax
@@ -0,0 +1,34 @@
+/**
+ * Copyright 2015 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ * This is test data, not real source code!
+ * StmtCfgTest builds and tests the CFG for the first block/method in this class.
+ */
+class GenTryStmt04 {
+  void m() {
+    // Test that finally handlers are generated in separate CFG branches.
+    // Non-identical CFG nodes with the same name are expected in this graph.
+    try {
+      if (condition) {
+        a();
+        return;
+      }
+    } finally {
+      x();
+    }
+    y();
+  }
+}
diff --git a/simplecfg/testdata/GenTryStmt05.javax b/simplecfg/testdata/GenTryStmt05.javax
new file mode 100644
index 0000000000000000000000000000000000000000..2d4d5540093ede8bcf6134e59af27a6f03337156
--- /dev/null
+++ b/simplecfg/testdata/GenTryStmt05.javax
@@ -0,0 +1,35 @@
+/**
+ * Copyright 2015 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ * This is test data, not real source code!
+ * StmtCfgTest builds and tests the CFG for the first block/method in this class.
+ */
+class GenTryStmt05 {
+  void z() {
+    try {
+      try {
+        if (condition) throw exception;
+      } finally {
+        f1();
+        return;
+      }
+      a();
+    } finally {
+      f2();
+    }
+    b();
+  }
+}
diff --git a/simplecfg/testdata/GenTryStmt06.javax b/simplecfg/testdata/GenTryStmt06.javax
new file mode 100644
index 0000000000000000000000000000000000000000..8af1b8308cc75a258ccbaa322c3caff67503c125
--- /dev/null
+++ b/simplecfg/testdata/GenTryStmt06.javax
@@ -0,0 +1,34 @@
+/**
+ * Copyright 2015 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ * This is test data, not real source code!
+ * StmtCfgTest builds and tests the CFG for the first block/method in this class.
+ */
+class GenTryStmt06 {
+  {
+    // Abrupt exit from nested finally block.
+    try {
+      try {
+        s();
+      } finally {
+        f1();
+        return;
+      }
+    } finally {
+      f2();
+    }
+  }
+}
diff --git a/simplecfg/testdata/GenTryStmt07.javax b/simplecfg/testdata/GenTryStmt07.javax
new file mode 100644
index 0000000000000000000000000000000000000000..02a27a98cd8c99ea6770bf94a8ba9caf0f028da7
--- /dev/null
+++ b/simplecfg/testdata/GenTryStmt07.javax
@@ -0,0 +1,35 @@
+/**
+ * Copyright 2015 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ * This is test data, not real source code!
+ * StmtCfgTest builds and tests the CFG for the first block/method in this class.
+ */
+class GenTryStmt07 {
+  {
+    try {
+      x();
+    } catch (Throwable unused) {
+      c1();
+      return;
+    } catch (Exception unused) {
+      // Never reached.
+      c2();
+    } finally {
+      f();
+    }
+    y();
+  }
+}
diff --git a/simplecfg/testdata/GenTryWithResources01.javax b/simplecfg/testdata/GenTryWithResources01.javax
new file mode 100644
index 0000000000000000000000000000000000000000..c0c5c43ce8718e34c2d2ec33db5311797808c2e1
--- /dev/null
+++ b/simplecfg/testdata/GenTryWithResources01.javax
@@ -0,0 +1,30 @@
+/**
+ * Copyright 2015 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ * This is test data, not real source code!
+ * StmtCfgTest builds and tests the CFG for the first block/method in this class.
+ */
+class GenTryWithResources01 {
+  {
+    try (AutoCloseable stream = openStream()) {
+      stmt();
+    } catch (IOException e) {
+      c();
+    } finally {
+      f();
+    }
+  }
+}
diff --git a/simplecfg/testdata/GenTryWithResources02.javax b/simplecfg/testdata/GenTryWithResources02.javax
new file mode 100644
index 0000000000000000000000000000000000000000..d814c2492e69522ae548e8db8876f9369121cea3
--- /dev/null
+++ b/simplecfg/testdata/GenTryWithResources02.javax
@@ -0,0 +1,31 @@
+/**
+ * Copyright 2015 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ * This is test data, not real source code!
+ * StmtCfgTest builds and tests the CFG for the first block/method in this class.
+ */
+class GenTryWithResources02 {
+  {
+    try (AutoCloseable s1 = o1();
+        AutoCloseable s2 = o2()) {
+      stmt();
+    } catch (IOException e) {
+      c();
+    } finally {
+      f();
+    }
+  }
+}
diff --git a/simplecfg/testdata/IfStmt01.javax b/simplecfg/testdata/IfStmt01.javax
new file mode 100644
index 0000000000000000000000000000000000000000..ef8dd0bfcd8a7c3c4611ac212626d94b1345c160
--- /dev/null
+++ b/simplecfg/testdata/IfStmt01.javax
@@ -0,0 +1,26 @@
+/**
+ * Copyright 2015 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ * This is test data, not real source code!
+ * StmtCfgTest builds and tests the CFG for the first block/method in this class.
+ */
+class IfStmt01 {
+  void f() {
+    if (call1()) {
+      onTrue();
+    }
+  }
+}
diff --git a/simplecfg/testdata/IfStmt02.javax b/simplecfg/testdata/IfStmt02.javax
new file mode 100644
index 0000000000000000000000000000000000000000..0271e6843c7bb0b95fcf8cc0dfc4597ea014114c
--- /dev/null
+++ b/simplecfg/testdata/IfStmt02.javax
@@ -0,0 +1,28 @@
+/**
+ * Copyright 2015 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ * This is test data, not real source code!
+ * StmtCfgTest builds and tests the CFG for the first block/method in this class.
+ */
+class IfStmt02 {
+  void f() {
+    if (call1()) {
+      onTrue();
+    } else {
+      onFalse();
+    }
+  }
+}
diff --git a/simplecfg/testdata/IfStmt03.javax b/simplecfg/testdata/IfStmt03.javax
new file mode 100644
index 0000000000000000000000000000000000000000..e515c9cdc0878e4acf7e9fbf7f846fed49027732
--- /dev/null
+++ b/simplecfg/testdata/IfStmt03.javax
@@ -0,0 +1,29 @@
+/**
+ * Copyright 2015 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ * This is test data, not real source code!
+ * StmtCfgTest builds and tests the CFG for the first block/method in this class.
+ */
+class IfStmt03 {
+  void f() {
+    if (call1()) {
+      onTrue();
+    } else {
+      onFalse();
+      call2();
+    }
+  }
+}
diff --git a/simplecfg/testdata/IfStmt04.javax b/simplecfg/testdata/IfStmt04.javax
new file mode 100644
index 0000000000000000000000000000000000000000..19c6c45ce5e5722e4e966e0a7c0c457d583fcb20
--- /dev/null
+++ b/simplecfg/testdata/IfStmt04.javax
@@ -0,0 +1,29 @@
+/**
+ * Copyright 2015 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ * This is test data, not real source code!
+ * StmtCfgTest builds and tests the CFG for the first block/method in this class.
+ */
+class IfStmt04 {
+  void f() {
+    if (call1()) {
+      onTrue();
+      call2();
+    } else {
+      onFalse();
+    }
+  }
+}
diff --git a/simplecfg/testdata/MethodAccess01.javax b/simplecfg/testdata/MethodAccess01.javax
new file mode 100644
index 0000000000000000000000000000000000000000..dad7cc795334f280e67c969422fda0e5a13daddd
--- /dev/null
+++ b/simplecfg/testdata/MethodAccess01.javax
@@ -0,0 +1,25 @@
+/**
+ * Copyright 2015 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ * This is test data, not real source code!
+ * StmtCfgTest builds and tests the CFG for the first block/method in this class.
+ */
+class MethodAccess01 {
+  void f() {
+    // Parameters are evaluated before the outer method call.
+    x(p1(), p2(), p3());
+  }
+}
diff --git a/simplecfg/testdata/NullableDataflow01.javax b/simplecfg/testdata/NullableDataflow01.javax
new file mode 100644
index 0000000000000000000000000000000000000000..41013edcba70b58bdc5b2ad9e0b6d822ef1aa397
--- /dev/null
+++ b/simplecfg/testdata/NullableDataflow01.javax
@@ -0,0 +1,53 @@
+/**
+ * Copyright 2015 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ * This is test data, not real source code!
+ */
+import javax.annotation.Nullable;
+
+public class NullableDataflow01 {
+  boolean x = false;
+
+  void fp1(@Nullable String p) {
+    boolean var = p == null;
+    if (!var) {
+      p.hashCode(); // False positive: condition variable not effectively final.
+    }
+    var = false;
+  }
+
+  void fp2(@Nullable String p) {
+    boolean var = p != null;
+    if (var) {
+      p.hashCode(); // False positive: condition variable not effectively final.
+    }
+    var = false;
+  }
+
+  void n1(@Nullable String p) {
+    boolean var = p == null;
+    if (!var) {
+      p.hashCode(); // Negative.
+    }
+  }
+
+  void n2(@Nullable String p) {
+    boolean var = p != null;
+    if (var) {
+      p.hashCode(); // Negative.
+    }
+  }
+}
diff --git a/simplecfg/testdata/NullableDereference01.javax b/simplecfg/testdata/NullableDereference01.javax
new file mode 100644
index 0000000000000000000000000000000000000000..e9b01b97c546e496ea3b971801364c424c5a889d
--- /dev/null
+++ b/simplecfg/testdata/NullableDereference01.javax
@@ -0,0 +1,37 @@
+/**
+ * Copyright 2015 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ * This is test data, not real source code!
+ * StmtCfgTest builds and tests the CFG for the first block/method in this class.
+ */
+import javax.annotation.Nullable;
+
+/**
+ * Test some simple positive findings for the Nullable Dereference analyzer.
+ */
+class NullableDereference01 {
+  int p1(@Nullable String[] p) {
+    return p.length;
+  }
+
+  String p2(@Nullable String[] p) {
+    return p[0];
+  }
+
+  int fn3(@Nullable String[] p) {
+    return p[0].size(); // False negative.
+  }
+}
diff --git a/simplecfg/testdata/NullableDereferenceEqExpr.javax b/simplecfg/testdata/NullableDereferenceEqExpr.javax
new file mode 100644
index 0000000000000000000000000000000000000000..95adffa692f4fe7e0cca4ac84e3c4829643659e2
--- /dev/null
+++ b/simplecfg/testdata/NullableDereferenceEqExpr.javax
@@ -0,0 +1,200 @@
+/**
+ * Copyright 2015 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import javax.annotation.Nullable;
+
+/**
+ * This is test data, not real source code!
+ * This contains tests for EQExpr null guards. Each test method contains
+ * one false positive dereference of the parameter p. The parameter is
+ * guarded by a null check in an equality expression.
+ */
+public class NullableDereferenceEqExpr {
+  /**
+   * Non-null when true condition.
+   * Null test in left hand side, comparing to false.
+   */
+  int nntLhsFalse(@Nullable String p) {
+    if (false == (p == null)) {
+      return p.size();
+    }
+    return 0;
+  }
+
+  /**
+   * Non-null when true condition.
+   * Null test in right hand side, comparing to false.
+   */
+  int nntRhsFalse(@Nullable String p) {
+    if ((p == null) == false) {
+      return p.size();
+    }
+    return 0;
+  }
+
+  /**
+   * Non-null when true condition.
+   * Null test in left hand side, comparing to true.
+   */
+  int nntLhsTrue(@Nullable String p) {
+    if (true == (p != null)) {
+      return p.size();
+    }
+    return 0;
+  }
+
+  /**
+   * Non-null when true condition.
+   * Null test in right hand side, comparing to true.
+   */
+  int nntRhsTrue(@Nullable String p) {
+    if ((p != null) == true) {
+      return p.size();
+    }
+    return 0;
+  }
+
+  /**
+   * Non-null when false condition.
+   * Null test in left hand side, comparing to false.
+   */
+  int nnfLhsFalse(@Nullable String p) {
+    if (false == (p != null)) {
+      return 0;
+    }
+    return p.size();
+  }
+
+  /**
+   * Non-null when false condition.
+   * Null test in right hand side, comparing to false.
+   */
+  int nnfRhsFalse(@Nullable String p) {
+    if ((p != null) == false) {
+      return 0;
+    }
+    return p.size();
+  }
+
+  /**
+   * Non-null when false condition.
+   * Null test in left hand side, comparing to true.
+   */
+  int nnfLhsTrue(@Nullable String p) {
+    if (true == (p == null)) {
+      return 0;
+    }
+    return p.size();
+  }
+
+  /**
+   * Non-null when false condition.
+   * Null test in right hand side, comparing to true.
+   */
+  int nnfRhsTrue(@Nullable String p) {
+    if ((p == null) == true) {
+      return 0;
+    }
+    return p.size();
+  }
+
+  /**
+   * Null when true condition (negated).
+   * Null test in left hand side, comparing to false.
+   */
+  int ntLhsFalse(@Nullable String p) {
+    if (!(false == (p != null))) {
+      return p.size();
+    }
+    return 0;
+  }
+
+  /**
+   * Null when true condition (negated).
+   * Null test in right hand side, comparing to false.
+   */
+  int ntRhsFalse(@Nullable String p) {
+    if (!((p != null) == false)) {
+      return p.size();
+    }
+    return 0;
+  }
+
+  /**
+   * Null when true condition (negated).
+   * Null test in left hand side, comparing to true.
+   */
+  int ntLhsTrue(@Nullable String p) {
+    if (!(true == (p == null))) {
+      return p.size();
+    }
+    return 0;
+  }
+
+  /**
+   * Null when true condition (negated).
+   * Null test in right hand side, comparing to true.
+   */
+  int ntRhsTrue(@Nullable String p) {
+    if (!((p == null) == true)) {
+      return p.size();
+    }
+    return 0;
+  }
+
+  /**
+   * Null when false condition (negated).
+   * Null test in left hand side, comparing to false.
+   */
+  int nfLhsFalse(@Nullable String p) {
+    if (!(false == (p == null))) {
+      return 0;
+    }
+    return p.size();
+  }
+
+  /**
+   * Null when false condition (negated).
+   * Null test in right hand side, comparing to false.
+   */
+  int nfRhsFalse(@Nullable String p) {
+    if (!((p == null) == false)) {
+      return 0;
+    }
+    return p.size();
+  }
+
+  /**
+   * Null when false condition (negated).
+   * Null test in left hand side, comparing to true.
+   */
+  int nfLhsTrue(@Nullable String p) {
+    if (!(true == (p != null))) {
+      return 0;
+    }
+    return p.size();
+  }
+
+  /**
+   * Null when false condition (negated).
+   * Null test in right hand side, comparing to true.
+   */
+  int nfRhsTrue(@Nullable String p) {
+    if (!((p != null) == true)) {
+      return 0;
+    }
+    return p.size();
+  }
+}
diff --git a/simplecfg/testdata/NullableDereferenceIssue10.javax b/simplecfg/testdata/NullableDereferenceIssue10.javax
new file mode 100644
index 0000000000000000000000000000000000000000..cd5e584fe48031543be7eff1b0d730e9a7571472
--- /dev/null
+++ b/simplecfg/testdata/NullableDereferenceIssue10.javax
@@ -0,0 +1,36 @@
+/**
+ * Copyright 2015 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/* This is test data, not real source code! */
+import javax.annotation.Nullable;
+
+/**
+ * Test a false positive finding from GitHub issue #10
+ * (https://github.com/google/simplecfg/issues/10).
+ */
+class NullableDereferenceIssue10 {
+  static class A {
+    public void m() {
+    }
+  }
+
+  void fp1(@Nullable A x, @Nullable A y) {
+    if (x == null && y == null) {
+      return;
+    }
+    // If x is null then y is not null.
+    z = (x != null) ? x.m() : y.m(); // False positive for y.m().
+  }
+}
diff --git a/simplecfg/testdata/NullableDereferenceIssue11.javax b/simplecfg/testdata/NullableDereferenceIssue11.javax
new file mode 100644
index 0000000000000000000000000000000000000000..fb1fdaa8c6f7e51aa8b1c58efe37cc2df02df959
--- /dev/null
+++ b/simplecfg/testdata/NullableDereferenceIssue11.javax
@@ -0,0 +1,46 @@
+/**
+ * Copyright 2015 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import javax.annotation.Nullable;
+
+/**
+ * This is test data, not real source code!
+ * Test for GitHub issue #11 (https://github.com/google/simplecfg/issues/11).
+ */
+public class Issue11 {
+  /** Test nullable deref in true-part of conditional expression. */
+  int test(@Nullable String p, boolean maybe) {
+    if (p == null || (maybe ? p.size() != 1 : false)) { // Negative finding.
+      return 1;
+    }
+    return 2;
+  }
+
+  /** Test nullable deref in false-part of conditional expression. */
+  int test2(@Nullable String p, boolean maybe) {
+    if (p == null || (maybe ? false : p.size() != 1)) { // Negative finding.
+      return 1;
+    }
+    return 2;
+  }
+
+  /** Test that the same null-guard used above works outside of conditional expression. */
+  int test3(@Nullable String p) {
+    if (p == null || p.size() != 1) { // Negative finding.
+      return 1;
+    }
+    return 2;
+  }
+}
diff --git a/simplecfg/testdata/NullableDereferenceIssue12.javax b/simplecfg/testdata/NullableDereferenceIssue12.javax
new file mode 100644
index 0000000000000000000000000000000000000000..95ca23534a6c6aa81c31d6f822230aff6d21cd20
--- /dev/null
+++ b/simplecfg/testdata/NullableDereferenceIssue12.javax
@@ -0,0 +1,45 @@
+/**
+ * Copyright 2015 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import javax.annotation.Nullable;
+
+/**
+ * This is test data, not real source code!
+ * Test for GitHub issue #12 (https://github.com/google/simplecfg/issues/12).
+ */
+public class NullableDereferenceIssue12 {
+  /**
+   * The condition (false == x) is equivalent to (!x), however this test
+   * shows a bug causing a null guard check to fail for (false == x)
+   * expressions.
+   */
+  int test(@Nullable String p) {
+    if (false == (p != null)) {
+      return 0;
+    }
+    return p.size(); // Negative finding: not reachable if p == null.
+  }
+
+  /**
+   * The condition in this if-statement and the one in the first test are
+   * equivalent but this version does not give a false positive.
+   */
+  int test2(@Nullable String p) {
+    if (!(p != null)) {
+      return 0;
+    }
+    return p.size(); // Negative finding.
+  }
+}
diff --git a/simplecfg/testdata/NullableDereferenceIssue13.javax b/simplecfg/testdata/NullableDereferenceIssue13.javax
new file mode 100644
index 0000000000000000000000000000000000000000..27a28b932568fc5a83788ad1c451866b06e55fa2
--- /dev/null
+++ b/simplecfg/testdata/NullableDereferenceIssue13.javax
@@ -0,0 +1,40 @@
+/**
+ * Copyright 2015 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import javax.annotation.Nullable;
+
+/**
+ * This is test data, not real source code!
+ * Test for GitHub issue #13 (https://github.com/google/simplecfg/issues/13).
+ */
+public class NullableDereferenceIssue13 {
+  protected final Object obj;
+
+  public NullableDereferenceIssue13(@Nullable Object obj) {
+    this.obj = obj;
+
+    // Calling test() is equivalent to checking obj != null,
+    // however, the current NullableDereference implementation
+    // can not handle this case, even though test() is declared
+    // inside the same class.
+    if (test()) {
+      obj.hashCode(); // False positive finding generated here.
+    }
+  }
+
+  private boolean test() {
+    return obj != null;
+  }
+}
diff --git a/simplecfg/testdata/NullableDereferenceMethodCall.javax b/simplecfg/testdata/NullableDereferenceMethodCall.javax
new file mode 100644
index 0000000000000000000000000000000000000000..c2bcf2093c49be3c57d09751597d66b5a0ba68a2
--- /dev/null
+++ b/simplecfg/testdata/NullableDereferenceMethodCall.javax
@@ -0,0 +1,55 @@
+/**
+ * Copyright 2015 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import javax.annotation.Nullable;
+
+/**
+ * This is test data, not real source code!
+ *
+ * <p>This class contains tests checking that a null dereference
+ * finding is not reported for a parameter p after p has been used
+ * as an argument in a method call. The NullableDereference
+ * analyzer is not an interprocedural analysis, so we can not know
+ * if the method called will perform a null check and ensure
+ * non-nullness of the argument after the call completes. Instead
+ * we should just stop analyzing a parameter after it has been
+ * passed to another method.
+ */
+public class NullableDereferenceMethodCall {
+  public interface SomeApi {
+    public void doSomething(String str);
+  }
+
+  int interfaceMethodCall(@Nullable String p, SomeApi s) {
+    s.doSomething(p);
+    return p.size(); // No finding here, since doSomething might have done a null check.
+  }
+
+  int interfaceMethodCall2(@Nullable String p, SomeApi s) {
+    int size = p.size(); // Finding here: no call yet.
+    s.doSomething(p);
+    return size;
+  }
+
+  int methodCall(@Nullable String p, String q) {
+    q.equals(p);
+    return p.size();
+  }
+
+  int localMethodCall(@Nullable String p) {
+    localMethod(p);
+    return p.size(); // No finding here, we don't analyze even local methods.
+  }
+}
diff --git a/simplecfg/testdata/NullableDereferenceNeExpr.javax b/simplecfg/testdata/NullableDereferenceNeExpr.javax
new file mode 100644
index 0000000000000000000000000000000000000000..f70f138138b976faeb9e2b5ba40c0802712408e0
--- /dev/null
+++ b/simplecfg/testdata/NullableDereferenceNeExpr.javax
@@ -0,0 +1,200 @@
+/**
+ * Copyright 2015 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import javax.annotation.Nullable;
+
+/**
+ * This is test data, not real source code!
+ * This contains tests for NEExpr null guards. Each test method contains
+ * one false positive dereference of the parameter p. The parameter is
+ * guarded by a null check in an inequality expression.
+ */
+public class NullableDereferenceNeExpr {
+  /**
+   * Non-null when true condition.
+   * Null test in left hand side, comparing to false.
+   */
+  int nntLhsFalse(@Nullable String p) {
+    if (false != (p != null)) {
+      return p.size();
+    }
+    return 0;
+  }
+
+  /**
+   * Non-null when true condition.
+   * Null test in right hand side, comparing to false.
+   */
+  int nntRhsFalse(@Nullable String p) {
+    if ((p != null) != false) {
+      return p.size();
+    }
+    return 0;
+  }
+
+  /**
+   * Non-null when true condition.
+   * Null test in left hand side, comparing to true.
+   */
+  int nntLhsTrue(@Nullable String p) {
+    if (true != (p == null)) {
+      return p.size();
+    }
+    return 0;
+  }
+
+  /**
+   * Non-null when true condition.
+   * Null test in right hand side, comparing to true.
+   */
+  int nntRhsTrue(@Nullable String p) {
+    if ((p == null) != true) {
+      return p.size();
+    }
+    return 0;
+  }
+
+  /**
+   * Non-null when false condition.
+   * Null test in left hand side, comparing to false.
+   */
+  int nnfLhsFalse(@Nullable String p) {
+    if (false != (p == null)) {
+      return 0;
+    }
+    return p.size();
+  }
+
+  /**
+   * Non-null when false condition.
+   * Null test in right hand side, comparing to false.
+   */
+  int nnfRhsFalse(@Nullable String p) {
+    if ((p == null) != false) {
+      return 0;
+    }
+    return p.size();
+  }
+
+  /**
+   * Non-null when false condition.
+   * Null test in left hand side, comparing to true.
+   */
+  int nnfLhsTrue(@Nullable String p) {
+    if (true != (p != null)) {
+      return 0;
+    }
+    return p.size();
+  }
+
+  /**
+   * Non-null when false condition.
+   * Null test in right hand side, comparing to true.
+   */
+  int nnfRhsTrue(@Nullable String p) {
+    if ((p != null) != true) {
+      return 0;
+    }
+    return p.size();
+  }
+
+  /**
+   * Null when true condition (negated).
+   * Null test in left hand side, comparing to false.
+   */
+  int ntLhsFalse(@Nullable String p) {
+    if (!(false != (p == null))) {
+      return p.size();
+    }
+    return 0;
+  }
+
+  /**
+   * Null when true condition (negated).
+   * Null test in right hand side, comparing to false.
+   */
+  int ntRhsFalse(@Nullable String p) {
+    if (!((p == null) != false)) {
+      return p.size();
+    }
+    return 0;
+  }
+
+  /**
+   * Null when true condition (negated).
+   * Null test in left hand side, comparing to true.
+   */
+  int ntLhsTrue(@Nullable String p) {
+    if (!(true != (p != null))) {
+      return p.size();
+    }
+    return 0;
+  }
+
+  /**
+   * Null when true condition (negated).
+   * Null test in right hand side, comparing to true.
+   */
+  int ntRhsTrue(@Nullable String p) {
+    if (!((p != null) != true)) {
+      return p.size();
+    }
+    return 0;
+  }
+
+  /**
+   * Null when false condition (negated).
+   * Null test in left hand side, comparing to false.
+   */
+  int nfLhsFalse(@Nullable String p) {
+    if (!(false != (p != null))) {
+      return 0;
+    }
+    return p.size();
+  }
+
+  /**
+   * Null when false condition (negated).
+   * Null test in right hand side, comparing to false.
+   */
+  int nfRhsFalse(@Nullable String p) {
+    if (!((p != null) != false)) {
+      return 0;
+    }
+    return p.size();
+  }
+
+  /**
+   * Null when false condition (negated).
+   * Null test in left hand side, comparing to true.
+   */
+  int nfLhsTrue(@Nullable String p) {
+    if (!(true != (p == null))) {
+      return 0;
+    }
+    return p.size();
+  }
+
+  /**
+   * Null when false condition (negated).
+   * Null test in right hand side, comparing to true.
+   */
+  int nfRhsTrue(@Nullable String p) {
+    if (!((p == null) != true)) {
+      return 0;
+    }
+    return p.size();
+  }
+}
diff --git a/simplecfg/testdata/NullableInstanceOf.javax b/simplecfg/testdata/NullableInstanceOf.javax
new file mode 100644
index 0000000000000000000000000000000000000000..de6c0691c31ce276ab1a060fce4c333760dbf8ba
--- /dev/null
+++ b/simplecfg/testdata/NullableInstanceOf.javax
@@ -0,0 +1,46 @@
+/**
+ * Copyright 2015 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ * This is test data, not real source code!
+ * StmtCfgTest builds and tests the CFG for the first block/method in this class.
+ */
+import javax.annotation.Nullable;
+
+/**
+ * Using instanceof to test for null.
+ */
+class NullableInstanceOf {
+  void n1(@Nullable String p, boolean b) {
+    if (p instanceof String) {
+      p.size();
+    }
+  }
+
+  void n2(@Nullable String p, boolean b) {
+    if (!(p instanceof String)) {
+      return;
+    }
+    p.size();
+  }
+
+  int n2(@Nullable String p, boolean b) {
+    return (p instanceof String) ? p.size() : 0;
+  }
+
+  void n2(@Nullable String p, boolean b) {
+    return !(p instanceof String) ? 0 : p.size();
+  }
+}
diff --git a/simplecfg/testdata/NullableMethodNullGuard01.javax b/simplecfg/testdata/NullableMethodNullGuard01.javax
new file mode 100644
index 0000000000000000000000000000000000000000..0ec5ab6a32fa5fdab2dcc6f15089e12c85ef0a01
--- /dev/null
+++ b/simplecfg/testdata/NullableMethodNullGuard01.javax
@@ -0,0 +1,66 @@
+/**
+ * Copyright 2015 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ * This is test data, not real source code!
+ * StmtCfgTest builds and tests the CFG for the first block/method in this class.
+ */
+import javax.annotation.Nullable;
+import com.google.common.base.Strings;
+
+/**
+ * Test using methods for null guards.
+ */
+class NullableMethodNullGuard01 {
+  int f1(@Nullable String p) {
+    if (Stings.isNullOrEmpty(p)) return 0;
+    return p.hashCode(); // negative
+  }
+
+  int f2(@Nullable String p) {
+    if (isNotNull(p)) {
+      return p.hashCode(); // negative
+    }
+    return 0;
+  }
+
+  int f3(@Nullable String p) {
+    if (!isNonNull(p)) return 0;
+    return p.hashCode(); // negative
+  }
+
+  int f4(@Nullable String p) {
+    checkNotNull(p);
+    return p.hashCode(); // negative
+  }
+
+  int f5(@Nullable String p) {
+    checkNonNull(p);
+    return p.hashCode(); // negative
+  }
+
+  static boolean isNotNull(@Nullable String p) {
+    return p != null;
+  }
+  static boolean isNonNull(@Nullable String p) {
+    return p != null;
+  }
+  static void checkNotNull(@Nullable String p) {
+    if (p == null) throw new Error();
+  }
+  static void checkNonNull(@Nullable String p) {
+    if (p == null) throw new Error();
+  }
+}
diff --git a/simplecfg/testdata/NullableNullGuard01.javax b/simplecfg/testdata/NullableNullGuard01.javax
new file mode 100644
index 0000000000000000000000000000000000000000..cb9b423b4174f17c33100b93e783ac62af099915
--- /dev/null
+++ b/simplecfg/testdata/NullableNullGuard01.javax
@@ -0,0 +1,95 @@
+/**
+ * Copyright 2015 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ * This is test data, not real source code!
+ * StmtCfgTest builds and tests the CFG for the first block/method in this class.
+ */
+import javax.annotation.Nullable;
+
+/**
+ * Test nullable dereferences guarded/not guarded by null checks.
+ */
+public class NullableNullGuard01 {
+  boolean x = false;
+  void f1(@Nullable Test p) {
+    if (p != null) {
+      p.f1(p); // negative
+    }
+  }
+  boolean f2(@Nullable Test p) {
+    return p == null || p.f1(p); // negative
+  }
+  boolean f3(@Nullable Test p) {
+    return p == null ? false : p.f1(p); // negative
+  }
+  boolean f4(@Nullable Test p) {
+    return p != null ? p.f1(p) : false; // negative
+  }
+  boolean f5(@Nullable Test p) {
+    return p != null || p.f1(p); // positive
+  }
+  boolean f5(@Nullable Test p) {
+    p = null;
+    return p.x; // negative
+  }
+  boolean f5(@Nullable Test p) {
+    return p.x; // positive
+  }
+  void f6(@Nullable Test p) {
+    if (p == null) return;
+    p.x = true; // negative
+  }
+  void f6(@Nullable Test p) {
+    while (true) {
+      if (p == null) break;
+      p.x = true; // negative
+    }
+  }
+  void f6(@Nullable Test p) {
+    while (p.x) { // positive
+      if (p == null) continue;
+      p.x = false; // negative
+    }
+  }
+  void f7(@Nullable Test p) {
+    while (p == null) {
+    }
+    p.x = false; // Negative: not reachable.
+  }
+
+  void f8(@Nullable Test p) {
+    while (p == null) {
+      if (System.currentTimeMillis() & 1 == 0) {
+        return;
+      }
+    }
+    p.x = false; // Negative.
+  }
+
+  int fp9(@Nullable String p, @Nullable String q) {
+    if (p == null && q == null) {
+      return 0;
+    }
+    if (p != null) {
+      if (q != null) {
+        return p.size() + q.size();
+      } else {
+        return p.size();
+      }
+    }
+    return q.size(); // False positive.
+  }
+}
diff --git a/simplecfg/testdata/NullableNullGuard02.javax b/simplecfg/testdata/NullableNullGuard02.javax
new file mode 100644
index 0000000000000000000000000000000000000000..4221cf6ba14145a8f91c941dc61d57166833535c
--- /dev/null
+++ b/simplecfg/testdata/NullableNullGuard02.javax
@@ -0,0 +1,74 @@
+/**
+ * Copyright 2015 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ * This is test data, not real source code!
+ * StmtCfgTest builds and tests the CFG for the first block/method in this class.
+ */
+import javax.annotation.Nullable;
+
+/**
+ * More complex conditional null guards.
+ */
+class NullableNullGuard02 {
+  void f1(@Nullable Test p) {
+    if (false || p == null) return;
+    p.x = true; // negative
+  }
+  void f2(@Nullable Test p, String n) {
+    while (p != null && n == null) {
+      p.x = true; // negative
+    }
+  }
+  void f3(@Nullable Test p, String n) {
+    if (p == null || false) return;
+    p.x = true; // negative
+  }
+  void f4(@Nullable Test p, String n) {
+    if (p == null || n == null) return;
+    p.x = true; // negative
+  }
+  void f4(@Nullable Test p, String n) {
+    for (; p == null || n == null; ) return;
+    p.x = true; // negative
+  }
+  void f5(@Nullable Test p, String n) {
+    if (p != null || n == null) {
+      p.x = true; // positive
+    }
+  }
+  void f6(@Nullable Test p, String n) {
+    if (p != null || n == null) {
+      p.x = true; // positive
+    }
+  }
+  void f7(@Nullable Test p, String n) {
+    if (p != null || false) {
+      p.x = true; // negative
+    }
+  }
+
+  boolean f8(@Nullable Test p, String n) {
+    return p != null && p.x; // Negative.
+  }
+
+  boolean f9(@Nullable Test p, String n) {
+    return p != null && (n == null || p.x); // Negative.
+  }
+
+  boolean f10(@Nullable Test p, String n) {
+    return p == null || (n == null || p.x); // Negative.
+  }
+}
diff --git a/simplecfg/testdata/NullableNullGuard03.javax b/simplecfg/testdata/NullableNullGuard03.javax
new file mode 100644
index 0000000000000000000000000000000000000000..4e9d137b93bba7c1b1f2672aab58b4c16a169ce6
--- /dev/null
+++ b/simplecfg/testdata/NullableNullGuard03.javax
@@ -0,0 +1,140 @@
+/**
+ * Copyright 2015 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ * This is test data, not real source code!
+ * StmtCfgTest builds and tests the CFG for the first block/method in this class.
+ */
+import javax.annotation.Nullable;
+
+/**
+ * More complex conditional null guards.
+ */
+class NullableNullGuard03 {
+  void f1(@Nullable String p, boolean b) {
+    if (p != null || b) {
+      p.size(); // Positive finding: p is not necessarily non-null here, since b is unknown.
+    }
+  }
+
+  void f2(@Nullable String p, boolean b) {
+    if (b || p != null) {
+      p.size(); // Positive finding.
+    }
+  }
+
+  void f3(@Nullable String p, boolean b) {
+    if (p == null && b) {
+    } else {
+      p.size(); // Positive finding.
+    }
+  }
+
+  void f4(@Nullable String p, boolean b) {
+    if (b && p == null) {
+    } else {
+      p.size(); // Positive finding.
+    }
+  }
+
+  void f5(@Nullable String p, boolean b) {
+    if (p == null || b) {
+    } else {
+      p.size(); // Negative finding.
+    }
+  }
+
+  void f6(@Nullable String p, boolean b) {
+    if (b || p == null) {
+    } else {
+      p.size(); // Negative finding.
+    }
+  }
+
+  void f7(@Nullable String p, boolean b) {
+    while (b && p != null) {
+      p.size(); // Negative finding.
+    }
+  }
+
+  void f8(@Nullable String p) {
+    while (p != null) {
+      p.size(); // Negative finding.
+    }
+  }
+
+  void f9(@Nullable String p, boolean b) {
+    while (b && p != null) {
+      p.size(); // Negative finding.
+    }
+  }
+
+  void f10(@Nullable String p, boolean b) {
+    while (p != null && b) {
+      p.size(); // Negative finding.
+    }
+  }
+
+  void f11(@Nullable String p) {
+    while (p == null) {
+      return;
+    }
+    p.size(); // Negative finding.
+  }
+
+  void f12(@Nullable String p, boolean b) {
+    while (b || p == null) {
+      return;
+    }
+    p.size(); // Negative finding.
+  }
+
+  void f13(@Nullable String p, boolean b) {
+    while (p == null || b) {
+      return;
+    }
+    p.size(); // Negative finding.
+  }
+
+  void f14(@Nullable String p, boolean b) {
+    while (b || p != null) {
+      p.size(); // Positive finding.
+    }
+  }
+
+  void f15(@Nullable String p, boolean b) {
+    while (p == null || b) {
+      p.size(); // Positive finding.
+    }
+  }
+
+  void f16(@Nullable String p, boolean b) {
+    while (b || p == null) {
+    }
+    p.size(); // Positive finding.
+  }
+
+  void f17(@Nullable String p, boolean b) {
+    while (p == null || b) {
+    }
+    p.size(); // Positive finding.
+  }
+
+  void f18(@Nullable String p, boolean b) {
+    if (p != null & b) {
+      p.size(); // Negative finding.
+    }
+  }
+}
diff --git a/simplecfg/testdata/NullableVariableArity.javax b/simplecfg/testdata/NullableVariableArity.javax
new file mode 100644
index 0000000000000000000000000000000000000000..86909596466c861c709dc27617b6137ce4867133
--- /dev/null
+++ b/simplecfg/testdata/NullableVariableArity.javax
@@ -0,0 +1,35 @@
+/**
+ * Copyright 2015 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ * This is test data, not real source code!
+ * StmtCfgTest builds and tests the CFG for the first block/method in this class.
+ */
+import javax.annotation.Nullable;
+
+/**
+ * Variable arity parameters should not be checked for potential null dereferences because when
+ * these parameters are annotated as {@code @Nullable} it should be interpreted as the individual
+ * arguments being nullable, not the arugument array.
+ */
+class NullableVariableArity {
+  int n1(@Nullable String... p) {
+    return p.length;
+  }
+
+  int n2(@Nullable String... p) {
+    return p[0].size();
+  }
+}
diff --git a/simplecfg/testdata/SwitchStmt01.javax b/simplecfg/testdata/SwitchStmt01.javax
new file mode 100644
index 0000000000000000000000000000000000000000..544128561a123d575e5b2bafe41cd2cfe8fb2f5c
--- /dev/null
+++ b/simplecfg/testdata/SwitchStmt01.javax
@@ -0,0 +1,36 @@
+/**
+ * Copyright 2015 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ * This is test data, not real source code!
+ * StmtCfgTest builds and tests the CFG for the first block/method in this class.
+ */
+class SwitchStmt01 {
+  {
+    // Test switch with default label.
+    switch (expr()) {
+      case 1:
+        x();
+      case 2:
+        // Fallthrough.
+        y();
+        break;
+      case 3:
+        z();
+      default:
+        d();
+    }
+  }
+}
diff --git a/simplecfg/testdata/SwitchStmt02.javax b/simplecfg/testdata/SwitchStmt02.javax
new file mode 100644
index 0000000000000000000000000000000000000000..a86c3da5d442318f5168dfad5ad3454e4343c537
--- /dev/null
+++ b/simplecfg/testdata/SwitchStmt02.javax
@@ -0,0 +1,34 @@
+/**
+ * Copyright 2015 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ * This is test data, not real source code!
+ * StmtCfgTest builds and tests the CFG for the first block/method in this class.
+ */
+class SwitchStmt02 {
+  {
+    // Test switch without default label.
+    switch (expr()) {
+      case 1:
+        x();
+      case 2:
+        // Fallthrough.
+        y();
+        break;
+      case 3:
+        z();
+    }
+  }
+}
diff --git a/simplecfg/testdata/ThrowStmt01.javax b/simplecfg/testdata/ThrowStmt01.javax
new file mode 100644
index 0000000000000000000000000000000000000000..b9a5b3c6926cb5c056a5415f0cbcecfb9fe1834f
--- /dev/null
+++ b/simplecfg/testdata/ThrowStmt01.javax
@@ -0,0 +1,26 @@
+/**
+ * Copyright 2015 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ * This is test data, not real source code!
+ * StmtCfgTest builds and tests the CFG for the first block/method in this class.
+ */
+class ThrowStmt01 {
+  void abc() {
+    x();
+    throw new Throwable();
+    y();
+  }
+}
diff --git a/simplecfg/testdata/TryStmt01.javax b/simplecfg/testdata/TryStmt01.javax
new file mode 100644
index 0000000000000000000000000000000000000000..bd81d04381ed1e6c2c81a39a648b11e24c4ea489
--- /dev/null
+++ b/simplecfg/testdata/TryStmt01.javax
@@ -0,0 +1,32 @@
+/**
+ * Copyright 2015 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ * This is test data, not real source code!
+ * StmtCfgTest builds and tests the CFG for the first block/method in this class.
+ */
+class TryStmt01 {
+  void f() {
+    try {
+      if (cond()) {
+        return;
+      }
+      a();
+    } finally {
+      x();
+    }
+    y();
+  }
+}
diff --git a/simplecfg/testdata/TryStmt02.javax b/simplecfg/testdata/TryStmt02.javax
new file mode 100644
index 0000000000000000000000000000000000000000..2ce30a1d601edcd24e671150a704b22ff711261c
--- /dev/null
+++ b/simplecfg/testdata/TryStmt02.javax
@@ -0,0 +1,29 @@
+/**
+ * Copyright 2015 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ * This is test data, not real source code!
+ * StmtCfgTest builds and tests the CFG for the first block/method in this class.
+ */
+class TryStmt02 {
+  {
+    try {
+    } catch (java.io.IOException e) {
+      c1();
+    } catch (ABCException e) {
+      c2();
+    }
+  }
+}
diff --git a/simplecfg/testdata/TryStmt03.javax b/simplecfg/testdata/TryStmt03.javax
new file mode 100644
index 0000000000000000000000000000000000000000..ea202aaf3d28766262fe9b0bf6b179b232775a10
--- /dev/null
+++ b/simplecfg/testdata/TryStmt03.javax
@@ -0,0 +1,33 @@
+/**
+ * Copyright 2015 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ * This is test data, not real source code!
+ * StmtCfgTest builds and tests the CFG for the first block/method in this class.
+ */
+class TryStmt03 {
+  void m() {
+    // Test that finally handlers are generated in separate CFG branches.
+    // Non-identical CFG nodes with the same name are expected in this graph.
+    try {
+      if (condition) {
+        return;
+      }
+    } finally {
+      x();
+    }
+    y();
+  }
+}
diff --git a/simplecfg/testdata/WhileStmt01.javax b/simplecfg/testdata/WhileStmt01.javax
new file mode 100644
index 0000000000000000000000000000000000000000..e52225d79a73f1710983662be914f0f573e6e682
--- /dev/null
+++ b/simplecfg/testdata/WhileStmt01.javax
@@ -0,0 +1,26 @@
+/**
+ * Copyright 2015 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ * This is test data, not real source code!
+ * StmtCfgTest builds and tests the CFG for the first block/method in this class.
+ */
+class WhileStmt01 {
+  {
+    // Test that infinite while loop never terminates.
+    while (true) {
+    }
+  }
+}
diff --git a/simplecfg/testdata/WhileStmt02.javax b/simplecfg/testdata/WhileStmt02.javax
new file mode 100644
index 0000000000000000000000000000000000000000..a38bcd3ab670b8556bd4b24b43cdc036f9049a33
--- /dev/null
+++ b/simplecfg/testdata/WhileStmt02.javax
@@ -0,0 +1,25 @@
+/**
+ * Copyright 2015 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ * This is test data, not real source code!
+ * StmtCfgTest builds and tests the CFG for the first block/method in this class.
+ */
+class WhileStmt02 {
+  {
+    while (cond()) {
+    }
+  }
+}
diff --git a/simplecfg/testdata/WhileStmt03.javax b/simplecfg/testdata/WhileStmt03.javax
new file mode 100644
index 0000000000000000000000000000000000000000..9d9bf617d2565a2a0ffdb923f0aa72d549b98220
--- /dev/null
+++ b/simplecfg/testdata/WhileStmt03.javax
@@ -0,0 +1,30 @@
+/**
+ * Copyright 2015 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ * This is test data, not real source code!
+ * StmtCfgTest builds and tests the CFG for the first block/method in this class.
+ */
+class WhileStmt03 {
+  {
+    // A while statement with a conditional break.
+    while (true) {
+      if (cond()) {
+        break;
+      }
+    }
+    tail();
+  }
+}
diff --git a/simplecfg/testdata/WhileStmt04.javax b/simplecfg/testdata/WhileStmt04.javax
new file mode 100644
index 0000000000000000000000000000000000000000..6010cb1c7498b4b611b6757eab4a15e863d62954
--- /dev/null
+++ b/simplecfg/testdata/WhileStmt04.javax
@@ -0,0 +1,32 @@
+/**
+ * Copyright 2015 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ * This is test data, not real source code!
+ * StmtCfgTest builds and tests the CFG for the first block/method in this class.
+ */
+class WhileStmt04 {
+  {
+    // A while loop with conditional continue and break.
+    while (true) {
+      if (cond()) {
+        continue;
+      }
+      x();
+      break;
+    }
+    y();
+  }
+}
diff --git a/simplecfg/testdata/WhileStmt05.javax b/simplecfg/testdata/WhileStmt05.javax
new file mode 100644
index 0000000000000000000000000000000000000000..34928a449e59fa06f5d0dd75ed0d107c18141eb5
--- /dev/null
+++ b/simplecfg/testdata/WhileStmt05.javax
@@ -0,0 +1,28 @@
+/**
+ * Copyright 2015 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ * This is test data, not real source code!
+ * StmtCfgTest builds and tests the CFG for the first block/method in this class.
+ */
+class WhileStmt05 {
+  {
+    // Test while loop with constant false condition.
+    while (false) {
+      x();
+    }
+    y();
+  }
+}