Port lint checks from old API to new PSI based API

This CL rewrites all the Java source file detectors in lint to be
based on the PSI API instead of the Lombok and ResolvedNode APIs.

In several cases, I took the opportunity to revisit older lint checks
and implement them in a better way. For example, a handful of
detectors which performed their own simple flow analysis now use the
general evaluators (ConstantEvaluator, TypeEvaluator and a new
ResourceEvalutor) to perform flow analysis as well. In many cases this
led to the detectors being able to catch more bugs, so unit tests have
been updated with new golden files.

As another example, the CutPasteDetector now uses a smarter algorithm
(which should also avoid bugs like issue 174340.)

I performed some other cleanup as well, such as removing the appliesTo
and getSpeed methods which aren't used for anything anymore.

Change-Id: If7f25186f857c9de988b672e5bb065051b0fd54d
diff --git a/common/src/main/java/com/android/SdkConstants.java b/common/src/main/java/com/android/SdkConstants.java
index 5c5ac74..319bfbf 100644
--- a/common/src/main/java/com/android/SdkConstants.java
+++ b/common/src/main/java/com/android/SdkConstants.java
@@ -577,6 +577,7 @@
     public static final String CLASS_INTENT = "android.content.Intent"; //$NON-NLS-1$
     public static final String CLASS_CONTEXT = "android.content.Context"; //$NON-NLS-1$
     public static final String CLASS_RESOURCES = "android.content.res.Resources"; //$NON-NLS-1$
+    public static final String CLS_TYPED_ARRAY = "android.content.res.TypedArray";   //$NON-NLS-1$
     public static final String CLASS_VIEW = "android.view.View"; //$NON-NLS-1$
     public static final String CLASS_VIEWGROUP = "android.view.ViewGroup"; //$NON-NLS-1$
     public static final String CLASS_NAME_LAYOUTPARAMS = "LayoutParams"; //$NON-NLS-1$
diff --git a/lint/cli/src/main/java/com/android/tools/lint/Reporter.java b/lint/cli/src/main/java/com/android/tools/lint/Reporter.java
index 11ba13d..807a318 100644
--- a/lint/cli/src/main/java/com/android/tools/lint/Reporter.java
+++ b/lint/cli/src/main/java/com/android/tools/lint/Reporter.java
@@ -35,6 +35,7 @@
 import com.android.tools.lint.checks.AppCompatCallDetector;
 import com.android.tools.lint.checks.AppIndexingApiDetector;
 import com.android.tools.lint.checks.ByteOrderMarkDetector;
+import com.android.tools.lint.checks.CleanupDetector;
 import com.android.tools.lint.checks.CommentDetector;
 import com.android.tools.lint.checks.DetectMissingPrefix;
 import com.android.tools.lint.checks.DosLineEndingDetector;
@@ -57,7 +58,6 @@
 import com.android.tools.lint.checks.RtlDetector;
 import com.android.tools.lint.checks.ScrollViewChildDetector;
 import com.android.tools.lint.checks.SecurityDetector;
-import com.android.tools.lint.checks.SharedPrefsDetector;
 import com.android.tools.lint.checks.SignatureOrSystemDetector;
 import com.android.tools.lint.checks.SupportAnnotationDetector;
 import com.android.tools.lint.checks.TextFieldDetector;
@@ -477,6 +477,7 @@
                         AppIndexingApiDetector.ISSUE_APP_INDEXING_API,
                         AppIndexingApiDetector.ISSUE_URL_ERROR,
                         ByteOrderMarkDetector.BOM,
+                        CleanupDetector.SHARED_PREF,
                         CommentDetector.STOP_SHIP,
                         DetectMissingPrefix.MISSING_NAMESPACE,
                         DuplicateResourceDetector.TYPE_MISMATCH,
@@ -511,7 +512,6 @@
                         RtlDetector.COMPAT,
                         ScrollViewChildDetector.ISSUE,
                         SecurityDetector.EXPORTED_SERVICE,
-                        SharedPrefsDetector.ISSUE,
                         SignatureOrSystemDetector.ISSUE,
                         SupportAnnotationDetector.CHECK_PERMISSION,
                         SupportAnnotationDetector.CHECK_RESULT,
diff --git a/lint/libs/lint-api/src/main/java/com/android/tools/lint/client/api/JavaParser.java b/lint/libs/lint-api/src/main/java/com/android/tools/lint/client/api/JavaParser.java
index 3d04881..fe450e2 100644
--- a/lint/libs/lint-api/src/main/java/com/android/tools/lint/client/api/JavaParser.java
+++ b/lint/libs/lint-api/src/main/java/com/android/tools/lint/client/api/JavaParser.java
@@ -58,19 +58,29 @@
  * <b>NOTE: This is not public or final API; if you rely on this be prepared
  * to adjust your code for the next tools release.</b>
  */
+// Currently ships with deprecated API support
+@SuppressWarnings({"deprecation", "UnusedParameters"})
 @Beta
 public abstract class JavaParser {
-    public static final String TYPE_OBJECT = "java.lang.Object";        //$NON-NLS-1$
-    public static final String TYPE_STRING = "java.lang.String";        //$NON-NLS-1$
-    public static final String TYPE_INT = "int";                        //$NON-NLS-1$
-    public static final String TYPE_LONG = "long";                      //$NON-NLS-1$
-    public static final String TYPE_CHAR = "char";                      //$NON-NLS-1$
-    public static final String TYPE_FLOAT = "float";                    //$NON-NLS-1$
-    public static final String TYPE_DOUBLE = "double";                  //$NON-NLS-1$
-    public static final String TYPE_BOOLEAN = "boolean";                //$NON-NLS-1$
-    public static final String TYPE_SHORT = "short";                    //$NON-NLS-1$
-    public static final String TYPE_BYTE = "byte";                      //$NON-NLS-1$
-    public static final String TYPE_NULL = "null";                      //$NON-NLS-1$
+    public static final String TYPE_OBJECT = "java.lang.Object";
+    public static final String TYPE_STRING = "java.lang.String";
+    public static final String TYPE_INT = "int";
+    public static final String TYPE_LONG = "long";
+    public static final String TYPE_CHAR = "char";
+    public static final String TYPE_FLOAT = "float";
+    public static final String TYPE_DOUBLE = "double";
+    public static final String TYPE_BOOLEAN = "boolean";
+    public static final String TYPE_SHORT = "short";
+    public static final String TYPE_BYTE = "byte";
+    public static final String TYPE_NULL = "null";
+    public static final String TYPE_INTEGER_WRAPPER = "java.lang.Integer";
+    public static final String TYPE_BOOLEAN_WRAPPER = "java.lang.Boolean";
+    public static final String TYPE_BYTE_WRAPPER = "java.lang.Byte";
+    public static final String TYPE_SHORT_WRAPPER = "java.lang.Short";
+    public static final String TYPE_LONG_WRAPPER = "java.lang.Long";
+    public static final String TYPE_DOUBLE_WRAPPER = "java.lang.Double";
+    public static final String TYPE_FLOAT_WRAPPER = "java.lang.Float";
+    public static final String TYPE_CHARACTER_WRAPPER = "java.lang.Character";
 
     /**
      * Prepare to parse the given contexts. This method will be called before
@@ -254,29 +264,6 @@
         PsiElement nameNode = JavaContext.findNameElement(element);
         if (nameNode != null) {
             element = nameNode;
-        } else {
-            /* TODO:
-            if (element instanceof PsiSwitchStatement
-                    || element instanceof PsiForStatement
-                    || element instanceof PsiIfStatement
-                    || element instanceof PsiWhileStatement
-                    || element instanceof PsiThrowStatement
-                    || element instanceof PsiReturnStatement) {
-                Location location = getLocation(context, element);
-                Position start = location.getStart();
-                if (start != null) {
-                    // Lint doesn't want to highlight the entire statement/block associated
-                    // with this node, it wants to just highlight the keyword.
-                    // TODO: Figure out how to find the keyword length; for Lombok
-                    // we could guess based on the node type, but we can't do
-                    // that here.
-                    int length = ?;
-                    return Location.create(location.getFile(), start,
-                            new DefaultPosition(start.getLine(), start.getColumn() + length,
-                                    start.getOffset() + length));
-                }
-            }
-            */
         }
 
         return getLocation(context, element);
@@ -393,6 +380,7 @@
      * A description of a type, such as a primitive int or the android.app.Activity class
      * @deprecated Use {@link PsiType} instead
      */
+    @SuppressWarnings("unused")
     @Deprecated
     public abstract static class TypeDescriptor {
         /**
@@ -553,6 +541,7 @@
      * A resolved declaration from an AST Node reference
      * @deprecated Use {@link JavaPsiScanner} APIs instead
      */
+    @SuppressWarnings("unused")
     @Deprecated
     public abstract static class ResolvedNode {
         @NonNull
@@ -620,6 +609,7 @@
      * A resolved class declaration (class, interface, enumeration or annotation)
      * @deprecated Use {@link JavaPsiScanner} APIs instead
      */
+    @SuppressWarnings("unused")
     @Deprecated
     public abstract static class ResolvedClass extends ResolvedNode {
         /** Returns the fully qualified name of this class */
@@ -744,6 +734,7 @@
      * A method or constructor declaration
      * @deprecated Use {@link JavaPsiScanner} APIs instead
      */
+    @SuppressWarnings("unused")
     @Deprecated
     public abstract static class ResolvedMethod extends ResolvedNode {
         @Override
@@ -867,9 +858,11 @@
         if (cls1 == null || cls2 == null) {
             return false;
         }
+        //noinspection ConstantConditions
         while (cls1.getContainingClass() != null) {
             cls1 = cls1.getContainingClass();
         }
+        //noinspection ConstantConditions
         while (cls2.getContainingClass() != null) {
             cls2 = cls2.getContainingClass();
         }
@@ -985,6 +978,7 @@
      * A package declaration
      * @deprecated Use {@link JavaPsiScanner} APIs instead
      */
+    @SuppressWarnings("unused")
     @Deprecated
     public abstract static class ResolvedPackage extends ResolvedNode {
         /** Returns the parent package of this package, if any. */
diff --git a/lint/libs/lint-api/src/main/java/com/android/tools/lint/detector/api/ConstantEvaluator.java b/lint/libs/lint-api/src/main/java/com/android/tools/lint/detector/api/ConstantEvaluator.java
index 23225e5..e9d95ff 100644
--- a/lint/libs/lint-api/src/main/java/com/android/tools/lint/detector/api/ConstantEvaluator.java
+++ b/lint/libs/lint-api/src/main/java/com/android/tools/lint/detector/api/ConstantEvaluator.java
@@ -32,6 +32,32 @@
 import com.android.tools.lint.client.api.JavaParser.ResolvedField;
 import com.android.tools.lint.client.api.JavaParser.ResolvedNode;
 import com.google.common.collect.Lists;
+import com.intellij.psi.JavaTokenType;
+import com.intellij.psi.PsiArrayInitializerExpression;
+import com.intellij.psi.PsiArrayType;
+import com.intellij.psi.PsiAssignmentExpression;
+import com.intellij.psi.PsiBinaryExpression;
+import com.intellij.psi.PsiClassType;
+import com.intellij.psi.PsiConditionalExpression;
+import com.intellij.psi.PsiDeclarationStatement;
+import com.intellij.psi.PsiElement;
+import com.intellij.psi.PsiExpression;
+import com.intellij.psi.PsiExpressionStatement;
+import com.intellij.psi.PsiField;
+import com.intellij.psi.PsiLiteral;
+import com.intellij.psi.PsiLocalVariable;
+import com.intellij.psi.PsiNewExpression;
+import com.intellij.psi.PsiParenthesizedExpression;
+import com.intellij.psi.PsiPrefixExpression;
+import com.intellij.psi.PsiPrimitiveType;
+import com.intellij.psi.PsiReference;
+import com.intellij.psi.PsiReferenceExpression;
+import com.intellij.psi.PsiStatement;
+import com.intellij.psi.PsiType;
+import com.intellij.psi.PsiTypeCastExpression;
+import com.intellij.psi.PsiTypeElement;
+import com.intellij.psi.tree.IElementType;
+import com.intellij.psi.util.PsiTreeUtil;
 
 import java.lang.reflect.Array;
 import java.util.List;
@@ -93,7 +119,9 @@
      *
      * @param node the node to compute the constant value for
      * @return the corresponding constant value - a String, an Integer, a Float, and so on
+     * @deprecated Use {@link #evaluate(PsiElement)} instead
      */
+    @Deprecated
     @Nullable
     public Object evaluate(@NonNull Node node) {
         if (node instanceof NullLiteral) {
@@ -538,6 +566,540 @@
     }
 
     /**
+     * Evaluates the given node and returns the constant value it resolves to, if any
+     *
+     * @param node the node to compute the constant value for
+     * @return the corresponding constant value - a String, an Integer, a Float, and so on
+     */
+    @Nullable
+    public Object evaluate(@Nullable PsiElement node) {
+        if (node == null) {
+            return null;
+        }
+        if (node instanceof PsiLiteral) {
+            return ((PsiLiteral)node).getValue();
+        } else if (node instanceof PsiPrefixExpression) {
+            IElementType operator = ((PsiPrefixExpression) node).getOperationTokenType();
+            Object operand = evaluate(((PsiPrefixExpression) node).getOperand());
+            if (operand == null) {
+                return null;
+            }
+            if (operator == JavaTokenType.EXCL) {
+                if (operand instanceof Boolean) {
+                    return !(Boolean) operand;
+                }
+            } else if (operator == JavaTokenType.PLUS) {
+                return operand;
+            } else if (operator == JavaTokenType.TILDE) {
+                if (operand instanceof Integer) {
+                    return ~(Integer) operand;
+                } else if (operand instanceof Long) {
+                    return ~(Long) operand;
+                } else if (operand instanceof Short) {
+                    return ~(Short) operand;
+                } else if (operand instanceof Character) {
+                    return ~(Character) operand;
+                } else if (operand instanceof Byte) {
+                    return ~(Byte) operand;
+                }
+            } else if (operator == JavaTokenType.MINUS) {
+                if (operand instanceof Integer) {
+                    return -(Integer) operand;
+                } else if (operand instanceof Long) {
+                    return -(Long) operand;
+                } else if (operand instanceof Double) {
+                    return -(Double) operand;
+                } else if (operand instanceof Float) {
+                    return -(Float) operand;
+                } else if (operand instanceof Short) {
+                    return -(Short) operand;
+                } else if (operand instanceof Character) {
+                    return -(Character) operand;
+                } else if (operand instanceof Byte) {
+                    return -(Byte) operand;
+                }
+            }
+        } else if (node instanceof PsiConditionalExpression) {
+            PsiConditionalExpression expression = (PsiConditionalExpression) node;
+            Object known = evaluate(expression.getCondition());
+            if (known == Boolean.TRUE && expression.getThenExpression() != null) {
+                return evaluate(expression.getThenExpression());
+            } else if (known == Boolean.FALSE && expression.getElseExpression() != null) {
+                return evaluate(expression.getElseExpression());
+            }
+        } else if (node instanceof PsiParenthesizedExpression) {
+            PsiParenthesizedExpression parenthesizedExpression = (PsiParenthesizedExpression) node;
+            PsiExpression expression = parenthesizedExpression.getExpression();
+            if (expression != null) {
+                return evaluate(expression);
+            }
+        } else if (node instanceof PsiBinaryExpression) {
+            IElementType operator = ((PsiBinaryExpression) node).getOperationTokenType();
+            Object operandLeft = evaluate(((PsiBinaryExpression) node).getLOperand());
+            Object operandRight = evaluate(((PsiBinaryExpression) node).getROperand());
+            if (operandLeft == null || operandRight == null) {
+                if (mAllowUnknown) {
+                    if (operandLeft == null) {
+                        return operandRight;
+                    } else {
+                        return operandLeft;
+                    }
+                }
+                return null;
+            }
+            if (operandLeft instanceof String && operandRight instanceof String) {
+                if (operator == JavaTokenType.PLUS) {
+                    return operandLeft.toString() + operandRight.toString();
+                }
+                return null;
+            } else if (operandLeft instanceof Boolean && operandRight instanceof Boolean) {
+                boolean left = (Boolean) operandLeft;
+                boolean right = (Boolean) operandRight;
+                if (operator == JavaTokenType.OROR) {
+                    return left || right;
+                } else if (operator == JavaTokenType.ANDAND) {
+                    return left && right;
+                } else if (operator == JavaTokenType.OR) {
+                    return left | right;
+                } else if (operator == JavaTokenType.XOR) {
+                    return left ^ right;
+                } else if (operator == JavaTokenType.AND) {
+                    return left & right;
+                } else if (operator == JavaTokenType.EQEQ) {
+                    return left == right;
+                } else if (operator == JavaTokenType.NE) {
+                    return left != right;
+                }
+            } else if (operandLeft instanceof Number && operandRight instanceof Number) {
+                Number left = (Number) operandLeft;
+                Number right = (Number) operandRight;
+                boolean isInteger =
+                        !(left instanceof Float || left instanceof Double
+                                || right instanceof Float || right instanceof Double);
+                boolean isWide =
+                        isInteger ? (left instanceof Long || right instanceof Long)
+                                : (left instanceof Double || right instanceof Double);
+
+                if (operator == JavaTokenType.OR) {
+                    if (isWide) {
+                        return left.longValue() | right.longValue();
+                    } else {
+                        return left.intValue() | right.intValue();
+                    }
+                } else if (operator == JavaTokenType.XOR) {
+                    if (isWide) {
+                        return left.longValue() ^ right.longValue();
+                    } else {
+                        return left.intValue() ^ right.intValue();
+                    }
+                } else if (operator == JavaTokenType.AND) {
+                    if (isWide) {
+                        return left.longValue() & right.longValue();
+                    } else {
+                        return left.intValue() & right.intValue();
+                    }
+                } else if (operator == JavaTokenType.EQEQ) {
+                    if (isInteger) {
+                        return left.longValue() == right.longValue();
+                    } else {
+                        return left.doubleValue() == right.doubleValue();
+                    }
+                } else if (operator == JavaTokenType.NE) {
+                    if (isInteger) {
+                        return left.longValue() != right.longValue();
+                    } else {
+                        return left.doubleValue() != right.doubleValue();
+                    }
+                } else if (operator == JavaTokenType.GT) {
+                    if (isInteger) {
+                        return left.longValue() > right.longValue();
+                    } else {
+                        return left.doubleValue() > right.doubleValue();
+                    }
+                } else if (operator == JavaTokenType.GE) {
+                    if (isInteger) {
+                        return left.longValue() >= right.longValue();
+                    } else {
+                        return left.doubleValue() >= right.doubleValue();
+                    }
+                } else if (operator == JavaTokenType.LT) {
+                    if (isInteger) {
+                        return left.longValue() < right.longValue();
+                    } else {
+                        return left.doubleValue() < right.doubleValue();
+                    }
+                } else if (operator == JavaTokenType.LE) {
+                    if (isInteger) {
+                        return left.longValue() <= right.longValue();
+                    } else {
+                        return left.doubleValue() <= right.doubleValue();
+                    }
+                } else if (operator == JavaTokenType.LTLT) {
+                    if (isWide) {
+                        return left.longValue() << right.intValue();
+                    } else {
+                        return left.intValue() << right.intValue();
+                    }
+                } else if (operator == JavaTokenType.GTGT) {
+                    if (isWide) {
+                        return left.longValue() >> right.intValue();
+                    } else {
+                        return left.intValue() >> right.intValue();
+                    }
+                } else if (operator == JavaTokenType.GTGTGT) {
+                    if (isWide) {
+                        return left.longValue() >>> right.intValue();
+                    } else {
+                        return left.intValue() >>> right.intValue();
+                    }
+                } else if (operator == JavaTokenType.PLUS) {
+                    if (isInteger) {
+                        if (isWide) {
+                            return left.longValue() + right.longValue();
+                        } else {
+                            return left.intValue() + right.intValue();
+                        }
+                    } else {
+                        if (isWide) {
+                            return left.doubleValue() + right.doubleValue();
+                        } else {
+                            return left.floatValue() + right.floatValue();
+                        }
+                    }
+                } else if (operator == JavaTokenType.MINUS) {
+                    if (isInteger) {
+                        if (isWide) {
+                            return left.longValue() - right.longValue();
+                        } else {
+                            return left.intValue() - right.intValue();
+                        }
+                    } else {
+                        if (isWide) {
+                            return left.doubleValue() - right.doubleValue();
+                        } else {
+                            return left.floatValue() - right.floatValue();
+                        }
+                    }
+                } else if (operator == JavaTokenType.ASTERISK) {
+                    if (isInteger) {
+                        if (isWide) {
+                            return left.longValue() * right.longValue();
+                        } else {
+                            return left.intValue() * right.intValue();
+                        }
+                    } else {
+                        if (isWide) {
+                            return left.doubleValue() * right.doubleValue();
+                        } else {
+                            return left.floatValue() * right.floatValue();
+                        }
+                    }
+                } else if (operator == JavaTokenType.DIV) {
+                    if (isInteger) {
+                        if (isWide) {
+                            return left.longValue() / right.longValue();
+                        } else {
+                            return left.intValue() / right.intValue();
+                        }
+                    } else {
+                        if (isWide) {
+                            return left.doubleValue() / right.doubleValue();
+                        } else {
+                            return left.floatValue() / right.floatValue();
+                        }
+                    }
+                } else if (operator == JavaTokenType.PERC) {
+                    if (isInteger) {
+                        if (isWide) {
+                            return left.longValue() % right.longValue();
+                        } else {
+                            return left.intValue() % right.intValue();
+                        }
+                    } else {
+                        if (isWide) {
+                            return left.doubleValue() % right.doubleValue();
+                        } else {
+                            return left.floatValue() % right.floatValue();
+                        }
+                    }
+                } else {
+                    return null;
+                }
+            }
+        } else if (node instanceof PsiTypeCastExpression) {
+            PsiTypeCastExpression cast = (PsiTypeCastExpression) node;
+            Object operandValue = evaluate(cast.getOperand());
+            if (operandValue instanceof Number) {
+                Number number = (Number) operandValue;
+                PsiTypeElement typeElement = cast.getCastType();
+                if (typeElement != null) {
+                    PsiType type = typeElement.getType();
+                    if (PsiType.FLOAT.equals(type)) {
+                        return number.floatValue();
+                    } else if (PsiType.DOUBLE.equals(type)) {
+                        return number.doubleValue();
+                    } else if (PsiType.INT.equals(type)) {
+                        return number.intValue();
+                    } else if (PsiType.LONG.equals(type)) {
+                        return number.longValue();
+                    } else if (PsiType.SHORT.equals(type)) {
+                        return number.shortValue();
+                    } else if (PsiType.BYTE.equals(type)) {
+                        return number.byteValue();
+                    }
+                }
+            }
+            return operandValue;
+        } else if (node instanceof PsiReference) {
+            PsiElement resolved = ((PsiReference) node).resolve();
+            if (resolved instanceof PsiField) {
+                PsiField field = (PsiField) resolved;
+                Object value = field.computeConstantValue();
+                if (value != null) {
+                    return value;
+                }
+                if (field.getInitializer() != null) {
+                    return evaluate(field.getInitializer());
+                }
+                return null;
+            } else if (resolved instanceof PsiLocalVariable) {
+                PsiLocalVariable variable = (PsiLocalVariable) resolved;
+                PsiStatement statement = PsiTreeUtil.getParentOfType(node, PsiStatement.class,
+                        false);
+                if (statement != null) {
+                    PsiStatement prev = PsiTreeUtil.getPrevSiblingOfType(statement,
+                            PsiStatement.class);
+                    String targetName = variable.getName();
+                    if (targetName == null) {
+                        return null;
+                    }
+                    while (prev != null) {
+                        if (prev instanceof PsiDeclarationStatement) {
+                            for (PsiElement element : ((PsiDeclarationStatement) prev)
+                                    .getDeclaredElements()) {
+                                if (variable.equals(element)) {
+                                    return evaluate(variable.getInitializer());
+                                }
+                            }
+                        } else if (prev instanceof PsiExpressionStatement) {
+                            PsiExpression expression = ((PsiExpressionStatement) prev)
+                                    .getExpression();
+                            if (expression instanceof PsiAssignmentExpression) {
+                                PsiAssignmentExpression assign
+                                        = (PsiAssignmentExpression) expression;
+                                PsiExpression lhs = assign.getLExpression();
+                                if (lhs instanceof PsiReferenceExpression) {
+                                    PsiReferenceExpression reference = (PsiReferenceExpression) lhs;
+                                    if (targetName.equals(reference.getReferenceName()) &&
+                                            reference.getQualifier() == null) {
+                                        return evaluate(assign.getRExpression());
+                                    }
+                                }
+                            }
+                        }
+                        prev = PsiTreeUtil.getPrevSiblingOfType(prev,
+                                PsiStatement.class);
+                    }
+                }
+            }
+        } else if (node instanceof PsiNewExpression) {
+            PsiNewExpression creation = (PsiNewExpression) node;
+            PsiArrayInitializerExpression initializer = creation.getArrayInitializer();
+            PsiType type = creation.getType();
+            if (type instanceof PsiArrayType) {
+                if (initializer != null) {
+                    PsiExpression[] initializers = initializer.getInitializers();
+                    Class<?> commonType = null;
+                    List<Object> values = Lists.newArrayListWithExpectedSize(initializers.length);
+                    int count = 0;
+                    for (PsiExpression expression : initializers) {
+                        Object value = evaluate(expression);
+                        if (value != null) {
+                            values.add(value);
+                            if (commonType == null) {
+                                commonType = value.getClass();
+                            } else {
+                                while (!commonType.isAssignableFrom(value.getClass())) {
+                                    commonType = commonType.getSuperclass();
+                                }
+                            }
+                        } else if (!mAllowUnknown) {
+                            // Inconclusive
+                            return null;
+                        }
+                        count++;
+                        if (count == 20) { // avoid large initializers
+                            break;
+                        }
+                    }
+                    type = type.getDeepComponentType();
+                    if (type == PsiType.INT) {
+                        if (!values.isEmpty()) {
+                            int[] array = new int[values.size()];
+                            for (int i = 0; i < values.size(); i++) {
+                                Object o = values.get(i);
+                                if (o instanceof Integer) {
+                                    array[i] = (Integer) o;
+                                }
+                            }
+                            return array;
+                        }
+                        return new int[0];
+                    } else if (type == PsiType.BOOLEAN) {
+                        if (!values.isEmpty()) {
+                            boolean[] array = new boolean[values.size()];
+                            for (int i = 0; i < values.size(); i++) {
+                                Object o = values.get(i);
+                                if (o instanceof Boolean) {
+                                    array[i] = (Boolean) o;
+                                }
+                            }
+                            return array;
+                        }
+                        return new boolean[0];
+                    } else if (type == PsiType.DOUBLE) {
+                        if (!values.isEmpty()) {
+                            double[] array = new double[values.size()];
+                            for (int i = 0; i < values.size(); i++) {
+                                Object o = values.get(i);
+                                if (o instanceof Double) {
+                                    array[i] = (Double) o;
+                                }
+                            }
+                            return array;
+                        }
+                        return new double[0];
+                    } else if (type == PsiType.LONG) {
+                        if (!values.isEmpty()) {
+                            long[] array = new long[values.size()];
+                            for (int i = 0; i < values.size(); i++) {
+                                Object o = values.get(i);
+                                if (o instanceof Long) {
+                                    array[i] = (Long) o;
+                                }
+                            }
+                            return array;
+                        }
+                        return new long[0];
+                    } else if (type == PsiType.FLOAT) {
+                        if (!values.isEmpty()) {
+                            float[] array = new float[values.size()];
+                            for (int i = 0; i < values.size(); i++) {
+                                Object o = values.get(i);
+                                if (o instanceof Float) {
+                                    array[i] = (Float) o;
+                                }
+                            }
+                            return array;
+                        }
+                        return new float[0];
+                    } else if (type == PsiType.CHAR) {
+                        if (!values.isEmpty()) {
+                            char[] array = new char[values.size()];
+                            for (int i = 0; i < values.size(); i++) {
+                                Object o = values.get(i);
+                                if (o instanceof Character) {
+                                    array[i] = (Character) o;
+                                }
+                            }
+                            return array;
+                        }
+                        return new char[0];
+                    } else if (type == PsiType.BYTE) {
+                        if (!values.isEmpty()) {
+                            byte[] array = new byte[values.size()];
+                            for (int i = 0; i < values.size(); i++) {
+                                Object o = values.get(i);
+                                if (o instanceof Byte) {
+                                    array[i] = (Byte) o;
+                                }
+                            }
+                            return array;
+                        }
+                        return new byte[0];
+                    } else if (type == PsiType.SHORT) {
+                        if (!values.isEmpty()) {
+                            short[] array = new short[values.size()];
+                            for (int i = 0; i < values.size(); i++) {
+                                Object o = values.get(i);
+                                if (o instanceof Short) {
+                                    array[i] = (Short) o;
+                                }
+                            }
+                            return array;
+                        }
+                        return new short[0];
+                    } else {
+                        if (!values.isEmpty()) {
+                            Object o = Array.newInstance(commonType, values.size());
+                            return values.toArray((Object[]) o);
+                        }
+                        return null;
+                    }
+                } else {
+                    // something like "new byte[3]" but with no initializer.
+                    // Look up the size and only if small, use it. E.g. if it was byte[3]
+                    // we return a byte[3] array, but if it's say byte[1024*1024] we don't
+                    // want to do that.
+                    PsiExpression[] arrayDimensions = creation.getArrayDimensions();
+                    int size = 0;
+                    if (arrayDimensions.length == 1) {
+                        Object fixedSize = evaluate(arrayDimensions[0]);
+                        if (fixedSize instanceof Number) {
+                            size = ((Number)fixedSize).intValue();
+                            if (size > 30) {
+                                size = 30;
+                            }
+                        }
+                    }
+                    type = type.getDeepComponentType();
+                    if (type instanceof PsiPrimitiveType) {
+                        if (PsiType.BYTE.equals(type)) {
+                            return new byte[size];
+                        }
+                        if (PsiType.BOOLEAN.equals(type)) {
+                            return new boolean[size];
+                        }
+                        if (PsiType.INT.equals(type)) {
+                            return new int[size];
+                        }
+                        if (PsiType.LONG.equals(type)) {
+                            return new long[size];
+                        }
+                        if (PsiType.CHAR.equals(type)) {
+                            return new char[size];
+                        }
+                        if (PsiType.FLOAT.equals(type)) {
+                            return new float[size];
+                        }
+                        if (PsiType.DOUBLE.equals(type)) {
+                            return new double[size];
+                        }
+                        if (PsiType.SHORT.equals(type)) {
+                            return new short[size];
+                        }
+                    } else if (type instanceof PsiClassType) {
+                        String className = type.getCanonicalText();
+                        if (TYPE_STRING.equals(className)) {
+                            //noinspection SSBasedInspection
+                            return new String[size];
+                        }
+                        if (TYPE_OBJECT.equals(className)) {
+                            //noinspection SSBasedInspection
+                            return new Object[size];
+                        }
+                    }
+                }
+            }
+        }
+
+        // TODO: Check for MethodInvocation and perform some common operations -
+        // Math.* methods, String utility methods like notNullize, etc
+
+        return null;
+    }
+
+    /**
      * Evaluates the given node and returns the constant value it resolves to, if any. Convenience
      * wrapper which creates a new {@linkplain ConstantEvaluator}, evaluates the node and returns
      * the result.
@@ -545,7 +1107,9 @@
      * @param context the context to use to resolve field references, if any
      * @param node    the node to compute the constant value for
      * @return the corresponding constant value - a String, an Integer, a Float, and so on
+     * @deprecated Use {@link #evaluate(JavaContext, PsiElement)} instead
      */
+    @Deprecated
     @Nullable
     public static Object evaluate(@NonNull JavaContext context, @NonNull Node node) {
         return new ConstantEvaluator(context).evaluate(node);
@@ -561,7 +1125,9 @@
      * @param allowUnknown whether we should construct the string even if some parts of it are
      *                     unknown
      * @return the corresponding string, if any
+     * @deprecated Use {@link #evaluateString(JavaContext, PsiElement, boolean)} instead
      */
+    @Deprecated
     @Nullable
     public static String evaluateString(@NonNull JavaContext context, @NonNull Node node,
             boolean allowUnknown) {
@@ -572,4 +1138,40 @@
         Object value = evaluator.evaluate(node);
         return value instanceof String ? (String) value : null;
     }
+
+    /**
+     * Evaluates the given node and returns the constant value it resolves to, if any. Convenience
+     * wrapper which creates a new {@linkplain ConstantEvaluator}, evaluates the node and returns
+     * the result.
+     *
+     * @param context the context to use to resolve field references, if any
+     * @param node    the node to compute the constant value for
+     * @return the corresponding constant value - a String, an Integer, a Float, and so on
+     */
+    @Nullable
+    public static Object evaluate(@Nullable JavaContext context, @NonNull PsiElement node) {
+        return new ConstantEvaluator(context).evaluate(node);
+    }
+
+    /**
+     * Evaluates the given node and returns the constant string it resolves to, if any. Convenience
+     * wrapper which creates a new {@linkplain ConstantEvaluator}, evaluates the node and returns
+     * the result if the result is a string.
+     *
+     * @param context      the context to use to resolve field references, if any
+     * @param node         the node to compute the constant value for
+     * @param allowUnknown whether we should construct the string even if some parts of it are
+     *                     unknown
+     * @return the corresponding string, if any
+     */
+    @Nullable
+    public static String evaluateString(@Nullable JavaContext context, @NonNull PsiElement node,
+            boolean allowUnknown) {
+        ConstantEvaluator evaluator = new ConstantEvaluator(context);
+        if (allowUnknown) {
+            evaluator.allowUnknowns();
+        }
+        Object value = evaluator.evaluate(node);
+        return value instanceof String ? (String) value : null;
+    }
 }
diff --git a/lint/libs/lint-api/src/main/java/com/android/tools/lint/detector/api/ResourceEvaluator.java b/lint/libs/lint-api/src/main/java/com/android/tools/lint/detector/api/ResourceEvaluator.java
new file mode 100644
index 0000000..382f00a
--- /dev/null
+++ b/lint/libs/lint-api/src/main/java/com/android/tools/lint/detector/api/ResourceEvaluator.java
@@ -0,0 +1,442 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * 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.android.tools.lint.detector.api;
+
+import static com.android.SdkConstants.ANDROID_PKG;
+import static com.android.SdkConstants.ANDROID_PKG_PREFIX;
+import static com.android.SdkConstants.CLASS_CONTEXT;
+import static com.android.SdkConstants.CLASS_FRAGMENT;
+import static com.android.SdkConstants.CLASS_RESOURCES;
+import static com.android.SdkConstants.CLASS_V4_FRAGMENT;
+import static com.android.SdkConstants.CLS_TYPED_ARRAY;
+import static com.android.SdkConstants.R_CLASS;
+import static com.android.SdkConstants.SUPPORT_ANNOTATIONS_PREFIX;
+
+import com.android.annotations.NonNull;
+import com.android.annotations.Nullable;
+import com.android.ide.common.resources.ResourceUrl;
+import com.android.resources.ResourceType;
+import com.android.tools.lint.client.api.JavaEvaluator;
+import com.intellij.psi.PsiAnnotation;
+import com.intellij.psi.PsiAssignmentExpression;
+import com.intellij.psi.PsiClass;
+import com.intellij.psi.PsiConditionalExpression;
+import com.intellij.psi.PsiDeclarationStatement;
+import com.intellij.psi.PsiElement;
+import com.intellij.psi.PsiExpression;
+import com.intellij.psi.PsiExpressionStatement;
+import com.intellij.psi.PsiField;
+import com.intellij.psi.PsiLocalVariable;
+import com.intellij.psi.PsiMethod;
+import com.intellij.psi.PsiMethodCallExpression;
+import com.intellij.psi.PsiModifierListOwner;
+import com.intellij.psi.PsiParameter;
+import com.intellij.psi.PsiParenthesizedExpression;
+import com.intellij.psi.PsiReference;
+import com.intellij.psi.PsiReferenceExpression;
+import com.intellij.psi.PsiStatement;
+import com.intellij.psi.util.PsiTreeUtil;
+
+import java.util.EnumSet;
+import java.util.Locale;
+
+/** Evaluates constant expressions */
+public class ResourceEvaluator {
+
+    /**
+     * Marker ResourceType used to signify that an expression is of type {@code @ColorInt},
+     * which isn't actually a ResourceType but one we want to specifically compare with.
+     * We're using {@link ResourceType#PUBLIC} because that one won't appear in the R
+     * class (and ResourceType is an enum we can't just create new constants for.)
+     */
+    public static final ResourceType COLOR_INT_MARKER_TYPE = ResourceType.PUBLIC;
+
+    public static final String COLOR_INT_ANNOTATION = SUPPORT_ANNOTATIONS_PREFIX + "ColorInt"; //$NON-NLS-1$
+    public static final String RES_SUFFIX = "Res";
+
+    private final JavaEvaluator mEvaluator;
+
+    private boolean mAllowDereference = true;
+
+    /**
+     * Creates a new resource evaluator
+     *
+     * @param evaluator the evaluator to use to resolve annotations references, if any
+     */
+    public ResourceEvaluator(@Nullable JavaEvaluator evaluator) {
+        mEvaluator = evaluator;
+    }
+
+    /**
+     * Whether we allow dereferencing resources when computing constants;
+     * e.g. if we ask for the resource for {@code x} when the code is
+     * {@code x = getString(R.string.name)}, if {@code allowDereference} is
+     * true we'll return R.string.name, otherwise we'll return null.
+     *
+     * @return this for constructor chaining
+     */
+    public ResourceEvaluator allowDereference(boolean allow) {
+        mAllowDereference = allow;
+        return this;
+    }
+
+    /**
+     * Evaluates the given node and returns the resource reference (type and name) it
+     * points to, if any
+     *
+     * @param evaluator the evaluator to use to look up annotations
+     * @param element the node to compute the constant value for
+     * @return the corresponding resource url (type and name)
+     */
+    @Nullable
+    public static ResourceUrl getResource(
+            @Nullable JavaEvaluator evaluator,
+            @NonNull PsiElement element) {
+        return new ResourceEvaluator(evaluator).getResource(element);
+    }
+
+    /**
+     * Evaluates the given node and returns the resource types implied by the given element,
+     * if any.
+     *
+     * @param evaluator the evaluator to use to look up annotations
+     * @param element the node to compute the constant value for
+     * @return the corresponding resource types
+     */
+    @Nullable
+    public static EnumSet<ResourceType> getResourceTypes(
+            @Nullable JavaEvaluator evaluator,
+            @NonNull PsiElement element) {
+        return new ResourceEvaluator(evaluator).getResourceTypes(element);
+    }
+
+    /**
+     * Evaluates the given node and returns the resource reference (type and name) it
+     * points to, if any
+     *
+     * @param element the node to compute the constant value for
+     * @return the corresponding constant value - a String, an Integer, a Float, and so on
+     */
+    @Nullable
+    public ResourceUrl getResource(@Nullable PsiElement element) {
+        if (element == null) {
+            return null;
+        }
+        if (element instanceof PsiConditionalExpression) {
+            PsiConditionalExpression expression = (PsiConditionalExpression) element;
+            Object known = ConstantEvaluator.evaluate(null, expression.getCondition());
+            if (known == Boolean.TRUE && expression.getThenExpression() != null) {
+                return getResource(expression.getThenExpression());
+            } else if (known == Boolean.FALSE && expression.getElseExpression() != null) {
+                return getResource(expression.getElseExpression());
+            }
+        } else if (element instanceof PsiParenthesizedExpression) {
+            PsiParenthesizedExpression parenthesizedExpression = (PsiParenthesizedExpression) element;
+            return getResource(parenthesizedExpression.getExpression());
+        } else if (element instanceof PsiMethodCallExpression && mAllowDereference) {
+            PsiMethodCallExpression call = (PsiMethodCallExpression) element;
+            PsiReferenceExpression expression = call.getMethodExpression();
+            PsiMethod method = call.resolveMethod();
+            if (method != null && method.getContainingClass() != null) {
+                String qualifiedName = method.getContainingClass().getQualifiedName();
+                String name = expression.getReferenceName();
+                if ((CLASS_RESOURCES.equals(qualifiedName)
+                        || CLASS_CONTEXT.equals(qualifiedName)
+                        || CLASS_FRAGMENT.equals(qualifiedName)
+                        || CLASS_V4_FRAGMENT.equals(qualifiedName)
+                        || CLS_TYPED_ARRAY.equals(qualifiedName))
+                        && name != null
+                        && name.startsWith("get")) {
+                    PsiExpression[] args = call.getArgumentList().getExpressions();
+                    if (args.length > 0) {
+                        return getResource(args[0]);
+                    }
+                }
+            }
+        } else if (element instanceof PsiReference) {
+            ResourceUrl url = getResourceConstant(element);
+            if (url != null) {
+                return url;
+            }
+            PsiElement resolved = ((PsiReference) element).resolve();
+            if (resolved instanceof PsiField) {
+                url = getResourceConstant(resolved);
+                if (url != null) {
+                    return url;
+                }
+                PsiField field = (PsiField) resolved;
+                if (field.getInitializer() != null) {
+                    return getResource(field.getInitializer());
+                }
+                return null;
+            } else if (resolved instanceof PsiLocalVariable) {
+                PsiLocalVariable variable = (PsiLocalVariable) resolved;
+                PsiStatement statement = PsiTreeUtil.getParentOfType(element, PsiStatement.class,
+                        false);
+                if (statement != null) {
+                    PsiStatement prev = PsiTreeUtil.getPrevSiblingOfType(statement,
+                            PsiStatement.class);
+                    String targetName = variable.getName();
+                    if (targetName == null) {
+                        return null;
+                    }
+                    while (prev != null) {
+                        if (prev instanceof PsiDeclarationStatement) {
+                            PsiDeclarationStatement prevStatement = (PsiDeclarationStatement) prev;
+                            for (PsiElement e : prevStatement.getDeclaredElements()) {
+                                if (variable.equals(e)) {
+                                    return getResource(variable.getInitializer());
+                                }
+                            }
+                        } else if (prev instanceof PsiExpressionStatement) {
+                            PsiExpression expression = ((PsiExpressionStatement) prev)
+                                    .getExpression();
+                            if (expression instanceof PsiAssignmentExpression) {
+                                PsiAssignmentExpression assign
+                                        = (PsiAssignmentExpression) expression;
+                                PsiExpression lhs = assign.getLExpression();
+                                if (lhs instanceof PsiReferenceExpression) {
+                                    PsiReferenceExpression reference = (PsiReferenceExpression) lhs;
+                                    if (targetName.equals(reference.getReferenceName()) &&
+                                            reference.getQualifier() == null) {
+                                        return getResource(assign.getRExpression());
+                                    }
+                                }
+                            }
+                        }
+                        prev = PsiTreeUtil.getPrevSiblingOfType(prev,
+                                PsiStatement.class);
+                    }
+                }
+            }
+        }
+
+        return null;
+    }
+
+    /**
+     * Evaluates the given node and returns the resource types applicable to the
+     * node, if any.
+     *
+     * @param element the element to compute the types for
+     * @return the corresponding resource types
+     */
+    @Nullable
+    public EnumSet<ResourceType> getResourceTypes(@Nullable PsiElement element) {
+        if (element == null) {
+            return null;
+        }
+        if (element instanceof PsiConditionalExpression) {
+            PsiConditionalExpression expression = (PsiConditionalExpression) element;
+            Object known = ConstantEvaluator.evaluate(null, expression.getCondition());
+            if (known == Boolean.TRUE && expression.getThenExpression() != null) {
+                return getResourceTypes(expression.getThenExpression());
+            } else if (known == Boolean.FALSE && expression.getElseExpression() != null) {
+                return getResourceTypes(expression.getElseExpression());
+            } else {
+                EnumSet<ResourceType> left = getResourceTypes(
+                        expression.getThenExpression());
+                EnumSet<ResourceType> right = getResourceTypes(
+                        expression.getElseExpression());
+                if (left == null) {
+                    return right;
+                } else if (right == null) {
+                    return left;
+                } else {
+                    EnumSet<ResourceType> copy = EnumSet.copyOf(left);
+                    copy.addAll(right);
+                    return copy;
+                }
+            }
+        } else if (element instanceof PsiParenthesizedExpression) {
+            PsiParenthesizedExpression parenthesizedExpression = (PsiParenthesizedExpression) element;
+            return getResourceTypes(parenthesizedExpression.getExpression());
+        } else if (element instanceof PsiMethodCallExpression && mAllowDereference) {
+            PsiMethodCallExpression call = (PsiMethodCallExpression) element;
+            PsiReferenceExpression expression = call.getMethodExpression();
+            PsiMethod method = call.resolveMethod();
+            if (method != null && method.getContainingClass() != null) {
+                EnumSet<ResourceType> types = getTypesFromAnnotations(method);
+                if (types != null) {
+                    return types;
+                }
+
+                String qualifiedName = method.getContainingClass().getQualifiedName();
+                String name = expression.getReferenceName();
+                if ((CLASS_RESOURCES.equals(qualifiedName)
+                        || CLASS_CONTEXT.equals(qualifiedName)
+                        || CLASS_FRAGMENT.equals(qualifiedName)
+                        || CLASS_V4_FRAGMENT.equals(qualifiedName)
+                        || CLS_TYPED_ARRAY.equals(qualifiedName))
+                        && name != null
+                        && name.startsWith("get")) {
+                    PsiExpression[] args = call.getArgumentList().getExpressions();
+                    if (args.length > 0) {
+                        types = getResourceTypes(args[0]);
+                        if (types != null) {
+                            return types;
+                        }
+                    }
+                }
+            }
+        } else if (element instanceof PsiReference) {
+            ResourceUrl url = getResourceConstant(element);
+            if (url != null) {
+                return EnumSet.of(url.type);
+            }
+            PsiElement resolved = ((PsiReference) element).resolve();
+            if (resolved instanceof PsiField) {
+                url = getResourceConstant(resolved);
+                if (url != null) {
+                    return EnumSet.of(url.type);
+                }
+                PsiField field = (PsiField) resolved;
+                if (field.getInitializer() != null) {
+                    return getResourceTypes(field.getInitializer());
+                }
+                return null;
+            } else if (resolved instanceof PsiParameter) {
+                return getTypesFromAnnotations((PsiParameter)resolved);
+            } else if (resolved instanceof PsiLocalVariable) {
+                PsiLocalVariable variable = (PsiLocalVariable) resolved;
+                PsiStatement statement = PsiTreeUtil.getParentOfType(element, PsiStatement.class,
+                        false);
+                if (statement != null) {
+                    PsiStatement prev = PsiTreeUtil.getPrevSiblingOfType(statement,
+                            PsiStatement.class);
+                    String targetName = variable.getName();
+                    if (targetName == null) {
+                        return null;
+                    }
+                    while (prev != null) {
+                        if (prev instanceof PsiDeclarationStatement) {
+                            PsiDeclarationStatement prevStatement = (PsiDeclarationStatement) prev;
+                            for (PsiElement e : prevStatement.getDeclaredElements()) {
+                                if (variable.equals(e)) {
+                                    return getResourceTypes(variable.getInitializer());
+                                }
+                            }
+                        } else if (prev instanceof PsiExpressionStatement) {
+                            PsiExpression expression = ((PsiExpressionStatement) prev)
+                                    .getExpression();
+                            if (expression instanceof PsiAssignmentExpression) {
+                                PsiAssignmentExpression assign
+                                        = (PsiAssignmentExpression) expression;
+                                PsiExpression lhs = assign.getLExpression();
+                                if (lhs instanceof PsiReferenceExpression) {
+                                    PsiReferenceExpression reference = (PsiReferenceExpression) lhs;
+                                    if (targetName.equals(reference.getReferenceName()) &&
+                                            reference.getQualifier() == null) {
+                                        return getResourceTypes(assign.getRExpression());
+                                    }
+                                }
+                            }
+                        }
+                        prev = PsiTreeUtil.getPrevSiblingOfType(prev,
+                                PsiStatement.class);
+                    }
+                }
+            }
+        }
+
+        return null;
+    }
+
+    @Nullable
+    private EnumSet<ResourceType> getTypesFromAnnotations(PsiModifierListOwner owner) {
+        if (mEvaluator == null) {
+            return null;
+        }
+        for (PsiAnnotation annotation : mEvaluator.getAllAnnotations(owner, true)) {
+            String signature = annotation.getQualifiedName();
+            if (signature == null) {
+                continue;
+            }
+            if (signature.equals(COLOR_INT_ANNOTATION)) {
+                return EnumSet.of(COLOR_INT_MARKER_TYPE);
+            }
+            if (signature.endsWith(RES_SUFFIX)
+                    && signature.startsWith(SUPPORT_ANNOTATIONS_PREFIX)) {
+                String typeString = signature
+                        .substring(SUPPORT_ANNOTATIONS_PREFIX.length(),
+                                signature.length() - RES_SUFFIX.length())
+                        .toLowerCase(Locale.US);
+                ResourceType type = ResourceType.getEnum(typeString);
+                if (type != null) {
+                    return EnumSet.of(type);
+                } else if (typeString.equals("any")) { // @AnyRes
+                    return getAnyRes();
+                }
+            }
+        }
+
+        return null;
+    }
+
+    /** Returns a resource URL based on the field reference in the code */
+    @Nullable
+    public static ResourceUrl getResourceConstant(@NonNull PsiElement node) {
+        // R.type.name
+        if (node instanceof PsiReferenceExpression) {
+            PsiReferenceExpression expression = (PsiReferenceExpression) node;
+            if (expression.getQualifier() instanceof PsiReferenceExpression) {
+                PsiReferenceExpression select = (PsiReferenceExpression) expression.getQualifier();
+                if (select.getQualifier() instanceof PsiReferenceExpression) {
+                    PsiReferenceExpression reference = (PsiReferenceExpression) select
+                            .getQualifier();
+                    if (R_CLASS.equals(reference.getReferenceName())) {
+                        String typeName = select.getReferenceName();
+                        String name = expression.getReferenceName();
+
+                        ResourceType type = ResourceType.getEnum(typeName);
+                        if (type != null && name != null) {
+                            boolean isFramework =
+                                    reference.getQualifier() instanceof PsiReferenceExpression
+                                            && ANDROID_PKG
+                                            .equals(((PsiReferenceExpression) reference.
+                                                    getQualifier()).getReferenceName());
+
+                            return ResourceUrl.create(type, name, isFramework, false);
+                        }
+                    }
+                }
+            }
+        } else if (node instanceof PsiField) {
+            PsiField field = (PsiField) node;
+            PsiClass typeClass = field.getContainingClass();
+            if (typeClass != null) {
+                PsiClass rClass = typeClass.getContainingClass();
+                if (rClass != null && R_CLASS.equals(rClass.getName())) {
+                    String name = field.getName();
+                    ResourceType type = ResourceType.getEnum(typeClass.getName());
+                    if (type != null && name != null) {
+                        String qualifiedName = rClass.getQualifiedName();
+                        boolean isFramework = qualifiedName != null
+                                && qualifiedName.startsWith(ANDROID_PKG_PREFIX);
+                        return ResourceUrl.create(type, name, isFramework, false);
+                    }
+                }
+            }
+        }
+        return null;
+    }
+
+    private static EnumSet<ResourceType> getAnyRes() {
+        EnumSet<ResourceType> types = EnumSet.allOf(ResourceType.class);
+        types.remove(ResourceEvaluator.COLOR_INT_MARKER_TYPE);
+        return types;
+    }
+}
diff --git a/lint/libs/lint-api/src/main/java/com/android/tools/lint/detector/api/TypeEvaluator.java b/lint/libs/lint-api/src/main/java/com/android/tools/lint/detector/api/TypeEvaluator.java
index bbafced..5a7094c 100644
--- a/lint/libs/lint-api/src/main/java/com/android/tools/lint/detector/api/TypeEvaluator.java
+++ b/lint/libs/lint-api/src/main/java/com/android/tools/lint/detector/api/TypeEvaluator.java
@@ -34,15 +34,27 @@
 import com.android.tools.lint.client.api.JavaParser.ResolvedNode;
 import com.android.tools.lint.client.api.JavaParser.ResolvedVariable;
 import com.android.tools.lint.client.api.JavaParser.TypeDescriptor;
+import com.intellij.psi.PsiAssignmentExpression;
+import com.intellij.psi.PsiClass;
+import com.intellij.psi.PsiDeclarationStatement;
+import com.intellij.psi.PsiElement;
+import com.intellij.psi.PsiExpression;
+import com.intellij.psi.PsiExpressionStatement;
+import com.intellij.psi.PsiField;
+import com.intellij.psi.PsiLocalVariable;
+import com.intellij.psi.PsiMethod;
+import com.intellij.psi.PsiReference;
+import com.intellij.psi.PsiReferenceExpression;
+import com.intellij.psi.PsiStatement;
+import com.intellij.psi.PsiType;
+import com.intellij.psi.util.PsiTreeUtil;
 
-import java.util.List;
 import java.util.ListIterator;
 
 import lombok.ast.BinaryExpression;
 import lombok.ast.BinaryOperator;
 import lombok.ast.BooleanLiteral;
 import lombok.ast.Cast;
-import lombok.ast.Catch;
 import lombok.ast.CharLiteral;
 import lombok.ast.Expression;
 import lombok.ast.ExpressionStatement;
@@ -90,8 +102,10 @@
 
 
     /**
-     * Returns true if the node evaluates to an instance of type SecureRandom
+     * Returns the inferred type of the given node
+     * @deprecated Use {@link #evaluate(PsiElement)} instead
      */
+    @Deprecated
     @Nullable
     public TypeDescriptor evaluate(@NonNull Node node) {
         ResolvedNode resolved = null;
@@ -248,6 +262,102 @@
     }
 
     /**
+     * Returns the inferred type of the given node
+     */
+    @Nullable
+    public PsiType evaluate(@Nullable PsiElement node) {
+        if (node == null) {
+            return null;
+        }
+
+        PsiElement resolved = null;
+        if (node instanceof PsiReference) {
+            resolved = ((PsiReference) node).resolve();
+        }
+        if (resolved instanceof PsiMethod) {
+            PsiMethod method = (PsiMethod) resolved;
+            if (method.isConstructor()) {
+                PsiClass containingClass = method.getContainingClass();
+                if (containingClass != null && mContext != null) {
+                    return mContext.getEvaluator().getClassType(containingClass);
+                }
+            } else {
+                return method.getReturnType();
+            }
+        }
+
+        if (resolved instanceof PsiField) {
+            PsiField field = (PsiField) resolved;
+            if (field.getInitializer() != null) {
+                PsiType type = evaluate(field.getInitializer());
+                if (type != null) {
+                    return type;
+                }
+            }
+            return field.getType();
+        } else if (resolved instanceof PsiLocalVariable) {
+            PsiLocalVariable variable = (PsiLocalVariable) resolved;
+            PsiStatement statement = PsiTreeUtil.getParentOfType(node, PsiStatement.class,
+                    false);
+            if (statement != null) {
+                PsiStatement prev = PsiTreeUtil.getPrevSiblingOfType(statement,
+                        PsiStatement.class);
+                String targetName = variable.getName();
+                if (targetName == null) {
+                    return null;
+                }
+                while (prev != null) {
+                    if (prev instanceof PsiDeclarationStatement) {
+                        for (PsiElement element : ((PsiDeclarationStatement)prev).getDeclaredElements()) {
+                            if (variable.equals(element)) {
+                                return evaluate(variable.getInitializer());
+                            }
+                        }
+                    } else if (prev instanceof PsiExpressionStatement) {
+                        PsiExpression expression = ((PsiExpressionStatement)prev).getExpression();
+                        if (expression instanceof PsiAssignmentExpression) {
+                            PsiAssignmentExpression assign = (PsiAssignmentExpression) expression;
+                            PsiExpression lhs = assign.getLExpression();
+                            if (lhs instanceof PsiReferenceExpression) {
+                                PsiReferenceExpression reference = (PsiReferenceExpression) lhs;
+                                if (targetName.equals(reference.getReferenceName()) &&
+                                        reference.getQualifier() == null) {
+                                    return evaluate(assign.getRExpression());
+                                }
+                            }
+                        }
+                    }
+                    prev = PsiTreeUtil.getPrevSiblingOfType(prev,
+                            PsiStatement.class);
+                }
+            }
+
+            return variable.getType();
+        } else if (node instanceof PsiExpression) {
+            PsiExpression expression = (PsiExpression) node;
+            return expression.getType();
+        }
+
+        return null;
+    }
+
+    /**
+     * Evaluates the given node and returns the likely type of the instance. Convenience
+     * wrapper which creates a new {@linkplain TypeEvaluator}, evaluates the node and returns
+     * the result.
+     *
+     * @param context the context to use to resolve field references, if any
+     * @param node    the node to compute the type for
+     * @return the corresponding type descriptor, if found
+     * @deprecated Use {@link #evaluate(JavaContext, PsiElement)} instead
+     */
+    @Deprecated
+    @Nullable
+    public static TypeDescriptor evaluate(@NonNull JavaContext context, @NonNull Node node) {
+        return new TypeEvaluator(context).evaluate(node);
+    }
+
+    /**
      * Evaluates the given node and returns the likely type of the instance. Convenience
      * wrapper which creates a new {@linkplain TypeEvaluator}, evaluates the node and returns
      * the result.
@@ -257,7 +367,7 @@
      * @return the corresponding type descriptor, if found
      */
     @Nullable
-    public static TypeDescriptor evaluate(@NonNull JavaContext context, @NonNull Node node) {
+    public static PsiType evaluate(@NonNull JavaContext context, @NonNull PsiElement node) {
         return new TypeEvaluator(context).evaluate(node);
     }
 }
diff --git a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/AddJavascriptInterfaceDetector.java b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/AddJavascriptInterfaceDetector.java
index e80b9f2..69c9286 100644
--- a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/AddJavascriptInterfaceDetector.java
+++ b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/AddJavascriptInterfaceDetector.java
@@ -17,13 +17,12 @@
 package com.android.tools.lint.checks;
 
 
-import static com.android.tools.lint.client.api.JavaParser.ResolvedMethod;
-import static com.android.tools.lint.client.api.JavaParser.ResolvedNode;
 import static com.android.tools.lint.client.api.JavaParser.TYPE_OBJECT;
 import static com.android.tools.lint.client.api.JavaParser.TYPE_STRING;
 
 import com.android.annotations.NonNull;
 import com.android.annotations.Nullable;
+import com.android.tools.lint.client.api.JavaEvaluator;
 import com.android.tools.lint.detector.api.Category;
 import com.android.tools.lint.detector.api.Detector;
 import com.android.tools.lint.detector.api.Implementation;
@@ -31,18 +30,17 @@
 import com.android.tools.lint.detector.api.JavaContext;
 import com.android.tools.lint.detector.api.Scope;
 import com.android.tools.lint.detector.api.Severity;
-import com.android.tools.lint.detector.api.Speed;
+import com.intellij.psi.JavaElementVisitor;
+import com.intellij.psi.PsiMethod;
+import com.intellij.psi.PsiMethodCallExpression;
 
 import java.util.Collections;
 import java.util.List;
 
-import lombok.ast.AstVisitor;
-import lombok.ast.MethodInvocation;
-
 /**
  * Ensures that addJavascriptInterface is not called for API levels below 17.
  */
-public class AddJavascriptInterfaceDetector extends Detector implements Detector.JavaScanner {
+public class AddJavascriptInterfaceDetector extends Detector implements Detector.JavaPsiScanner {
     public static final Issue ISSUE = Issue.create(
             "AddJavascriptInterface", //$NON-NLS-1$
             "addJavascriptInterface Called",
@@ -62,12 +60,6 @@
     private static final String WEB_VIEW = "android.webkit.WebView"; //$NON-NLS-1$
     private static final String ADD_JAVASCRIPT_INTERFACE = "addJavascriptInterface"; //$NON-NLS-1$
 
-    @NonNull
-    @Override
-    public Speed getSpeed() {
-        return Speed.FAST;
-    }
-
     // ---- Implements JavaScanner ----
 
     @Nullable
@@ -77,30 +69,20 @@
     }
 
     @Override
-    public void visitMethod(@NonNull JavaContext context, @Nullable AstVisitor visitor,
-            @NonNull MethodInvocation node) {
+    public void visitMethod(@NonNull JavaContext context, @Nullable JavaElementVisitor visitor,
+            @NonNull PsiMethodCallExpression node, @NonNull PsiMethod method) {
         // Ignore the issue if we never build for any API less than 17.
         if (context.getMainProject().getMinSdk() >= 17) {
             return;
         }
 
-        // Ignore if the method doesn't fit our description.
-        ResolvedNode resolved = context.resolve(node);
-        if (!(resolved instanceof ResolvedMethod)) {
-            return;
-        }
-        ResolvedMethod method = (ResolvedMethod) resolved;
-        if (!method.getContainingClass().isSubclassOf(WEB_VIEW, false)) {
-            return;
-        }
-        if (method.getArgumentCount() != 2
-                || !method.getArgumentType(0).matchesName(TYPE_OBJECT)
-                || !method.getArgumentType(1).matchesName(TYPE_STRING)) {
+        JavaEvaluator evaluator = context.getEvaluator();
+        if (!evaluator.methodMatches(method, WEB_VIEW, true, TYPE_OBJECT, TYPE_STRING)) {
             return;
         }
 
         String message = "`WebView.addJavascriptInterface` should not be called with minSdkVersion < 17 for security reasons: " +
-                         "JavaScript can use reflection to manipulate application";
-        context.report(ISSUE, node, context.getLocation(node.astName()), message);
+                "JavaScript can use reflection to manipulate application";
+        context.report(ISSUE, node, context.getNameLocation(node), message);
     }
 }
diff --git a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/AlarmDetector.java b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/AlarmDetector.java
index 6b46fad..94a718c0 100644
--- a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/AlarmDetector.java
+++ b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/AlarmDetector.java
@@ -18,32 +18,27 @@
 
 import com.android.annotations.NonNull;
 import com.android.annotations.Nullable;
-import com.android.tools.lint.client.api.JavaParser.ResolvedMethod;
-import com.android.tools.lint.client.api.JavaParser.ResolvedNode;
+import com.android.tools.lint.client.api.JavaEvaluator;
 import com.android.tools.lint.detector.api.Category;
 import com.android.tools.lint.detector.api.ConstantEvaluator;
-import com.android.tools.lint.detector.api.Context;
 import com.android.tools.lint.detector.api.Detector;
 import com.android.tools.lint.detector.api.Implementation;
 import com.android.tools.lint.detector.api.Issue;
 import com.android.tools.lint.detector.api.JavaContext;
 import com.android.tools.lint.detector.api.Scope;
 import com.android.tools.lint.detector.api.Severity;
-import com.android.tools.lint.detector.api.Speed;
+import com.intellij.psi.JavaElementVisitor;
+import com.intellij.psi.PsiExpression;
+import com.intellij.psi.PsiMethod;
+import com.intellij.psi.PsiMethodCallExpression;
 
-import java.io.File;
 import java.util.Collections;
-import java.util.Iterator;
 import java.util.List;
 
-import lombok.ast.AstVisitor;
-import lombok.ast.Expression;
-import lombok.ast.MethodInvocation;
-
 /**
  * Makes sure that alarms are handled correctly
  */
-public class AlarmDetector extends Detector implements Detector.JavaScanner {
+public class AlarmDetector extends Detector implements Detector.JavaPsiScanner {
 
     private static final Implementation IMPLEMENTATION = new Implementation(
             AlarmDetector.class,
@@ -71,17 +66,6 @@
     public AlarmDetector() {
     }
 
-    @Override
-    public boolean appliesTo(@NonNull Context context, @NonNull File file) {
-        return true;
-    }
-
-    @NonNull
-    @Override
-    public Speed getSpeed() {
-        return Speed.FAST;
-    }
-
     // ---- Implements JavaScanner ----
 
     @Override
@@ -90,42 +74,30 @@
     }
 
     @Override
-    public void visitMethod(@NonNull JavaContext context, @Nullable AstVisitor visitor,
-            @NonNull MethodInvocation node) {
-        ResolvedNode resolved = context.resolve(node);
-        if (resolved instanceof ResolvedMethod) {
-            ResolvedMethod method = (ResolvedMethod) resolved;
-            if (method.getContainingClass().matches("android.app.AlarmManager")
-                    && method.getArgumentCount() == 4) {
-                ensureAtLeast(context, node, 1, 5000L);
-                ensureAtLeast(context, node, 2, 60000L);
-            }
+    public void visitMethod(@NonNull JavaContext context, @Nullable JavaElementVisitor visitor,
+            @NonNull PsiMethodCallExpression node, @NonNull PsiMethod method) {
+        JavaEvaluator evaluator = context.getEvaluator();
+        if (evaluator.isMemberInClass(method, "android.app.AlarmManager") &&
+                evaluator.getParameterCount(method) == 4) {
+            ensureAtLeast(context, node, 1, 5000L);
+            ensureAtLeast(context, node, 2, 60000L);
         }
     }
 
     private static void ensureAtLeast(@NonNull JavaContext context,
-            @NonNull MethodInvocation node, int parameter, long min) {
-        Iterator<Expression> iterator = node.astArguments().iterator();
-        Expression argument = null;
-        for (int i = 0; i <= parameter; i++) {
-            if (!iterator.hasNext()) {
-                return;
-            }
-            argument = iterator.next();
-        }
-        if (argument == null) {
-            return;
-        }
-
+            @NonNull PsiMethodCallExpression node, int parameter, long min) {
+        PsiExpression argument = node.getArgumentList().getExpressions()[parameter];
         long value = getLongValue(context, argument);
         if (value < min) {
-            String message = String.format("Value will be forced up to %d as of Android 5.1; "
+            String message = String.format("Value will be forced up to %1$d as of Android 5.1; "
                     + "don't rely on this to be exact", min);
             context.report(ISSUE, argument, context.getLocation(argument), message);
         }
     }
 
-    private static long getLongValue(@NonNull JavaContext context, @NonNull Expression argument) {
+    private static long getLongValue(
+            @NonNull JavaContext context,
+            @NonNull PsiExpression argument) {
         Object value = ConstantEvaluator.evaluate(context, argument);
         if (value instanceof Number) {
             return ((Number)value).longValue();
diff --git a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/AllowAllHostnameVerifierDetector.java b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/AllowAllHostnameVerifierDetector.java
index e11a04f..9285e01 100644
--- a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/AllowAllHostnameVerifierDetector.java
+++ b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/AllowAllHostnameVerifierDetector.java
@@ -18,33 +18,29 @@
 
 import com.android.annotations.NonNull;
 import com.android.annotations.Nullable;
-import com.android.tools.lint.client.api.JavaParser;
-import com.android.tools.lint.client.api.JavaParser.ResolvedField;
-import com.android.tools.lint.client.api.JavaParser.ResolvedMethod;
-import com.android.tools.lint.client.api.JavaParser.ResolvedNode;
+import com.android.tools.lint.client.api.JavaEvaluator;
 import com.android.tools.lint.detector.api.Category;
 import com.android.tools.lint.detector.api.Detector;
-import com.android.tools.lint.detector.api.Detector.JavaScanner;
+import com.android.tools.lint.detector.api.Detector.JavaPsiScanner;
 import com.android.tools.lint.detector.api.Implementation;
 import com.android.tools.lint.detector.api.Issue;
 import com.android.tools.lint.detector.api.JavaContext;
 import com.android.tools.lint.detector.api.Location;
 import com.android.tools.lint.detector.api.Scope;
 import com.android.tools.lint.detector.api.Severity;
+import com.intellij.psi.JavaElementVisitor;
+import com.intellij.psi.PsiElement;
+import com.intellij.psi.PsiExpression;
+import com.intellij.psi.PsiField;
+import com.intellij.psi.PsiMethod;
+import com.intellij.psi.PsiMethodCallExpression;
+import com.intellij.psi.PsiNewExpression;
 
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.List;
 
-import lombok.ast.AstVisitor;
-import lombok.ast.ConstructorInvocation;
-import lombok.ast.Expression;
-import lombok.ast.ForwardingAstVisitor;
-import lombok.ast.Identifier;
-import lombok.ast.MethodInvocation;
-import lombok.ast.Node;
-
-public class AllowAllHostnameVerifierDetector extends Detector implements JavaScanner {
+public class AllowAllHostnameVerifierDetector extends Detector implements JavaPsiScanner {
 
     @SuppressWarnings("unchecked")
     private static final Implementation IMPLEMENTATION =
@@ -71,11 +67,8 @@
     }
 
     @Override
-    public void visitConstructor(
-            @NonNull JavaContext context,
-            @Nullable AstVisitor visitor,
-            @NonNull ConstructorInvocation node,
-            @NonNull ResolvedMethod constructor) {
+    public void visitConstructor(@NonNull JavaContext context, @Nullable JavaElementVisitor visitor,
+            @NonNull PsiNewExpression node, @NonNull PsiMethod constructor) {
         Location location = context.getLocation(node);
         context.report(ISSUE, node, location,
                 "Using the AllowAllHostnameVerifier HostnameVerifier is unsafe " +
@@ -90,26 +83,21 @@
     }
 
     @Override
-    public void visitMethod(@NonNull JavaContext context, @Nullable AstVisitor visitor,
-            @NonNull MethodInvocation node) {
-        ResolvedNode resolved = context.resolve(node);
-        if (resolved instanceof ResolvedMethod) {
-            ResolvedMethod method = (ResolvedMethod) resolved;
-            if (method.getArgumentCount() == 1 &&
-                    node.astArguments().size() == 1 &&
-                    method.getArgumentType(0).matchesName("javax.net.ssl.HostnameVerifier")) {
-                Expression argument = node.astArguments().first();
-                ResolvedNode resolvedArgument = context.resolve(argument);
-                if (resolvedArgument instanceof ResolvedField) {
-                    ResolvedField field = (ResolvedField) resolvedArgument;
-                    if (field.getName().equals("ALLOW_ALL_HOSTNAME_VERIFIER")) {
-                        Location location = context.getLocation(argument);
-                        String message = "Using the ALLOW_ALL_HOSTNAME_VERIFIER HostnameVerifier "
-                                + "is unsafe because it always returns true, which could cause "
-                                + "insecure network traffic due to trusting TLS/SSL server "
-                                + "certificates for wrong hostnames";
-                        context.report(ISSUE, argument, location, message);
-                    }
+    public void visitMethod(@NonNull JavaContext context, @Nullable JavaElementVisitor visitor,
+            @NonNull PsiMethodCallExpression node, @NonNull PsiMethod method) {
+        JavaEvaluator evaluator = context.getEvaluator();
+        if (evaluator.methodMatches(method, null, false, "javax.net.ssl.HostnameVerifier")) {
+            PsiExpression argument = node.getArgumentList().getExpressions()[0];
+            PsiElement resolvedArgument = evaluator.resolve(argument);
+            if (resolvedArgument instanceof PsiField) {
+                PsiField field = (PsiField) resolvedArgument;
+                if ("ALLOW_ALL_HOSTNAME_VERIFIER".equals(field.getName())) {
+                    Location location = context.getLocation(argument);
+                    String message = "Using the ALLOW_ALL_HOSTNAME_VERIFIER HostnameVerifier "
+                            + "is unsafe because it always returns true, which could cause "
+                            + "insecure network traffic due to trusting TLS/SSL server "
+                            + "certificates for wrong hostnames";
+                    context.report(ISSUE, argument, location, message);
                 }
             }
         }
diff --git a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/AlwaysShowActionDetector.java b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/AlwaysShowActionDetector.java
index 91fe2b6..8e1e6ed 100644
--- a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/AlwaysShowActionDetector.java
+++ b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/AlwaysShowActionDetector.java
@@ -24,7 +24,7 @@
 import com.android.resources.ResourceFolderType;
 import com.android.tools.lint.detector.api.Category;
 import com.android.tools.lint.detector.api.Context;
-import com.android.tools.lint.detector.api.Detector.JavaScanner;
+import com.android.tools.lint.detector.api.Detector.JavaPsiScanner;
 import com.android.tools.lint.detector.api.Implementation;
 import com.android.tools.lint.detector.api.Issue;
 import com.android.tools.lint.detector.api.JavaContext;
@@ -32,8 +32,11 @@
 import com.android.tools.lint.detector.api.ResourceXmlDetector;
 import com.android.tools.lint.detector.api.Scope;
 import com.android.tools.lint.detector.api.Severity;
-import com.android.tools.lint.detector.api.Speed;
 import com.android.tools.lint.detector.api.XmlContext;
+import com.intellij.psi.JavaElementVisitor;
+import com.intellij.psi.PsiElement;
+import com.intellij.psi.PsiField;
+import com.intellij.psi.PsiJavaCodeReferenceElement;
 
 import org.w3c.dom.Attr;
 
@@ -42,17 +45,13 @@
 import java.util.Collections;
 import java.util.List;
 
-import lombok.ast.AstVisitor;
-import lombok.ast.ForwardingAstVisitor;
-import lombok.ast.Node;
-import lombok.ast.Select;
-
 /**
  * Check which looks for usage of showAsAction="always" in menus (or
  * MenuItem.SHOW_AS_ACTION_ALWAYS in code), which is usually a style guide violation.
  * (Use ifRoom instead).
  */
-public class AlwaysShowActionDetector extends ResourceXmlDetector implements JavaScanner {
+public class AlwaysShowActionDetector extends ResourceXmlDetector implements
+        JavaPsiScanner {
 
     /** The main issue discovered by this detector */
     @SuppressWarnings("unchecked")
@@ -100,12 +99,6 @@
         return folderType == ResourceFolderType.MENU;
     }
 
-    @NonNull
-    @Override
-    public Speed getSpeed() {
-        return Speed.FAST;
-    }
-
     @Override
     public Collection<String> getApplicableAttributes() {
         return Collections.singletonList(ATTR_SHOW_AS_ACTION);
@@ -196,17 +189,16 @@
     // ---- Implements JavaScanner ----
 
     @Override
-    public
-    List<Class<? extends Node>> getApplicableNodeTypes() {
-        return Collections.<Class<? extends Node>>singletonList(Select.class);
+    public List<Class<? extends PsiElement>> getApplicablePsiTypes() {
+        return Collections.<Class<? extends PsiElement>>singletonList(PsiJavaCodeReferenceElement.class);
     }
 
     @Override
-    public AstVisitor createJavaVisitor(@NonNull JavaContext context) {
+    public JavaElementVisitor createPsiVisitor(@NonNull JavaContext context) {
         return new FieldAccessChecker(context);
     }
 
-    private class FieldAccessChecker extends ForwardingAstVisitor {
+    private class FieldAccessChecker extends JavaElementVisitor {
         private final JavaContext mContext;
 
         public FieldAccessChecker(JavaContext context) {
@@ -214,26 +206,28 @@
         }
 
         @Override
-        public boolean visitSelect(Select node) {
-            String description = node.astIdentifier().astValue();
-            boolean isIfRoom = description.equals("SHOW_AS_ACTION_IF_ROOM"); //$NON-NLS-1$
-            boolean isAlways = description.equals("SHOW_AS_ACTION_ALWAYS");  //$NON-NLS-1$
-            if ((isIfRoom || isAlways)
-                    && node.astOperand().toString().equals("MenuItem")) { //$NON-NLS-1$
-                if (isAlways) {
-                    if (mContext.getDriver().isSuppressed(mContext, ISSUE, node)) {
-                        return super.visitSelect(node);
+        public void visitReferenceElement(PsiJavaCodeReferenceElement node) {
+            String description = node.getReferenceName();
+            boolean isIfRoom = "SHOW_AS_ACTION_IF_ROOM".equals(description); //$NON-NLS-1$
+            boolean isAlways = "SHOW_AS_ACTION_ALWAYS".equals(description);  //$NON-NLS-1$
+            if (isIfRoom || isAlways) {
+                PsiElement resolved = node.resolve();
+                if (resolved instanceof PsiField
+                        && mContext.getEvaluator().isMemberInClass((PsiField)resolved,
+                        "android.view.MenuItem")) {
+                    if (isAlways) {
+                        if (mContext.getDriver().isSuppressed(mContext, ISSUE, node)) {
+                            return;
+                        }
+                        if (mAlwaysFields == null) {
+                            mAlwaysFields = new ArrayList<Location>();
+                        }
+                        mAlwaysFields.add(mContext.getLocation(node));
+                    } else {
+                        mHasIfRoomRefs = true;
                     }
-                    if (mAlwaysFields == null) {
-                        mAlwaysFields = new ArrayList<Location>();
-                    }
-                    mAlwaysFields.add(mContext.getLocation(node));
-                } else {
-                    mHasIfRoomRefs = true;
                 }
             }
-
-            return super.visitSelect(node);
         }
     }
 }
diff --git a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/AndroidAutoDetector.java b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/AndroidAutoDetector.java
index af50f3e..3a7237d 100644
--- a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/AndroidAutoDetector.java
+++ b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/AndroidAutoDetector.java
@@ -21,18 +21,18 @@
 import static com.android.SdkConstants.DOT_XML;
 import static com.android.SdkConstants.TAG_INTENT_FILTER;
 import static com.android.SdkConstants.TAG_SERVICE;
+import static com.android.tools.lint.client.api.JavaParser.TYPE_STRING;
 import static com.android.xml.AndroidManifest.NODE_ACTION;
 import static com.android.xml.AndroidManifest.NODE_APPLICATION;
 import static com.android.xml.AndroidManifest.NODE_METADATA;
 
-import com.android.SdkConstants;
 import com.android.annotations.NonNull;
 import com.android.annotations.Nullable;
 import com.android.resources.ResourceFolderType;
-import com.android.tools.lint.client.api.JavaParser;
 import com.android.tools.lint.detector.api.Category;
 import com.android.tools.lint.detector.api.Context;
-import com.android.tools.lint.detector.api.Detector;
+import com.android.tools.lint.detector.api.Detector.JavaPsiScanner;
+import com.android.tools.lint.detector.api.Detector.XmlScanner;
 import com.android.tools.lint.detector.api.Implementation;
 import com.android.tools.lint.detector.api.Issue;
 import com.android.tools.lint.detector.api.JavaContext;
@@ -41,32 +41,28 @@
 import com.android.tools.lint.detector.api.ResourceXmlDetector;
 import com.android.tools.lint.detector.api.Scope;
 import com.android.tools.lint.detector.api.Severity;
-import com.android.tools.lint.detector.api.Speed;
 import com.android.tools.lint.detector.api.XmlContext;
+import com.intellij.psi.JavaRecursiveElementVisitor;
+import com.intellij.psi.PsiClass;
+import com.intellij.psi.PsiMethod;
 
 import org.w3c.dom.Attr;
 import org.w3c.dom.Element;
 
-import java.lang.reflect.Modifier;
 import java.util.Arrays;
 import java.util.Collection;
-import java.util.Collections;
 import java.util.EnumSet;
 import java.util.List;
 
-import lombok.ast.ClassDeclaration;
-import lombok.ast.ForwardingAstVisitor;
-import lombok.ast.MethodDeclaration;
-import lombok.ast.Node;
-
 /**
  * Detector for Android Auto issues.
  * <p> Uses a {@code <meta-data>} tag with a {@code name="com.google.android.gms.car.application"}
  * as a trigger for validating Automotive specific issues.
  */
 public class AndroidAutoDetector extends ResourceXmlDetector
-        implements Detector.XmlScanner, Detector.JavaScanner {
+        implements XmlScanner, JavaPsiScanner {
 
+    @SuppressWarnings("unchecked")
     public static final Implementation IMPL = new Implementation(
             AndroidAutoDetector.class,
             EnumSet.of(Scope.RESOURCE_FILE, Scope.MANIFEST, Scope.JAVA_FILE),
@@ -152,7 +148,6 @@
             "android.support.v4.media.session.MediaSessionCompat.Callback"; //$NON-NLS-1$
     private static final String METHOD_MEDIA_SESSION_PLAY_FROM_SEARCH =
             "onPlayFromSearch"; //$NON-NLS-1$
-    private static final String STRING_ARG = "java.lang.String"; //$NON-NLS-1$
     private static final String BUNDLE_ARG = "android.os.Bundle"; //$NON-NLS-1$
 
     /**
@@ -206,12 +201,6 @@
     }
 
     @Override
-    @NonNull
-    public Speed getSpeed() {
-        return Speed.FAST;
-    }
-
-    @Override
     public void visitElement(@NonNull XmlContext context, @NonNull Element element) {
         String tagName = element.getTagName();
         if (NODE_METADATA.equals(tagName) && !mDoAutomotiveAppCheck) {
@@ -353,17 +342,16 @@
     }
 
     @Override
-    public void checkClass(@NonNull JavaContext context, @Nullable ClassDeclaration declaration,
-            @NonNull Node node, @NonNull JavaParser.ResolvedClass resolvedClass) {
+    public void checkClass(@NonNull JavaContext context, @NonNull PsiClass declaration) {
         // Only check classes that are not declared abstract.
-        if (declaration != null && (resolvedClass.getModifiers() & Modifier.ABSTRACT) == 0) {
+        if (!context.getEvaluator().isAbstract(declaration)) {
             MediaSessionCallbackVisitor visitor = new MediaSessionCallbackVisitor(context);
             declaration.accept(visitor);
             if (!visitor.isPlayFromSearchMethodFound()
                     && context.isEnabled(MISSING_ON_PLAY_FROM_SEARCH)) {
 
-                context.report(MISSING_ON_PLAY_FROM_SEARCH, declaration.astName(),
-                        context.getLocation(declaration.astName()),
+                context.report(MISSING_ON_PLAY_FROM_SEARCH, declaration,
+                        context.getNameLocation(declaration),
                         "This class does not override `" +
                         METHOD_MEDIA_SESSION_PLAY_FROM_SEARCH + "` from `MediaSession.Callback`" +
                         " The method should be overridden and implemented to support " +
@@ -372,17 +360,11 @@
         }
     }
 
-    @Override
-    @Nullable
-    public List<String> getApplicableMethodNames() {
-        return Collections.singletonList(METHOD_MEDIA_SESSION_PLAY_FROM_SEARCH);
-    }
-
     /**
      * A Visitor class to search for {@code MediaSession.Callback#onPlayFromSearch(..)}
      * method declaration.
      */
-    private static class MediaSessionCallbackVisitor extends ForwardingAstVisitor {
+    private static class MediaSessionCallbackVisitor extends JavaRecursiveElementVisitor {
 
         private final JavaContext mContext;
 
@@ -397,28 +379,18 @@
         }
 
         @Override
-        public boolean visitMethodDeclaration(MethodDeclaration node) {
-            JavaParser.ResolvedNode result = mContext.resolve(node);
-            if (result != null
-                    && METHOD_MEDIA_SESSION_PLAY_FROM_SEARCH.equals(result.getName())
-                    && result instanceof JavaParser.ResolvedMethod) {
-                JavaParser.ResolvedMethod method = (JavaParser.ResolvedMethod) result;
-                if (method.getArgumentCount() == 2) {
-                    JavaParser.TypeDescriptor firstArg = method.getArgumentType(0);
-                    JavaParser.TypeDescriptor secondArg = method.getArgumentType(1);
-                    if (firstArg.getTypeClass() != null
-                            && firstArg.getTypeClass().matches(STRING_ARG)
-                            && secondArg.getTypeClass() != null
-                            && secondArg.getTypeClass().matches(BUNDLE_ARG)) {
-                        mOnPlayFromSearchFound = true;
-                    }
-                }
+        public void visitMethod(PsiMethod method) {
+            super.visitMethod(method);
+            if (METHOD_MEDIA_SESSION_PLAY_FROM_SEARCH.equals(method.getName())
+                    && mContext.getEvaluator().parametersMatch(method, TYPE_STRING,
+                    BUNDLE_ARG)) {
+                mOnPlayFromSearchFound = true;
             }
-            return super.visitMethodDeclaration(node);
         }
     }
 
-    // Used by AS to show errors.
+    // Used by the IDE to show errors.
+    @SuppressWarnings("unused")
     @NonNull
     public static String[] getAllowedAutomotiveAppTypes() {
         return new String[]{VAL_NAME_MEDIA, VAL_NAME_NOTIFICATION};
diff --git a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/AndroidTvDetector.java b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/AndroidTvDetector.java
index a1d175b..92230de1 100644
--- a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/AndroidTvDetector.java
+++ b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/AndroidTvDetector.java
@@ -539,6 +539,7 @@
      * @param format The format of the error message.
      * @return the corresponding featureName, or null if not recognized
      */
+    @SuppressWarnings("unused") // Used by the IDE
     @Nullable
     public static String getHardwareFeature(@NonNull String errorMessage,
             @NonNull TextFormat format) {
diff --git a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/AnnotationDetector.java b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/AnnotationDetector.java
index 350fed4..d4c27f5 100644
--- a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/AnnotationDetector.java
+++ b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/AnnotationDetector.java
@@ -21,18 +21,15 @@
 import static com.android.SdkConstants.INT_DEF_ANNOTATION;
 import static com.android.SdkConstants.STRING_DEF_ANNOTATION;
 import static com.android.SdkConstants.SUPPORT_ANNOTATIONS_PREFIX;
-import static com.android.SdkConstants.SUPPRESS_LINT;
 import static com.android.SdkConstants.TYPE_DEF_FLAG_ATTRIBUTE;
-import static com.android.tools.lint.checks.PermissionRequirement.getAnnotationStrings;
+import static com.android.tools.lint.checks.PermissionRequirement.getAnnotationBooleanValue;
 import static com.android.tools.lint.checks.SupportAnnotationDetector.ATTR_ALL_OF;
 import static com.android.tools.lint.checks.SupportAnnotationDetector.ATTR_ANY_OF;
 import static com.android.tools.lint.checks.SupportAnnotationDetector.ATTR_FROM;
-import static com.android.tools.lint.checks.SupportAnnotationDetector.ATTR_FROM_INCLUSIVE;
 import static com.android.tools.lint.checks.SupportAnnotationDetector.ATTR_MAX;
 import static com.android.tools.lint.checks.SupportAnnotationDetector.ATTR_MIN;
 import static com.android.tools.lint.checks.SupportAnnotationDetector.ATTR_MULTIPLE;
 import static com.android.tools.lint.checks.SupportAnnotationDetector.ATTR_TO;
-import static com.android.tools.lint.checks.SupportAnnotationDetector.ATTR_TO_INCLUSIVE;
 import static com.android.tools.lint.checks.SupportAnnotationDetector.CHECK_RESULT_ANNOTATION;
 import static com.android.tools.lint.checks.SupportAnnotationDetector.COLOR_INT_ANNOTATION;
 import static com.android.tools.lint.checks.SupportAnnotationDetector.FLOAT_RANGE_ANNOTATION;
@@ -43,7 +40,6 @@
 import static com.android.tools.lint.checks.SupportAnnotationDetector.RES_SUFFIX;
 import static com.android.tools.lint.checks.SupportAnnotationDetector.SIZE_ANNOTATION;
 import static com.android.tools.lint.checks.SupportAnnotationDetector.filterRelevantAnnotations;
-import static com.android.tools.lint.checks.SupportAnnotationDetector.getBoolean;
 import static com.android.tools.lint.checks.SupportAnnotationDetector.getDoubleAttribute;
 import static com.android.tools.lint.checks.SupportAnnotationDetector.getLongAttribute;
 import static com.android.tools.lint.client.api.JavaParser.TYPE_DOUBLE;
@@ -51,81 +47,71 @@
 import static com.android.tools.lint.client.api.JavaParser.TYPE_INT;
 import static com.android.tools.lint.client.api.JavaParser.TYPE_LONG;
 import static com.android.tools.lint.client.api.JavaParser.TYPE_STRING;
-import static com.android.tools.lint.detector.api.JavaContext.findSurroundingClass;
-import static com.android.tools.lint.detector.api.JavaContext.getParentOfType;
 import static com.android.tools.lint.detector.api.LintUtils.findSubstring;
 
 import com.android.annotations.NonNull;
 import com.android.annotations.Nullable;
 import com.android.tools.lint.client.api.IssueRegistry;
-import com.android.tools.lint.client.api.JavaParser;
-import com.android.tools.lint.client.api.JavaParser.ResolvedAnnotation;
-import com.android.tools.lint.client.api.JavaParser.ResolvedClass;
-import com.android.tools.lint.client.api.JavaParser.ResolvedField;
-import com.android.tools.lint.client.api.JavaParser.ResolvedNode;
-import com.android.tools.lint.client.api.JavaParser.TypeDescriptor;
 import com.android.tools.lint.detector.api.Category;
-import com.android.tools.lint.detector.api.Context;
+import com.android.tools.lint.detector.api.ConstantEvaluator;
 import com.android.tools.lint.detector.api.Detector;
+import com.android.tools.lint.detector.api.Detector.JavaPsiScanner;
 import com.android.tools.lint.detector.api.Implementation;
 import com.android.tools.lint.detector.api.Issue;
 import com.android.tools.lint.detector.api.JavaContext;
 import com.android.tools.lint.detector.api.Location;
 import com.android.tools.lint.detector.api.Scope;
 import com.android.tools.lint.detector.api.Severity;
-import com.android.tools.lint.detector.api.Speed;
 import com.android.tools.lint.detector.api.TextFormat;
 import com.google.common.base.Joiner;
-import com.google.common.base.Objects;
 import com.google.common.base.Splitter;
 import com.google.common.collect.Lists;
 import com.google.common.collect.Maps;
+import com.intellij.psi.JavaElementVisitor;
+import com.intellij.psi.PsiAnnotation;
+import com.intellij.psi.PsiAnnotationMemberValue;
+import com.intellij.psi.PsiAnnotationOwner;
+import com.intellij.psi.PsiArrayInitializerMemberValue;
+import com.intellij.psi.PsiAssignmentExpression;
+import com.intellij.psi.PsiClass;
+import com.intellij.psi.PsiCodeBlock;
+import com.intellij.psi.PsiConditionalExpression;
+import com.intellij.psi.PsiDeclarationStatement;
+import com.intellij.psi.PsiElement;
+import com.intellij.psi.PsiExpression;
+import com.intellij.psi.PsiExpressionStatement;
+import com.intellij.psi.PsiField;
+import com.intellij.psi.PsiJavaCodeReferenceElement;
+import com.intellij.psi.PsiLiteral;
+import com.intellij.psi.PsiLocalVariable;
+import com.intellij.psi.PsiMethod;
+import com.intellij.psi.PsiMethodCallExpression;
+import com.intellij.psi.PsiModifierList;
+import com.intellij.psi.PsiModifierListOwner;
+import com.intellij.psi.PsiNameValuePair;
+import com.intellij.psi.PsiParameter;
+import com.intellij.psi.PsiParenthesizedExpression;
+import com.intellij.psi.PsiReference;
+import com.intellij.psi.PsiReferenceExpression;
+import com.intellij.psi.PsiStatement;
+import com.intellij.psi.PsiSwitchLabelStatement;
+import com.intellij.psi.PsiSwitchStatement;
+import com.intellij.psi.PsiType;
+import com.intellij.psi.PsiTypeCastExpression;
+import com.intellij.psi.PsiVariable;
+import com.intellij.psi.util.PsiTreeUtil;
 
-import java.io.File;
+import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.List;
 import java.util.ListIterator;
 import java.util.Map;
 
-import lombok.ast.Annotation;
-import lombok.ast.AnnotationDeclaration;
-import lombok.ast.AnnotationElement;
-import lombok.ast.AnnotationValue;
-import lombok.ast.ArrayInitializer;
-import lombok.ast.AstVisitor;
-import lombok.ast.BinaryExpression;
-import lombok.ast.BinaryOperator;
-import lombok.ast.Block;
-import lombok.ast.Case;
-import lombok.ast.Cast;
-import lombok.ast.ClassDeclaration;
-import lombok.ast.ConstructorDeclaration;
-import lombok.ast.Expression;
-import lombok.ast.ExpressionStatement;
-import lombok.ast.ForwardingAstVisitor;
-import lombok.ast.InlineIfExpression;
-import lombok.ast.IntegralLiteral;
-import lombok.ast.MethodDeclaration;
-import lombok.ast.MethodInvocation;
-import lombok.ast.Modifiers;
-import lombok.ast.Node;
-import lombok.ast.Select;
-import lombok.ast.Statement;
-import lombok.ast.StrictListAccessor;
-import lombok.ast.StringLiteral;
-import lombok.ast.Switch;
-import lombok.ast.TypeBody;
-import lombok.ast.TypeMember;
-import lombok.ast.VariableDeclaration;
-import lombok.ast.VariableDefinition;
-import lombok.ast.VariableDefinitionEntry;
-import lombok.ast.VariableReference;
-
 /**
  * Checks annotations to make sure they are valid
  */
-public class AnnotationDetector extends Detector implements Detector.JavaScanner {
+public class AnnotationDetector extends Detector implements JavaPsiScanner {
 
     public static final Implementation IMPLEMENTATION = new Implementation(
               AnnotationDetector.class,
@@ -216,31 +202,23 @@
     public AnnotationDetector() {
     }
 
-    @Override
-    public boolean appliesTo(@NonNull Context context, @NonNull File file) {
-        return true;
-    }
-
-    @NonNull
-    @Override
-    public Speed getSpeed() {
-        return Speed.FAST;
-    }
-
     // ---- Implements JavaScanner ----
 
     @Override
-    public List<Class<? extends Node>> getApplicableNodeTypes() {
-        //noinspection unchecked
-        return Arrays.<Class<? extends Node>>asList(Annotation.class, Switch.class);
+    public List<Class<? extends PsiElement>> getApplicablePsiTypes() {
+        List<Class<? extends PsiElement>> types = new ArrayList<Class<? extends PsiElement>>(2);
+        types.add(PsiAnnotation.class);
+        types.add(PsiSwitchStatement.class);
+        return types;
     }
 
+    @Nullable
     @Override
-    public AstVisitor createJavaVisitor(@NonNull JavaContext context) {
+    public JavaElementVisitor createPsiVisitor(@NonNull JavaContext context) {
         return new AnnotationChecker(context);
     }
 
-    private static class AnnotationChecker extends ForwardingAstVisitor {
+    private static class AnnotationChecker extends JavaElementVisitor {
         private final JavaContext mContext;
 
         public AnnotationChecker(JavaContext context) {
@@ -248,49 +226,48 @@
         }
 
         @Override
-        public boolean visitAnnotation(Annotation node) {
-            String typeName = node.astAnnotationTypeReference().getTypeName();
-            if (typeName.equals("Override")) {
-                return super.visitAnnotation(node);
-            }
-            TypeDescriptor typeDescriptor = mContext.getType(node);
-            if (typeDescriptor == null) {
-                return super.visitAnnotation(node);
-            }
-            String type = typeDescriptor.getName();
-            if (type.startsWith("java.lang.")){
-                return super.visitAnnotation(node);
+        public void visitAnnotation(PsiAnnotation annotation) {
+            String type = annotation.getQualifiedName();
+            if (type == null || type.startsWith("java.lang.")) {
+                return;
             }
 
-            if (SUPPRESS_LINT.equals(type) || FQCN_SUPPRESS_LINT.equals(type)) {
-                Node parent = node.getParent();
-                if (parent instanceof Modifiers) {
-                    parent = parent.getParent();
-                    if (parent instanceof VariableDefinition) {
-                        for (AnnotationElement element : node.astElements()) {
-                            AnnotationValue valueNode = element.astValue();
-                            if (valueNode == null) {
-                                continue;
-                            }
-                            if (valueNode instanceof StringLiteral) {
-                                StringLiteral literal = (StringLiteral) valueNode;
-                                String id = literal.astValue();
-                                if (!checkSuppressLint(node, id)) {
-                                    return super.visitAnnotation(node);
-                                }
-                            } else if (valueNode instanceof ArrayInitializer) {
-                                ArrayInitializer array = (ArrayInitializer) valueNode;
-                                StrictListAccessor<Expression, ArrayInitializer> expressions =
-                                        array.astExpressions();
-                                if (expressions == null) {
-                                    continue;
-                                }
-                                for (Expression arrayElement : expressions) {
-                                    if (arrayElement instanceof StringLiteral) {
-                                        String id = ((StringLiteral) arrayElement).astValue();
-                                        if (!checkSuppressLint(node, id)) {
-                                            return super.visitAnnotation(node);
-                                        }
+            if (FQCN_SUPPRESS_LINT.equals(type)) {
+                PsiAnnotationOwner owner = annotation.getOwner();
+                if (owner == null) {
+                    return;
+                }
+                if (owner instanceof PsiModifierList) {
+                    PsiElement parent = ((PsiModifierList) owner).getParent();
+                    // Only flag local variables and parameters (not classes, fields and methods)
+                    if (!(parent instanceof PsiDeclarationStatement
+                          || parent instanceof PsiLocalVariable
+                          || parent instanceof PsiParameter)) {
+                        return;
+                    }
+                } else {
+                    return;
+                }
+                PsiNameValuePair[] attributes = annotation.getParameterList().getAttributes();
+                if (attributes.length == 1) {
+                    PsiNameValuePair attribute = attributes[0];
+                    PsiAnnotationMemberValue value = attribute.getValue();
+                    if (value instanceof PsiLiteral) {
+                        Object v = ((PsiLiteral) value).getValue();
+                        if (v instanceof String) {
+                            String id = (String) v;
+                            checkSuppressLint(annotation, id);
+                        }
+                    } else if (value instanceof PsiArrayInitializerMemberValue) {
+                        PsiArrayInitializerMemberValue initializer =
+                                (PsiArrayInitializerMemberValue) value;
+                        for (PsiAnnotationMemberValue expression : initializer.getInitializers()) {
+                            if (expression instanceof PsiLiteral) {
+                                Object v = ((PsiLiteral) expression).getValue();
+                                if (v instanceof String) {
+                                    String id = (String) v;
+                                    if (!checkSuppressLint(annotation, id)) {
+                                        return;
                                     }
                                 }
                             }
@@ -300,174 +277,189 @@
             } else if (type.startsWith(SUPPORT_ANNOTATIONS_PREFIX)) {
                 if (CHECK_RESULT_ANNOTATION.equals(type)) {
                     // Check that the return type of this method is not void!
+                    if (annotation.getParent() instanceof PsiModifierList
+                            && annotation.getParent().getParent() instanceof PsiMethod) {
+                        PsiMethod method = (PsiMethod) annotation.getParent().getParent();
+                        if (!method.isConstructor()
+                                && PsiType.VOID.equals(method.getReturnType())) {
+                            mContext.report(ANNOTATION_USAGE, annotation,
+                                    mContext.getLocation(annotation),
+                                    "@CheckResult should not be specified on `void` methods");
+                        }
+                    }
                 } else if (INT_RANGE_ANNOTATION.equals(type)
                         || FLOAT_RANGE_ANNOTATION.equals(type)) {
                     // Check that the annotated element's type is int or long.
                     // Also make sure that from <= to.
-                    ResolvedNode resolved = mContext.resolve(node);
-                    if (resolved instanceof ResolvedAnnotation) {
-                        ResolvedAnnotation annotation = (ResolvedAnnotation) resolved;
-                        boolean invalid;
-                        if (INT_RANGE_ANNOTATION.equals(type)) {
-                            checkTargetType(node, TYPE_INT, TYPE_LONG);
+                    boolean invalid;
+                    if (INT_RANGE_ANNOTATION.equals(type)) {
+                        checkTargetType(annotation, TYPE_INT, TYPE_LONG);
 
-                            long from = getLongAttribute(annotation, ATTR_FROM, Long.MIN_VALUE);
-                            long to = getLongAttribute(annotation, ATTR_TO, Long.MAX_VALUE);
-                            invalid = from > to;
-                        } else {
-                            checkTargetType(node, TYPE_FLOAT, TYPE_DOUBLE);
+                        long from = getLongAttribute(annotation, ATTR_FROM, Long.MIN_VALUE);
+                        long to = getLongAttribute(annotation, ATTR_TO, Long.MAX_VALUE);
+                        invalid = from > to;
+                    } else {
+                        checkTargetType(annotation, TYPE_FLOAT, TYPE_DOUBLE);
 
-                            double from = getDoubleAttribute(annotation, ATTR_FROM,
-                                    Double.NEGATIVE_INFINITY);
-                            double to = getDoubleAttribute(annotation, ATTR_TO,
-                                    Double.POSITIVE_INFINITY);
-                            invalid = from > to;
-                        }
-                        if (invalid) {
-                            mContext.report(ANNOTATION_USAGE, node, mContext.getLocation(node),
-                                    "Invalid range: the `from` attribute must be less than "
-                                            + "the `to` attribute");
-                        }
+                        double from = getDoubleAttribute(annotation, ATTR_FROM,
+                                Double.NEGATIVE_INFINITY);
+                        double to = getDoubleAttribute(annotation, ATTR_TO,
+                                Double.POSITIVE_INFINITY);
+                        invalid = from > to;
+                    }
+                    if (invalid) {
+                        mContext.report(ANNOTATION_USAGE, annotation, mContext.getLocation(annotation),
+                                "Invalid range: the `from` attribute must be less than "
+                                        + "the `to` attribute");
                     }
                 } else if (SIZE_ANNOTATION.equals(type)) {
                     // Check that the annotated element's type is an array, or a collection
                     // (or at least not an int or long; if so, suggest IntRange)
                     // Make sure the size and the modulo is not negative.
+                    int unset = -42;
+                    long exact = getLongAttribute(annotation, ATTR_VALUE, unset);
+                    long min = getLongAttribute(annotation, ATTR_MIN, Long.MIN_VALUE);
+                    long max = getLongAttribute(annotation, ATTR_MAX, Long.MAX_VALUE);
+                    long multiple = getLongAttribute(annotation, ATTR_MULTIPLE, 1);
+                    if (min > max) {
+                        mContext.report(ANNOTATION_USAGE, annotation, mContext.getLocation(annotation),
+                                "Invalid size range: the `min` attribute must be less than "
+                                        + "the `max` attribute");
+                    } else if (multiple < 1) {
+                        mContext.report(ANNOTATION_USAGE, annotation, mContext.getLocation(annotation),
+                                "The size multiple must be at least 1");
 
-                    ResolvedNode resolved = mContext.resolve(node);
-                    if (resolved instanceof ResolvedAnnotation) {
-                        ResolvedAnnotation annotation = (ResolvedAnnotation) resolved;
-                        int unset = -42;
-                        long exact = getLongAttribute(annotation, ATTR_VALUE, unset);
-                        long min = getLongAttribute(annotation, ATTR_MIN, Long.MIN_VALUE);
-                        long max = getLongAttribute(annotation, ATTR_MAX, Long.MAX_VALUE);
-                        long multiple = getLongAttribute(annotation, ATTR_MULTIPLE, 1);
-                        if (min > max) {
-                            mContext.report(ANNOTATION_USAGE, node, mContext.getLocation(node),
-                                    "Invalid size range: the `min` attribute must be less than "
-                                            + "the `max` attribute");
-                        } else if (multiple < 1) {
-                            mContext.report(ANNOTATION_USAGE, node, mContext.getLocation(node),
-                                    "The size multiple must be at least 1");
-
-                        } else if (exact < 0 && exact != unset) {
-                            mContext.report(ANNOTATION_USAGE, node, mContext.getLocation(node),
-                                    "The size can't be negative");
-                        }
+                    } else if (exact < 0 && exact != unset) {
+                        mContext.report(ANNOTATION_USAGE, annotation, mContext.getLocation(annotation),
+                                "The size can't be negative");
                     }
                 } else if (COLOR_INT_ANNOTATION.equals(type)) {
                     // Check that ColorInt applies to the right type
-                    checkTargetType(node, TYPE_INT, TYPE_LONG);
+                    checkTargetType(annotation, TYPE_INT, TYPE_LONG);
                 } else if (INT_DEF_ANNOTATION.equals(type)) {
                     // Make sure IntDef constants are unique
-                    ResolvedNode resolved = mContext.resolve(node);
-                    if (resolved != null) {
-                        ensureUniqueValues(((ResolvedAnnotation) resolved), node);
-                    }
+                    ensureUniqueValues(annotation);
                 } else if (PERMISSION_ANNOTATION.equals(type) ||
                         PERMISSION_ANNOTATION_READ.equals(type) ||
                         PERMISSION_ANNOTATION_WRITE.equals(type)) {
                     // Check that if there are no arguments, this is specified on a parameter,
                     // and conversely, on methods and fields there is a valid argument.
-                    if (node.getParent() instanceof Modifiers &&
-                            node.getParent().getParent() instanceof MethodDeclaration) {
-                        ResolvedNode resolved = mContext.resolve(node);
-                        if (resolved != null) {
-                            ResolvedAnnotation annotation = (ResolvedAnnotation) resolved;
-                            String value = (String)annotation.getValue(ATTR_VALUE);
-                            String[] anyOf = getAnnotationStrings(annotation.getValue(ATTR_ANY_OF));
-                            String[] allOf = getAnnotationStrings(annotation.getValue(ATTR_ALL_OF));
+                    if (annotation.getParent() instanceof PsiModifierList
+                        && annotation.getParent().getParent() instanceof PsiMethod) {
+                        String value = PermissionRequirement.getAnnotationStringValue(annotation, ATTR_VALUE);
+                        String[] anyOf = PermissionRequirement.getAnnotationStringValues(annotation, ATTR_ANY_OF);
+                        String[] allOf = PermissionRequirement.getAnnotationStringValues(annotation, ATTR_ALL_OF);
 
-                            int set = 0;
-                            //noinspection VariableNotUsedInsideIf
-                            if (value != null) {
-                                set++;
-                            }
-                            //noinspection VariableNotUsedInsideIf
-                            if (allOf != null) {
-                                set++;
-                            }
-                            //noinspection VariableNotUsedInsideIf
-                            if (anyOf != null) {
-                                set++;
-                            }
+                        int set = 0;
+                        //noinspection VariableNotUsedInsideIf
+                        if (value != null) {
+                            set++;
+                        }
+                        //noinspection VariableNotUsedInsideIf
+                        if (allOf != null) {
+                            set++;
+                        }
+                        //noinspection VariableNotUsedInsideIf
+                        if (anyOf != null) {
+                            set++;
+                        }
 
-                            if (set == 0) {
-                                mContext.report(ANNOTATION_USAGE, node,
-                                        mContext.getLocation(node),
-                                        "For methods, permission annotation should specify one "
-                                                + "of `value`, `anyOf` or `allOf`");
-                            } else if (set > 1) {
-                                mContext.report(ANNOTATION_USAGE, node,
-                                        mContext.getLocation(node),
-                                        "Only specify on of `value`, `anyOf` or `allOf`");
-                            }
+                        if (set == 0) {
+                            mContext.report(ANNOTATION_USAGE, annotation,
+                                    mContext.getLocation(annotation),
+                                    "For methods, permission annotation should specify one "
+                                            + "of `value`, `anyOf` or `allOf`");
+                        } else if (set > 1) {
+                            mContext.report(ANNOTATION_USAGE, annotation,
+                                    mContext.getLocation(annotation),
+                                    "Only specify one of `value`, `anyOf` or `allOf`");
                         }
                     }
 
-                    // Also make sure you set only one of value, anyOf, allOf
-
                 } else if (type.endsWith(RES_SUFFIX)) {
                     // Check that resource type annotations are on ints
-                    checkTargetType(node, TYPE_INT, TYPE_LONG);
+                    checkTargetType(annotation, TYPE_INT, TYPE_LONG);
                 }
             } else {
                 // Look for typedefs (and make sure they're specified on the right type)
-                ResolvedNode resolved = mContext.resolve(node);
-                if (resolved != null) {
-                    for (ResolvedAnnotation annotation : resolved.getAnnotations()) {
-                        String name = annotation.getName();
-                        if (INT_DEF_ANNOTATION.equals(name)) {
-                            checkTargetType(node, TYPE_INT, TYPE_LONG);
-                        } else if (STRING_DEF_ANNOTATION.equals(type)) {
-                            checkTargetType(node, TYPE_STRING, null);
+                PsiJavaCodeReferenceElement referenceElement = annotation
+                        .getNameReferenceElement();
+                if (referenceElement != null) {
+                    PsiElement resolved = referenceElement.resolve();
+                    if (resolved instanceof PsiClass) {
+                        PsiClass cls = (PsiClass) resolved;
+                        if (cls.isAnnotationType() && cls.getModifierList() != null) {
+                            for (PsiAnnotation a : cls.getModifierList().getAnnotations()) {
+                                String name = a.getQualifiedName();
+                                if (INT_DEF_ANNOTATION.equals(name)) {
+                                    checkTargetType(annotation, TYPE_INT, TYPE_LONG);
+                                } else if (STRING_DEF_ANNOTATION.equals(type)) {
+                                    checkTargetType(annotation, TYPE_STRING, null);
+                                }
+                            }
                         }
                     }
                 }
             }
-
-            return super.visitAnnotation(node);
         }
 
-        private void checkTargetType(@NonNull Annotation node, @NonNull String type1,
+        private void checkTargetType(@NonNull PsiAnnotation node, @NonNull String type1,
                 @Nullable String type2) {
-            Node parent = node.getParent();
-            if (parent != null) {
-                if (parent instanceof Modifiers) {
-                    Node parentParent = parent.getParent();
-                    if (parentParent != null) {
-                        TypeDescriptor typeDescriptor = mContext.getType(parentParent);
-                        if (typeDescriptor != null
-                                && !typeDescriptor.matchesName(type1)
-                                && (type2 == null || !typeDescriptor.matchesName(type2))) {
-                            String expectedTypes = type2 == null ? type1 : type1 + " or " + type2;
-                            String typeName = typeDescriptor.getName();
-                            if (typeName.equals(TYPE_STRING)) {
-                                typeName = "String";
-                            }
-                            String message = String.format(
-                                    "This annotation does not apply for type %1$s; expected %2$s",
-                                    typeName, expectedTypes);
-                            Location location = mContext.getLocation(node);
-                            mContext.report(ANNOTATION_USAGE, node, location, message);
+            PsiAnnotationOwner owner = node.getOwner();
+            if (owner instanceof PsiModifierList) {
+                PsiElement parent = ((PsiModifierList) owner).getParent();
+                PsiType type;
+                if (parent instanceof PsiDeclarationStatement) {
+                    PsiElement[] elements = ((PsiDeclarationStatement) parent).getDeclaredElements();
+                    if (elements.length > 0) {
+                        PsiElement element = elements[0];
+                        if (element instanceof PsiLocalVariable) {
+                            type = ((PsiLocalVariable)element).getType();
+                        } else {
+                            return;
                         }
+                    } else {
+                        return;
                     }
+                } else if (parent instanceof PsiMethod) {
+                    PsiMethod method = (PsiMethod) parent;
+                    type = method.isConstructor()
+                            ? mContext.getEvaluator().getClassType(method.getContainingClass())
+                            : method.getReturnType();
+                } else if (parent instanceof PsiVariable) {
+                    // Field or local variable or parameter
+                    type = ((PsiVariable)parent).getType();
+                } else {
+                    return;
+                }
+                if (type == null) {
+                    return;
+                }
+                String typeName = type.getCanonicalText();
+                if (!typeName.equals(type1)
+                        && (type2 == null || !typeName.equals(type2))) {
+                    String expectedTypes = type2 == null ? type1 : type1 + " or " + type2;
+                    if (typeName.equals(TYPE_STRING)) {
+                        typeName = "String";
+                    }
+                    String message = String.format(
+                            "This annotation does not apply for type %1$s; expected %2$s",
+                            typeName, expectedTypes);
+                    Location location = mContext.getLocation(node);
+                    mContext.report(ANNOTATION_USAGE, node, location, message);
                 }
             }
         }
 
         @Override
-        public boolean visitSwitch(Switch node) {
-            Expression condition = node.astCondition();
-            TypeDescriptor type = mContext.getType(condition);
-            if (type != null && type.matchesName(TYPE_INT)) {
-                ResolvedAnnotation annotation = findIntDef(condition);
+        public void visitSwitchStatement(PsiSwitchStatement statement) {
+            PsiExpression condition = statement.getExpression();
+            if (condition != null && PsiType.INT.equals(condition.getType())) {
+                PsiAnnotation annotation = findIntDef(condition);
                 if (annotation != null) {
-                    checkSwitch(node, annotation);
+                    checkSwitch(statement, annotation);
                 }
             }
-
-            return super.visitSwitch(node);
         }
 
         /**
@@ -475,122 +467,151 @@
          * with a given node
          */
         @Nullable
-        private ResolvedAnnotation findIntDef(@NonNull Node node) {
-            if ((node instanceof VariableReference || node instanceof Select)) {
-                ResolvedNode resolved = mContext.resolve(node);
-                if (resolved == null) {
-                    return null;
-                }
-
-                ResolvedAnnotation annotation = SupportAnnotationDetector.findIntDef(
-                        filterRelevantAnnotations(resolved.getAnnotations()));
-                if (annotation != null) {
-                    return annotation;
-                }
-
-                if (node instanceof VariableReference) {
-                    Statement statement = getParentOfType(node, Statement.class, false);
-                    if (statement != null) {
-                        ListIterator<Node> iterator =
-                                statement.getParent().getChildren().listIterator();
-                        while (iterator.hasNext()) {
-                            if (iterator.next() == statement) {
-                                if (iterator.hasPrevious()) { // should always be true
-                                    iterator.previous();
-                                }
-                                break;
-                            }
-                        }
-
-                        String targetName = ((VariableReference) node).astIdentifier().astValue();
-                        while (iterator.hasPrevious()) {
-                            Node previous = iterator.previous();
-                            if (previous instanceof VariableDeclaration) {
-                                VariableDeclaration declaration = (VariableDeclaration) previous;
-                                VariableDefinition definition = declaration.astDefinition();
-                                for (VariableDefinitionEntry entry : definition
-                                        .astVariables()) {
-                                    if (entry.astInitializer() != null
-                                            && entry.astName().astValue().equals(targetName)) {
-                                        return findIntDef(entry.astInitializer());
-                                    }
-                                }
-                            } else if (previous instanceof ExpressionStatement) {
-                                ExpressionStatement expressionStatement =
-                                        (ExpressionStatement) previous;
-                                Expression expression = expressionStatement.astExpression();
-                                if (expression instanceof BinaryExpression &&
-                                        ((BinaryExpression) expression).astOperator()
-                                                == BinaryOperator.ASSIGN) {
-                                    BinaryExpression binaryExpression
-                                            = (BinaryExpression) expression;
-                                    if (targetName.equals(binaryExpression.astLeft().toString())) {
-                                        return findIntDef(binaryExpression.astRight());
-                                    }
-                                }
-                            }
-                        }
-                    }
-                }
-            } else if (node instanceof MethodInvocation) {
-                ResolvedNode resolved = mContext.resolve(node);
-                if (resolved != null) {
-                    ResolvedAnnotation annotation = SupportAnnotationDetector
-                            .findIntDef(filterRelevantAnnotations(resolved.getAnnotations()));
+        private PsiAnnotation findIntDef(@NonNull PsiElement node) {
+            if (node instanceof PsiReferenceExpression) {
+                PsiElement resolved = ((PsiReference) node).resolve();
+                if (resolved instanceof PsiModifierListOwner) {
+                    PsiAnnotation[] annotations = mContext.getEvaluator().getAllAnnotations(
+                            (PsiModifierListOwner)resolved, true);
+                    PsiAnnotation annotation = SupportAnnotationDetector.findIntDef(
+                            filterRelevantAnnotations(annotations));
                     if (annotation != null) {
                         return annotation;
                     }
                 }
-            } else if (node instanceof InlineIfExpression) {
-                InlineIfExpression expression = (InlineIfExpression) node;
-                if (expression.astIfTrue() != null) {
-                    ResolvedAnnotation result = findIntDef(expression.astIfTrue());
+
+                if (resolved instanceof PsiLocalVariable) {
+                    PsiLocalVariable variable = (PsiLocalVariable) resolved;
+                    PsiStatement statement = PsiTreeUtil.getParentOfType(node, PsiStatement.class,
+                            false);
+                    if (statement != null) {
+                        PsiStatement prev = PsiTreeUtil.getPrevSiblingOfType(statement,
+                                PsiStatement.class);
+                        String targetName = variable.getName();
+                        if (targetName == null) {
+                            return null;
+                        }
+                        while (prev != null) {
+                            if (prev instanceof PsiDeclarationStatement) {
+                                for (PsiElement element : ((PsiDeclarationStatement) prev)
+                                        .getDeclaredElements()) {
+                                    if (variable.equals(element)) {
+                                        PsiExpression initializer = variable.getInitializer();
+                                        if (initializer != null) {
+                                            return findIntDef(initializer);
+                                        }
+                                        break;
+                                    }
+                                }
+                            } else if (prev instanceof PsiExpressionStatement) {
+                                PsiExpression expression = ((PsiExpressionStatement) prev)
+                                        .getExpression();
+                                if (expression instanceof PsiAssignmentExpression) {
+                                    PsiAssignmentExpression assign
+                                            = (PsiAssignmentExpression) expression;
+                                    PsiExpression lhs = assign.getLExpression();
+                                    if (lhs instanceof PsiReferenceExpression) {
+                                        PsiReferenceExpression reference = (PsiReferenceExpression) lhs;
+                                        if (targetName.equals(reference.getReferenceName()) &&
+                                                reference.getQualifier() == null) {
+                                            PsiExpression rExpression = assign.getRExpression();
+                                            if (rExpression != null) {
+                                                return findIntDef(rExpression);
+                                            }
+                                            break;
+                                        }
+                                    }
+                                }
+                            }
+                            prev = PsiTreeUtil.getPrevSiblingOfType(prev,
+                                    PsiStatement.class);
+                        }
+                    }
+
+                }
+            } else if (node instanceof PsiMethodCallExpression) {
+                PsiMethod method = ((PsiMethodCallExpression) node).resolveMethod();
+                if (method != null) {
+                    PsiAnnotation[] annotations = mContext.getEvaluator().getAllAnnotations(method, true);
+                    PsiAnnotation annotation = SupportAnnotationDetector.findIntDef(
+                            filterRelevantAnnotations(annotations));
+                    if (annotation != null) {
+                        return annotation;
+                    }
+                }
+            } else if (node instanceof PsiConditionalExpression) {
+                PsiConditionalExpression expression = (PsiConditionalExpression) node;
+                if (expression.getThenExpression() != null) {
+                    PsiAnnotation result = findIntDef(expression.getThenExpression());
                     if (result != null) {
                         return result;
                     }
                 }
-                if (expression.astIfFalse() != null) {
-                    ResolvedAnnotation result = findIntDef(expression.astIfFalse());
+                if (expression.getElseExpression() != null) {
+                    PsiAnnotation result = findIntDef(expression.getElseExpression());
                     if (result != null) {
                         return result;
                     }
                 }
-            } else if (node instanceof Cast) {
-                Cast cast = (Cast) node;
-                return findIntDef(cast.astOperand());
+            } else if (node instanceof PsiTypeCastExpression) {
+                PsiTypeCastExpression cast = (PsiTypeCastExpression) node;
+                if (cast.getOperand() != null) {
+                    return findIntDef(cast.getOperand());
+                }
+            } else if (node instanceof PsiParenthesizedExpression) {
+                PsiParenthesizedExpression expression = (PsiParenthesizedExpression) node;
+                if (expression.getExpression() != null) {
+                    return findIntDef(expression.getExpression());
+                }
             }
 
             return null;
         }
 
-        private void checkSwitch(@NonNull Switch node, @NonNull ResolvedAnnotation annotation) {
-            Block block = node.astBody();
+        private void checkSwitch(@NonNull PsiSwitchStatement node, @NonNull PsiAnnotation annotation) {
+            PsiCodeBlock block = node.getBody();
             if (block == null) {
                 return;
             }
 
-            Object allowed = annotation.getValue();
-            if (!(allowed instanceof Object[])) {
+            PsiAnnotationMemberValue value = annotation.findAttributeValue(ATTR_VALUE);
+            if (value == null) {
+                value = annotation.findAttributeValue(null);
+            }
+            if (value == null) {
                 return;
             }
-            Object[] allowedValues = (Object[]) allowed;
-            List<ResolvedField> fields = Lists.newArrayListWithCapacity(allowedValues.length);
-            for (Object o : allowedValues) {
-                if (o instanceof ResolvedField) {
-                    fields.add((ResolvedField) o);
+
+            if (!(value instanceof PsiArrayInitializerMemberValue)) {
+                return;
+            }
+
+            PsiArrayInitializerMemberValue array = (PsiArrayInitializerMemberValue)value;
+            PsiAnnotationMemberValue[] allowedValues = array.getInitializers();
+
+            List<PsiElement> fields = Lists.newArrayListWithCapacity(allowedValues.length);
+            for (PsiAnnotationMemberValue allowedValue : allowedValues) {
+                if (allowedValue instanceof PsiReferenceExpression) {
+                    PsiElement resolved = ((PsiReferenceExpression) allowedValue).resolve();
+                    if (resolved != null) {
+                        fields.add(resolved);
+                    }
+                } else if (allowedValue instanceof PsiLiteral) {
+                    fields.add(allowedValue);
                 }
             }
 
+
             // Empty switch: arguably we could skip these (since the IDE already warns about
             // empty switches) but it's useful since the quickfix will kick in and offer all
             // the missing ones when you're editing.
-            //   if (block.astContents().isEmpty()) { return; }
+            //   if (block.getStatements().length == 0) { return; }
 
-            for (Statement statement : block.astContents()) {
-                if (statement instanceof Case) {
-                    Case caseStatement = (Case) statement;
-                    Expression expression = caseStatement.astCondition();
-                    if (expression instanceof IntegralLiteral) {
+            for (PsiStatement statement : block.getStatements()) {
+                if (statement instanceof PsiSwitchLabelStatement) {
+                    PsiSwitchLabelStatement caseStatement = (PsiSwitchLabelStatement) statement;
+                    PsiExpression expression = caseStatement.getCaseValue();
+                    if (expression instanceof PsiLiteral) {
                         // Report warnings if you specify hardcoded constants.
                         // It's the wrong thing to do.
                         List<String> list = computeFieldNames(node, Arrays.asList(allowedValues));
@@ -601,26 +622,29 @@
                                 mContext.getLocation(expression), message);
                         return; // Don't look for other missing typedef constants since you might
                         // have aliased with value
-                    } else if (expression != null) { // default case can have null expression
-                        ResolvedNode resolved = mContext.resolve(expression);
+                    } else if (expression instanceof PsiReferenceExpression) { // default case can have null expression
+                        PsiElement resolved = ((PsiReferenceExpression) expression).resolve();
                         if (resolved == null) {
                             // If there are compilation issues (e.g. user is editing code) we
                             // can't be certain, so don't flag anything.
                             return;
                         }
-                        if (resolved instanceof ResolvedField) {
+                        if (resolved instanceof PsiField) {
                             // We can't just do
                             //    fields.remove(resolved);
                             // since the fields list contains instances of potentially
-                            // different types with different hash codes. The
-                            // equals method on ResolvedExternalField deliberately handles
+                            // different types with different hash codes (due to the
+                            // external annotations, which are not of the same type as
+                            // for example the ECJ based ones.
+                            //
+                            // The equals method on external field class deliberately handles
                             // this (but it can't make its hash code match what
                             // the ECJ fields do, which is tied to the ECJ binding hash code.)
                             // So instead, manually check for equals. These lists tend to
                             // be very short anyway.
-                            ListIterator<ResolvedField> iterator = fields.listIterator();
+                            ListIterator<PsiElement> iterator = fields.listIterator();
                             while (iterator.hasNext()) {
-                                ResolvedField field = iterator.next();
+                                PsiElement field = iterator.next();
                                 if (field.equals(resolved)) {
                                     iterator.remove();
                                     break;
@@ -640,155 +664,101 @@
             }
         }
 
-        private void ensureUniqueValues(@NonNull ResolvedAnnotation annotation,
-                                        @NonNull Annotation node) {
-            Object allowed = annotation.getValue();
-            if (allowed instanceof Object[]) {
-                Object[] allowedValues = (Object[]) allowed;
-                Map<Number,Integer> valueToIndex =
-                        Maps.newHashMapWithExpectedSize(allowedValues.length);
-
-                List<Node> constants = null;
-                for (AnnotationElement element : node.astElements()) {
-                    if (element.astName() == null
-                            || ATTR_VALUE.equals(element.astName().astValue())) {
-                        AnnotationValue value = element.astValue();
-                        if (value instanceof ArrayInitializer) {
-                            ArrayInitializer initializer = (ArrayInitializer)value;
-                            constants = Lists.newArrayListWithExpectedSize(allowedValues.length);
-                            for (Expression expression : initializer.astExpressions()) {
-                                constants.add(expression);
-                            }
-                        }
-                        break;
-                    }
-                }
-                if (constants != null) {
-                    if (constants.size() != allowedValues.length) {
-                        constants = null;
-                    } else {
-                        boolean flag = annotation.getValue(TYPE_DEF_FLAG_ATTRIBUTE) == Boolean.TRUE;
-                        if (flag) {
-                            ensureUsingFlagStyle(constants);
-                        }
-                    }
-                }
-
-                for (int index = 0; index < allowedValues.length; index++) {
-                    Object o = allowedValues[index];
-                    if (o instanceof Number) {
-                        Number number = (Number)o;
-                        if (valueToIndex.containsKey(number)) {
-                            @SuppressWarnings("UnnecessaryLocalVariable")
-                            Number repeatedValue = number;
-
-                            Location location;
-                            String message;
-                            if (constants != null) {
-                                Node constant = constants.get(index);
-                                int prevIndex = valueToIndex.get(number);
-                                Node prevConstant = constants.get(prevIndex);
-                                message = String.format(
-                                        "Constants `%1$s` and `%2$s` specify the same exact "
-                                                + "value (%3$s); this is usually a cut & paste or "
-                                                + "merge error",
-                                        constant.toString(), prevConstant.toString(),
-                                        repeatedValue.toString());
-                                location = mContext.getLocation(constant);
-                                Location secondary = mContext.getLocation(prevConstant);
-                                secondary.setMessage("Previous same value");
-                                location.setSecondary(secondary);
-                            } else {
-                                message = String.format(
-                                        "More than one constant specifies the same exact "
-                                                + "value (%1$s); this is usually a cut & paste or"
-                                                + "merge error",
-                                        repeatedValue.toString());
-                                location = mContext.getLocation(node);
-                            }
-                            Node scope = getAnnotationScope(node);
-                            mContext.report(UNIQUE, scope, location, message);
-                            break;
-                        }
-                        valueToIndex.put(number, index);
-                    }
-                }
+        private void ensureUniqueValues(@NonNull PsiAnnotation node) {
+            PsiAnnotationMemberValue value = node.findAttributeValue(ATTR_VALUE);
+            if (value == null) {
+                value = node.findAttributeValue(null);
             }
-        }
-
-        @NonNull
-        private static List<VariableDefinitionEntry> findDeclarations(
-                @Nullable ClassDeclaration cls,
-                @NonNull List<VariableReference> references) {
-            if (cls == null) {
-                return Collections.emptyList();
-            }
-            Map<String, VariableReference> referenceMap = Maps.newHashMap();
-            for (VariableReference reference : references) {
-                String name = reference.astIdentifier().astValue();
-                referenceMap.put(name, reference);
-            }
-            List<VariableDefinitionEntry> declarations = Lists.newArrayList();
-            for (TypeMember member : cls.astBody().astMembers()) {
-                if (member instanceof VariableDeclaration) {
-                    VariableDeclaration declaration = (VariableDeclaration)member;
-                    VariableDefinitionEntry field = declaration.astDefinition().astVariables()
-                            .first();
-                    String name = field.astName().astValue();
-                    if (referenceMap.containsKey(name)) {
-                        // TODO: When the Lombok ECJ bridge properly handles resolving variable
-                        // definitions into ECJ bindings this code should check that
-                        // mContext.resolve(field) == mContext.resolve(referenceMap.get(name)) !
-                        declarations.add(field);
-                    }
-                }
-            }
-
-            return declarations;
-        }
-
-        private void ensureUsingFlagStyle(@NonNull List<Node> constants) {
-            if (constants.size() < 3) {
+            if (value == null) {
                 return;
             }
 
-            List<VariableReference> references =
-                    Lists.newArrayListWithExpectedSize(constants.size());
-            for (Node constant : constants) {
-                if (constant instanceof VariableReference) {
-                    references.add((VariableReference) constant);
-                }
+            if (!(value instanceof PsiArrayInitializerMemberValue)) {
+                return;
             }
-            List<VariableDefinitionEntry> entries = findDeclarations(
-                    findSurroundingClass(constants.get(0)), references);
-            for (VariableDefinitionEntry entry : entries) {
-                Expression declaration = entry.astInitializer();
-                if (declaration == null) {
-                    continue;
-                }
-                if (declaration instanceof IntegralLiteral) {
-                    IntegralLiteral literal = (IntegralLiteral) declaration;
-                    // Allow -1, 0 and 1. You can write 1 as "1 << 0" but IntelliJ for
-                    // example warns that that's a redundant shift.
-                    long value = literal.astLongValue();
-                    if (Math.abs(value) <= 1) {
-                        continue;
+
+            PsiArrayInitializerMemberValue array = (PsiArrayInitializerMemberValue) value;
+            PsiAnnotationMemberValue[] initializers = array.getInitializers();
+            Map<Number,Integer> valueToIndex =
+                    Maps.newHashMapWithExpectedSize(initializers.length);
+
+            boolean flag = getAnnotationBooleanValue(node, TYPE_DEF_FLAG_ATTRIBUTE) == Boolean.TRUE;
+            if (flag) {
+                ensureUsingFlagStyle(initializers);
+            }
+
+            ConstantEvaluator constantEvaluator = new ConstantEvaluator(mContext);
+            for (int index = 0; index < initializers.length; index++) {
+                PsiAnnotationMemberValue expression = initializers[index];
+                Object o = constantEvaluator.evaluate(expression);
+                if (o instanceof Number) {
+                    Number number = (Number) o;
+                    if (valueToIndex.containsKey(number)) {
+                        @SuppressWarnings("UnnecessaryLocalVariable")
+                        Number repeatedValue = number;
+
+                        Location location;
+                        String message;
+                        int prevIndex = valueToIndex.get(number);
+                        PsiElement prevConstant = initializers[prevIndex];
+                        message = String.format(
+                                "Constants `%1$s` and `%2$s` specify the same exact "
+                                        + "value (%3$s); this is usually a cut & paste or "
+                                        + "merge error",
+                                expression.getText(), prevConstant.getText(),
+                                repeatedValue.toString());
+                        location = mContext.getLocation(expression);
+                        Location secondary = mContext.getLocation(prevConstant);
+                        secondary.setMessage("Previous same value");
+                        location.setSecondary(secondary);
+                        PsiElement scope = getAnnotationScope(node);
+                        mContext.report(UNIQUE, scope, location, message);
+                        break;
                     }
-                    // Only warn if we're setting a specific bit
-                    if (Long.bitCount(value) != 1) {
-                        continue;
-                    }
-                    int shift = Long.numberOfTrailingZeros(value);
-                    String message = String.format(
-                            "Consider declaring this constant using 1 << %1$d instead",
-                            shift);
-                    mContext.report(FLAG_STYLE, declaration, mContext.getLocation(declaration),
-                            message);
+                    valueToIndex.put(number, index);
                 }
             }
         }
 
-        private boolean checkSuppressLint(@NonNull Annotation node, @NonNull String id) {
+        private void ensureUsingFlagStyle(@NonNull PsiAnnotationMemberValue[] constants) {
+            if (constants.length < 3) {
+                return;
+            }
+
+            for (PsiAnnotationMemberValue constant : constants) {
+                if (constant instanceof PsiReferenceExpression) {
+                    PsiElement resolved = ((PsiReferenceExpression) constant).resolve();
+                    if (resolved instanceof PsiField) {
+                        PsiExpression initializer = ((PsiField) resolved).getInitializer();
+                        if (initializer instanceof PsiLiteral) {
+                            PsiLiteral literal = (PsiLiteral) initializer;
+                            Object o = literal.getValue();
+                            if (!(o instanceof Number)) {
+                                continue;
+                            }
+                            long value = ((Number)o).longValue();
+                            // Allow -1, 0 and 1. You can write 1 as "1 << 0" but IntelliJ for
+                            // example warns that that's a redundant shift.
+                            if (Math.abs(value) <= 1) {
+                                continue;
+                            }
+                            // Only warn if we're setting a specific bit
+                            if (Long.bitCount(value) != 1) {
+                                continue;
+                            }
+                            int shift = Long.numberOfTrailingZeros(value);
+                            String message = String.format(
+                                    "Consider declaring this constant using 1 << %1$d instead",
+                                    shift);
+                            Location location = mContext.getLocation(initializer);
+                            mContext.report(FLAG_STYLE, initializer, location, message);
+                        }
+                    }
+                }
+            }
+        }
+
+        private boolean checkSuppressLint(@NonNull PsiAnnotation node, @NonNull String id) {
             IssueRegistry registry = mContext.getDriver().getRegistry();
             Issue issue = registry.getIssue(id);
             // Special-case the ApiDetector issue, since it does both source file analysis
@@ -796,34 +766,9 @@
             // annotations outside of methods only on fields
             if (issue != null && !issue.getImplementation().getScope().contains(Scope.JAVA_FILE)
                     || issue == ApiDetector.UNSUPPORTED) {
-                // Ensure that this isn't a field
-                Node parent = node.getParent();
-                while (parent != null) {
-                    if (parent instanceof MethodDeclaration
-                            || parent instanceof ConstructorDeclaration
-                            || parent instanceof Block) {
-                        break;
-                    } else if (parent instanceof TypeBody) { // It's a field
-                        return true;
-                    } else if (issue == ApiDetector.UNSUPPORTED
-                            && parent instanceof VariableDefinition) {
-                        VariableDefinition definition = (VariableDefinition) parent;
-                        for (VariableDefinitionEntry entry : definition.astVariables()) {
-                            Expression initializer = entry.astInitializer();
-                            if (initializer instanceof Select) {
-                                return true;
-                            }
-                        }
-                    }
-                    parent = parent.getParent();
-                    if (parent == null) {
-                        return true;
-                    }
-                }
-
                 // This issue doesn't have AST access: annotations are not
                 // available for local variables or parameters
-                Node scope = getAnnotationScope(node);
+                PsiElement scope = getAnnotationScope(node);
                 mContext.report(INSIDE_METHOD, scope, mContext.getLocation(node), String.format(
                     "The `@SuppressLint` annotation cannot be used on a local " +
                     "variable with the lint check '%1$s': move out to the " +
@@ -835,25 +780,35 @@
         }
 
         @NonNull
-        private List<String> computeFieldNames(@NonNull Switch node, Iterable allowedValues) {
+        private static List<String> computeFieldNames(@NonNull PsiSwitchStatement node,
+                Iterable<?> allowedValues) {
             List<String> list = Lists.newArrayList();
             for (Object o : allowedValues) {
-                if (o instanceof ResolvedField) {
-                    ResolvedField field = (ResolvedField) o;
+                if (o instanceof PsiReferenceExpression) {
+                    PsiElement resolved = ((PsiReferenceExpression) o).resolve();
+                    if (resolved != null) {
+                        o = resolved;
+                    }
+                } else if (o instanceof PsiLiteral) {
+                    list.add("`" + ((PsiLiteral) o).getValue() + '`');
+                    continue;
+                }
+
+                if (o instanceof PsiField) {
+                    PsiField field = (PsiField) o;
                     // Only include class name if necessary
                     String name = field.getName();
-                    ClassDeclaration clz = findSurroundingClass(node);
+                    PsiClass clz = PsiTreeUtil.getParentOfType(node, PsiClass.class, true);
                     if (clz != null) {
-                        ResolvedNode resolved = mContext.resolve(clz);
-                        ResolvedClass containingClass = field.getContainingClass();
-                        if (containingClass != null && !containingClass.equals(resolved)
-                                && resolved instanceof ResolvedClass) {
-                            if (Objects.equal(containingClass.getPackage(),
-                                    ((ResolvedClass) resolved).getPackage())) {
-                                name = containingClass.getSimpleName() + '.' + field.getName();
-                            } else {
+                        PsiClass containingClass = field.getContainingClass();
+                        if (containingClass != null && !containingClass.equals(clz)) {
+
+                            //if (Objects.equal(containingClass.getPackage(),
+                            //        ((ResolvedClass) resolved).getPackage())) {
+                            //    name = containingClass.getSimpleName() + '.' + field.getName();
+                            //} else {
                                 name = containingClass.getName() + '.' + field.getName();
-                            }
+                            //}
                         }
                     }
                     list.add('`' + name + '`');
@@ -895,9 +850,8 @@
      * suppress the error on this annotated element, not the whole surrounding class.
      */
     @NonNull
-    private static Node getAnnotationScope(@NonNull Annotation node) {
-        Node scope = getParentOfType(node,
-              AnnotationDeclaration.class, true);
+    private static PsiElement getAnnotationScope(@NonNull PsiAnnotation node) {
+        PsiElement scope = PsiTreeUtil.getParentOfType(node, PsiAnnotation.class, true);
         if (scope == null) {
             scope = node;
         }
diff --git a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/Api.java b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/Api.java
index 8b08290..4cf2b23 100644
--- a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/Api.java
+++ b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/Api.java
@@ -57,7 +57,7 @@
             inputStream.close();
 
             // Also read in API (unless regenerating the map for newer libraries)
-            //noinspection PointlessBooleanExpression
+            //noinspection PointlessBooleanExpression,TestOnlyProblems
             if (!ApiLookup.DEBUG_FORCE_REGENERATE_BINARY) {
                 inputStream = Api.class.getResourceAsStream("api-versions-support-library.xml");
                 if (inputStream != null) {
diff --git a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/ApiClass.java b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/ApiClass.java
index 64478f2..2abe571 100644
--- a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/ApiClass.java
+++ b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/ApiClass.java
@@ -40,7 +40,7 @@
 public class ApiClass implements Comparable<ApiClass> {
     private final String mName;
     private final int mSince;
-    private int mDeprecatedIn;
+    private final int mDeprecatedIn;
 
     private final List<Pair<String, Integer>> mSuperClasses = Lists.newArrayList();
     private final List<Pair<String, Integer>> mInterfaces = Lists.newArrayList();
diff --git a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/ApiDetector.java b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/ApiDetector.java
index 7ceeb8f..fa373b5 100644
--- a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/ApiDetector.java
+++ b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/ApiDetector.java
@@ -34,8 +34,8 @@
 import static com.android.SdkConstants.CHECK_BOX;
 import static com.android.SdkConstants.CLASS_CONSTRUCTOR;
 import static com.android.SdkConstants.CONSTRUCTOR_NAME;
+import static com.android.SdkConstants.FQCN_TARGET_API;
 import static com.android.SdkConstants.PREFIX_ANDROID;
-import static com.android.SdkConstants.R_CLASS;
 import static com.android.SdkConstants.SWITCH;
 import static com.android.SdkConstants.TAG;
 import static com.android.SdkConstants.TAG_ITEM;
@@ -45,8 +45,8 @@
 import static com.android.SdkConstants.VIEW_TAG;
 import static com.android.tools.lint.checks.RtlDetector.ATTR_SUPPORTS_RTL;
 import static com.android.tools.lint.detector.api.ClassContext.getFqcn;
-import static com.android.tools.lint.detector.api.ClassContext.getInternalName;
 import static com.android.tools.lint.detector.api.LintUtils.getNextInstruction;
+import static com.android.tools.lint.detector.api.LintUtils.skipParentheses;
 import static com.android.tools.lint.detector.api.Location.SearchDirection.BACKWARD;
 import static com.android.tools.lint.detector.api.Location.SearchDirection.EOL_NEAREST;
 import static com.android.tools.lint.detector.api.Location.SearchDirection.FORWARD;
@@ -67,32 +67,75 @@
 import com.android.sdklib.SdkVersionInfo;
 import com.android.sdklib.repositoryv2.AndroidSdkHandler;
 import com.android.tools.lint.client.api.IssueRegistry;
-import com.android.tools.lint.client.api.JavaParser.ResolvedMethod;
-import com.android.tools.lint.client.api.JavaParser.ResolvedNode;
-import com.android.tools.lint.client.api.JavaParser.TypeDescriptor;
+import com.android.tools.lint.client.api.JavaEvaluator;
 import com.android.tools.lint.client.api.LintDriver;
 import com.android.tools.lint.detector.api.Category;
 import com.android.tools.lint.detector.api.ClassContext;
 import com.android.tools.lint.detector.api.Context;
 import com.android.tools.lint.detector.api.DefaultPosition;
-import com.android.tools.lint.detector.api.Detector;
+import com.android.tools.lint.detector.api.Detector.ClassScanner;
+import com.android.tools.lint.detector.api.Detector.JavaPsiScanner;
 import com.android.tools.lint.detector.api.Implementation;
 import com.android.tools.lint.detector.api.Issue;
 import com.android.tools.lint.detector.api.JavaContext;
 import com.android.tools.lint.detector.api.LintUtils;
 import com.android.tools.lint.detector.api.Location;
 import com.android.tools.lint.detector.api.Location.SearchHints;
-import com.android.tools.lint.detector.api.Position;
 import com.android.tools.lint.detector.api.ResourceXmlDetector;
 import com.android.tools.lint.detector.api.Scope;
 import com.android.tools.lint.detector.api.Severity;
-import com.android.tools.lint.detector.api.Speed;
 import com.android.tools.lint.detector.api.TextFormat;
 import com.android.tools.lint.detector.api.XmlContext;
-import com.android.utils.Pair;
 import com.google.common.collect.Lists;
-import com.google.common.collect.Maps;
-import com.google.common.collect.Sets;
+import com.intellij.psi.JavaElementVisitor;
+import com.intellij.psi.JavaTokenType;
+import com.intellij.psi.PsiAnnotation;
+import com.intellij.psi.PsiAnnotationMemberValue;
+import com.intellij.psi.PsiAnnotationParameterList;
+import com.intellij.psi.PsiArrayInitializerMemberValue;
+import com.intellij.psi.PsiAssignmentExpression;
+import com.intellij.psi.PsiBinaryExpression;
+import com.intellij.psi.PsiBlockStatement;
+import com.intellij.psi.PsiCallExpression;
+import com.intellij.psi.PsiClass;
+import com.intellij.psi.PsiClassType;
+import com.intellij.psi.PsiCodeBlock;
+import com.intellij.psi.PsiComment;
+import com.intellij.psi.PsiConditionalExpression;
+import com.intellij.psi.PsiDisjunctionType;
+import com.intellij.psi.PsiElement;
+import com.intellij.psi.PsiExpression;
+import com.intellij.psi.PsiExpressionList;
+import com.intellij.psi.PsiField;
+import com.intellij.psi.PsiFile;
+import com.intellij.psi.PsiIfStatement;
+import com.intellij.psi.PsiImportStatementBase;
+import com.intellij.psi.PsiImportStaticStatement;
+import com.intellij.psi.PsiJavaCodeReferenceElement;
+import com.intellij.psi.PsiLiteral;
+import com.intellij.psi.PsiLiteralExpression;
+import com.intellij.psi.PsiLocalVariable;
+import com.intellij.psi.PsiMethod;
+import com.intellij.psi.PsiMethodCallExpression;
+import com.intellij.psi.PsiModifierList;
+import com.intellij.psi.PsiModifierListOwner;
+import com.intellij.psi.PsiNameValuePair;
+import com.intellij.psi.PsiParameter;
+import com.intellij.psi.PsiParameterList;
+import com.intellij.psi.PsiPolyadicExpression;
+import com.intellij.psi.PsiPrimitiveType;
+import com.intellij.psi.PsiReferenceExpression;
+import com.intellij.psi.PsiResourceList;
+import com.intellij.psi.PsiReturnStatement;
+import com.intellij.psi.PsiStatement;
+import com.intellij.psi.PsiSwitchLabelStatement;
+import com.intellij.psi.PsiTryStatement;
+import com.intellij.psi.PsiType;
+import com.intellij.psi.PsiTypeCastExpression;
+import com.intellij.psi.PsiTypeElement;
+import com.intellij.psi.PsiWhiteSpace;
+import com.intellij.psi.tree.IElementType;
+import com.intellij.psi.util.PsiTreeUtil;
 
 import org.objectweb.asm.Opcodes;
 import org.objectweb.asm.Type;
@@ -118,54 +161,19 @@
 import org.w3c.dom.NodeList;
 
 import java.io.File;
-import java.lang.reflect.Field;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.EnumSet;
 import java.util.List;
-import java.util.Map;
-import java.util.Set;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
-import lombok.ast.Annotation;
-import lombok.ast.AnnotationElement;
-import lombok.ast.AnnotationMethodDeclaration;
-import lombok.ast.AnnotationValue;
-import lombok.ast.AstVisitor;
-import lombok.ast.BinaryExpression;
-import lombok.ast.BinaryOperator;
-import lombok.ast.Case;
-import lombok.ast.Cast;
-import lombok.ast.ConstructorDeclaration;
-import lombok.ast.ConstructorInvocation;
-import lombok.ast.Expression;
-import lombok.ast.ForwardingAstVisitor;
-import lombok.ast.If;
-import lombok.ast.ImportDeclaration;
-import lombok.ast.InlineIfExpression;
-import lombok.ast.IntegralLiteral;
-import lombok.ast.MethodDeclaration;
-import lombok.ast.MethodInvocation;
-import lombok.ast.Modifiers;
-import lombok.ast.Select;
-import lombok.ast.StrictListAccessor;
-import lombok.ast.StringLiteral;
-import lombok.ast.SuperConstructorInvocation;
-import lombok.ast.Switch;
-import lombok.ast.Try;
-import lombok.ast.TypeDeclaration;
-import lombok.ast.TypeReference;
-import lombok.ast.VariableDefinition;
-import lombok.ast.VariableDefinitionEntry;
-import lombok.ast.VariableReference;
-
 /**
  * Looks for usages of APIs that are not supported in all the versions targeted
  * by this application (according to its minimum API requirement in the manifest).
  */
 public class ApiDetector extends ResourceXmlDetector
-        implements Detector.ClassScanner, Detector.JavaScanner {
+        implements ClassScanner, JavaPsiScanner {
 
     /**
      * Whether we flag variable, field, parameter and return type declarations of a type
@@ -316,18 +324,11 @@
     protected ApiLookup mApiDatabase;
     private boolean mWarnedMissingDb;
     private int mMinApi = -1;
-    private Map<String, List<Pair<String, Location>>> mPendingFields;
 
     /** Constructs a new API check */
     public ApiDetector() {
     }
 
-    @NonNull
-    @Override
-    public Speed getSpeed() {
-        return Speed.SLOW;
-    }
-
     @Override
     public void beforeCheckProject(@NonNull Context context) {
         if (mApiDatabase == null) {
@@ -856,7 +857,8 @@
             }
 
             List tryCatchBlocks = method.tryCatchBlocks;
-            if (!tryCatchBlocks.isEmpty()) {
+            // single-catch blocks are already handled by an AST level check in ApiVisitor
+            if (tryCatchBlocks.size() > 1) {
                 List<String> checked = Lists.newArrayList();
                 for (Object o : tryCatchBlocks) {
                     TryCatchBlockNode tryCatchBlock = (TryCatchBlockNode) o;
@@ -1084,9 +1086,6 @@
                         }
 
                         String fqcn = getFqcn(owner) + '#' + name;
-                        if (mPendingFields != null) {
-                            mPendingFields.remove(fqcn);
-                        }
                         String message = String.format(
                                 "Field requires API level %1$d (current min is %2$d): `%3$s`",
                                 api, minSdk, fqcn);
@@ -1545,48 +1544,33 @@
         context.report(UNSUPPORTED, method, node, location, message);
     }
 
-    @Override
-    public void afterCheckProject(@NonNull Context context) {
-        if (mPendingFields != null) {
-            for (List<Pair<String, Location>> list : mPendingFields.values()) {
-                for (Pair<String, Location> pair : list) {
-                    String message = pair.getFirst();
-                    Location location = pair.getSecond();
-                    context.report(INLINED, location, message);
-                }
-            }
-        }
-
-        super.afterCheckProject(context);
-    }
-
     // ---- Implements JavaScanner ----
 
     @Nullable
     @Override
-    public AstVisitor createJavaVisitor(@NonNull JavaContext context) {
+    public JavaElementVisitor createPsiVisitor(@NonNull JavaContext context) {
         if (mApiDatabase == null) {
-            return new ForwardingAstVisitor() {
+            return new JavaElementVisitor() {
+                @Override
+                public void visitElement(PsiElement element) {
+                    // No-op. Workaround for super currently calling
+                    //   ProgressIndicatorProvider.checkCanceled();
+                }
             };
         }
         return new ApiVisitor(context);
     }
 
-    @Nullable
     @Override
-    public List<Class<? extends lombok.ast.Node>> getApplicableNodeTypes() {
-        List<Class<? extends lombok.ast.Node>> types =
-                new ArrayList<Class<? extends lombok.ast.Node>>(10);
-        types.add(ImportDeclaration.class);
-        types.add(Select.class);
-        types.add(MethodDeclaration.class);
-        types.add(ConstructorDeclaration.class);
-        types.add(VariableDefinitionEntry.class);
-        types.add(VariableReference.class);
-        types.add(Try.class);
-        types.add(Cast.class);
-        types.add(BinaryExpression.class);
-        types.add(MethodInvocation.class);
+    public List<Class<? extends PsiElement>> getApplicablePsiTypes() {
+        List<Class<? extends PsiElement>> types = new ArrayList<Class<? extends PsiElement>>(7);
+        types.add(PsiImportStaticStatement.class);
+        types.add(PsiReferenceExpression.class);
+        types.add(PsiLocalVariable.class);
+        types.add(PsiTryStatement.class);
+        types.add(PsiTypeCastExpression.class);
+        types.add(PsiAssignmentExpression.class);
+        types.add(PsiCallExpression.class);
         return types;
     }
 
@@ -1601,7 +1585,7 @@
      *              level of the constant
      */
     public static boolean isBenignConstantUsage(
-            @Nullable lombok.ast.Node node,
+            @Nullable PsiElement node,
             @NonNull String name,
             @NonNull String owner) {
         if (owner.equals("android/os/Build$VERSION_CODES")) {     //$NON-NLS-1$
@@ -1639,21 +1623,19 @@
 
         // It's okay to reference the constant as a case constant (since that
         // code path won't be taken) or in a condition of an if statement
-        lombok.ast.Node curr = node.getParent();
+        PsiElement curr = node.getParent();
         while (curr != null) {
-            Class<? extends lombok.ast.Node> nodeType = curr.getClass();
-            if (nodeType == Case.class) {
-                Case caseStatement = (Case) curr;
-                Expression condition = caseStatement.astCondition();
-                return condition != null && isAncestor(condition, node);
-            } else if (nodeType == If.class) {
-                If ifStatement = (If) curr;
-                Expression condition = ifStatement.astCondition();
-                return condition != null && isAncestor(condition, node);
-            } else if (nodeType == InlineIfExpression.class) {
-                InlineIfExpression ifStatement = (InlineIfExpression) curr;
-                Expression condition = ifStatement.astCondition();
-                return condition != null && isAncestor(condition, node);
+            if (curr instanceof PsiSwitchLabelStatement) {
+                PsiExpression condition = ((PsiSwitchLabelStatement) curr).getCaseValue();
+                return condition != null && PsiTreeUtil.isAncestor(condition, node, false);
+            } else if (curr instanceof PsiIfStatement) {
+                PsiExpression condition = ((PsiIfStatement) curr).getCondition();
+                return condition != null && PsiTreeUtil.isAncestor(condition, node, false);
+            } else if (curr instanceof PsiConditionalExpression) {
+                PsiExpression condition = ((PsiConditionalExpression) curr).getCondition();
+                return PsiTreeUtil.isAncestor(condition, node, false);
+            } else if (curr instanceof PsiMethod || curr instanceof PsiClass) {
+                break;
             }
             curr = curr.getParent();
         }
@@ -1661,446 +1643,291 @@
         return false;
     }
 
-    private static boolean isAncestor(
-            @NonNull lombok.ast.Node ancestor,
-            @Nullable lombok.ast.Node node) {
-        while (node != null) {
-            if (node == ancestor) {
-                return true;
-            }
-            node = node.getParent();
-        }
-
-        return false;
-    }
-
-    private final class ApiVisitor extends ForwardingAstVisitor {
-        private JavaContext mContext;
-        private Map<String, String> mClassToImport = Maps.newHashMap();
-        private List<String> mStarImports;
-        private Set<String> mLocalVars;
-        private lombok.ast.Node mCurrentMethod;
-        private Set<String> mFields;
-        private List<String> mStaticStarImports;
+    private final class ApiVisitor extends JavaElementVisitor {
+        private final JavaContext mContext;
 
         private ApiVisitor(JavaContext context) {
             mContext = context;
         }
 
         @Override
-        public boolean visitImportDeclaration(ImportDeclaration node) {
-            if (node.astStarImport()) {
-                // Similarly, if you're inheriting from a constants class, figure out
-                // how that works... :=(
-                String fqcn = node.asFullyQualifiedName();
-                int strip = fqcn.lastIndexOf('*');
-                if (strip != -1) {
-                    strip = fqcn.lastIndexOf('.', strip);
-                    if (strip != -1) {
-                        String pkgName = getInternalName(fqcn.substring(0, strip));
-                        if (ApiLookup.isRelevantOwner(pkgName)) {
-                            if (node.astStaticImport()) {
-                                if (mStaticStarImports == null) {
-                                    mStaticStarImports = Lists.newArrayList();
-                                }
-                                mStaticStarImports.add(pkgName);
-                            } else {
-                                if (mStarImports == null) {
-                                    mStarImports = Lists.newArrayList();
-                                }
-                                mStarImports.add(pkgName);
-                            }
-                        }
-                    }
-                }
-            } else if (node.astStaticImport()) {
-                String fqcn = node.asFullyQualifiedName();
-                String fieldName = getInternalName(fqcn);
-                int index = fieldName.lastIndexOf('$');
-                if (index != -1) {
-                    String owner = fieldName.substring(0, index);
-                    String name = fieldName.substring(index + 1);
-                    checkField(node, name, owner);
-                }
-            } else {
-                // Store in map -- if it's "one of ours"
-                // Use override detector's map for that purpose
-                String fqcn = node.asFullyQualifiedName();
-
-                int last = fqcn.lastIndexOf('.');
-                if (last != -1) {
-                    String className = fqcn.substring(last + 1);
-                    mClassToImport.put(className, fqcn);
+        public void visitImportStaticStatement(PsiImportStaticStatement statement) {
+            if (!statement.isOnDemand()) {
+                PsiElement resolved = statement.resolve();
+                if (resolved instanceof PsiField) {
+                    checkField(statement, (PsiField)resolved);
                 }
             }
-
-            return super.visitImportDeclaration(node);
         }
 
         @Override
-        public boolean visitSelect(Select node) {
-            boolean result = super.visitSelect(node);
-
-            if (node.getParent() instanceof Select) {
+        public void visitReferenceExpression(PsiReferenceExpression expression) {
+            if (skipParentheses(expression.getParent()) instanceof PsiReferenceExpression) {
                 // We only want to look at the leaf expressions; e.g. if you have
                 // "foo.bar.baz" we only care about the select foo.bar.baz, not foo.bar
-                return result;
+                return;
             }
 
-            // See if this corresponds to a field reference. We assume it's a field if
-            // it's a select (x.y) and either the identifier y is capitalized (e.g.
-            // foo.VIEW_MASK) or if it's a member of an R class (R.id.foo).
-            String name = node.astIdentifier().astValue();
-            boolean isField = Character.isUpperCase(name.charAt(0));
-            if (!isField) {
-                // See if there's an R class
-                Select current = node;
-                //noinspection ConstantConditions
-                while (current != null) {
-                    Expression operand = current.astOperand();
-                    if (operand instanceof Select) {
-                        current = (Select) operand;
-                        if (R_CLASS.equals(current.astIdentifier().astValue())) {
-                            isField = true;
-                            break;
-                        }
-                    } else if (operand instanceof VariableReference) {
-                        VariableReference reference = (VariableReference) operand;
-                        if (R_CLASS.equals(reference.astIdentifier().astValue())) {
-                            isField = true;
-                        }
-                        break;
-                    } else {
-                        break;
-                    }
-                }
+            PsiElement resolved = expression.resolve();
+            if (resolved instanceof PsiField) {
+                checkField(expression, (PsiField)resolved);
             }
-
-            if (isField) {
-                Expression operand = node.astOperand();
-                if (operand.getClass() == Select.class) {
-                    // Possibly a fully qualified name in place
-                    String cls = operand.toString();
-
-                    // See if it's an imported class with an inner class
-                    // (e.g. Manifest.permission.FIELD)
-                    if (Character.isUpperCase(cls.charAt(0))) {
-                        int firstDot = cls.indexOf('.');
-                        if (firstDot != -1) {
-                            String base = cls.substring(0, firstDot);
-                            String fqcn = mClassToImport.get(base);
-                            if (fqcn != null) {
-                                // Yes imported
-                                String owner = getInternalName(fqcn + cls.substring(firstDot));
-                                checkField(node, name, owner);
-                                return result;
-                            }
-
-                            // Might be a star import: have to iterate and check here
-                            if (mStarImports != null) {
-                                for (String packagePrefix : mStarImports) {
-                                    String owner = getInternalName(packagePrefix + '/' + cls);
-                                    if (checkField(node, name, owner)) {
-                                        mClassToImport.put(name, owner);
-                                        return result;
-                                    }
-                                }
-                            }
-                        }
-                    }
-
-                    // See if it's a fully qualified reference in place
-                    String owner = getInternalName(cls);
-                    checkField(node, name, owner);
-                    return result;
-                } else if (operand.getClass() == VariableReference.class) {
-                    String className = ((VariableReference) operand).astIdentifier().astValue();
-                    // Not a FQCN that we care about: look in imports
-                    String fqcn = mClassToImport.get(className);
-                    if (fqcn != null) {
-                        // Yes imported
-                        String owner = getInternalName(fqcn);
-                        checkField(node, name, owner);
-                        return result;
-                    }
-
-                    if (Character.isUpperCase(className.charAt(0))) {
-                        // Might be a star import: have to iterate and check here
-                        if (mStarImports != null) {
-                            for (String packagePrefix : mStarImports) {
-                                String owner = getInternalName(packagePrefix) + '/' + className;
-                                if (checkField(node, name, owner)) {
-                                    mClassToImport.put(name, owner);
-                                    return result;
-                                }
-                            }
-                        }
-                    }
-                }
-            }
-            return result;
         }
 
         @Override
-        public boolean visitVariableReference(VariableReference node) {
-            boolean result = super.visitVariableReference(node);
+        public void visitTypeCastExpression(PsiTypeCastExpression expression) {
+            PsiTypeElement castTypeElement = expression.getCastType();
+            PsiExpression operand = expression.getOperand();
+            if (operand == null || castTypeElement == null) {
+                return;
+            }
+            PsiType operandType = operand.getType();
+            PsiType castType = castTypeElement.getType();
+            if (castType.equals(operandType)) {
+                return;
+            }
+            if (!(operandType instanceof PsiClassType)) {
+                return;
+            }
+            if (!(castType instanceof PsiClassType)) {
+                return;
+            }
+            PsiClassType classType = (PsiClassType)operandType;
+            PsiClassType interfaceType = (PsiClassType)castType;
+            checkCast(expression, classType, interfaceType);
+        }
 
-            if (node.getParent() != null) {
-                lombok.ast.Node parent = node.getParent();
-                Class<? extends lombok.ast.Node> parentClass = parent.getClass();
-                if (parentClass == Select.class
-                        || parentClass == Switch.class // look up on the switch expression type
-                        || parentClass == Case.class
-                        || parentClass == ConstructorInvocation.class
-                        || parentClass == SuperConstructorInvocation.class
-                        || parentClass == AnnotationElement.class) {
-                    return result;
-                }
-
-                if (parent instanceof MethodInvocation &&
-                        ((MethodInvocation) parent).astOperand() == node) {
-                    return result;
-                } else if (parent instanceof BinaryExpression) {
-                    BinaryExpression expression = (BinaryExpression) parent;
-                    if (expression.astLeft() == node) {
-                        return result;
-                    }
-                }
+        private void checkCast(@NonNull PsiElement node, @NonNull PsiClassType classType, @NonNull PsiClassType interfaceType) {
+            if (classType.equals(interfaceType)) {
+                return;
+            }
+            JavaEvaluator evaluator = mContext.getEvaluator();
+            String classTypeInternal = evaluator.getInternalName(classType);
+            String interfaceTypeInternal = evaluator.getInternalName(interfaceType);
+            if ("java/lang/Object".equals(interfaceTypeInternal)) {
+                return;
             }
 
-            String name = node.astIdentifier().astValue();
-            if (Character.isUpperCase(name.charAt(0))
-                    && (mLocalVars == null || !mLocalVars.contains(name))
-                    && (mFields == null || !mFields.contains(name))) {
-                // Potential field reference: check it
-                if (mStaticStarImports != null) {
-                    for (String owner : mStaticStarImports) {
-                        if (checkField(node, name, owner)) {
-                            break;
+            int api = mApiDatabase.getValidCastVersion(classTypeInternal, interfaceTypeInternal);
+            if (api == -1) {
+                return;
+            }
+
+            int minSdk = getMinSdk(mContext);
+            if (api <= minSdk) {
+                return;
+            }
+
+            if (isSuppressed(api, node, minSdk)) {
+                return;
+            }
+
+            Location location = mContext.getLocation(node);
+            String message = String.format("Cast from %1$s to %2$s requires API level %3$d (current min is %4$d)",
+                    classType.getClassName(),
+                    interfaceType.getClassName(), api, minSdk);
+            mContext.report(UNSUPPORTED, location, message);
+        }
+
+        @Override
+        public void visitCallExpression(PsiCallExpression expression) {
+            PsiMethod method = expression.resolveMethod();
+            if (method != null) {
+                PsiParameterList parameterList = method.getParameterList();
+                if (parameterList.getParametersCount() > 0) {
+                    PsiParameter[] parameters = parameterList.getParameters();
+                    PsiExpressionList argumentList = expression.getArgumentList();
+                    if (argumentList != null) {
+                        PsiExpression[] arguments = argumentList.getExpressions();
+                        for (int i = 0; i < parameters.length; i++) {
+                            PsiType parameterType = parameters[i].getType();
+                            if (parameterType instanceof PsiClassType) {
+                                if (i >= arguments.length) {
+                                    // We can end up with more arguments than parameters when
+                                    // there is a varargs call.
+                                    break;
+                                }
+                                PsiExpression argument = arguments[i];
+                                PsiType argumentType = argument.getType();
+                                if (argumentType == null || parameterType.equals(argumentType)
+                                        || !(argumentType instanceof PsiClassType)) {
+                                    continue;
+                                }
+                                checkCast(argument, (PsiClassType) argumentType,
+                                        (PsiClassType) parameterType);
+                            }
                         }
                     }
                 }
             }
-
-            return result;
         }
 
         @Override
-        public boolean visitVariableDefinitionEntry(VariableDefinitionEntry node) {
+        public void visitLocalVariable(PsiLocalVariable variable) {
+            PsiExpression initializer = variable.getInitializer();
+            if (initializer == null) {
+                return;
+            }
+
+            PsiType initializerType = initializer.getType();
+            if (!(initializerType instanceof PsiClassType)) {
+                return;
+            }
+
+            PsiType interfaceType = variable.getType();
+            if (initializerType.equals(interfaceType)) {
+                return;
+            }
+
+            if (!(interfaceType instanceof PsiClassType)) {
+                return;
+            }
+
+            checkCast(initializer, (PsiClassType)initializerType, (PsiClassType)interfaceType);
+        }
+
+        @Override
+        public void visitAssignmentExpression(PsiAssignmentExpression expression) {
+            PsiExpression rExpression = expression.getRExpression();
+            if (rExpression == null) {
+                return;
+            }
+
+            PsiType rhsType = rExpression.getType();
+            if (!(rhsType instanceof PsiClassType)) {
+                return;
+            }
+
+            PsiType interfaceType = expression.getLExpression().getType();
+            if (rhsType.equals(interfaceType)) {
+                return;
+            }
+
+            if (!(interfaceType instanceof PsiClassType)) {
+                return;
+            }
+
+            checkCast(rExpression, (PsiClassType)rhsType, (PsiClassType)interfaceType);
+        }
+
+        @Override
+        public void visitTryStatement(PsiTryStatement statement) {
+            PsiResourceList resourceList = statement.getResourceList();
             //noinspection VariableNotUsedInsideIf
-            if (mCurrentMethod != null) {
-                if (mLocalVars == null) {
-                    mLocalVars = Sets.newHashSet();
+            if (resourceList != null) {
+                int api = 19; // minSdk for try with resources
+                int minSdk = getMinSdk(mContext);
+
+                if (api > minSdk && api > getTargetApi(statement)) {
+                    Location location = mContext.getLocation(statement);
+                    String message = String.format("Try-with-resources requires "
+                            + "API level %1$d (current min is %2$d)", api, minSdk);
+                    LintDriver driver = mContext.getDriver();
+                    if (!driver.isSuppressed(mContext, UNSUPPORTED, statement)) {
+                        mContext.report(UNSUPPORTED, statement, location, message);
+                    }
                 }
-                mLocalVars.add(node.astName().astValue());
-            } else {
-                if (mFields == null) {
-                    mFields = Sets.newHashSet();
-                }
-                mFields.add(node.astName().astValue());
             }
 
-            Expression initializer = node.astInitializer();
-            if (initializer != null
-                    // Checking cast expressions is handled already; prevent duplicate errors
-                    && !(initializer instanceof Cast)) {
-                TypeDescriptor classType = mContext.getType(initializer);
-                if (classType != null && !classType.isPrimitive()) {
-                    String classOwner = classType.getInternalName();
-                    if (mApiDatabase.isKnownClass(classOwner)) {
-                        TypeDescriptor interfaceType = mContext.getType(node);
-                        if (interfaceType != null) {
-                            checkCast(initializer, classOwner, classType, interfaceType);
+            for (PsiParameter parameter : statement.getCatchBlockParameters()) {
+                PsiTypeElement typeElement = parameter.getTypeElement();
+                if (typeElement != null) {
+                    checkCatchTypeElement(statement, typeElement, typeElement.getType());
+                }
+            }
+        }
+
+        private void checkCatchTypeElement(@NonNull PsiTryStatement statement,
+                @NonNull PsiTypeElement typeElement,
+                @Nullable PsiType type) {
+            PsiClass resolved = null;
+            if (type instanceof PsiDisjunctionType) {
+                PsiDisjunctionType disjunctionType = (PsiDisjunctionType)type;
+                type = disjunctionType.getLeastUpperBound();
+                if (type instanceof PsiClassType) {
+                    resolved = ((PsiClassType)type).resolve();
+                }
+                for (PsiElement child : typeElement.getChildren()) {
+                    if (child instanceof PsiTypeElement) {
+                        PsiTypeElement childTypeElement = (PsiTypeElement)child;
+                        PsiType childType = childTypeElement.getType();
+                        if (!type.equals(childType)) {
+                            checkCatchTypeElement(statement, childTypeElement, childType);
                         }
                     }
                 }
+            } else if (type instanceof PsiClassType) {
+                resolved = ((PsiClassType)type).resolve();
             }
-
-            return super.visitVariableDefinitionEntry(node);
-        }
-
-        @Override
-        public boolean visitMethodDeclaration(MethodDeclaration node) {
-            mLocalVars = null;
-            mCurrentMethod = node;
-            return super.visitMethodDeclaration(node);
-        }
-
-        @Override
-        public boolean visitConstructorDeclaration(ConstructorDeclaration node) {
-            mLocalVars = null;
-            mCurrentMethod = node;
-            return super.visitConstructorDeclaration(node);
-        }
-
-        @Override
-        public boolean visitCast(Cast node) {
-            TypeDescriptor classType = mContext.getType(node.astOperand());
-            if (classType != null && !classType.isPrimitive()) {
-                TypeDescriptor interfaceType = mContext.getType(node);
-                checkCast(node, classType.getInternalName(), classType, interfaceType);
-            }
-
-            return super.visitCast(node);
-        }
-
-        @Override
-        public boolean visitBinaryExpression(BinaryExpression node) {
-            if (node.astOperator() == BinaryOperator.ASSIGN &&
-                    // Checking cast expressions is handled already; prevent duplicate errors
-                    !(node.astRight() instanceof Cast)) {
-                TypeDescriptor classType = mContext.getType(node.astRight());
-                if (classType != null && !classType.isPrimitive()) {
-                    String classOwner = classType.getInternalName();
-                    if (mApiDatabase.isKnownClass(classOwner)) {
-                        TypeDescriptor interfaceType = mContext.getType(node.astLeft());
-                        if (interfaceType != null && !interfaceType.isPrimitive()) {
-                            checkCast(node, classOwner, classType, interfaceType);
-                        }
+            if (resolved != null) {
+                String signature = mContext.getEvaluator().getInternalName(resolved);
+                int api = mApiDatabase.getClassVersion(signature);
+                if (api == -1) {
+                    return;
+                }
+                int minSdk = getMinSdk(mContext);
+                if (api <= minSdk) {
+                    return;
+                }
+                int target = getTargetApi(statement);
+                if (target != -1) {
+                    if (api <= target) {
+                        return;
                     }
                 }
-            }
-            return super.visitBinaryExpression(node);
-        }
 
-        @Override
-        public boolean visitMethodInvocation(MethodInvocation node) {
-            ResolvedMethod method = null;
-            int parameterIndex = 0;
-            for (Expression argument : node.astArguments()) {
-                TypeDescriptor argumentType = mContext.getType(argument);
-                if (argumentType != null && !argumentType.isPrimitive()) {
-                    if (method == null) {
-                        ResolvedNode resolved = mContext.resolve(node);
-                        if (resolved instanceof ResolvedMethod) {
-                            method = (ResolvedMethod) resolved;
-                        } else {
-                            break;
-                        }
-                    }
-                    if (parameterIndex >= method.getArgumentCount()) {
-                        // We can end up with more arguments than parameters when
-                        // there is a varargs call.
-                        break;
-                    }
-                    TypeDescriptor parameterType = method.getArgumentType(parameterIndex);
-                    String argumentOwner = argumentType.getInternalName();
-                    if (mApiDatabase.isKnownClass(argumentOwner)) {
-                        checkCast(argument, argumentOwner, argumentType,
-                                parameterType);
-                    }
+                Location location;
+                location = mContext.getLocation(typeElement);
+                String fqcn = resolved.getName();
+                String message = String.format("Class requires API level %1$d (current min is %2$d): %3$s", api, minSdk, fqcn);
+
+                // Special case reflective operation exception which can be implicitly used
+                // with multi-catches: see issue 153406
+                if (api == 19 && "ReflectiveOperationException".equals(fqcn)) {
+                    message = String.format("Multi-catch with these reflection exceptions requires API level 19 (current min is %2$d) " +
+                                    "because they get compiled to the common but new super type `ReflectiveOperationException`. " +
+                                    "As a workaround either create individual catch statements, or catch `Exception`.",
+                            api, minSdk);
                 }
-                parameterIndex++;
+                mContext.report(UNSUPPORTED, location, message);
             }
-
-            return super.visitMethodInvocation(node);
-        }
-
-        private void checkCast(
-                @NonNull lombok.ast.Node node,
-                @NonNull String classOwner,
-                @NonNull TypeDescriptor classType,
-                @Nullable TypeDescriptor interfaceType) {
-            if (interfaceType != null && !interfaceType.equals(classType)) {
-                String interfaceInternalName = interfaceType.getInternalName();
-                int api = mApiDatabase.getValidCastVersion(classOwner, interfaceInternalName);
-                if (api != -1 && !"java/lang/Object".equals(interfaceInternalName)) {
-                    int minSdk = getMinSdk(mContext);
-                    if (api > minSdk && api > getLocalMinSdk(node)) {
-                        LintDriver driver = mContext.getDriver();
-                        if (!driver.isSuppressed(mContext, UNSUPPORTED, node)) {
-                            Location location = mContext.getLocation(node);
-                            String message = String.format(
-                                    "Cast from %1$s to %2$s requires API level %3$d (current min is %4$d)",
-                                    classType.getSimpleName(), interfaceType.getSimpleName(),
-                                    api, minSdk);
-                            mContext.report(UNSUPPORTED, node, location, message);
-                        }
-                    }
-                }
-            }
-        }
-
-        @Override
-        public boolean visitTry(Try node) {
-            Object nativeNode = node.getNativeNode();
-            if (nativeNode != null && nativeNode.getClass().getName().equals(
-                    "org.eclipse.jdt.internal.compiler.ast.TryStatement")) {
-                boolean isTryWithResources = false;
-                try {
-                    Field field = nativeNode.getClass().getDeclaredField("resources");
-                    Object value = field.get(nativeNode);
-                    if (value instanceof Object[]) {
-                        Object[] resources = (Object[]) value;
-                        isTryWithResources = resources.length > 0;
-                    }
-                } catch (NoSuchFieldException e) {
-                    // Unexpected: ECJ parser internals have changed; can't detect try block
-                } catch (IllegalAccessException e) {
-                    // Unexpected: ECJ parser internals have changed; can't detect try block
-                }
-                if (isTryWithResources) {
-                    int minSdk = getMinSdk(mContext);
-                    int api = 19;  // minSdk for try with resources
-                    if (api > minSdk && api > getLocalMinSdk(node)) {
-                        Location location = mContext.getLocation(node);
-                        String message = String.format("Try-with-resources requires "
-                                + "API level %1$d (current min is %2$d)", api, minSdk);
-                        LintDriver driver = mContext.getDriver();
-                        if (!driver.isSuppressed(mContext, UNSUPPORTED, node)) {
-                            mContext.report(UNSUPPORTED, node, location, message);
-                        }
-                    }
-                }
-            }
-
-            return super.visitTry(node);
-        }
-
-        @Override
-        public void endVisit(lombok.ast.Node node) {
-            if (node == mCurrentMethod) {
-                mCurrentMethod = null;
-            }
-            super.endVisit(node);
         }
 
         /**
          * Checks a Java source field reference. Returns true if the field is known
          * regardless of whether it's an invalid field or not
          */
-        private boolean checkField(
-                @NonNull lombok.ast.Node node,
-                @NonNull String name,
-                @NonNull String owner) {
+        private boolean checkField(@NonNull PsiElement node, @NonNull PsiField field) {
+            PsiType type = field.getType();
+            // Only look for compile time constants. See JLS 15.28 and JLS 13.4.9.
+            if (!(type instanceof PsiPrimitiveType) && !LintUtils.isString(type)) {
+                return false;
+            }
+            String name = field.getName();
+            PsiClass containingClass = field.getContainingClass();
+            if (containingClass == null || name == null) {
+                return false;
+            }
+            String owner = mContext.getEvaluator().getInternalName(containingClass);
             int api = mApiDatabase.getFieldVersion(owner, name);
             if (api != -1) {
                 int minSdk = getMinSdk(mContext);
                 if (api > minSdk
-                        && api > getLocalMinSdk(node)) {
+                        && api > getTargetApi(node)) {
                     if (isBenignConstantUsage(node, name, owner)) {
                         return true;
                     }
 
-                    Location location = mContext.getLocation(node);
                     String fqcn = getFqcn(owner) + '#' + name;
 
-                    if (node instanceof ImportDeclaration) {
-                        // Replace import statement location range with just
-                        // the identifier part
-                        ImportDeclaration d = (ImportDeclaration) node;
-                        int startOffset = d.astParts().first().getPosition().getStart();
-                        Position start = location.getStart();
-                        assert start != null : location;
-                        int startColumn = start.getColumn();
-                        int startLine = start.getLine();
-                        start = new DefaultPosition(startLine,
-                                startColumn + startOffset - start.getOffset(), startOffset);
-                        int fqcnLength = fqcn.length();
-                        Position end = new DefaultPosition(startLine,
-                                start.getColumn() + fqcnLength,
-                                start.getOffset() + fqcnLength);
-                        location = Location.create(location.getFile(), start, end);
+                    // For import statements, place the underlines only under the
+                    // reference, not the import and static keywords
+                    if (node instanceof PsiImportStatementBase) {
+                        PsiJavaCodeReferenceElement reference
+                                = ((PsiImportStatementBase) node).getImportReference();
+                        if (reference != null) {
+                            node = reference;
+                        }
                     }
 
                     String message = String.format(
@@ -2108,9 +1935,9 @@
                             api, minSdk, fqcn);
 
                     LintDriver driver = mContext.getDriver();
-                    if (driver.isSuppressed(mContext, INLINED, node)) {
-                        return true;
-                    }
+                    //if (driver.isSuppressed(mContext, INLINED, node)) {
+                    //    return true;
+                    //}
 
                     // Also allow to suppress these issues with NewApi, since some
                     // fields used to get identified that way
@@ -2118,38 +1945,8 @@
                         return true;
                     }
 
-                    // We can't report the issue right away; we don't yet know if
-                    // this is an actual inlined (static primitive or String) yet.
-                    // So just make a note of it, and report these after the project
-                    // checking has finished; any fields that aren't inlined will be
-                    // cleared when they're noticed by the class check.
-                    if (mPendingFields == null) {
-                        mPendingFields = Maps.newHashMapWithExpectedSize(20);
-                    }
-                    List<Pair<String, Location>> list = mPendingFields.get(fqcn);
-                    if (list == null) {
-                        list = new ArrayList<Pair<String, Location>>();
-                        mPendingFields.put(fqcn, list);
-                    } else {
-                        // See if this location already exists. This can happen if
-                        // we have multiple references to an inlined field on the same
-                        // line. Since the class file only gives us line information, we
-                        // can't distinguish between these in the client as separate usages,
-                        // so they end up being identical errors.
-                        for (Pair<String, Location> pair : list) {
-                            Location existingLocation = pair.getSecond();
-                            //noinspection FileEqualsUsage
-                            if (location.getFile().equals(existingLocation.getFile())) {
-                                Position start = location.getStart();
-                                Position existingStart = existingLocation.getStart();
-                                if (start != null && existingStart != null
-                                        && start.getLine() == existingStart.getLine()) {
-                                    return true;
-                                }
-                            }
-                        }
-                    }
-                    list.add(Pair.of(message, location));
+                    Location location = mContext.getLocation(node);
+                    mContext.report(INLINED, node, location, message);
                 }
 
                 return true;
@@ -2157,106 +1954,101 @@
 
             return false;
         }
+    }
 
-        /**
-         * Returns the minimum SDK to use according to the given AST node, or null
-         * if no {@code TargetApi} annotations were found
-         *
-         * @return the API level to use for this node, or -1
-         */
-        public int getLocalMinSdk(@Nullable lombok.ast.Node scope) {
-            while (scope != null) {
-                Class<? extends lombok.ast.Node> type = scope.getClass();
-                // The Lombok AST uses a flat hierarchy of node type implementation classes
-                // so no need to do instanceof stuff here.
-                if (type == VariableDefinition.class) {
-                    // Variable
-                    VariableDefinition declaration = (VariableDefinition) scope;
-                    int targetApi = getTargetApi(declaration.astModifiers());
-                    if (targetApi != -1) {
-                        return targetApi;
-                    }
-                } else if (type == MethodDeclaration.class) {
-                    // Method
-                    // Look for annotations on the method
-                    MethodDeclaration declaration = (MethodDeclaration) scope;
-                    int targetApi = getTargetApi(declaration.astModifiers());
-                    if (targetApi != -1) {
-                        return targetApi;
-                    }
-                } else if (type == ConstructorDeclaration.class) {
-                    // Constructor
-                    // Look for annotations on the method
-                    ConstructorDeclaration declaration = (ConstructorDeclaration) scope;
-                    int targetApi = getTargetApi(declaration.astModifiers());
-                    if (targetApi != -1) {
-                        return targetApi;
-                    }
-                } else if (TypeDeclaration.class.isAssignableFrom(type)) {
-                    // Class, annotation, enum, interface
-                    TypeDeclaration declaration = (TypeDeclaration) scope;
-                    int targetApi = getTargetApi(declaration.astModifiers());
-                    if (targetApi != -1) {
-                        return targetApi;
-                    }
-                } else if (type == AnnotationMethodDeclaration.class) {
-                    // Look for annotations on the method
-                    AnnotationMethodDeclaration declaration = (AnnotationMethodDeclaration) scope;
-                    int targetApi = getTargetApi(declaration.astModifiers());
-                    if (targetApi != -1) {
-                        return targetApi;
-                    }
-                }
-
-                scope = scope.getParent();
-            }
-
-            return -1;
+    private static boolean isSuppressed(int api, PsiElement element, int minSdk) {
+        if (api <= minSdk) {
+            return true;
         }
+        //if (mySeenTargetApi) {
+            int target = getTargetApi(element);
+            if (target != -1) {
+                if (api <= target) {
+                    return true;
+                }
+            }
+        //}
+// TODO: This MUST BE RESTORED
+//        if (context.getDriver().isSuppressed(UNSUPPORTED, element))
+//        if (/*mySeenSuppress &&*/
+//                (IntellijLintUtils.isSuppressed(element, myFile, UNSUPPORTED) || IntellijLintUtils.isSuppressed(element, myFile, INLINED))) {
+//            return true;
+//        }
+
+        if (isWithinVersionCheckConditional(element, api)) {
+            return true;
+        }
+        if (isPrecededByVersionCheckExit(element, api)) {
+            return true;
+        }
+
+        return false;
+    }
+
+    public static int getTargetApi(@Nullable PsiElement scope) {
+        while (scope != null) {
+            if (scope instanceof PsiModifierListOwner) {
+                PsiModifierList modifierList = ((PsiModifierListOwner) scope).getModifierList();
+                int targetApi = getTargetApi(modifierList);
+                if (targetApi != -1) {
+                    return targetApi;
+                }
+            }
+            scope = scope.getParent();
+            if (scope instanceof PsiFile) {
+                break;
+            }
+        }
+
+        return -1;
     }
 
     /**
      * Returns the API level for the given AST node if specified with
      * an {@code @TargetApi} annotation.
      *
-     * @param modifiers the modifier to check
+     * @param modifierList the modifier list to check
      * @return the target API level, or -1 if not specified
      */
-    public static int getTargetApi(@Nullable Modifiers modifiers) {
-        if (modifiers == null) {
-            return -1;
-        }
-        StrictListAccessor<Annotation, Modifiers> annotations = modifiers.astAnnotations();
-        if (annotations == null) {
+    public static int getTargetApi(@Nullable PsiModifierList modifierList) {
+        if (modifierList == null) {
             return -1;
         }
 
-        for (Annotation annotation : annotations) {
-            TypeReference t = annotation.astAnnotationTypeReference();
-            String typeName = t.getTypeName();
-            if (typeName.endsWith(TARGET_API)) {
-                StrictListAccessor<AnnotationElement, Annotation> values =
-                        annotation.astElements();
-                if (values != null) {
-                    for (AnnotationElement element : values) {
-                        AnnotationValue valueNode = element.astValue();
-                        if (valueNode == null) {
-                            continue;
+        for (PsiAnnotation annotation : modifierList.getAnnotations()) {
+            String fqcn = annotation.getQualifiedName();
+            if (fqcn != null && (fqcn.equals(FQCN_TARGET_API)
+                    || fqcn.equals(TARGET_API))) { // when missing imports
+                PsiAnnotationParameterList parameterList = annotation.getParameterList();
+                for (PsiNameValuePair pair : parameterList.getAttributes()) {
+                    PsiAnnotationMemberValue v = pair.getValue();
+                    if (v instanceof PsiLiteral) {
+                        PsiLiteral literal = (PsiLiteral)v;
+                        Object value = literal.getValue();
+                        if (value instanceof Integer) {
+                            return (Integer) value;
+                        } else if (value instanceof String) {
+                            return codeNameToApi((String) value);
                         }
-                        if (valueNode instanceof IntegralLiteral) {
-                            IntegralLiteral literal = (IntegralLiteral) valueNode;
-                            return literal.astIntValue();
-                        } else if (valueNode instanceof StringLiteral) {
-                            String value = ((StringLiteral) valueNode).astValue();
-                            return SdkVersionInfo.getApiByBuildCode(value, true);
-                        } else if (valueNode instanceof Select) {
-                            Select select = (Select) valueNode;
-                            String codename = select.astIdentifier().astValue();
-                            return SdkVersionInfo.getApiByBuildCode(codename, true);
-                        } else if (valueNode instanceof VariableReference) {
-                            VariableReference reference = (VariableReference) valueNode;
-                            String codename = reference.astIdentifier().astValue();
-                            return SdkVersionInfo.getApiByBuildCode(codename, true);
+                    } else if (v instanceof PsiArrayInitializerMemberValue) {
+                        PsiArrayInitializerMemberValue mv = (PsiArrayInitializerMemberValue)v;
+                        for (PsiAnnotationMemberValue mmv : mv.getInitializers()) {
+                            if (mmv instanceof PsiLiteral) {
+                                PsiLiteral literal = (PsiLiteral)mmv;
+                                Object value = literal.getValue();
+                                if (value instanceof Integer) {
+                                    return (Integer) value;
+                                } else if (value instanceof String) {
+                                    return codeNameToApi((String) value);
+                                }
+                            }
+                        }
+                    } else if (v instanceof PsiExpression) {
+                        if (v instanceof PsiReferenceExpression) {
+                            String name = ((PsiReferenceExpression)v).getQualifiedName();
+                            return codeNameToApi(name);
+                        } else {
+                            return codeNameToApi(v.getText());
                         }
                     }
                 }
@@ -2266,6 +2058,15 @@
         return -1;
     }
 
+    public static int codeNameToApi(@NonNull String text) {
+        int dotIndex = text.lastIndexOf('.');
+        if (dotIndex != -1) {
+            text = text.substring(dotIndex + 1);
+        }
+
+        return SdkVersionInfo.getApiByBuildCode(text, true);
+    }
+
     public static int getRequiredVersion(@NonNull Issue issue, @NonNull String errorMessage,
             @NonNull TextFormat format) {
         errorMessage = format.toText(errorMessage);
@@ -2441,4 +2242,265 @@
             super.add(from, to);
         }
     }
+
+    public static boolean isPrecededByVersionCheckExit(PsiElement element, int api) {
+        PsiElement current = PsiTreeUtil.getParentOfType(element, PsiStatement.class);
+        if (current != null) {
+            PsiElement prev = getPreviousStatement(current);
+            if (prev == null) {
+                //noinspection unchecked
+                current = PsiTreeUtil.getParentOfType(current, PsiStatement.class, true,
+                        PsiMethod.class, PsiClass.class);
+            } else {
+                current = prev;
+            }
+        }
+        while (current != null) {
+            if (current instanceof PsiIfStatement) {
+                PsiIfStatement ifStatement = (PsiIfStatement)current;
+                PsiStatement thenBranch = ifStatement.getThenBranch();
+                PsiStatement elseBranch = ifStatement.getElseBranch();
+                if (thenBranch != null) {
+                    Boolean level = isVersionCheckConditional(api, thenBranch, ifStatement);
+                    if (level != null) {
+                        // See if the body does an immediate return
+                        if (isUnconditionalReturn(thenBranch)) {
+                            return true;
+                        }
+                    }
+                }
+                if (elseBranch != null) {
+                    Boolean level = isVersionCheckConditional(api, elseBranch, ifStatement);
+                    if (level != null) {
+                        if (isUnconditionalReturn(elseBranch)) {
+                            return true;
+                        }
+                    }
+                }
+            }
+            PsiElement prev = getPreviousStatement(current);
+            if (prev == null) {
+                //noinspection unchecked
+                current = PsiTreeUtil.getParentOfType(current, PsiStatement.class, true,
+                        PsiMethod.class, PsiClass.class);
+                if (current == null) {
+                    return false;
+                }
+            } else {
+                current = prev;
+            }
+        }
+
+        return false;
+    }
+
+    private static boolean isUnconditionalReturn(PsiStatement statement) {
+        if (statement instanceof PsiBlockStatement) {
+            PsiBlockStatement blockStatement = (PsiBlockStatement)statement;
+            PsiCodeBlock block = blockStatement.getCodeBlock();
+            PsiStatement[] statements = block.getStatements();
+            if (statements.length == 1 && statements[0] instanceof PsiReturnStatement) {
+                return true;
+            }
+        }
+        if (statement instanceof PsiReturnStatement) {
+            return true;
+        }
+        return false;
+    }
+
+
+    @Nullable
+    public static PsiStatement getPreviousStatement(PsiElement element) {
+        final PsiElement prevStatement = PsiTreeUtil.skipSiblingsBackward(element,
+                PsiWhiteSpace.class, PsiComment.class);
+        return prevStatement instanceof PsiStatement ? (PsiStatement)prevStatement : null;
+    }
+
+    public static boolean isWithinVersionCheckConditional(PsiElement element, int api) {
+        PsiElement current = element.getParent();
+        PsiElement prev = element;
+        while (current != null) {
+            if (current instanceof PsiIfStatement) {
+                PsiIfStatement ifStatement = (PsiIfStatement)current;
+                Boolean isConditional = isVersionCheckConditional(api, prev, ifStatement);
+                if (isConditional != null) {
+                    return isConditional;
+                }
+            } else if (current instanceof PsiPolyadicExpression && isAndedWithConditional(current, api, prev)) {
+                return true;
+            } else if (current instanceof PsiMethod || current instanceof PsiFile) {
+                return false;
+            }
+            prev = current;
+            current = current.getParent();
+        }
+
+        return false;
+    }
+
+    @Nullable
+    private static Boolean isVersionCheckConditional(int api, PsiElement prev, PsiIfStatement ifStatement) {
+        PsiExpression condition = ifStatement.getCondition();
+        if (condition != prev && condition instanceof PsiBinaryExpression) {
+            Boolean isConditional = isVersionCheckConditional(api, prev, ifStatement, (PsiBinaryExpression)condition);
+            if (isConditional != null) {
+                return isConditional;
+            }
+        } else if (condition instanceof PsiPolyadicExpression) {
+            PsiPolyadicExpression ppe = (PsiPolyadicExpression)condition;
+            if (ppe.getOperationTokenType() == JavaTokenType.ANDAND && (prev == ifStatement.getThenBranch())) {
+                if (isAndedWithConditional(ppe, api, prev)) {
+                    return true;
+                }
+            }
+        } else if (condition instanceof PsiMethodCallExpression) {
+            PsiMethodCallExpression call = (PsiMethodCallExpression) condition;
+            PsiMethod method = call.resolveMethod();
+            if (method != null) {
+                PsiCodeBlock body = method.getBody();
+                if (body != null) {
+                    PsiStatement[] statements = body.getStatements();
+                    if (statements.length == 1) {
+                        PsiStatement statement = statements[0];
+                        if (statement instanceof PsiReturnStatement) {
+                            PsiReturnStatement returnStatement = (PsiReturnStatement) statement;
+                            PsiExpression returnValue = returnStatement.getReturnValue();
+                            if (returnValue instanceof PsiBinaryExpression) {
+                                Boolean isConditional = isVersionCheckConditional(api, null, null, (PsiBinaryExpression)returnValue);
+                                if (isConditional != null) {
+                                    return isConditional;
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+        }
+        return null;
+    }
+
+    @Nullable
+    private static Boolean isVersionCheckConditional(int api,
+            @Nullable PsiElement prev,
+            @Nullable PsiIfStatement ifStatement,
+            @NonNull PsiBinaryExpression binary) {
+        IElementType tokenType = binary.getOperationTokenType();
+        if (tokenType == JavaTokenType.GT || tokenType == JavaTokenType.GE ||
+                tokenType == JavaTokenType.LE || tokenType == JavaTokenType.LT ||
+                tokenType == JavaTokenType.EQEQ) {
+            PsiExpression left = binary.getLOperand();
+            if (left instanceof PsiReferenceExpression) {
+                PsiReferenceExpression ref = (PsiReferenceExpression)left;
+                if (SDK_INT.equals(ref.getReferenceName())) {
+                    PsiExpression right = binary.getROperand();
+                    int level = -1;
+                    if (right instanceof PsiReferenceExpression) {
+                        PsiReferenceExpression ref2 = (PsiReferenceExpression)right;
+                        String codeName = ref2.getReferenceName();
+                        if (codeName == null) {
+                            return false;
+                        }
+                        level = SdkVersionInfo.getApiByBuildCode(codeName, true);
+                    } else if (right instanceof PsiLiteralExpression) {
+                        PsiLiteralExpression lit = (PsiLiteralExpression)right;
+                        Object value = lit.getValue();
+                        if (value instanceof Integer) {
+                            level = ((Integer)value).intValue();
+                        }
+                    }
+                    if (level != -1) {
+                        boolean fromThen = ifStatement == null || prev == ifStatement.getThenBranch();
+                        boolean fromElse = ifStatement != null && prev == ifStatement.getElseBranch();
+                        assert fromThen == !fromElse;
+                        if (tokenType == JavaTokenType.GE) {
+                            // if (SDK_INT >= ICE_CREAM_SANDWICH) { <call> } else { ... }
+                            return level >= api && fromThen;
+                        }
+                        else if (tokenType == JavaTokenType.GT) {
+                            // if (SDK_INT > ICE_CREAM_SANDWICH) { <call> } else { ... }
+                            return level >= api - 1 && fromThen;
+                        }
+                        else if (tokenType == JavaTokenType.LE) {
+                            // if (SDK_INT <= ICE_CREAM_SANDWICH) { ... } else { <call> }
+                            return level >= api - 1 && fromElse;
+                        }
+                        else if (tokenType == JavaTokenType.LT) {
+                            // if (SDK_INT < ICE_CREAM_SANDWICH) { ... } else { <call> }
+                            return level >= api && fromElse;
+                        }
+                        else if (tokenType == JavaTokenType.EQEQ) {
+                            // if (SDK_INT == ICE_CREAM_SANDWICH) { <call> } else {  }
+                            return level >= api && fromThen;
+                        } else {
+                            assert false : tokenType;
+                        }
+                    }
+                }
+            }
+        } else if (tokenType == JavaTokenType.ANDAND && (ifStatement != null && prev == ifStatement.getThenBranch())) {
+            if (isAndedWithConditional(ifStatement.getCondition(), api, prev)) {
+                return true;
+            }
+        }
+        return null;
+    }
+
+    private static boolean isAndedWithConditional(PsiElement element, int api, @Nullable PsiElement before) {
+        if (element instanceof PsiBinaryExpression) {
+            PsiBinaryExpression inner = (PsiBinaryExpression)element;
+            if (inner.getOperationTokenType() == JavaTokenType.ANDAND) {
+                return isAndedWithConditional(inner.getLOperand(), api, before) ||
+                        inner.getROperand() != before &&  isAndedWithConditional(inner.getROperand(), api, before);
+            } else  if (inner.getLOperand() instanceof PsiReferenceExpression &&
+                    SDK_INT.equals(((PsiReferenceExpression)inner.getLOperand()).getReferenceName())) {
+                int level = -1;
+                IElementType tokenType = inner.getOperationTokenType();
+                PsiExpression right = inner.getROperand();
+                if (right instanceof PsiReferenceExpression) {
+                    PsiReferenceExpression ref2 = (PsiReferenceExpression)right;
+                    String codeName = ref2.getReferenceName();
+                    if (codeName == null) {
+                        return false;
+                    }
+                    level = SdkVersionInfo.getApiByBuildCode(codeName, true);
+                } else if (right instanceof PsiLiteralExpression) {
+                    PsiLiteralExpression lit = (PsiLiteralExpression)right;
+                    Object value = lit.getValue();
+                    if (value instanceof Integer) {
+                        level = ((Integer)value).intValue();
+                    }
+                }
+                if (level != -1) {
+                    if (tokenType == JavaTokenType.GE) {
+                        // if (SDK_INT >= ICE_CREAM_SANDWICH && <call>
+                        return level >= api;
+                    }
+                    else if (tokenType == JavaTokenType.GT) {
+                        // if (SDK_INT > ICE_CREAM_SANDWICH) && <call>
+                        return level >= api - 1;
+                    }
+                    else if (tokenType == JavaTokenType.EQEQ) {
+                        // if (SDK_INT == ICE_CREAM_SANDWICH) && <call>
+                        return level >= api;
+                    }
+                }
+            }
+        }
+        else if (element instanceof PsiPolyadicExpression) {
+            PsiPolyadicExpression ppe = (PsiPolyadicExpression)element;
+            if (ppe.getOperationTokenType() == JavaTokenType.ANDAND) {
+                for (PsiExpression operand : ppe.getOperands()) {
+                    if (operand == before) {
+                        break;
+                    }
+                    else if (isAndedWithConditional(operand, api, before)) {
+                        return true;
+                    }
+                }
+            }
+        }
+
+        return false;
+    }
 }
diff --git a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/AppCompatCallDetector.java b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/AppCompatCallDetector.java
index 8a8483e..b833036 100644
--- a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/AppCompatCallDetector.java
+++ b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/AppCompatCallDetector.java
@@ -21,9 +21,7 @@
 
 import com.android.annotations.NonNull;
 import com.android.annotations.Nullable;
-import com.android.tools.lint.client.api.JavaParser.ResolvedClass;
-import com.android.tools.lint.client.api.JavaParser.ResolvedMethod;
-import com.android.tools.lint.client.api.JavaParser.ResolvedNode;
+import com.android.tools.lint.client.api.JavaEvaluator;
 import com.android.tools.lint.detector.api.Category;
 import com.android.tools.lint.detector.api.Context;
 import com.android.tools.lint.detector.api.Detector;
@@ -33,17 +31,17 @@
 import com.android.tools.lint.detector.api.LintUtils;
 import com.android.tools.lint.detector.api.Scope;
 import com.android.tools.lint.detector.api.Severity;
-import com.android.tools.lint.detector.api.Speed;
 import com.android.tools.lint.detector.api.TextFormat;
+import com.intellij.psi.JavaElementVisitor;
+import com.intellij.psi.PsiClass;
+import com.intellij.psi.PsiMethod;
+import com.intellij.psi.PsiMethodCallExpression;
+import com.intellij.psi.util.PsiTreeUtil;
 
 import java.util.Arrays;
 import java.util.List;
 
-import lombok.ast.AstVisitor;
-import lombok.ast.ClassDeclaration;
-import lombok.ast.MethodInvocation;
-
-public class AppCompatCallDetector extends Detector implements Detector.JavaScanner {
+public class AppCompatCallDetector extends Detector implements Detector.JavaPsiScanner {
     public static final Issue ISSUE = Issue.create(
             "AppCompatMethod",
             "Using Wrong AppCompat Method",
@@ -70,12 +68,6 @@
     public AppCompatCallDetector() {
     }
 
-    @NonNull
-    @Override
-    public Speed getSpeed() {
-        return Speed.NORMAL;
-    }
-
     @Override
     public void beforeCheckProject(@NonNull Context context) {
         Boolean dependsOnAppCompat = context.getProject().dependsOn(APPCOMPAT_LIB_ARTIFACT);
@@ -95,10 +87,10 @@
     }
 
     @Override
-    public void visitMethod(@NonNull JavaContext context, @Nullable AstVisitor visitor,
-            @NonNull MethodInvocation node) {
-        if (mDependsOnAppCompat && isAppBarActivityCall(context, node)) {
-            String name = node.astName().astValue();
+    public void visitMethod(@NonNull JavaContext context, @Nullable JavaElementVisitor visitor,
+            @NonNull PsiMethodCallExpression node, @NonNull PsiMethod method) {
+        if (mDependsOnAppCompat && isAppBarActivityCall(context, node, method)) {
+            String name = method.getName();
             String replace = null;
             if (GET_ACTION_BAR.equals(name)) {
                 replace = "getSupportActionBar";
@@ -122,24 +114,15 @@
     }
 
     private static boolean isAppBarActivityCall(@NonNull JavaContext context,
-            @NonNull MethodInvocation node) {
-        ResolvedNode resolved = context.resolve(node);
-        if (resolved instanceof ResolvedMethod) {
-            ResolvedMethod method = (ResolvedMethod) resolved;
-            ResolvedClass containingClass = method.getContainingClass();
-            if (containingClass.isSubclassOf(CLASS_ACTIVITY, false)) {
-                // Make sure that the calling context is a subclass of ActionBarActivity;
-                // we don't want to flag these calls if they are in non-appcompat activities
-                // such as PreferenceActivity (see b.android.com/58512)
-                ClassDeclaration surroundingClass = JavaContext.findSurroundingClass(node);
-                if (surroundingClass != null) {
-                    ResolvedNode clz = context.resolve(surroundingClass);
-                    return clz instanceof ResolvedClass &&
-                            ((ResolvedClass)clz).isSubclassOf(
-                                    "android.support.v7.app.ActionBarActivity",
-                                    false);
-                }
-            }
+            @NonNull PsiMethodCallExpression node, @NonNull PsiMethod method) {
+        JavaEvaluator evaluator = context.getEvaluator();
+        if (evaluator.isMemberInSubClassOf(method, CLASS_ACTIVITY, false)) {
+            // Make sure that the calling context is a subclass of ActionBarActivity;
+            // we don't want to flag these calls if they are in non-appcompat activities
+            // such as PreferenceActivity (see b.android.com/58512)
+            PsiClass cls = PsiTreeUtil.getParentOfType(node, PsiClass.class, true);
+            return cls != null && evaluator.extendsClass(cls,
+                    "android.support.v7.app.ActionBarActivity", false);
         }
         return false;
     }
diff --git a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/AppCompatResourceDetector.java b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/AppCompatResourceDetector.java
index 91ed21f..78e075c 100644
--- a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/AppCompatResourceDetector.java
+++ b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/AppCompatResourceDetector.java
@@ -22,7 +22,6 @@
 import com.android.annotations.NonNull;
 import com.android.resources.ResourceFolderType;
 import com.android.tools.lint.detector.api.Category;
-import com.android.tools.lint.detector.api.Detector.JavaScanner;
 import com.android.tools.lint.detector.api.Implementation;
 import com.android.tools.lint.detector.api.Issue;
 import com.android.tools.lint.detector.api.Project;
@@ -43,7 +42,7 @@
  * Using app:showAsAction instead of android:showAsAction leads to problems, but
  * isn't caught by the API Detector since it's not in the Android namespace.
  */
-public class AppCompatResourceDetector extends ResourceXmlDetector implements JavaScanner {
+public class AppCompatResourceDetector extends ResourceXmlDetector {
     /** The main issue discovered by this detector */
     public static final Issue ISSUE = Issue.create(
             "AppCompatResource", //$NON-NLS-1$
@@ -62,7 +61,7 @@
                     AppCompatResourceDetector.class,
                     Scope.RESOURCE_FILE_SCOPE));
 
-    /** Constructs a new {@link com.android.tools.lint.checks.AppCompatResourceDetector} */
+    /** Constructs a new {@link AppCompatResourceDetector} */
     public AppCompatResourceDetector() {
     }
 
diff --git a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/AppIndexingApiDetector.java b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/AppIndexingApiDetector.java
index 632346c..9029ed2 100644
--- a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/AppIndexingApiDetector.java
+++ b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/AppIndexingApiDetector.java
@@ -41,12 +41,14 @@
 import com.android.ide.common.res2.ResourceItem;
 import com.android.ide.common.resources.ResourceUrl;
 import com.android.resources.ResourceType;
-import com.android.tools.lint.client.api.JavaParser;
+import com.android.tools.lint.client.api.JavaEvaluator;
 import com.android.tools.lint.client.api.LintClient;
 import com.android.tools.lint.client.api.XmlParser;
 import com.android.tools.lint.detector.api.Category;
 import com.android.tools.lint.detector.api.Context;
 import com.android.tools.lint.detector.api.Detector;
+import com.android.tools.lint.detector.api.Detector.JavaPsiScanner;
+import com.android.tools.lint.detector.api.Detector.XmlScanner;
 import com.android.tools.lint.detector.api.Implementation;
 import com.android.tools.lint.detector.api.Issue;
 import com.android.tools.lint.detector.api.JavaContext;
@@ -57,6 +59,13 @@
 import com.android.tools.lint.detector.api.XmlContext;
 import com.google.common.collect.Lists;
 import com.google.common.collect.Sets;
+import com.intellij.psi.JavaRecursiveElementVisitor;
+import com.intellij.psi.PsiAnonymousClass;
+import com.intellij.psi.PsiClass;
+import com.intellij.psi.PsiElement;
+import com.intellij.psi.PsiExpression;
+import com.intellij.psi.PsiField;
+import com.intellij.psi.PsiMethodCallExpression;
 
 import org.w3c.dom.Attr;
 import org.w3c.dom.Document;
@@ -71,21 +80,16 @@
 import java.util.List;
 import java.util.Set;
 
-import lombok.ast.ClassDeclaration;
-import lombok.ast.Expression;
-import lombok.ast.ForwardingAstVisitor;
-import lombok.ast.MethodInvocation;
-
 
 /**
  * Check if the usage of App Indexing is correct.
  */
-public class AppIndexingApiDetector extends Detector
-        implements Detector.XmlScanner, Detector.JavaScanner {
+public class AppIndexingApiDetector extends Detector implements XmlScanner, JavaPsiScanner {
 
     private static final Implementation URL_IMPLEMENTATION = new Implementation(
             AppIndexingApiDetector.class, Scope.MANIFEST_SCOPE);
 
+    @SuppressWarnings("unchecked")
     private static final Implementation APP_INDEXING_API_IMPLEMENTATION =
             new Implementation(
                     AppIndexingApiDetector.class,
@@ -155,7 +159,7 @@
         MISSING_SLASH("attribute should start with '/'"),
         UNKNOWN("unknown error type");
 
-        private String message;
+        private final String message;
 
         IssueType(String str) {
             this.message = str;
@@ -223,32 +227,32 @@
     }
 
     @Override
-    public void checkClass(@NonNull JavaContext javaContext,
-                           @Nullable ClassDeclaration node,
-                           @NonNull lombok.ast.Node declarationOrAnonymous,
-                           @NonNull JavaParser.ResolvedClass cls) {
-        if (node == null) {
+    public void checkClass(@NonNull JavaContext context, @NonNull PsiClass declaration) {
+        if (declaration.getName() == null) {
             return;
         }
 
         // In case linting the base class itself.
-        if (!cls.isInheritingFrom(CLASS_ACTIVITY, true)) {
+        if (!context.getEvaluator().extendsClass(declaration, CLASS_ACTIVITY, true)) {
             return;
         }
 
-        node.accept(new MethodVisitor(javaContext));
+        declaration.accept(new MethodVisitor(context, declaration));
     }
 
-    static class MethodVisitor extends ForwardingAstVisitor {
-        private final JavaContext mJavaContext;
-        private List<MethodInvocation> mStartMethods;
-        private List<MethodInvocation> mEndMethods;
-        private List<MethodInvocation> mConnectMethods;
-        private List<MethodInvocation> mDisconnectMethods;
+    static class MethodVisitor extends JavaRecursiveElementVisitor {
+        private final JavaContext mContext;
+        private final PsiClass mCls;
+
+        private final List<PsiMethodCallExpression> mStartMethods;
+        private final List<PsiMethodCallExpression> mEndMethods;
+        private final List<PsiMethodCallExpression> mConnectMethods;
+        private final List<PsiMethodCallExpression> mDisconnectMethods;
         private boolean mHasAddAppIndexApi;
 
-        MethodVisitor(JavaContext javaContext) {
-            mJavaContext = javaContext;
+        MethodVisitor(JavaContext context, PsiClass cls) {
+            mCls = cls;
+            mContext = context;
             mStartMethods = Lists.newArrayListWithExpectedSize(2);
             mEndMethods = Lists.newArrayListWithExpectedSize(2);
             mConnectMethods = Lists.newArrayListWithExpectedSize(2);
@@ -256,83 +260,81 @@
         }
 
         @Override
-        public boolean visitMethodInvocation(MethodInvocation node) {
-            JavaParser.ResolvedNode resolved = mJavaContext.resolve(node);
-            if (!(resolved instanceof JavaParser.ResolvedMethod)) {
-                return super.visitMethodInvocation(node);
-            }
-            JavaParser.ResolvedMethod method = (JavaParser.ResolvedMethod) resolved;
-            String methodName = node.astName().astValue();
+        public void visitClass(PsiClass aClass) {
+            if (aClass == mCls) {
+                super.visitClass(aClass);
+                report();
+            } // else: don't go into inner classes
+        }
 
+        @Override
+        public void visitMethodCallExpression(PsiMethodCallExpression node) {
+            super.visitMethodCallExpression(node);
+
+            String methodName = node.getMethodExpression().getReferenceName();
+            if (methodName == null) {
+                return;
+            }
+
+            JavaEvaluator evaluator = mContext.getEvaluator();
             if (methodName.equals(APP_INDEX_START)) {
-                if (method.getContainingClass().getName().equals(APP_INDEXING_API_CLASS)) {
+                if (evaluator.isMemberInClass(node.resolveMethod(), APP_INDEXING_API_CLASS)) {
                     mStartMethods.add(node);
                 }
             } else if (methodName.equals(APP_INDEX_END)) {
-                if (method.getContainingClass().getName().equals(APP_INDEXING_API_CLASS)) {
+                if (evaluator.isMemberInClass(node.resolveMethod(), APP_INDEXING_API_CLASS)) {
                     mEndMethods.add(node);
                 }
             } else if (methodName.equals(APP_INDEX_VIEW)) {
-                if (method.getContainingClass().getName().equals(APP_INDEXING_API_CLASS)) {
+                if (evaluator.isMemberInClass(node.resolveMethod(), APP_INDEXING_API_CLASS)) {
                     mStartMethods.add(node);
                 }
             } else if (methodName.equals(APP_INDEX_VIEW_END)) {
-                if (method.getContainingClass().getName().equals(APP_INDEXING_API_CLASS)) {
+                if (evaluator.isMemberInClass(node.resolveMethod(), APP_INDEXING_API_CLASS)) {
                     mEndMethods.add(node);
                 }
             } else if (methodName.equals(CLIENT_CONNECT)) {
-                if (method.getContainingClass().getName().equals(GOOGLE_API_CLIENT_CLASS)) {
+                if (evaluator.isMemberInClass(node.resolveMethod(), GOOGLE_API_CLIENT_CLASS)) {
                     mConnectMethods.add(node);
                 }
             } else if (methodName.equals(CLIENT_DISCONNECT)) {
-                if (method.getContainingClass().getName().equals(GOOGLE_API_CLIENT_CLASS)) {
+                if (evaluator.isMemberInClass(node.resolveMethod(), GOOGLE_API_CLIENT_CLASS)) {
                     mDisconnectMethods.add(node);
                 }
             } else if (methodName.equals(ADD_API)) {
-                if (method.getContainingClass().getName().equals(GOOGLE_API_CLIENT_BUILDER_CLASS)) {
-                    JavaParser.ResolvedNode arg0 = mJavaContext
-                            .resolve(node.astArguments().first());
-                    if (arg0 instanceof JavaParser.ResolvedField) {
-                        JavaParser.ResolvedField resolvedArg0 = (JavaParser.ResolvedField) arg0;
-                        JavaParser.ResolvedClass cls = resolvedArg0.getContainingClass();
-                        if (cls != null && cls.getName().equals(API_CLASS)) {
+                if (evaluator.isMemberInClass(node.resolveMethod(), GOOGLE_API_CLIENT_BUILDER_CLASS)) {
+                    PsiExpression[] args = node.getArgumentList().getExpressions();
+                    if (args.length > 0) {
+                        PsiElement resolved = evaluator.resolve(args[0]);
+                        if (resolved instanceof PsiField &&
+                                evaluator.isMemberInClass((PsiField) resolved, API_CLASS)) {
                             mHasAddAppIndexApi = true;
                         }
                     }
                 }
             }
-            return super.visitMethodInvocation(node);
         }
 
         @Override
-        public void endVisit(lombok.ast.Node root){
-            if (!(root instanceof ClassDeclaration)) {
-                return;
-            }
-            ClassDeclaration node = (ClassDeclaration)root;
-            JavaParser.ResolvedNode resolvedNode = mJavaContext.resolve(node);
-            if (resolvedNode == null || !(resolvedNode instanceof JavaParser.ResolvedClass)) {
-                return;
-            }
+        public void visitAnonymousClass(PsiAnonymousClass aClass) {
+            // Don't jump into inner classes
+        }
 
-            if (!((JavaParser.ResolvedClass)resolvedNode).isInheritingFrom(CLASS_ACTIVITY, true)) {
-                return;
-            }
-
+        private void report() {
             // finds the activity classes that need app activity annotation
-            Set<String> activitiesToCheck = getActivitiesToCheck(mJavaContext);
+            Set<String> activitiesToCheck = getActivitiesToCheck(mContext);
 
             // app indexing API used but no support in manifest
-            boolean hasIntent = activitiesToCheck.contains(resolvedNode.getName());
+            boolean hasIntent = activitiesToCheck.contains(mCls.getQualifiedName());
             if (!hasIntent) {
-                for (MethodInvocation method : mStartMethods) {
-                    mJavaContext.report(ISSUE_APP_INDEXING_API, method,
-                            mJavaContext.getLocation(method.astName()),
+                for (PsiMethodCallExpression call : mStartMethods) {
+                    mContext.report(ISSUE_APP_INDEXING_API, call,
+                            mContext.getNameLocation(call),
                             "Missing support for Google App Indexing in the manifest");
                 }
-                for (MethodInvocation method : mEndMethods) {
-                    mJavaContext.report(ISSUE_APP_INDEXING_API, method,
-                            mJavaContext.getLocation(method.astName()),
+                for (PsiMethodCallExpression call : mEndMethods) {
+                    mContext.report(ISSUE_APP_INDEXING_API, call,
+                            mContext.getNameLocation(call),
                             "Missing support for Google App Indexing in the manifest");
                 }
                 return;
@@ -340,64 +342,72 @@
 
             // `AppIndex.AppIndexApi.start / end / view / viewEnd` should exist
             if (mStartMethods.isEmpty() && mEndMethods.isEmpty()) {
-                mJavaContext.report(ISSUE_APP_INDEXING_API, node,
-                        mJavaContext.getLocation(node.astName()),
+                mContext.report(ISSUE_APP_INDEXING_API, mCls,
+                        mContext.getNameLocation(mCls),
                         "Missing support for Google App Indexing API");
                 return;
             }
 
-            for (MethodInvocation startNode : mStartMethods) {
-                Expression startClient = startNode.astArguments().first();
+            for (PsiMethodCallExpression startNode : mStartMethods) {
+                PsiExpression[] expressions = startNode.getArgumentList().getExpressions();
+                if (expressions.length == 0) {
+                    continue;
+                }
+                PsiExpression startClient = expressions[0];
 
                 // GoogleApiClient should `addApi(AppIndex.APP_INDEX_API)`
                 if (!mHasAddAppIndexApi) {
                     String message = String.format(
                             "GoogleApiClient `%1$s` has not added support for App Indexing API",
-                            startClient.toString());
-                    mJavaContext.report(ISSUE_APP_INDEXING_API, startClient,
-                            mJavaContext.getLocation(startClient), message);
+                            startClient.getText());
+                    mContext.report(ISSUE_APP_INDEXING_API, startClient,
+                            mContext.getLocation(startClient), message);
                 }
 
                 // GoogleApiClient `connect` should exist
                 if (!hasOperand(startClient, mConnectMethods)) {
-                    String message = String
-                            .format("GoogleApiClient `%1$s` is not connected", startClient.toString());
-                    mJavaContext.report(ISSUE_APP_INDEXING_API, startClient,
-                            mJavaContext.getLocation(startClient), message);
+                    String message = String.format("GoogleApiClient `%1$s` is not connected",
+                                    startClient.getText());
+                    mContext.report(ISSUE_APP_INDEXING_API, startClient,
+                            mContext.getLocation(startClient), message);
                 }
 
                 // `AppIndex.AppIndexApi.end` should pair with `AppIndex.AppIndexApi.start`
                 if (!hasFirstArgument(startClient, mEndMethods)) {
-                    mJavaContext.report(ISSUE_APP_INDEXING_API, startNode,
-                            mJavaContext.getLocation(startNode.astName()),
+                    mContext.report(ISSUE_APP_INDEXING_API, startNode,
+                            mContext.getNameLocation(startNode),
                             "Missing corresponding `AppIndex.AppIndexApi.end` method");
                 }
             }
 
-            for (MethodInvocation endNode : mEndMethods) {
-                Expression endClient = endNode.astArguments().first();
+            for (PsiMethodCallExpression endNode : mEndMethods) {
+                PsiExpression[] expressions = endNode.getArgumentList().getExpressions();
+                if (expressions.length == 0) {
+                    continue;
+                }
+                PsiExpression endClient = expressions[0];
 
                 // GoogleApiClient should `addApi(AppIndex.APP_INDEX_API)`
                 if (!mHasAddAppIndexApi) {
                     String message = String.format(
                             "GoogleApiClient `%1$s` has not added support for App Indexing API",
-                            endClient.toString());
-                    mJavaContext.report(ISSUE_APP_INDEXING_API, endClient,
-                            mJavaContext.getLocation(endClient), message);
+                            endClient.getText());
+                    mContext.report(ISSUE_APP_INDEXING_API, endClient,
+                            mContext.getLocation(endClient), message);
                 }
 
                 // GoogleApiClient `disconnect` should exist
                 if (!hasOperand(endClient, mDisconnectMethods)) {
                     String message = String.format("GoogleApiClient `%1$s`"
-                            + " is not disconnected", endClient.toString());
-                    mJavaContext.report(ISSUE_APP_INDEXING_API, endClient,
-                            mJavaContext.getLocation(endClient), message);
+                            + " is not disconnected", endClient.getText());
+                    mContext.report(ISSUE_APP_INDEXING_API, endClient,
+                            mContext.getLocation(endClient), message);
                 }
 
                 // `AppIndex.AppIndexApi.start` should pair with `AppIndex.AppIndexApi.end`
                 if (!hasFirstArgument(endClient, mStartMethods)) {
-                    mJavaContext.report(ISSUE_APP_INDEXING_API, endNode,
-                            mJavaContext.getLocation(endNode.astName()),
+                    mContext.report(ISSUE_APP_INDEXING_API, endNode,
+                            mContext.getNameLocation(endNode),
                             "Missing corresponding `AppIndex.AppIndexApi.start` method");
                 }
             }
@@ -674,11 +684,14 @@
      * @param list     The methods list.
      * @return If such a method exists in the list.
      */
-    private static boolean hasFirstArgument(Expression argument, List<MethodInvocation> list) {
-        for (MethodInvocation method : list) {
-            Expression argument1 = method.astArguments().first();
-            if (argument.toString().equals(argument1.toString())) {
-                return true;
+    private static boolean hasFirstArgument(PsiExpression argument, List<PsiMethodCallExpression> list) {
+        for (PsiMethodCallExpression call : list) {
+            PsiExpression[] expressions = call.getArgumentList().getExpressions();
+            if (expressions.length > 0) {
+                PsiExpression argument2 = expressions[0];
+                if (argument.getText().equals(argument2.getText())) {
+                    return true;
+                }
             }
         }
         return false;
@@ -691,10 +704,10 @@
      * @param list    The methods list.
      * @return If such a method exists in the list.
      */
-    private static boolean hasOperand(Expression operand, List<MethodInvocation> list) {
-        for (MethodInvocation method : list) {
-            Expression operand1 = method.astOperand();
-            if (operand.toString().equals(operand1.toString())) {
+    private static boolean hasOperand(PsiExpression operand, List<PsiMethodCallExpression> list) {
+        for (PsiMethodCallExpression method : list) {
+            PsiElement operand2 = method.getMethodExpression().getQualifier();
+            if (operand2 != null && operand.getText().equals(operand2.getText())) {
                 return true;
             }
         }
diff --git a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/AppLinksAutoVerifyDetector.java b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/AppLinksAutoVerifyDetector.java
index 7e29756..684e012 100644
--- a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/AppLinksAutoVerifyDetector.java
+++ b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/AppLinksAutoVerifyDetector.java
@@ -39,7 +39,6 @@
 import com.android.tools.lint.detector.api.XmlContext;
 import com.google.common.collect.Lists;
 import com.google.common.collect.Maps;
-
 import com.google.gson.JsonArray;
 import com.google.gson.JsonElement;
 import com.google.gson.JsonObject;
@@ -110,10 +109,10 @@
 
     /* Maps website host url to a future task which will send HTTP request to fetch the JSON file
      * and also return the status code during the fetching process. */
-    private Map<String, Future<HttpResult>> mFutures = Maps.newHashMap();
+    private final Map<String, Future<HttpResult>> mFutures = Maps.newHashMap();
 
     /* Maps website host url to host attribute in AndroidManifest.xml. */
-    private Map<String, Attr> mJsonHost = Maps.newHashMap();
+    private final Map<String, Attr> mJsonHost = Maps.newHashMap();
 
     @Override
     public void visitDocument(@NonNull XmlContext context, @NonNull Document document) {
@@ -409,8 +408,8 @@
     static final class HttpResult {
 
         /* HTTP response code or others errors related to HTTP connection, JSON file parsing. */
-        private int mStatus;
-        private JsonElement mJsonFile;
+        private final int mStatus;
+        private final JsonElement mJsonFile;
 
         @VisibleForTesting
         HttpResult(int status, JsonElement jsonFile) {
diff --git a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/AssertDetector.java b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/AssertDetector.java
index 309dff9..0bc5aa4 100644
--- a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/AssertDetector.java
+++ b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/AssertDetector.java
@@ -16,33 +16,32 @@
 
 package com.android.tools.lint.checks;
 
+import static com.android.tools.lint.detector.api.LintUtils.isNullLiteral;
+
 import com.android.annotations.NonNull;
+import com.android.annotations.Nullable;
 import com.android.tools.lint.detector.api.Category;
-import com.android.tools.lint.detector.api.Context;
 import com.android.tools.lint.detector.api.Detector;
+import com.android.tools.lint.detector.api.Detector.JavaPsiScanner;
 import com.android.tools.lint.detector.api.Implementation;
 import com.android.tools.lint.detector.api.Issue;
 import com.android.tools.lint.detector.api.JavaContext;
 import com.android.tools.lint.detector.api.Scope;
 import com.android.tools.lint.detector.api.Severity;
+import com.intellij.psi.JavaElementVisitor;
+import com.intellij.psi.PsiAssertStatement;
+import com.intellij.psi.PsiBinaryExpression;
+import com.intellij.psi.PsiElement;
+import com.intellij.psi.PsiExpression;
+import com.intellij.psi.PsiLiteral;
 
-import java.io.File;
 import java.util.Collections;
 import java.util.List;
 
-import lombok.ast.Assert;
-import lombok.ast.AstVisitor;
-import lombok.ast.BinaryExpression;
-import lombok.ast.BooleanLiteral;
-import lombok.ast.Expression;
-import lombok.ast.ForwardingAstVisitor;
-import lombok.ast.Node;
-import lombok.ast.NullLiteral;
-
 /**
  * Looks for assertion usages.
  */
-public class AssertDetector extends Detector implements Detector.JavaScanner {
+public class AssertDetector extends Detector implements JavaPsiScanner {
     /** Using assertions */
     public static final Issue ISSUE = Issue.create(
             "Assert", //$NON-NLS-1$
@@ -71,37 +70,34 @@
             .addMoreInfo(
             "https://code.google.com/p/android/issues/detail?id=65183"); //$NON-NLS-1$
 
-    /** Constructs a new {@link com.android.tools.lint.checks.AssertDetector} check */
+    /** Constructs a new {@link AssertDetector} check */
     public AssertDetector() {
     }
 
-    @Override
-    public boolean appliesTo(@NonNull Context context, @NonNull File file) {
-        return true;
-    }
-
     // ---- Implements JavaScanner ----
 
+
     @Override
-    public List<Class<? extends Node>> getApplicableNodeTypes() {
-        return Collections.<Class<? extends Node>>singletonList(Assert.class);
+    public List<Class<? extends PsiElement>> getApplicablePsiTypes() {
+        return Collections.<Class<? extends PsiElement>>singletonList(PsiAssertStatement.class);
     }
 
+    @Nullable
     @Override
-    public AstVisitor createJavaVisitor(@NonNull final JavaContext context) {
-        return new ForwardingAstVisitor() {
-            @Override
-            public boolean visitAssert(Assert node) {
-                if (!context.getMainProject().isAndroidProject()) {
-                    return true;
-                }
+    public JavaElementVisitor createPsiVisitor(@NonNull final JavaContext context) {
+        if (!context.getMainProject().isAndroidProject()) {
+            return null;
+        }
 
-                Expression assertion = node.astAssertion();
+        return new JavaElementVisitor() {
+            @Override
+            public void visitAssertStatement(PsiAssertStatement node) {
+                PsiExpression assertion = node.getAssertCondition();
                 // Allow "assert true"; it's basically a no-op
-                if (assertion instanceof BooleanLiteral) {
-                    Boolean b = ((BooleanLiteral) assertion).astValue();
-                    if (b != null && b) {
-                        return false;
+                if (assertion instanceof PsiLiteral) {
+                    Object value = ((PsiLiteral)assertion).getValue();
+                    if (Boolean.TRUE.equals(value)) {
+                        return;
                     }
                 } else {
                     // Allow assertions of the form "assert foo != null" because they are often used
@@ -110,13 +106,12 @@
                     // may know that it won't be when it's called correctly, so the assertion helps
                     // to clear nullness warnings.
                     if (isNullCheck(assertion)) {
-                        return false;
+                        return;
                     }
                 }
                 String message
                         = "Assertions are unreliable. Use `BuildConfig.DEBUG` conditional checks instead.";
                 context.report(ISSUE, node, context.getLocation(node), message);
-                return false;
             }
         };
     }
@@ -126,14 +121,15 @@
      * true for expressions like "a != null" and "a != null && b != null" and
      * "b == null || c != null".
      */
-    private static boolean isNullCheck(Expression expression) {
-        if (expression instanceof BinaryExpression) {
-            BinaryExpression binExp = (BinaryExpression) expression;
-            if (binExp.astLeft() instanceof NullLiteral ||
-                    binExp.astRight() instanceof NullLiteral) {
+    private static boolean isNullCheck(PsiExpression expression) {
+        if (expression instanceof PsiBinaryExpression) {
+            PsiBinaryExpression binExp = (PsiBinaryExpression) expression;
+            PsiExpression lOperand = binExp.getLOperand();
+            PsiExpression rOperand = binExp.getROperand();
+            if (isNullLiteral(lOperand) || isNullLiteral(rOperand)) {
                 return true;
             } else {
-                return isNullCheck(binExp.astLeft()) && isNullCheck(binExp.astRight());
+                return isNullCheck(lOperand) && isNullCheck(rOperand);
             }
         } else {
             return false;
diff --git a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/BadHostnameVerifierDetector.java b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/BadHostnameVerifierDetector.java
index 2128896..180c94f 100644
--- a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/BadHostnameVerifierDetector.java
+++ b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/BadHostnameVerifierDetector.java
@@ -20,36 +20,29 @@
 
 import com.android.annotations.NonNull;
 import com.android.annotations.Nullable;
-import com.android.tools.lint.client.api.JavaParser.ResolvedClass;
-import com.android.tools.lint.client.api.JavaParser.ResolvedMethod;
-import com.android.tools.lint.client.api.JavaParser.ResolvedNode;
+import com.android.tools.lint.client.api.JavaEvaluator;
 import com.android.tools.lint.detector.api.Category;
-import com.android.tools.lint.detector.api.ConstantEvaluator;
 import com.android.tools.lint.detector.api.Detector;
-import com.android.tools.lint.detector.api.Detector.ClassScanner;
-import com.android.tools.lint.detector.api.Detector.JavaScanner;
+import com.android.tools.lint.detector.api.Detector.JavaPsiScanner;
 import com.android.tools.lint.detector.api.Implementation;
 import com.android.tools.lint.detector.api.Issue;
 import com.android.tools.lint.detector.api.JavaContext;
+import com.android.tools.lint.detector.api.LintUtils;
 import com.android.tools.lint.detector.api.Location;
 import com.android.tools.lint.detector.api.Scope;
 import com.android.tools.lint.detector.api.Severity;
+import com.intellij.psi.JavaRecursiveElementVisitor;
+import com.intellij.psi.PsiClass;
+import com.intellij.psi.PsiExpression;
+import com.intellij.psi.PsiMethod;
+import com.intellij.psi.PsiMethodCallExpression;
+import com.intellij.psi.PsiReturnStatement;
+import com.intellij.psi.PsiThrowStatement;
 
 import java.util.Collections;
 import java.util.List;
 
-import lombok.ast.BooleanLiteral;
-import lombok.ast.ClassDeclaration;
-import lombok.ast.Expression;
-import lombok.ast.ForwardingAstVisitor;
-import lombok.ast.MethodDeclaration;
-import lombok.ast.MethodInvocation;
-import lombok.ast.Node;
-import lombok.ast.NormalTypeBody;
-import lombok.ast.Return;
-import lombok.ast.Throw;
-
-public class BadHostnameVerifierDetector extends Detector implements JavaScanner {
+public class BadHostnameVerifierDetector extends Detector implements JavaPsiScanner {
 
     @SuppressWarnings("unchecked")
     private static final Implementation IMPLEMENTATION =
@@ -76,50 +69,29 @@
     }
 
     @Override
-    public void checkClass(@NonNull JavaContext context, @Nullable ClassDeclaration node,
-            @NonNull Node declarationOrAnonymous, @NonNull ResolvedClass cls) {
-        NormalTypeBody body;
-        if (declarationOrAnonymous instanceof NormalTypeBody) {
-            body = (NormalTypeBody) declarationOrAnonymous;
-        } else if (node != null) {
-            body = node.astBody();
-        } else {
-            return;
-        }
-
-        for (Node member : body.astMembers()) {
-            if (member instanceof MethodDeclaration) {
-                MethodDeclaration declaration = (MethodDeclaration)member;
-                if ("verify".equals(declaration.astMethodName().astValue())
-                        && declaration.astParameters().size() == 2) {
-                    ResolvedNode resolved = context.resolve(declaration);
-                    if (resolved instanceof ResolvedMethod) {
-                        ResolvedMethod method = (ResolvedMethod) resolved;
-                        if (method.getArgumentCount() == 2
-                                && method.getArgumentType(0).matchesName(TYPE_STRING)
-                                && method.getArgumentType(1).matchesName("javax.net.ssl.SSLSession")) {
-
-                            ComplexVisitor visitor = new ComplexVisitor(context);
-                            declaration.accept(visitor);
-                            if (visitor.isComplex()) {
-                                return;
-                            }
-
-                            Location location = context.getNameLocation(declaration);
-                            String message = String.format("`%1$s` always returns `true`, which " +
-                                    "could cause insecure network traffic due to trusting "
-                                    + "TLS/SSL server certificates for wrong hostnames",
-                                    method.getName());
-                            context.report(ISSUE, location, message);
-                            break;
-                        }
-                    }
+    public void checkClass(@NonNull JavaContext context, @NonNull PsiClass declaration) {
+        JavaEvaluator evaluator = context.getEvaluator();
+        for (PsiMethod method : declaration.findMethodsByName("verify", false)) {
+            if (evaluator.methodMatches(method, null, false,
+                    TYPE_STRING, "javax.net.ssl.SSLSession")) {
+                ComplexVisitor visitor = new ComplexVisitor(context);
+                declaration.accept(visitor);
+                if (visitor.isComplex()) {
+                    return;
                 }
+
+                Location location = context.getNameLocation(method);
+                String message = String.format("`%1$s` always returns `true`, which " +
+                                "could cause insecure network traffic due to trusting "
+                                + "TLS/SSL server certificates for wrong hostnames",
+                        method.getName());
+                context.report(ISSUE, location, message);
+                break;
             }
         }
     }
 
-    private static class ComplexVisitor extends ForwardingAstVisitor {
+    private static class ComplexVisitor extends JavaRecursiveElementVisitor {
         @SuppressWarnings("unused")
         private final JavaContext mContext;
         private boolean mComplex;
@@ -129,34 +101,33 @@
         }
 
         @Override
-        public boolean visitThrow(Throw node) {
+        public void visitThrowStatement(PsiThrowStatement statement) {
+            super.visitThrowStatement(statement);
             mComplex = true;
-            return super.visitThrow(node);
         }
 
         @Override
-        public boolean visitMethodInvocation(MethodInvocation node) {
+        public void visitMethodCallExpression(PsiMethodCallExpression expression) {
+            super.visitMethodCallExpression(expression);
             // TODO: Ignore certain known safe methods, e.g. Logging etc
             mComplex = true;
-            return super.visitMethodInvocation(node);
         }
 
         @Override
-        public boolean visitReturn(Return node) {
-            Expression argument = node.astValue();
+        public void visitReturnStatement(PsiReturnStatement node) {
+            PsiExpression argument = node.getReturnValue();
             if (argument != null) {
                 // TODO: Only do this if certain that there isn't some intermediate
                 // assignment, as exposed by the unit test
                 //Object value = ConstantEvaluator.evaluate(mContext, argument);
                 //if (Boolean.TRUE.equals(value)) {
-                if (argument instanceof BooleanLiteral &&
-                        Boolean.TRUE.equals(((BooleanLiteral)argument).astValue())) {
+                if (LintUtils.isTrueLiteral(argument)) {
                     mComplex = false;
                 } else {
                     mComplex = true; // "return false" or some complicated logic
                 }
             }
-            return super.visitReturn(node);
+            super.visitReturnStatement(node);
         }
 
         public boolean isComplex() {
diff --git a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/BuiltinIssueRegistry.java b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/BuiltinIssueRegistry.java
index f9c7c3f..2519188 100644
--- a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/BuiltinIssueRegistry.java
+++ b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/BuiltinIssueRegistry.java
@@ -75,6 +75,7 @@
         issues.add(CipherGetInstanceDetector.ISSUE);
         issues.add(CleanupDetector.COMMIT_FRAGMENT);
         issues.add(CleanupDetector.RECYCLE_RESOURCE);
+        issues.add(CleanupDetector.SHARED_PREF);
         issues.add(ClickableViewAccessibilityDetector.ISSUE);
         issues.add(CommentDetector.EASTER_EGG);
         issues.add(CommentDetector.STOP_SHIP);
@@ -228,7 +229,6 @@
         issues.add(SecurityDetector.WORLD_WRITEABLE);
         issues.add(ServiceCastDetector.ISSUE);
         issues.add(SetJavaScriptEnabledDetector.ISSUE);
-        issues.add(SharedPrefsDetector.ISSUE);
         issues.add(SignatureOrSystemDetector.ISSUE);
         issues.add(SQLiteDetector.ISSUE);
         issues.add(SslCertificateSocketFactoryDetector.CREATE_SOCKET);
@@ -319,7 +319,7 @@
             }
 
             if (scope.contains(Scope.JAVA_FILE)) {
-                initialSize += 72;
+                initialSize += 74;
             } else if (scope.contains(Scope.CLASS_FILE)) {
                 initialSize += 15;
             } else if (scope.contains(Scope.MANIFEST)) {
diff --git a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/CallSuperDetector.java b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/CallSuperDetector.java
index 42df6d9..497b739 100644
--- a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/CallSuperDetector.java
+++ b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/CallSuperDetector.java
@@ -19,38 +19,35 @@
 import static com.android.SdkConstants.CLASS_VIEW;
 import static com.android.SdkConstants.SUPPORT_ANNOTATIONS_PREFIX;
 import static com.android.tools.lint.checks.SupportAnnotationDetector.filterRelevantAnnotations;
+import static com.android.tools.lint.detector.api.LintUtils.skipParentheses;
 
 import com.android.annotations.NonNull;
 import com.android.annotations.Nullable;
-import com.android.tools.lint.client.api.JavaParser.ResolvedAnnotation;
-import com.android.tools.lint.client.api.JavaParser.ResolvedMethod;
-import com.android.tools.lint.client.api.JavaParser.ResolvedNode;
+import com.android.tools.lint.client.api.JavaEvaluator;
 import com.android.tools.lint.detector.api.Category;
-import com.android.tools.lint.detector.api.Context;
 import com.android.tools.lint.detector.api.Detector;
+import com.android.tools.lint.detector.api.Detector.JavaPsiScanner;
 import com.android.tools.lint.detector.api.Implementation;
 import com.android.tools.lint.detector.api.Issue;
 import com.android.tools.lint.detector.api.JavaContext;
 import com.android.tools.lint.detector.api.Location;
 import com.android.tools.lint.detector.api.Scope;
 import com.android.tools.lint.detector.api.Severity;
-import com.android.tools.lint.detector.api.Speed;
+import com.intellij.psi.JavaElementVisitor;
+import com.intellij.psi.JavaRecursiveElementVisitor;
+import com.intellij.psi.PsiAnnotation;
+import com.intellij.psi.PsiElement;
+import com.intellij.psi.PsiMethod;
+import com.intellij.psi.PsiReferenceExpression;
+import com.intellij.psi.PsiSuperExpression;
 
-import java.io.File;
 import java.util.Collections;
 import java.util.List;
 
-import lombok.ast.AstVisitor;
-import lombok.ast.ForwardingAstVisitor;
-import lombok.ast.MethodDeclaration;
-import lombok.ast.MethodInvocation;
-import lombok.ast.Node;
-import lombok.ast.Super;
-
 /**
  * Makes sure that methods call super when overriding methods.
  */
-public class CallSuperDetector extends Detector implements Detector.JavaScanner {
+public class CallSuperDetector extends Detector implements JavaPsiScanner {
     private static final String CALL_SUPER_ANNOTATION = SUPPORT_ANNOTATIONS_PREFIX + "CallSuper"; //$NON-NLS-1$
     private static final String ON_DETACHED_FROM_WINDOW = "onDetachedFromWindow";   //$NON-NLS-1$
     private static final String ON_VISIBILITY_CHANGED = "onVisibilityChanged";      //$NON-NLS-1$
@@ -76,52 +73,35 @@
     public CallSuperDetector() {
     }
 
-    @Override
-    public boolean appliesTo(@NonNull Context context, @NonNull File file) {
-        return true;
-    }
-
-    @NonNull
-    @Override
-    public Speed getSpeed() {
-        return Speed.FAST;
-    }
-
     // ---- Implements JavaScanner ----
 
+
     @Override
-    public List<Class<? extends Node>> getApplicableNodeTypes() {
-        return Collections.<Class<? extends Node>>singletonList(MethodDeclaration.class);
+    public List<Class<? extends PsiElement>> getApplicablePsiTypes() {
+        return Collections.<Class<? extends PsiElement>>singletonList(PsiMethod.class);
     }
 
     @Override
-    public AstVisitor createJavaVisitor(@NonNull final JavaContext context) {
-        return new ForwardingAstVisitor() {
+    public JavaElementVisitor createPsiVisitor(@NonNull final JavaContext context) {
+        return new JavaElementVisitor() {
             @Override
-            public boolean visitMethodDeclaration(MethodDeclaration node) {
-                ResolvedNode resolved = context.resolve(node);
-                if (resolved instanceof ResolvedMethod) {
-                    ResolvedMethod method = (ResolvedMethod) resolved;
-                    checkCallSuper(context, node, method);
-                }
-
-                return false;
+            public void visitMethod(PsiMethod method) {
+                checkCallSuper(context, method);
             }
         };
     }
 
     private static void checkCallSuper(@NonNull JavaContext context,
-            @NonNull MethodDeclaration declaration,
-            @NonNull ResolvedMethod method) {
+            @NonNull PsiMethod method) {
 
-        ResolvedMethod superMethod = getRequiredSuperMethod(method);
+        PsiMethod superMethod = getRequiredSuperMethod(context, method);
         if (superMethod != null) {
-            if (!SuperCallVisitor.callsSuper(context, declaration, superMethod)) {
+            if (!SuperCallVisitor.callsSuper(method, superMethod)) {
                 String methodName = method.getName();
                 String message = "Overriding method should call `super."
                         + methodName + "`";
-                Location location = context.getNameLocation(declaration);
-                context.report(ISSUE, declaration, location, message);
+                Location location = context.getNameLocation(method);
+                context.report(ISSUE, method, location, message);
             }
         }
     }
@@ -131,8 +111,14 @@
      * to be invoked, and if so, returns it (otherwise returns null)
      */
     @Nullable
-    private static ResolvedMethod getRequiredSuperMethod(
-            @NonNull ResolvedMethod method) {
+    private static PsiMethod getRequiredSuperMethod(@NonNull JavaContext context,
+            @NonNull PsiMethod method) {
+
+        JavaEvaluator evaluator = context.getEvaluator();
+        PsiMethod directSuper = evaluator.getSuperMethod(method);
+        if (directSuper == null) {
+            return null;
+        }
 
         String name = method.getName();
         if (ON_DETACHED_FROM_WINDOW.equals(name)) {
@@ -141,82 +127,69 @@
             // is still dangerous if supporting older versions so flag
             // this for now (should make annotation carry metadata like
             // compileSdkVersion >= N).
-            if (!method.getContainingClass().isSubclassOf(CLASS_VIEW, false)) {
+            if (!evaluator.isMemberInSubClassOf(method, CLASS_VIEW, false)) {
                 return null;
             }
-            return method.getSuperMethod();
+            return directSuper;
         } else if (ON_VISIBILITY_CHANGED.equals(name)) {
             // From Android Wear API; doesn't yet have an annotation
             // but we want to enforce this right away until the AAR
             // is updated to supply it once @CallSuper is available in
             // the support library
-            if (!method.getContainingClass().isSubclassOf(
+            if (!evaluator.isMemberInSubClassOf(method,
                     "android.support.wearable.watchface.WatchFaceService.Engine", false)) {
                 return null;
             }
-            return method.getSuperMethod();
+            return directSuper;
         }
 
         // Look up annotations metadata
-        ResolvedMethod directSuper = method.getSuperMethod();
-        ResolvedMethod superMethod = directSuper;
+        PsiMethod superMethod = directSuper;
         while (superMethod != null) {
-            Iterable<ResolvedAnnotation> annotations = superMethod.getAnnotations();
+            PsiAnnotation[] annotations = superMethod.getModifierList().getAnnotations();
             annotations = filterRelevantAnnotations(annotations);
-            for (ResolvedAnnotation annotation : annotations) {
-                String signature = annotation.getSignature();
+            for (PsiAnnotation annotation : annotations) {
+                String signature = annotation.getQualifiedName();
                 if (CALL_SUPER_ANNOTATION.equals(signature)) {
                     return directSuper;
-                } else if (signature.endsWith(".OverrideMustInvoke")) {
+                } else if (signature != null && signature.endsWith(".OverrideMustInvoke")) {
                     // Handle findbugs annotation on the fly too
                     return directSuper;
                 }
             }
-            superMethod = superMethod.getSuperMethod();
+            superMethod = evaluator.getSuperMethod(superMethod);
         }
 
         return null;
     }
 
     /** Visits a method and determines whether the method calls its super method */
-    private static class SuperCallVisitor extends ForwardingAstVisitor {
-        private final JavaContext mContext;
-        private final ResolvedMethod mMethod;
+    private static class SuperCallVisitor extends JavaRecursiveElementVisitor {
+        private final PsiMethod mMethod;
         private boolean mCallsSuper;
 
-        public static boolean callsSuper(
-                @NonNull JavaContext context,
-                @NonNull MethodDeclaration methodDeclaration,
-                @NonNull ResolvedMethod method) {
-            SuperCallVisitor visitor = new SuperCallVisitor(context, method);
-            methodDeclaration.accept(visitor);
+        public static boolean callsSuper(@NonNull PsiMethod method,
+                @NonNull PsiMethod superMethod) {
+            SuperCallVisitor visitor = new SuperCallVisitor(superMethod);
+            method.accept(visitor);
             return visitor.mCallsSuper;
         }
 
-        private SuperCallVisitor(@NonNull JavaContext context, @NonNull ResolvedMethod method) {
-            mContext = context;
+        private SuperCallVisitor(@NonNull PsiMethod method) {
             mMethod = method;
         }
 
         @Override
-        public boolean visitSuper(Super node) {
-            ResolvedNode resolved = null;
-            if (node.getParent() instanceof MethodInvocation) {
-                resolved = mContext.resolve(node.getParent());
-            }
-            if (resolved == null) {
-                resolved = mContext.resolve(node);
-            }
-            if (mMethod.equals(resolved)) {
-                mCallsSuper = true;
-                return true;
-            }
-            return false;
-        }
+        public void visitSuperExpression(PsiSuperExpression node) {
+            super.visitSuperExpression(node);
 
-        @Override
-        public boolean visitNode(Node node) {
-            return mCallsSuper || super.visitNode(node);
+            PsiElement parent = skipParentheses(node.getParent());
+            if (parent instanceof PsiReferenceExpression) {
+                PsiElement resolved = ((PsiReferenceExpression) parent).resolve();
+                if (mMethod.equals(resolved)) {
+                    mCallsSuper = true;
+                }
+            }
         }
     }
 }
diff --git a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/CipherGetInstanceDetector.java b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/CipherGetInstanceDetector.java
index 5eb91cd..1d8d973 100644
--- a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/CipherGetInstanceDetector.java
+++ b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/CipherGetInstanceDetector.java
@@ -18,40 +18,37 @@
 
 import com.android.annotations.NonNull;
 import com.android.annotations.Nullable;
-import com.android.tools.lint.client.api.JavaParser.ResolvedField;
-import com.android.tools.lint.client.api.JavaParser.ResolvedMethod;
-import com.android.tools.lint.client.api.JavaParser.ResolvedNode;
 import com.android.tools.lint.detector.api.Category;
+import com.android.tools.lint.detector.api.ConstantEvaluator;
 import com.android.tools.lint.detector.api.Detector;
 import com.android.tools.lint.detector.api.Implementation;
 import com.android.tools.lint.detector.api.Issue;
 import com.android.tools.lint.detector.api.JavaContext;
 import com.android.tools.lint.detector.api.Scope;
 import com.android.tools.lint.detector.api.Severity;
-import com.android.tools.lint.detector.api.Speed;
 import com.google.common.collect.Sets;
+import com.intellij.psi.JavaElementVisitor;
+import com.intellij.psi.PsiElement;
+import com.intellij.psi.PsiExpression;
+import com.intellij.psi.PsiExpressionList;
+import com.intellij.psi.PsiLiteral;
+import com.intellij.psi.PsiMethod;
+import com.intellij.psi.PsiMethodCallExpression;
 
 import java.util.Collections;
 import java.util.List;
 import java.util.Set;
 
-import lombok.ast.AstVisitor;
-import lombok.ast.Expression;
-import lombok.ast.MethodInvocation;
-import lombok.ast.Node;
-import lombok.ast.StrictListAccessor;
-import lombok.ast.StringLiteral;
-
 /**
  * Ensures that Cipher.getInstance is not called with AES as the parameter.
  */
-public class CipherGetInstanceDetector extends Detector implements Detector.JavaScanner {
+public class CipherGetInstanceDetector extends Detector implements Detector.JavaPsiScanner {
     public static final Issue ISSUE = Issue.create(
             "GetInstance", //$NON-NLS-1$
             "Cipher.getInstance with ECB",
-            "`Cipher#getInstance` should not be called with ECB as the cipher mode or without "
-                    + "setting the cipher mode because the default mode on android is ECB, which "
-                    + "is insecure.",
+            "`Cipher#getInstance` should not be called with ECB as the cipher mode or without " +
+            "setting the cipher mode because the default mode on android is ECB, which " +
+            "is insecure.",
             Category.SECURITY,
             9,
             Severity.WARNING,
@@ -65,12 +62,6 @@
             Sets.newHashSet("AES", "DES", "DESede"); //$NON-NLS-1$
     private static final String ECB = "ECB"; //$NON-NLS-1$
 
-    @NonNull
-    @Override
-    public Speed getSpeed() {
-        return Speed.FAST;
-    }
-
     // ---- Implements JavaScanner ----
 
     @Nullable
@@ -80,39 +71,25 @@
     }
 
     @Override
-    public void visitMethod(@NonNull JavaContext context, @Nullable AstVisitor visitor,
-            @NonNull MethodInvocation node) {
-        // Ignore if the method doesn't fit our description.
-        ResolvedNode resolved = context.resolve(node);
-        if (!(resolved instanceof ResolvedMethod)) {
+    public void visitMethod(@NonNull JavaContext context, @Nullable JavaElementVisitor visitor,
+            @NonNull PsiMethodCallExpression node, @NonNull PsiMethod method) {
+        if (!context.getEvaluator().isMemberInSubClassOf(method, CIPHER, false)) {
             return;
         }
-        ResolvedMethod method = (ResolvedMethod) resolved;
-        if (!method.getContainingClass().isSubclassOf(CIPHER, false)) {
-            return;
-        }
-        StrictListAccessor<Expression, MethodInvocation> argumentList = node.astArguments();
-        if (argumentList != null && argumentList.size() == 1) {
-            Expression expression = argumentList.first();
-            if (expression instanceof StringLiteral) {
-                StringLiteral argument = (StringLiteral)expression;
-                String parameter = argument.astValue();
-                checkParameter(context, node, argument, parameter, false);
-            } else {
-                ResolvedNode resolve = context.resolve(expression);
-                if (resolve instanceof ResolvedField) {
-                    ResolvedField field = (ResolvedField) resolve;
-                    Object value = field.getValue();
-                    if (value instanceof String) {
-                        checkParameter(context, node, expression, (String)value, true);
-                    }
-                }
+        PsiExpressionList argumentList = node.getArgumentList();
+        PsiExpression[] arguments = argumentList.getExpressions();
+        if (arguments.length == 1) {
+            PsiExpression expression = arguments[0];
+            Object value = ConstantEvaluator.evaluate(context, expression);
+            if (value instanceof String) {
+                checkParameter(context, node, expression, (String)value,
+                        !(expression instanceof PsiLiteral));
             }
         }
     }
 
     private static void checkParameter(@NonNull JavaContext context,
-            @NonNull MethodInvocation call, @NonNull Node node, @NonNull String value,
+            @NonNull PsiMethodCallExpression call, @NonNull PsiElement node, @NonNull String value,
             boolean includeValue) {
         if (ALGORITHM_ONLY.contains(value)) {
             String message = "`Cipher.getInstance` should not be called without setting the"
diff --git a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/CleanupDetector.java b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/CleanupDetector.java
index ba32081..e296e50 100644
--- a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/CleanupDetector.java
+++ b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/CleanupDetector.java
@@ -16,20 +16,18 @@
 
 package com.android.tools.lint.checks;
 
+import static com.android.SdkConstants.CLASS_CONTENTPROVIDER;
 import static com.android.SdkConstants.CLASS_CONTEXT;
+import static com.android.tools.lint.detector.api.LintUtils.skipParentheses;
+import static com.intellij.psi.util.PsiTreeUtil.getParentOfType;
 
 import com.android.SdkConstants;
 import com.android.annotations.NonNull;
 import com.android.annotations.Nullable;
-import com.android.tools.lint.client.api.JavaParser.ResolvedClass;
-import com.android.tools.lint.client.api.JavaParser.ResolvedField;
-import com.android.tools.lint.client.api.JavaParser.ResolvedMethod;
-import com.android.tools.lint.client.api.JavaParser.ResolvedNode;
-import com.android.tools.lint.client.api.JavaParser.ResolvedVariable;
-import com.android.tools.lint.client.api.JavaParser.TypeDescriptor;
+import com.android.tools.lint.client.api.JavaEvaluator;
 import com.android.tools.lint.detector.api.Category;
 import com.android.tools.lint.detector.api.Detector;
-import com.android.tools.lint.detector.api.Detector.JavaScanner;
+import com.android.tools.lint.detector.api.Detector.JavaPsiScanner;
 import com.android.tools.lint.detector.api.Implementation;
 import com.android.tools.lint.detector.api.Issue;
 import com.android.tools.lint.detector.api.JavaContext;
@@ -37,28 +35,42 @@
 import com.android.tools.lint.detector.api.Scope;
 import com.android.tools.lint.detector.api.Severity;
 import com.google.common.collect.Lists;
+import com.intellij.psi.JavaElementVisitor;
+import com.intellij.psi.JavaRecursiveElementVisitor;
+import com.intellij.psi.PsiAssertStatement;
+import com.intellij.psi.PsiAssignmentExpression;
+import com.intellij.psi.PsiClass;
+import com.intellij.psi.PsiClassType;
+import com.intellij.psi.PsiCodeBlock;
+import com.intellij.psi.PsiDeclarationStatement;
+import com.intellij.psi.PsiDoWhileStatement;
+import com.intellij.psi.PsiElement;
+import com.intellij.psi.PsiExpression;
+import com.intellij.psi.PsiExpressionStatement;
+import com.intellij.psi.PsiField;
+import com.intellij.psi.PsiIfStatement;
+import com.intellij.psi.PsiLocalVariable;
+import com.intellij.psi.PsiMethod;
+import com.intellij.psi.PsiMethodCallExpression;
+import com.intellij.psi.PsiNewExpression;
+import com.intellij.psi.PsiReference;
+import com.intellij.psi.PsiReferenceExpression;
+import com.intellij.psi.PsiReturnStatement;
+import com.intellij.psi.PsiStatement;
+import com.intellij.psi.PsiType;
+import com.intellij.psi.PsiWhileStatement;
 
 import java.util.Arrays;
 import java.util.List;
 
-import lombok.ast.AstVisitor;
-import lombok.ast.BinaryExpression;
-import lombok.ast.BinaryOperator;
-import lombok.ast.ConstructorInvocation;
-import lombok.ast.Expression;
-import lombok.ast.ForwardingAstVisitor;
-import lombok.ast.MethodInvocation;
-import lombok.ast.Node;
-import lombok.ast.Return;
-import lombok.ast.StrictListAccessor;
-import lombok.ast.VariableDefinitionEntry;
-import lombok.ast.VariableReference;
+// TODO: Make sure I handle this for the SharedPrefs editor code!:
+//                  // If you just do "return editor.commit() don't warn
 
 /**
  * Checks for missing {@code recycle} calls on resources that encourage it, and
  * for missing {@code commit} calls on FragmentTransactions, etc.
  */
-public class CleanupDetector extends Detector implements JavaScanner {
+public class CleanupDetector extends Detector implements JavaPsiScanner {
 
     private static final Implementation IMPLEMENTATION = new Implementation(
             CleanupDetector.class,
@@ -90,6 +102,21 @@
             Severity.WARNING,
             IMPLEMENTATION);
 
+    /** The main issue discovered by this detector */
+    public static final Issue SHARED_PREF = Issue.create(
+            "CommitPrefEdits", //$NON-NLS-1$
+            "Missing `commit()` on `SharedPreference` editor",
+
+            "After calling `edit()` on a `SharedPreference`, you must call `commit()` " +
+            "or `apply()` on the editor to save the results.",
+
+            Category.CORRECTNESS,
+            6,
+            Severity.WARNING,
+            new Implementation(
+                    CleanupDetector.class,
+                    Scope.JAVA_FILE_SCOPE));
+
     // Target method names
     private static final String RECYCLE = "recycle";                                  //$NON-NLS-1$
     private static final String RELEASE = "release";                                  //$NON-NLS-1$
@@ -102,16 +129,17 @@
     private static final String OBTAIN_STYLED_ATTRIBUTES = "obtainStyledAttributes";  //$NON-NLS-1$
     private static final String BEGIN_TRANSACTION = "beginTransaction";               //$NON-NLS-1$
     private static final String COMMIT = "commit";                                    //$NON-NLS-1$
+    private static final String APPLY = "apply";                                      //$NON-NLS-1$
     private static final String COMMIT_ALLOWING_LOSS = "commitAllowingStateLoss";     //$NON-NLS-1$
     private static final String QUERY = "query";                                      //$NON-NLS-1$
     private static final String RAW_QUERY = "rawQuery";                               //$NON-NLS-1$
     private static final String QUERY_WITH_FACTORY = "queryWithFactory";              //$NON-NLS-1$
     private static final String RAW_QUERY_WITH_FACTORY = "rawQueryWithFactory";       //$NON-NLS-1$
     private static final String CLOSE = "close";                                      //$NON-NLS-1$
+    private static final String EDIT = "edit";                                        //$NON-NLS-1$
 
     private static final String MOTION_EVENT_CLS = "android.view.MotionEvent";        //$NON-NLS-1$
     private static final String PARCEL_CLS = "android.os.Parcel";                     //$NON-NLS-1$
-    private static final String TYPED_ARRAY_CLS = "android.content.res.TypedArray";   //$NON-NLS-1$
     private static final String VELOCITY_TRACKER_CLS = "android.view.VelocityTracker";//$NON-NLS-1$
     private static final String DIALOG_FRAGMENT = "android.app.DialogFragment";       //$NON-NLS-1$
     private static final String DIALOG_V4_FRAGMENT =
@@ -131,11 +159,16 @@
             = "android.content.ContentProviderClient";
 
     public static final String CONTENT_RESOLVER_CLS = "android.content.ContentResolver";
-    public static final String CONTENT_PROVIDER_CLS = "android.content.ContentProvider";
+
     @SuppressWarnings("SpellCheckingInspection")
     public static final String SQLITE_DATABASE_CLS = "android.database.sqlite.SQLiteDatabase";
     public static final String CURSOR_CLS = "android.database.Cursor";
 
+    public static final String ANDROID_CONTENT_SHARED_PREFERENCES =
+            "android.content.SharedPreferences"; //$NON-NLS-1$
+    private static final String ANDROID_CONTENT_SHARED_PREFERENCES_EDITOR =
+            "android.content.SharedPreferences.Editor"; //$NON-NLS-1$
+
     /** Constructs a new {@link CleanupDetector} */
     public CleanupDetector() {
     }
@@ -159,7 +192,10 @@
                 ACQUIRE_CPC,
 
                 // Cursor close check
-                QUERY, RAW_QUERY, QUERY_WITH_FACTORY, RAW_QUERY_WITH_FACTORY
+                QUERY, RAW_QUERY, QUERY_WITH_FACTORY, RAW_QUERY_WITH_FACTORY,
+
+                // SharedPreferences check
+                EDIT
         );
     }
 
@@ -170,60 +206,70 @@
     }
 
     @Override
-    public void visitMethod(@NonNull JavaContext context, @Nullable AstVisitor visitor,
-            @NonNull MethodInvocation node) {
-
-        String name = node.astName().astValue();
+    public void visitMethod(@NonNull JavaContext context, @Nullable JavaElementVisitor visitor,
+            @NonNull PsiMethodCallExpression call, @NonNull PsiMethod method) {
+        String name = method.getName();
         if (BEGIN_TRANSACTION.equals(name)) {
-            checkTransactionCommits(context, node);
+            checkTransactionCommits(context, call, method);
+        } else if (EDIT.equals(name)) {
+            checkEditorApplied(context, call, method);
         } else {
-            checkResourceRecycled(context, node, name);
+            checkResourceRecycled(context, call, method);
         }
     }
 
     @Override
-    public void visitConstructor(@NonNull JavaContext context, @Nullable AstVisitor visitor,
-            @NonNull ConstructorInvocation node, @NonNull ResolvedMethod constructor) {
-        checkRecycled(context, node, constructor.getContainingClass().getSignature(), RELEASE);
+    public void visitConstructor(@NonNull JavaContext context, @Nullable JavaElementVisitor visitor,
+            @NonNull PsiNewExpression node, @NonNull PsiMethod constructor) {
+        PsiClass containingClass = constructor.getContainingClass();
+        if (containingClass != null) {
+            String type = containingClass.getQualifiedName();
+            if (type != null) {
+                checkRecycled(context, node, type, RELEASE);
+            }
+        }
     }
 
     private static void checkResourceRecycled(@NonNull JavaContext context,
-            @NonNull MethodInvocation node, @NonNull String name) {
+            @NonNull PsiMethodCallExpression node, @NonNull PsiMethod method) {
+        String name = method.getName();
         // Recycle detector
-        ResolvedNode resolved = context.resolve(node);
-        if (!(resolved instanceof ResolvedMethod)) {
+        PsiClass containingClass = method.getContainingClass();
+        if (containingClass == null) {
             return;
         }
-        ResolvedMethod method = (ResolvedMethod) resolved;
-        ResolvedClass containingClass = method.getContainingClass();
+        JavaEvaluator evaluator = context.getEvaluator();
         if ((OBTAIN.equals(name) || OBTAIN_NO_HISTORY.equals(name)) &&
-                containingClass.isSubclassOf(MOTION_EVENT_CLS, false)) {
+                evaluator.extendsClass(containingClass, MOTION_EVENT_CLS, false)) {
             checkRecycled(context, node, MOTION_EVENT_CLS, RECYCLE);
-        } else if (OBTAIN.equals(name) && containingClass.isSubclassOf(PARCEL_CLS, false)) {
+        } else if (OBTAIN.equals(name) && evaluator.extendsClass(containingClass, PARCEL_CLS, false)) {
             checkRecycled(context, node, PARCEL_CLS, RECYCLE);
         } else if (OBTAIN.equals(name) &&
-                containingClass.isSubclassOf(VELOCITY_TRACKER_CLS, false)) {
+                evaluator.extendsClass(containingClass, VELOCITY_TRACKER_CLS, false)) {
             checkRecycled(context, node, VELOCITY_TRACKER_CLS, RECYCLE);
         } else if ((OBTAIN_STYLED_ATTRIBUTES.equals(name)
                 || OBTAIN_ATTRIBUTES.equals(name)
                 || OBTAIN_TYPED_ARRAY.equals(name)) &&
-                (containingClass.isSubclassOf(CLASS_CONTEXT, false) ||
-                        containingClass.isSubclassOf(SdkConstants.CLASS_RESOURCES, false))) {
-            TypeDescriptor returnType = method.getReturnType();
-            if (returnType != null && returnType.matchesSignature(TYPED_ARRAY_CLS)) {
-                checkRecycled(context, node, TYPED_ARRAY_CLS, RECYCLE);
+                (evaluator.extendsClass(containingClass, CLASS_CONTEXT, false) ||
+                        evaluator.extendsClass(containingClass, SdkConstants.CLASS_RESOURCES, false))) {
+            PsiType returnType = method.getReturnType();
+            if (returnType instanceof PsiClassType) {
+                PsiClass cls = ((PsiClassType)returnType).resolve();
+                if (cls != null && SdkConstants.CLS_TYPED_ARRAY.equals(cls.getQualifiedName())) {
+                    checkRecycled(context, node, SdkConstants.CLS_TYPED_ARRAY, RECYCLE);
+                }
             }
-        } else if (ACQUIRE_CPC.equals(name) && containingClass.isSubclassOf(
+        } else if (ACQUIRE_CPC.equals(name) && evaluator.extendsClass(containingClass,
                 CONTENT_RESOLVER_CLS, false)) {
             checkRecycled(context, node, CONTENT_PROVIDER_CLIENT_CLS, RELEASE);
         } else if ((QUERY.equals(name)
                 || RAW_QUERY.equals(name)
                 || QUERY_WITH_FACTORY.equals(name)
                 || RAW_QUERY_WITH_FACTORY.equals(name))
-                && (containingClass.isSubclassOf(SQLITE_DATABASE_CLS, false) ||
-                    containingClass.isSubclassOf(CONTENT_RESOLVER_CLS, false) ||
-                    containingClass.isSubclassOf(CONTENT_PROVIDER_CLS, false) ||
-                    containingClass.isSubclassOf(CONTENT_PROVIDER_CLIENT_CLS, false))) {
+                && (evaluator.extendsClass(containingClass, SQLITE_DATABASE_CLS, false) ||
+                    evaluator.extendsClass(containingClass, CONTENT_RESOLVER_CLS, false) ||
+                    evaluator.extendsClass(containingClass, CLASS_CONTENTPROVIDER, false) ||
+                    evaluator.extendsClass(containingClass, CONTENT_PROVIDER_CLIENT_CLS, false))) {
             // Other potential cursors-returning methods that should be tracked:
             //    android.app.DownloadManager#query
             //    android.content.ContentProviderClient#query
@@ -242,37 +288,34 @@
         }
     }
 
-    private static void checkRecycled(@NonNull final JavaContext context, @NonNull Node node,
+    private static void checkRecycled(@NonNull final JavaContext context, @NonNull PsiElement node,
             @NonNull final String recycleType, @NonNull final String recycleName) {
-        Node variableNode = getVariableNode(node);
-        if (variableNode == null) {
-            return;
-        }
-        ResolvedVariable boundVariable = getResolvedVariable(context, variableNode);
+        PsiLocalVariable boundVariable = getVariableElement(node);
         if (boundVariable == null) {
             return;
         }
 
-        Node method = JavaContext.findSurroundingMethod(node);
+        PsiMethod method = getParentOfType(node, PsiMethod.class, true);
         if (method == null) {
             return;
         }
-        FinishVisitor visitor = new FinishVisitor(context, variableNode, boundVariable) {
+        FinishVisitor visitor = new FinishVisitor(context, boundVariable) {
             @Override
-            protected boolean isCleanupCall(@NonNull MethodInvocation call) {
-                String methodName = call.astName().astValue();
+            protected boolean isCleanupCall(@NonNull PsiMethodCallExpression call) {
+                PsiReferenceExpression methodExpression = call.getMethodExpression();
+                String methodName = methodExpression.getReferenceName();
                 if (!recycleName.equals(methodName)) {
                     return false;
                 }
-                ResolvedNode resolved = mContext.resolve(call);
-                if (resolved instanceof ResolvedMethod) {
-                    ResolvedClass containingClass = ((ResolvedMethod) resolved).getContainingClass();
-                    if (containingClass.isSubclassOf(recycleType, false)) {
+                PsiMethod method = call.resolveMethod();
+                if (method != null) {
+                    PsiClass containingClass = method.getContainingClass();
+                    if (mContext.getEvaluator().extendsClass(containingClass, recycleType, false)) {
                         // Yes, called the right recycle() method; now make sure
                         // we're calling it on the right variable
-                        Expression operand = call.astOperand();
-                        if (operand != null) {
-                            resolved = mContext.resolve(operand);
+                        PsiExpression operand = methodExpression.getQualifierExpression();
+                        if (operand instanceof PsiReferenceExpression) {
+                            PsiElement resolved = ((PsiReferenceExpression) operand).resolve();
                             //noinspection SuspiciousMethodCalls
                             if (resolved != null && mVariables.contains(resolved)) {
                                 return true;
@@ -299,49 +342,51 @@
                     "This `%1$s` should be freed up after use with `#%2$s()`", className,
                     recycleName);
         }
-        Node locationNode = node instanceof MethodInvocation ?
-                ((MethodInvocation) node).astName() : node;
+
+        PsiElement locationNode = node instanceof PsiMethodCallExpression ?
+                ((PsiMethodCallExpression)node).getMethodExpression().getReferenceNameElement() : node;
+        if (locationNode == null) {
+            locationNode = node;
+        }
         Location location = context.getLocation(locationNode);
         context.report(RECYCLE_RESOURCE, node, location, message);
     }
 
-    private static boolean checkTransactionCommits(@NonNull JavaContext context,
-            @NonNull MethodInvocation node) {
-        if (isBeginTransaction(context, node)) {
-            Node variableNode = getVariableNode(node);
-            ResolvedVariable boundVariable = variableNode != null
-                    ? getResolvedVariable(context, variableNode) : null;
+    private static void checkTransactionCommits(@NonNull JavaContext context,
+            @NonNull PsiMethodCallExpression node, @NonNull PsiMethod calledMethod) {
+        if (isBeginTransaction(context, calledMethod)) {
+            PsiLocalVariable boundVariable = getVariableElement(node);
             if (boundVariable == null && isCommittedInChainedCalls(context, node)) {
-                return true;
+                return;
             }
 
             if (boundVariable != null) {
-                Node method = JavaContext.findSurroundingMethod(node);
+                PsiMethod method = getParentOfType(node, PsiMethod.class, true);
                 if (method == null) {
-                    return true;
+                    return;
                 }
 
-                FinishVisitor commitVisitor = new FinishVisitor(context, variableNode,
-                        boundVariable) {
+                FinishVisitor commitVisitor = new FinishVisitor(context, boundVariable) {
                     @Override
-                    protected boolean isCleanupCall(@NonNull MethodInvocation call) {
+                    protected boolean isCleanupCall(@NonNull PsiMethodCallExpression call) {
                         if (isTransactionCommitMethodCall(mContext, call)) {
-                            Expression operand = call.astOperand();
+                            PsiExpression operand = call.getMethodExpression().getQualifierExpression();
                             if (operand != null) {
-                                ResolvedNode resolved = mContext.resolve(operand);
+                                PsiElement resolved = mContext.getEvaluator().resolve(operand);
                                 //noinspection SuspiciousMethodCalls
                                 if (resolved != null && mVariables.contains(resolved)) {
                                     return true;
-                                } else if (resolved instanceof ResolvedMethod
-                                        && operand instanceof MethodInvocation
-                                        && isCommittedInChainedCalls(mContext,(MethodInvocation) operand)) {
+                                } else if (resolved instanceof PsiMethod
+                                        && operand instanceof PsiMethodCallExpression
+                                        && isCommittedInChainedCalls(mContext,
+                                            (PsiMethodCallExpression) operand)) {
                                     // Check that the target of the committed chains is the
                                     // right variable!
-                                    while (operand instanceof MethodInvocation) {
-                                        operand = ((MethodInvocation)operand).astOperand();
+                                    while (operand instanceof PsiMethodCallExpression) {
+                                        operand = ((PsiMethodCallExpression)operand).getMethodExpression().getQualifierExpression();
                                     }
-                                    if (operand instanceof VariableReference) {
-                                        resolved = mContext.resolve(operand);
+                                    if (operand instanceof PsiReferenceExpression) {
+                                        resolved = ((PsiReferenceExpression)operand).resolve();
                                         //noinspection SuspiciousMethodCalls
                                         if (resolved != null && mVariables.contains(resolved)) {
                                             return true;
@@ -350,11 +395,10 @@
                                 }
                             }
                         } else if (isShowFragmentMethodCall(mContext, call)) {
-                            StrictListAccessor<Expression, MethodInvocation> arguments =
-                                    call.astArguments();
-                            if (arguments.size() == 2) {
-                                Expression first = arguments.first();
-                                ResolvedNode resolved = mContext.resolve(first);
+                            PsiExpression[] arguments = call.getArgumentList().getExpressions();
+                            if (arguments.length == 2) {
+                                PsiExpression first = arguments[0];
+                                PsiElement resolved = mContext.getEvaluator().resolve(first);
                                 //noinspection SuspiciousMethodCalls
                                 if (resolved != null && mVariables.contains(resolved)) {
                                     return true;
@@ -367,42 +411,45 @@
 
                 method.accept(commitVisitor);
                 if (commitVisitor.isCleanedUp() || commitVisitor.variableEscapes()) {
-                    return true;
+                    return;
                 }
             }
 
             String message = "This transaction should be completed with a `commit()` call";
-            context.report(COMMIT_FRAGMENT, node, context.getLocation(node.astName()),
-                    message);
+            context.report(COMMIT_FRAGMENT, node, context.getNameLocation(node), message);
         }
-        return false;
     }
 
     private static boolean isCommittedInChainedCalls(@NonNull JavaContext context,
-            @NonNull MethodInvocation node) {
+            @NonNull PsiMethodCallExpression node) {
         // Look for chained calls since the FragmentManager methods all return "this"
         // to allow constructor chaining, e.g.
         //    getFragmentManager().beginTransaction().addToBackStack("test")
         //            .disallowAddToBackStack().hide(mFragment2).setBreadCrumbShortTitle("test")
         //            .show(mFragment2).setCustomAnimations(0, 0).commit();
-        Node parent = node.getParent();
-        while (parent instanceof MethodInvocation) {
-            MethodInvocation methodInvocation = (MethodInvocation) parent;
-            if (isTransactionCommitMethodCall(context, methodInvocation)
-                    || isShowFragmentMethodCall(context, methodInvocation)) {
-                return true;
+        PsiElement parent = skipParentheses(node.getParent());
+        while (parent != null) {
+            if (parent instanceof PsiMethodCallExpression) {
+                PsiMethodCallExpression methodInvocation = (PsiMethodCallExpression) parent;
+                if (isTransactionCommitMethodCall(context, methodInvocation)
+                        || isShowFragmentMethodCall(context, methodInvocation)) {
+                    return true;
+                }
+            } else if (!(parent instanceof PsiReferenceExpression)) {
+                // reference expressions are method references
+                return false;
             }
 
-            parent = parent.getParent();
+            parent = skipParentheses(parent.getParent());
         }
 
         return false;
     }
 
     private static boolean isTransactionCommitMethodCall(@NonNull JavaContext context,
-            @NonNull MethodInvocation call) {
+            @NonNull PsiMethodCallExpression call) {
 
-        String methodName = call.astName().astValue();
+        String methodName = call.getMethodExpression().getReferenceName();
         return (COMMIT.equals(methodName) || COMMIT_ALLOWING_LOSS.equals(methodName)) &&
                 isMethodOnFragmentClass(context, call,
                         FRAGMENT_TRANSACTION_CLS,
@@ -411,8 +458,8 @@
     }
 
     private static boolean isShowFragmentMethodCall(@NonNull JavaContext context,
-            @NonNull MethodInvocation call) {
-        String methodName = call.astName().astValue();
+            @NonNull PsiMethodCallExpression call) {
+        String methodName = call.getMethodExpression().getReferenceName();
         return SHOW.equals(methodName)
                 && isMethodOnFragmentClass(context, call,
                 DIALOG_FRAGMENT, DIALOG_V4_FRAGMENT, true);
@@ -420,64 +467,247 @@
 
     private static boolean isMethodOnFragmentClass(
             @NonNull JavaContext context,
-            @NonNull MethodInvocation call,
+            @NonNull PsiMethodCallExpression call,
             @NonNull String fragmentClass,
             @NonNull String v4FragmentClass,
             boolean returnForUnresolved) {
-        ResolvedNode resolved = context.resolve(call);
-        if (resolved instanceof ResolvedMethod) {
-            ResolvedClass containingClass = ((ResolvedMethod) resolved).getContainingClass();
-            return containingClass.isSubclassOf(fragmentClass, false) ||
-                    containingClass.isSubclassOf(v4FragmentClass, false);
-        } else if (resolved == null) {
+        PsiMethod method = call.resolveMethod();
+        if (method != null) {
+            PsiClass containingClass = method.getContainingClass();
+            JavaEvaluator evaluator = context.getEvaluator();
+            return evaluator.extendsClass(containingClass, fragmentClass, false) ||
+                    evaluator.extendsClass(containingClass, v4FragmentClass, false);
+        } else {
             // If we *can't* resolve the method call, caller can decide
             // whether to consider the method called or not
             return returnForUnresolved;
         }
+    }
+
+    private static void checkEditorApplied(@NonNull JavaContext context,
+            @NonNull PsiMethodCallExpression node, @NonNull PsiMethod calledMethod) {
+        if (isSharedEditorCreation(context, calledMethod)) {
+            PsiLocalVariable boundVariable = getVariableElement(node, true);
+            if (isEditorCommittedInChainedCalls(context, node)) {
+                return;
+            }
+
+            if (boundVariable != null) {
+                PsiMethod method = getParentOfType(node, PsiMethod.class, true);
+                if (method == null) {
+                    return;
+                }
+
+                FinishVisitor commitVisitor = new FinishVisitor(context, boundVariable) {
+                    @Override
+                    protected boolean isCleanupCall(@NonNull PsiMethodCallExpression call) {
+                        if (isEditorApplyMethodCall(mContext, call)
+                                || isEditorCommitMethodCall(mContext, call)) {
+                            PsiExpression operand = call.getMethodExpression().getQualifierExpression();
+                            if (operand != null) {
+                                PsiElement resolved = mContext.getEvaluator().resolve(operand);
+                                //noinspection SuspiciousMethodCalls
+                                if (resolved != null && mVariables.contains(resolved)) {
+                                    return true;
+                                } else if (resolved instanceof PsiMethod
+                                        && operand instanceof PsiMethodCallExpression
+                                        && isCommittedInChainedCalls(mContext,
+                                        (PsiMethodCallExpression) operand)) {
+                                    // Check that the target of the committed chains is the
+                                    // right variable!
+                                    while (operand instanceof PsiMethodCallExpression) {
+                                        operand = ((PsiMethodCallExpression)operand).
+                                                getMethodExpression().getQualifierExpression();
+                                    }
+                                    if (operand instanceof PsiReferenceExpression) {
+                                        resolved = ((PsiReferenceExpression)operand).resolve();
+                                        //noinspection SuspiciousMethodCalls
+                                        if (resolved != null && mVariables.contains(resolved)) {
+                                            return true;
+                                        }
+                                    }
+                                }
+                            }
+                        }
+                        return false;
+                    }
+                };
+
+                method.accept(commitVisitor);
+                if (commitVisitor.isCleanedUp() || commitVisitor.variableEscapes()) {
+                    return;
+                }
+            }
+
+            String message = "`SharedPreferences.edit()` without a corresponding `commit()` or "
+                    + "`apply()` call";
+            context.report(SHARED_PREF, node, context.getLocation(node), message);
+        }
+    }
+
+    private static boolean isSharedEditorCreation(@NonNull JavaContext context,
+            @NonNull PsiMethod method) {
+        String methodName = method.getName();
+        if (EDIT.equals(methodName)) {
+            PsiClass containingClass = method.getContainingClass();
+            JavaEvaluator evaluator = context.getEvaluator();
+            return evaluator.extendsClass(containingClass, ANDROID_CONTENT_SHARED_PREFERENCES,
+                    false);
+        }
 
         return false;
     }
 
-    @Nullable
-    public static Node getVariableNode(@NonNull Node expression) {
-        Node parent = expression.getParent();
-        if (parent instanceof BinaryExpression) {
-            BinaryExpression binaryExpression = (BinaryExpression) parent;
-            if (binaryExpression.astOperator() == BinaryOperator.ASSIGN) {
-                return binaryExpression.astLeft();
-            }
-        } else if (parent instanceof VariableDefinitionEntry) {
-            return parent;
-        }
-
-        return null;
-    }
-
-    @Nullable
-    public static ResolvedVariable getResolvedVariable(@NonNull JavaContext context,
-            @NonNull Node variable) {
-        ResolvedNode resolved = context.resolve(variable);
-        if (resolved instanceof ResolvedVariable) {
-            return (ResolvedVariable) resolved;
-        }
-
-        return null;
-    }
-
-    private static boolean isBeginTransaction(@NonNull JavaContext context,
-            @NonNull MethodInvocation node) {
-        String methodName = node.astName().astValue();
-        assert methodName.equals(BEGIN_TRANSACTION) : methodName;
-        if (BEGIN_TRANSACTION.equals(methodName)) {
-            ResolvedNode resolved = context.resolve(node);
-            if (resolved instanceof ResolvedMethod) {
-                ResolvedMethod method = (ResolvedMethod) resolved;
-                ResolvedClass containingClass = method.getContainingClass();
-                if (containingClass.isSubclassOf(FRAGMENT_MANAGER_CLS, false)
-                        || containingClass.isSubclassOf(FRAGMENT_MANAGER_V4_CLS,
-                        false)) {
+    private static boolean isEditorCommittedInChainedCalls(@NonNull JavaContext context,
+            @NonNull PsiMethodCallExpression node) {
+        PsiElement parent = skipParentheses(node.getParent());
+        while (parent != null) {
+            if (parent instanceof PsiMethodCallExpression) {
+                PsiMethodCallExpression methodInvocation = (PsiMethodCallExpression) parent;
+                if (isEditorCommitMethodCall(context, methodInvocation)
+                        || isEditorApplyMethodCall(context, methodInvocation)) {
                     return true;
                 }
+            } else if (!(parent instanceof PsiReferenceExpression)) {
+                // reference expressions are method references
+                return false;
+            }
+
+            parent = skipParentheses(parent.getParent());
+        }
+
+        return false;
+    }
+
+    private static boolean isEditorCommitMethodCall(@NonNull JavaContext context,
+            @NonNull PsiMethodCallExpression call) {
+        String methodName = call.getMethodExpression().getReferenceName();
+        if (COMMIT.equals(methodName)) {
+            PsiMethod method = call.resolveMethod();
+            if (method != null) {
+                PsiClass containingClass = method.getContainingClass();
+                JavaEvaluator evaluator = context.getEvaluator();
+                if (evaluator.extendsClass(containingClass,
+                        ANDROID_CONTENT_SHARED_PREFERENCES_EDITOR, false)) {
+                    suggestApplyIfApplicable(context, call);
+                    return true;
+                }
+            }
+        }
+
+        return false;
+    }
+
+    private static boolean isEditorApplyMethodCall(@NonNull JavaContext context,
+            @NonNull PsiMethodCallExpression call) {
+        String methodName = call.getMethodExpression().getReferenceName();
+        if (APPLY.equals(methodName)) {
+            PsiMethod method = call.resolveMethod();
+            if (method != null) {
+                PsiClass containingClass = method.getContainingClass();
+                JavaEvaluator evaluator = context.getEvaluator();
+                return evaluator.extendsClass(containingClass,
+                        ANDROID_CONTENT_SHARED_PREFERENCES_EDITOR, false);
+            }
+        }
+
+        return false;
+    }
+
+    private static void suggestApplyIfApplicable(@NonNull JavaContext context,
+            @NonNull PsiMethodCallExpression node) {
+        if (context.getProject().getMinSdkVersion().getApiLevel() >= 9) {
+            // See if the return value is read: can only replace commit with
+            // apply if the return value is not considered
+            PsiElement parent = skipParentheses(node.getParent());
+            while (parent instanceof PsiReferenceExpression) {
+                parent = skipParentheses(parent.getParent());
+            }
+            boolean returnValueIgnored = false;
+            if (parent instanceof PsiMethodCallExpression ||
+                    parent instanceof PsiNewExpression ||
+                    parent instanceof PsiClass ||
+                    parent instanceof PsiCodeBlock ||
+                    parent instanceof PsiExpressionStatement) {
+                returnValueIgnored = true;
+            } else if (parent instanceof PsiStatement) {
+                if (parent instanceof PsiIfStatement) {
+                    returnValueIgnored = ((PsiIfStatement)parent).getCondition() != node;
+                } else if (parent instanceof PsiWhileStatement) {
+                    returnValueIgnored = ((PsiWhileStatement)parent).getCondition() != node;
+                } else if (parent instanceof PsiDoWhileStatement) {
+                    returnValueIgnored = ((PsiDoWhileStatement)parent).getCondition() != node;
+                } else if (parent instanceof PsiAssertStatement) {
+                    returnValueIgnored = ((PsiAssertStatement)parent).getAssertCondition() != node;
+                } else if (parent instanceof PsiReturnStatement
+                        || parent instanceof PsiDeclarationStatement) {
+                    returnValueIgnored = false;
+                } else {
+                    returnValueIgnored = true;
+                }
+            }
+            if (returnValueIgnored) {
+                String message = "Consider using `apply()` instead; `commit` writes "
+                        + "its data to persistent storage immediately, whereas "
+                        + "`apply` will handle it in the background";
+                context.report(SHARED_PREF, node, context.getLocation(node), message);
+            }
+        }
+    }
+
+    /** Returns the variable the expression is assigned to, if any */
+    @Nullable
+    public static PsiLocalVariable getVariableElement(@NonNull PsiElement rhs) {
+        return getVariableElement(rhs, false);
+    }
+
+    @Nullable
+    public static PsiLocalVariable getVariableElement(@NonNull PsiElement rhs,
+            boolean allowChainedCalls) {
+        PsiElement parent = skipParentheses(rhs.getParent());
+
+        // Handle some types of chained calls; e.g. you might have
+        //    var = prefs.edit().put(key,value)
+        // and here we want to skip past the put call
+        if (allowChainedCalls) {
+            while (true) {
+                if ((parent instanceof PsiReferenceExpression)) {
+                    PsiElement parentParent = skipParentheses(parent.getParent());
+                    if ((parentParent instanceof PsiMethodCallExpression)) {
+                        parent = skipParentheses(parentParent.getParent());
+                    } else {
+                        break;
+                    }
+                } else {
+                    break;
+                }
+            }
+        }
+
+        if (parent instanceof PsiAssignmentExpression) {
+            PsiAssignmentExpression assignment = (PsiAssignmentExpression) parent;
+            PsiExpression lhs = assignment.getLExpression();
+            if (lhs instanceof PsiReference) {
+                PsiElement resolved = ((PsiReference)lhs).resolve();
+                if (resolved instanceof PsiLocalVariable) {
+                    return (PsiLocalVariable) resolved;
+                }
+            }
+        } else if (parent instanceof PsiLocalVariable) {
+            return (PsiLocalVariable) parent;
+        }
+
+        return null;
+    }
+
+    private static boolean isBeginTransaction(@NonNull JavaContext context, @NonNull PsiMethod method) {
+        String methodName = method.getName();
+        if (BEGIN_TRANSACTION.equals(methodName)) {
+            PsiClass containingClass = method.getContainingClass();
+            JavaEvaluator evaluator = context.getEvaluator();
+            if (evaluator.extendsClass(containingClass, FRAGMENT_MANAGER_CLS, false)
+                    || evaluator.extendsClass(containingClass, FRAGMENT_MANAGER_V4_CLS, false)) {
+                return true;
             }
         }
 
@@ -490,19 +720,18 @@
      * case of a TypedArray we're looking for a "recycle", call, in the
      * case of a database cursor we're looking for a "close" call, etc.
      */
-    private abstract static class FinishVisitor extends ForwardingAstVisitor {
+    private abstract static class FinishVisitor extends JavaRecursiveElementVisitor {
         protected final JavaContext mContext;
-        protected final List<ResolvedVariable> mVariables;
-        private final Node mOriginalVariableNode;
+        protected final List<PsiLocalVariable> mVariables;
+        private final PsiLocalVariable mOriginalVariableNode;
 
         private boolean mContainsCleanup;
         private boolean mEscapes;
 
-        public FinishVisitor(JavaContext context, @NonNull Node variableNode,
-                @NonNull ResolvedVariable variable) {
+        public FinishVisitor(JavaContext context, @NonNull PsiLocalVariable variableNode) {
             mContext = context;
             mOriginalVariableNode = variableNode;
-            mVariables = Lists.newArrayList(variable);
+            mVariables = Lists.newArrayList(variableNode);
         }
 
         public boolean isCleanedUp() {
@@ -514,40 +743,40 @@
         }
 
         @Override
-        public boolean visitNode(Node node) {
-            return mContainsCleanup || super.visitNode(node);
+        public void visitElement(PsiElement element) {
+            if (!mContainsCleanup) {
+                super.visitElement(element);
+            }
         }
 
-        protected abstract boolean isCleanupCall(@NonNull MethodInvocation call);
+        protected abstract boolean isCleanupCall(@NonNull PsiMethodCallExpression call);
 
         @Override
-        public boolean visitMethodInvocation(MethodInvocation call) {
+        public void visitMethodCallExpression(PsiMethodCallExpression call) {
             if (mContainsCleanup) {
-                return true;
+                return;
             }
 
-            super.visitMethodInvocation(call);
+            super.visitMethodCallExpression(call);
 
             // Look for escapes
             if (!mEscapes) {
-                for (Expression expression : call.astArguments()) {
-                    if (expression instanceof VariableReference) {
-                        ResolvedNode resolved = mContext.resolve(expression);
+                for (PsiExpression expression : call.getArgumentList().getExpressions()) {
+                    if (expression instanceof PsiReferenceExpression) {
+                        PsiElement resolved = ((PsiReferenceExpression) expression).resolve();
                         //noinspection SuspiciousMethodCalls
                         if (resolved != null && mVariables.contains(resolved)) {
+                            boolean wasEscaped = mEscapes;
                             mEscapes = true;
 
                             // Special case: MotionEvent.obtain(MotionEvent): passing in an
                             // event here does not recycle the event, and we also know it
                             // doesn't escape
-                            if (OBTAIN.equals(call.astName().astValue())) {
-                                ResolvedNode r = mContext.resolve(call);
-                                if (r instanceof ResolvedMethod) {
-                                    ResolvedMethod method = (ResolvedMethod) r;
-                                    ResolvedClass cls = method.getContainingClass();
-                                    if (cls.matches(MOTION_EVENT_CLS)) {
-                                        mEscapes = false;
-                                    }
+                            if (OBTAIN.equals(call.getMethodExpression().getReferenceName())) {
+                                PsiMethod method = call.resolveMethod();
+                                if (mContext.getEvaluator()
+                                        .isMemberInClass(method, MOTION_EVENT_CLS)) {
+                                    mEscapes = wasEscaped;
                                 }
                             }
                         }
@@ -557,81 +786,72 @@
 
             if (isCleanupCall(call)) {
                 mContainsCleanup = true;
-                return true;
-            } else {
-                return false;
             }
         }
 
         @Override
-        public boolean visitVariableDefinitionEntry(VariableDefinitionEntry node) {
-            Expression initializer = node.astInitializer();
-            if (initializer instanceof VariableReference) {
-                ResolvedNode resolved = mContext.resolve(initializer);
+        public void visitLocalVariable(PsiLocalVariable variable) {
+            super.visitLocalVariable(variable);
+
+            PsiExpression initializer = variable.getInitializer();
+            if (initializer instanceof PsiReferenceExpression) {
+                PsiElement resolved = ((PsiReferenceExpression) initializer).resolve();
                 //noinspection SuspiciousMethodCalls
                 if (resolved != null && mVariables.contains(resolved)) {
-                    ResolvedNode resolvedVariable = mContext.resolve(node);
-                    if (resolvedVariable instanceof ResolvedVariable) {
-                        ResolvedVariable variable = (ResolvedVariable) resolvedVariable;
-                        mVariables.add(variable);
-                    } else if (resolvedVariable instanceof ResolvedField) {
+                    mVariables.add(variable);
+                }
+            }
+        }
+
+        @Override
+        public void visitAssignmentExpression(PsiAssignmentExpression expression) {
+            super.visitAssignmentExpression(expression);
+
+            // TEMPORARILY DISABLED; see testDatabaseCursorReassignment
+            // This can result in some false positives right now. Play it
+            // safe instead.
+            boolean clearLhs = false;
+
+            PsiExpression rhs = expression.getRExpression();
+            if (rhs instanceof PsiReferenceExpression) {
+                PsiElement resolved = ((PsiReferenceExpression) rhs).resolve();
+                //noinspection SuspiciousMethodCalls
+                if (resolved != null && mVariables.contains(resolved)) {
+                    clearLhs = false;
+                    PsiElement lhs = mContext.getEvaluator().resolve(expression.getLExpression());
+                    if (lhs instanceof PsiLocalVariable) {
+                        mVariables.add((PsiLocalVariable)lhs);
+                    } else if (lhs instanceof PsiField) {
                         mEscapes = true;
                     }
                 }
             }
-            return super.visitVariableDefinitionEntry(node);
-        }
 
-        @Override
-        public boolean visitBinaryExpression(BinaryExpression node) {
-            if (node.astOperator() == BinaryOperator.ASSIGN) {
-                Expression rhs = node.astRight();
-                // TEMPORARILY DISABLED; see testDatabaseCursorReassignment
-                // This can result in some false positives right now. Play it
-                // safe instead.
-                boolean clearLhs = false;
-                if (rhs instanceof VariableReference) {
-                    ResolvedNode resolved = mContext.resolve(rhs);
+            //noinspection ConstantConditions
+            if (clearLhs) {
+                // If we reassign one of the variables, clear it out
+                PsiElement lhs = mContext.getEvaluator().resolve(expression.getLExpression());
+                //noinspection SuspiciousMethodCalls
+                if (lhs != null && !lhs.equals(mOriginalVariableNode)
+                        && mVariables.contains(lhs)) {
                     //noinspection SuspiciousMethodCalls
-                    if (resolved != null && mVariables.contains(resolved)) {
-                        clearLhs = false;
-                        ResolvedNode resolvedLhs = mContext.resolve(node.astLeft());
-                        if (resolvedLhs instanceof ResolvedVariable) {
-                            ResolvedVariable variable = (ResolvedVariable) resolvedLhs;
-                            mVariables.add(variable);
-                        } else if (resolvedLhs instanceof ResolvedField) {
-                            mEscapes = true;
-                        }
-                    }
-                }
-
-                if (clearLhs) {
-                    // If we reassign one of the variables, clear it out
-                    Expression lhs = node.astLeft();
-                    ResolvedNode resolved = mContext.resolve(lhs);
-                    //noinspection SuspiciousMethodCalls
-                    if (resolved != null && !lhs.equals(mOriginalVariableNode)
-                            && mVariables.contains(resolved)) {
-                        //noinspection SuspiciousMethodCalls
-                        mVariables.remove(resolved);
-                    }
+                    mVariables.remove(lhs);
                 }
             }
-            return super.visitBinaryExpression(node);
         }
 
         @Override
-        public boolean visitReturn(Return node) {
-            Expression value = node.astValue();
-            if (value instanceof VariableReference) {
-                ResolvedNode resolved = mContext.resolve(value);
+        public void visitReturnStatement(PsiReturnStatement statement) {
+            PsiExpression returnValue = statement.getReturnValue();
+            if (returnValue instanceof PsiReference) {
+                PsiElement resolved = ((PsiReference) returnValue).resolve();
                 //noinspection SuspiciousMethodCalls
                 if (resolved != null && mVariables.contains(resolved)) {
                     mEscapes = true;
                 }
             }
 
-            return super.visitReturn(node);
+            super.visitReturnStatement(statement);
         }
     }
 }
diff --git a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/CommentDetector.java b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/CommentDetector.java
index f16296f..529f696 100644
--- a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/CommentDetector.java
+++ b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/CommentDetector.java
@@ -19,29 +19,26 @@
 import com.android.annotations.NonNull;
 import com.android.annotations.Nullable;
 import com.android.tools.lint.detector.api.Category;
-import com.android.tools.lint.detector.api.Context;
 import com.android.tools.lint.detector.api.Detector;
+import com.android.tools.lint.detector.api.Detector.JavaPsiScanner;
 import com.android.tools.lint.detector.api.Implementation;
 import com.android.tools.lint.detector.api.Issue;
 import com.android.tools.lint.detector.api.JavaContext;
 import com.android.tools.lint.detector.api.Location;
 import com.android.tools.lint.detector.api.Scope;
 import com.android.tools.lint.detector.api.Severity;
-import com.android.tools.lint.detector.api.Speed;
+import com.intellij.psi.JavaElementVisitor;
+import com.intellij.psi.PsiComment;
+import com.intellij.psi.PsiElement;
+import com.intellij.psi.PsiLiteralExpression;
 
-import java.io.File;
 import java.util.Collections;
 import java.util.List;
 
-import lombok.ast.AstVisitor;
-import lombok.ast.Comment;
-import lombok.ast.ForwardingAstVisitor;
-import lombok.ast.Node;
-
 /**
  * Looks for issues in Java comments
  */
-public class CommentDetector extends Detector implements Detector.JavaScanner {
+public class CommentDetector extends Detector implements JavaPsiScanner {
     private static final String STOPSHIP_COMMENT = "STOPSHIP"; //$NON-NLS-1$
 
     private static final Implementation IMPLEMENTATION = new Implementation(
@@ -77,7 +74,7 @@
 
     private static final String ESCAPE_STRING = "\\u002a\\u002f"; //$NON-NLS-1$
 
-    /** Lombok's AST only passes comment nodes for Javadoc so I need to do manual token scanning
+    /** The current AST only passes comment nodes for Javadoc so I need to do manual token scanning
          instead */
     private static final boolean USE_AST = false;
 
@@ -87,27 +84,17 @@
     }
 
     @Override
-    public boolean appliesTo(@NonNull Context context, @NonNull File file) {
-        return true;
-    }
-
-    @NonNull
-    @Override
-    public Speed getSpeed() {
-        return Speed.NORMAL;
-    }
-
-    @Override
-    public List<Class<? extends Node>> getApplicableNodeTypes() {
+    public List<Class<? extends PsiElement>> getApplicablePsiTypes() {
         if (USE_AST) {
-            return Collections.<Class<? extends Node>>singletonList(Comment.class);
+            return Collections.<Class<? extends PsiElement>>singletonList(
+                    PsiLiteralExpression.class);
         } else {
             return null;
         }
     }
 
     @Override
-    public AstVisitor createJavaVisitor(@NonNull JavaContext context) {
+    public JavaElementVisitor createPsiVisitor(@NonNull JavaContext context) {
         // Lombok does not generate comment nodes for block and line comments, only for
         // javadoc comments!
         if (USE_AST) {
@@ -148,7 +135,7 @@
         }
     }
 
-    private static class CommentChecker extends ForwardingAstVisitor {
+    private static class CommentChecker extends JavaElementVisitor {
         private final JavaContext mContext;
 
         public CommentChecker(JavaContext context) {
@@ -156,16 +143,16 @@
         }
 
         @Override
-        public boolean visitComment(Comment node) {
-            String contents = node.astContent();
-            checkComment(mContext, node, contents, node.getPosition().getStart(), 0, contents.length());
-            return super.visitComment(node);
+        public void visitComment(PsiComment comment) {
+            String contents = comment.getText();
+            checkComment(mContext, comment, contents, comment.getTextRange().getStartOffset(), 0,
+                    contents.length());
         }
     }
 
     private static void checkComment(
             @NonNull JavaContext context,
-            @Nullable Comment node,
+            @Nullable PsiComment node,
             @NonNull String source,
             int offset,
             int start,
diff --git a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/ControlFlowGraph.java b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/ControlFlowGraph.java
index 3a907de..3bda0b8 100644
--- a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/ControlFlowGraph.java
+++ b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/ControlFlowGraph.java
@@ -391,6 +391,7 @@
      * @return a dot description of this control flow graph,
      *    useful for debugging
      */
+    @SuppressWarnings("unused")
     public String toDot(@Nullable Set<Node> highlight) {
         StringBuilder sb = new StringBuilder();
         sb.append("digraph G {\n");
diff --git a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/CustomViewDetector.java b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/CustomViewDetector.java
index a243e40..85b53ba 100644
--- a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/CustomViewDetector.java
+++ b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/CustomViewDetector.java
@@ -16,42 +16,38 @@
 
 package com.android.tools.lint.checks;
 
+import static com.android.SdkConstants.CLASS_CONTEXT;
 import static com.android.SdkConstants.CLASS_VIEW;
 import static com.android.SdkConstants.CLASS_VIEWGROUP;
 import static com.android.SdkConstants.DOT_LAYOUT_PARAMS;
 import static com.android.SdkConstants.R_STYLEABLE_PREFIX;
+import static com.android.tools.lint.detector.api.LintUtils.skipParentheses;
 
 import com.android.annotations.NonNull;
 import com.android.annotations.Nullable;
-import com.android.tools.lint.client.api.JavaParser.ResolvedClass;
-import com.android.tools.lint.client.api.JavaParser.ResolvedNode;
 import com.android.tools.lint.detector.api.Category;
-import com.android.tools.lint.detector.api.Context;
 import com.android.tools.lint.detector.api.Detector;
 import com.android.tools.lint.detector.api.Implementation;
 import com.android.tools.lint.detector.api.Issue;
 import com.android.tools.lint.detector.api.JavaContext;
 import com.android.tools.lint.detector.api.Scope;
 import com.android.tools.lint.detector.api.Severity;
-import com.android.tools.lint.detector.api.Speed;
-import com.google.common.collect.Iterators;
+import com.intellij.psi.JavaElementVisitor;
+import com.intellij.psi.PsiClass;
+import com.intellij.psi.PsiExpression;
+import com.intellij.psi.PsiExpressionStatement;
+import com.intellij.psi.PsiMethod;
+import com.intellij.psi.PsiMethodCallExpression;
+import com.intellij.psi.util.PsiTreeUtil;
 
-import java.io.File;
 import java.util.Collections;
 import java.util.List;
 
-import lombok.ast.AstVisitor;
-import lombok.ast.ClassDeclaration;
-import lombok.ast.Expression;
-import lombok.ast.ExpressionStatement;
-import lombok.ast.MethodInvocation;
-import lombok.ast.StrictListAccessor;
-
 /**
  * Makes sure that custom views use a declare styleable that matches
  * the name of the custom view
  */
-public class CustomViewDetector extends Detector implements Detector.JavaScanner {
+public class CustomViewDetector extends Detector implements Detector.JavaPsiScanner {
 
     private static final Implementation IMPLEMENTATION = new Implementation(
             CustomViewDetector.class,
@@ -76,21 +72,10 @@
 
     private static final String OBTAIN_STYLED_ATTRIBUTES = "obtainStyledAttributes"; //$NON-NLS-1$
 
-    /** Constructs a new {@link com.android.tools.lint.checks.CustomViewDetector} check */
+    /** Constructs a new {@link CustomViewDetector} check */
     public CustomViewDetector() {
     }
 
-    @Override
-    public boolean appliesTo(@NonNull Context context, @NonNull File file) {
-        return true;
-    }
-
-    @NonNull
-    @Override
-    public Speed getSpeed() {
-        return Speed.FAST;
-    }
-
     // ---- Implements JavaScanner ----
 
     @Override
@@ -99,14 +84,14 @@
     }
 
     @Override
-    public void visitMethod(@NonNull JavaContext context, @Nullable AstVisitor visitor,
-            @NonNull MethodInvocation node) {
-        if (node.getParent() instanceof ExpressionStatement) {
-            if (!context.isContextMethod(node)) {
+    public void visitMethod(@NonNull JavaContext context, @Nullable JavaElementVisitor visitor,
+            @NonNull PsiMethodCallExpression node, @NonNull PsiMethod method) {
+        if (skipParentheses(node.getParent()) instanceof PsiExpressionStatement) {
+            if (!context.getEvaluator().isMemberInSubClassOf(method, CLASS_CONTEXT, false)) {
                 return;
             }
-            StrictListAccessor<Expression, MethodInvocation> expressions = node.astArguments();
-            int size = expressions.size();
+            PsiExpression[] arguments = node.getArgumentList().getExpressions();
+            int size = arguments.length;
             // Which parameter contains the styleable (attrs) ?
             int parameterIndex;
             if (size == 1) {
@@ -118,43 +103,34 @@
                 // obtainStyledAttributes(AttributeSet set, int[] attrs, int defStyleAttr, int defStyleRes)
                 parameterIndex = 1;
             }
-            Expression expression = Iterators.get(node.astArguments().iterator(), parameterIndex,
-                    null);
-            if (expression == null) {
-                return;
-            }
-            String s = expression.toString();
+            PsiExpression expression = arguments[parameterIndex];
+            String s = expression.getText();
             if (!s.startsWith(R_STYLEABLE_PREFIX)) {
                 return;
             }
             String styleableName = s.substring(R_STYLEABLE_PREFIX.length());
-            ClassDeclaration cls = JavaContext.findSurroundingClass(node);
+            PsiClass cls = PsiTreeUtil.getParentOfType(node, PsiClass.class, false);
             if (cls == null) {
                 return;
             }
 
-            ResolvedNode resolved = context.resolve(cls);
-            if (!(resolved instanceof ResolvedClass)) {
-                return;
-            }
-
-            String className = cls.astName().astValue();
-            ResolvedClass resolvedClass = (ResolvedClass) resolved;
-            if (resolvedClass.isSubclassOf(CLASS_VIEW, false)) {
+            String className = cls.getName();
+            if (context.getEvaluator().extendsClass(cls, CLASS_VIEW, false)) {
                 if (!styleableName.equals(className)) {
                     String message = String.format(
-                        "By convention, the custom view (`%1$s`) and the declare-styleable (`%2$s`) "
-                                + "should have the same name (various editor features rely on "
-                                + "this convention)",
-                        className, styleableName);
+                            "By convention, the custom view (`%1$s`) and the declare-styleable (`%2$s`) "
+                                    + "should have the same name (various editor features rely on "
+                                    + "this convention)",
+                            className, styleableName);
                     context.report(ISSUE, node, context.getLocation(expression), message);
                 }
-            } else if (resolvedClass.isSubclassOf(CLASS_VIEWGROUP + DOT_LAYOUT_PARAMS, false)) {
-                ClassDeclaration outer = JavaContext.findSurroundingClass(cls.getParent());
+            } else if (context.getEvaluator().extendsClass(cls,
+                    CLASS_VIEWGROUP + DOT_LAYOUT_PARAMS, false)) {
+                PsiClass outer = PsiTreeUtil.getParentOfType(cls, PsiClass.class, true);
                 if (outer == null) {
                     return;
                 }
-                String layoutClassName = outer.astName().astValue();
+                String layoutClassName = outer.getName();
                 String expectedName = layoutClassName + "_Layout";
                 if (!styleableName.equals(expectedName)) {
                     String message = String.format(
diff --git a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/CutPasteDetector.java b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/CutPasteDetector.java
index 07dbcd3..9388fdf 100644
--- a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/CutPasteDetector.java
+++ b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/CutPasteDetector.java
@@ -17,11 +17,12 @@
 package com.android.tools.lint.checks;
 
 import static com.android.SdkConstants.RESOURCE_CLZ_ID;
+import static com.android.tools.lint.detector.api.LintUtils.nextNonWhitespace;
+import static com.android.tools.lint.detector.api.LintUtils.skipParentheses;
 
 import com.android.annotations.NonNull;
 import com.android.annotations.Nullable;
 import com.android.tools.lint.detector.api.Category;
-import com.android.tools.lint.detector.api.Context;
 import com.android.tools.lint.detector.api.Detector;
 import com.android.tools.lint.detector.api.Implementation;
 import com.android.tools.lint.detector.api.Issue;
@@ -30,30 +31,33 @@
 import com.android.tools.lint.detector.api.Scope;
 import com.android.tools.lint.detector.api.Severity;
 import com.google.common.collect.Maps;
+import com.intellij.psi.JavaElementVisitor;
+import com.intellij.psi.PsiArrayAccessExpression;
+import com.intellij.psi.PsiAssignmentExpression;
+import com.intellij.psi.PsiBinaryExpression;
+import com.intellij.psi.PsiDoWhileStatement;
+import com.intellij.psi.PsiElement;
+import com.intellij.psi.PsiExpression;
+import com.intellij.psi.PsiForStatement;
+import com.intellij.psi.PsiForeachStatement;
+import com.intellij.psi.PsiIfStatement;
+import com.intellij.psi.PsiLocalVariable;
+import com.intellij.psi.PsiMethod;
+import com.intellij.psi.PsiMethodCallExpression;
+import com.intellij.psi.PsiReference;
+import com.intellij.psi.PsiReferenceExpression;
+import com.intellij.psi.PsiTypeCastExpression;
+import com.intellij.psi.PsiWhileStatement;
+import com.intellij.psi.util.PsiTreeUtil;
 
-import java.io.File;
 import java.util.Collections;
 import java.util.List;
 import java.util.Map;
 
-import lombok.ast.ArrayAccess;
-import lombok.ast.AstVisitor;
-import lombok.ast.BinaryExpression;
-import lombok.ast.Cast;
-import lombok.ast.Expression;
-import lombok.ast.ForwardingAstVisitor;
-import lombok.ast.If;
-import lombok.ast.MethodInvocation;
-import lombok.ast.Node;
-import lombok.ast.Select;
-import lombok.ast.Statement;
-import lombok.ast.VariableDefinitionEntry;
-import lombok.ast.VariableReference;
-
 /**
  * Detector looking for cut & paste issues
  */
-public class CutPasteDetector extends Detector implements Detector.JavaScanner {
+public class CutPasteDetector extends Detector implements Detector.JavaPsiScanner {
     /** The main issue discovered by this detector */
     public static final Issue ISSUE = Issue.create(
             "CutPasteId", //$NON-NLS-1$
@@ -73,8 +77,8 @@
                     CutPasteDetector.class,
                     Scope.JAVA_FILE_SCOPE));
 
-    private Node mLastMethod;
-    private Map<String, MethodInvocation> mIds;
+    private PsiMethod mLastMethod;
+    private Map<String, PsiMethodCallExpression> mIds;
     private Map<String, String> mLhs;
     private Map<String, String> mCallOperands;
 
@@ -82,11 +86,6 @@
     public CutPasteDetector() {
     }
 
-    @Override
-    public boolean appliesTo(@NonNull Context context, @NonNull File file) {
-        return true;
-    }
-
     // ---- Implements JavaScanner ----
 
     @Override
@@ -95,16 +94,16 @@
     }
 
     @Override
-    public void visitMethod(@NonNull JavaContext context, @Nullable AstVisitor visitor,
-            @NonNull MethodInvocation call) {
+    public void visitMethod(@NonNull JavaContext context, @Nullable JavaElementVisitor visitor,
+            @NonNull PsiMethodCallExpression call, @NonNull PsiMethod calledMethod) {
         String lhs = getLhs(call);
         if (lhs == null) {
             return;
         }
 
-        Node method = JavaContext.findSurroundingMethod(call);
+        PsiMethod method = PsiTreeUtil.getParentOfType(call, PsiMethod.class, false);
         if (method == null) {
-            return;
+            return; // prevent doing the same work for multiple findViewById calls in same method
         } else if (method != mLastMethod) {
             mIds = Maps.newHashMap();
             mLhs = Maps.newHashMap();
@@ -112,16 +111,22 @@
             mLastMethod = method;
         }
 
-        String callOperand = call.astOperand() != null ? call.astOperand().toString() : "";
+        PsiReferenceExpression methodExpression = call.getMethodExpression();
+        String callOperand = methodExpression.getQualifier() != null
+                ? methodExpression.getQualifier().getText() : "";
 
-        Expression first = call.astArguments().first();
-        if (first instanceof Select) {
-            Select select = (Select) first;
-            String id = select.astIdentifier().astValue();
-            Expression operand = select.astOperand();
-            if (operand instanceof Select) {
-                Select type = (Select) operand;
-                if (type.astIdentifier().astValue().equals(RESOURCE_CLZ_ID)) {
+        PsiExpression[] arguments = call.getArgumentList().getExpressions();
+        if (arguments.length == 0) {
+            return;
+        }
+        PsiExpression first = arguments[0];
+        if (first instanceof PsiReferenceExpression) {
+            PsiReferenceExpression psiReferenceExpression = (PsiReferenceExpression) first;
+            String id = psiReferenceExpression.getReferenceName();
+            PsiElement operand = psiReferenceExpression.getQualifier();
+            if (operand instanceof PsiReferenceExpression) {
+                PsiReferenceExpression type = (PsiReferenceExpression) operand;
+                if (RESOURCE_CLZ_ID.equals(type.getReferenceName())) {
                     if (mIds.containsKey(id)) {
                         if (lhs.equals(mLhs.get(id))) {
                             return;
@@ -129,7 +134,7 @@
                         if (!callOperand.equals(mCallOperands.get(id))) {
                             return;
                         }
-                        MethodInvocation earlierCall = mIds.get(id);
+                        PsiMethodCallExpression earlierCall = mIds.get(id);
                         if (!isReachableFrom(method, earlierCall, call)) {
                             return;
                         }
@@ -138,36 +143,45 @@
                         secondary.setMessage("First usage here");
                         location.setSecondary(secondary);
                         context.report(ISSUE, call, location, String.format(
-                            "The id `%1$s` has already been looked up in this method; possible " +
-                            "cut & paste error?", first.toString()));
+                                "The id `%1$s` has already been looked up in this method; possible "
+                                        +
+                                        "cut & paste error?", first.getText()));
                     } else {
                         mIds.put(id, call);
                         mLhs.put(id, lhs);
                         mCallOperands.put(id, callOperand);
                     }
                 }
+
             }
         }
     }
 
     @Nullable
-    private static String getLhs(@NonNull MethodInvocation call) {
-        Node parent = call.getParent();
-        if (parent instanceof Cast) {
+    private static String getLhs(@NonNull PsiMethodCallExpression call) {
+        PsiElement parent = skipParentheses(call.getParent());
+        if (parent instanceof PsiTypeCastExpression) {
             parent = parent.getParent();
         }
 
-        if (parent instanceof VariableDefinitionEntry) {
-            VariableDefinitionEntry vde = (VariableDefinitionEntry) parent;
-            return vde.astName().astValue();
-        } else if (parent instanceof BinaryExpression) {
-            BinaryExpression be = (BinaryExpression) parent;
-            Expression left = be.astLeft();
-            if (left instanceof VariableReference || left instanceof Select) {
-                return be.astLeft().toString();
-            } else if (left instanceof ArrayAccess) {
-                ArrayAccess aa = (ArrayAccess) left;
-                return aa.astOperand().toString();
+        if (parent instanceof PsiLocalVariable) {
+            return ((PsiLocalVariable)parent).getName();
+        } else if (parent instanceof PsiBinaryExpression) {
+            PsiBinaryExpression be = (PsiBinaryExpression) parent;
+            PsiExpression left = be.getLOperand();
+            if (left instanceof PsiReference) {
+                return left.getText();
+            } else if (left instanceof PsiArrayAccessExpression) {
+                PsiArrayAccessExpression aa = (PsiArrayAccessExpression) left;
+                return aa.getArrayExpression().getText();
+            }
+        } else if (parent instanceof PsiAssignmentExpression) {
+            PsiExpression left = ((PsiAssignmentExpression) parent).getLExpression();
+            if (left instanceof PsiReference) {
+                return left.getText();
+            } else if (left instanceof PsiArrayAccessExpression) {
+                PsiArrayAccessExpression aa = (PsiArrayAccessExpression) left;
+                return aa.getArrayExpression().getText();
             }
         }
 
@@ -175,68 +189,64 @@
     }
 
     private static boolean isReachableFrom(
-            @NonNull Node method,
-            @NonNull MethodInvocation from,
-            @NonNull MethodInvocation to) {
-        ReachableVisitor visitor = new ReachableVisitor(from, to);
-        method.accept(visitor);
+            @NonNull PsiMethod method,
+            @NonNull PsiMethodCallExpression from,
+            @NonNull PsiMethodCallExpression to) {
+        PsiElement current = from;
+        //noinspection ConstantConditions
+        while (current != null && current != method) {
+            if (containsElement(current, to)) {
+                return true;
+            }
 
-        return visitor.isReachable();
+            if (current.getNextSibling() != null) {
+                current = current.getNextSibling();
+            } else {
+                while (current.getParent() != null && current.getParent() != method) {
+                    if (nextNonWhitespace(current.getParent()) != null) {
+                        current = nextNonWhitespace(current.getParent());
+                        assert current != null;
+                        if (current.getParent() instanceof PsiIfStatement) {
+                            // Don't want to move from then to else siblings
+                            current = current.getParent();
+                        } else {
+                            break;
+                        }
+                    } else {
+                        current = current.getParent();
+                    }
+                }
+
+                PsiElement parent = skipParentheses(current.getParent());
+                if (parent instanceof PsiForStatement
+                        || parent instanceof PsiForeachStatement
+                        || parent instanceof PsiWhileStatement
+                        || parent instanceof PsiDoWhileStatement) {
+                    if (containsElement(current, to)) {
+                        return true;
+                    }
+                } else if (parent == method) {
+                    return false;
+                }
+            }
+        }
+
+        return false;
     }
 
-    private static class ReachableVisitor extends ForwardingAstVisitor {
-        @NonNull private final MethodInvocation mFrom;
-        @NonNull private final MethodInvocation mTo;
-        private boolean mReachable;
-        private boolean mSeenEnd;
-
-        public ReachableVisitor(@NonNull MethodInvocation from, @NonNull MethodInvocation to) {
-            mFrom = from;
-            mTo = to;
+    private static boolean containsElement(@NonNull PsiElement root, @NonNull PsiElement element) {
+        if (root.equals(element)) {
+            return true;
         }
 
-        boolean isReachable() {
-            return mReachable;
-        }
-
-        @Override
-        public boolean visitMethodInvocation(MethodInvocation node) {
-            if (node == mFrom) {
-                mReachable = true;
-            } else if (node == mTo) {
-                mSeenEnd = true;
-
+        PsiElement curr = root.getFirstChild();
+        while (curr != null) {
+            if (containsElement(curr, element)) {
+                return true;
             }
-            return super.visitMethodInvocation(node);
+            curr = curr.getNextSibling();
         }
 
-        @Override
-        public boolean visitIf(If node) {
-            Expression condition = node.astCondition();
-            Statement body = node.astStatement();
-            Statement elseBody = node.astElseStatement();
-            if (condition != null) {
-                condition.accept(this);
-            }
-            if (body != null) {
-                boolean wasReachable = mReachable;
-                body.accept(this);
-                mReachable = wasReachable;
-            }
-            if (elseBody != null) {
-                boolean wasReachable = mReachable;
-                elseBody.accept(this);
-                mReachable = wasReachable;
-            }
-
-            endVisit(node);
-
-            return false;
-        }
-
-        @Override
-        public boolean visitNode(Node node) {
-            return mSeenEnd;
-        }
+        return false;
     }
 }
diff --git a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/DateFormatDetector.java b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/DateFormatDetector.java
index 1ad7853..05c4550 100644
--- a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/DateFormatDetector.java
+++ b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/DateFormatDetector.java
@@ -18,29 +18,28 @@
 
 import com.android.annotations.NonNull;
 import com.android.annotations.Nullable;
-import com.android.tools.lint.client.api.JavaParser.ResolvedMethod;
-import com.android.tools.lint.client.api.JavaParser.TypeDescriptor;
 import com.android.tools.lint.detector.api.Category;
 import com.android.tools.lint.detector.api.Detector;
-import com.android.tools.lint.detector.api.Detector.JavaScanner;
 import com.android.tools.lint.detector.api.Implementation;
 import com.android.tools.lint.detector.api.Issue;
 import com.android.tools.lint.detector.api.JavaContext;
 import com.android.tools.lint.detector.api.Location;
 import com.android.tools.lint.detector.api.Scope;
 import com.android.tools.lint.detector.api.Severity;
-import com.android.tools.lint.detector.api.Speed;
+import com.intellij.psi.JavaElementVisitor;
+import com.intellij.psi.PsiMethod;
+import com.intellij.psi.PsiNewExpression;
+import com.intellij.psi.PsiParameter;
+import com.intellij.psi.PsiParameterList;
+import com.intellij.psi.PsiType;
 
 import java.util.Collections;
 import java.util.List;
 
-import lombok.ast.AstVisitor;
-import lombok.ast.ConstructorInvocation;
-
 /**
  * Checks for errors related to Date Formats
  */
-public class DateFormatDetector extends Detector implements JavaScanner {
+public class DateFormatDetector extends Detector implements Detector.JavaPsiScanner {
 
     private static final Implementation IMPLEMENTATION = new Implementation(
             DateFormatDetector.class,
@@ -76,12 +75,6 @@
     public DateFormatDetector() {
     }
 
-    @NonNull
-    @Override
-    public Speed getSpeed() {
-        return Speed.FAST;
-    }
-
     // ---- Implements JavaScanner ----
 
     @Nullable
@@ -91,8 +84,8 @@
     }
 
     @Override
-    public void visitConstructor(@NonNull JavaContext context, @Nullable AstVisitor visitor,
-            @NonNull ConstructorInvocation node, @NonNull ResolvedMethod constructor) {
+    public void visitConstructor(@NonNull JavaContext context, @Nullable JavaElementVisitor visitor,
+            @NonNull PsiNewExpression node, @NonNull PsiMethod constructor) {
         if (!specifiesLocale(constructor)) {
             Location location = context.getLocation(node);
             String message =
@@ -103,13 +96,16 @@
         }
     }
 
-    private static boolean specifiesLocale(@NonNull ResolvedMethod method) {
-        for (int i = 0, n = method.getArgumentCount(); i < n; i++) {
-            TypeDescriptor argumentType = method.getArgumentType(i);
-            if (argumentType.matchesSignature(LOCALE_CLS)) {
-                return true;
+    private static boolean specifiesLocale(@NonNull PsiMethod method) {
+        PsiParameterList parameterList = method.getParameterList();
+        PsiParameter[] parameters = parameterList.getParameters();
+        for (PsiParameter parameter : parameters) {
+            PsiType type = parameter.getType();
+            if (type.getCanonicalText().equals(LOCALE_CLS)) {
+                    return true;
             }
         }
+
         return false;
     }
 }
diff --git a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/DetectMissingPrefix.java b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/DetectMissingPrefix.java
index 83765a8..0db4d64 100644
--- a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/DetectMissingPrefix.java
+++ b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/DetectMissingPrefix.java
@@ -133,7 +133,7 @@
             Element element = attribute.getOwnerElement();
             if (isCustomView(element) && context.getResourceFolderType() != null) {
                 return;
-            } else if (context.getResourceFolderType() == ResourceFolderType.LAYOUT) {
+            } else if (context.getResourceFolderType() == LAYOUT) {
                 // Data binding: These look like Android framework views but
                 // are data binding directives not in the Android namespace
                 Element root = element.getOwnerDocument().getDocumentElement();
@@ -158,7 +158,7 @@
                     "Attribute is missing the Android namespace prefix");
         } else if (!ANDROID_URI.equals(uri)
                 && !TOOLS_URI.equals(uri)
-                && context.getResourceFolderType() == ResourceFolderType.LAYOUT
+                && context.getResourceFolderType() == LAYOUT
                 && !isCustomView(attribute.getOwnerElement())
                 && !isFragment(attribute.getOwnerElement())
                 && !attribute.getLocalName().startsWith(ATTR_LAYOUT_RESOURCE_PREFIX)
@@ -168,7 +168,7 @@
                 // ....&& !attribute.getLocalName().startsWith(ATTR_LAYOUT_RESOURCE_PREFIX)
                 && attribute.getOwnerElement().getParentNode().getNodeType() == Node.ELEMENT_NODE
                 && !isCustomView((Element) attribute.getOwnerElement().getParentNode())) {
-            if (context.getResourceFolderType() == ResourceFolderType.LAYOUT
+            if (context.getResourceFolderType() == LAYOUT
                     && AUTO_URI.equals(uri)) {
                 // Data binding: Can add attributes like onClickListener to buttons etc.
                 Element root = attribute.getOwnerDocument().getDocumentElement();
diff --git a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/FragmentDetector.java b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/FragmentDetector.java
index bd01fbb..e980135 100644
--- a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/FragmentDetector.java
+++ b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/FragmentDetector.java
@@ -18,32 +18,27 @@
 
 import static com.android.SdkConstants.CLASS_FRAGMENT;
 import static com.android.SdkConstants.CLASS_V4_FRAGMENT;
-import static com.android.tools.lint.client.api.JavaParser.ResolvedClass;
 
 import com.android.annotations.NonNull;
 import com.android.annotations.Nullable;
+import com.android.tools.lint.client.api.JavaEvaluator;
 import com.android.tools.lint.detector.api.Category;
 import com.android.tools.lint.detector.api.Detector;
-import com.android.tools.lint.detector.api.Detector.JavaScanner;
+import com.android.tools.lint.detector.api.Detector.JavaPsiScanner;
 import com.android.tools.lint.detector.api.Implementation;
 import com.android.tools.lint.detector.api.Issue;
 import com.android.tools.lint.detector.api.JavaContext;
 import com.android.tools.lint.detector.api.Location;
 import com.android.tools.lint.detector.api.Scope;
 import com.android.tools.lint.detector.api.Severity;
-import com.android.tools.lint.detector.api.Speed;
+import com.intellij.psi.PsiAnonymousClass;
+import com.intellij.psi.PsiClass;
+import com.intellij.psi.PsiElement;
+import com.intellij.psi.PsiMethod;
 
-import java.lang.reflect.Modifier;
 import java.util.Arrays;
 import java.util.List;
 
-import lombok.ast.ClassDeclaration;
-import lombok.ast.ConstructorDeclaration;
-import lombok.ast.ConstructorInvocation;
-import lombok.ast.Node;
-import lombok.ast.NormalTypeBody;
-import lombok.ast.TypeMember;
-
 /**
  * Checks that Fragment subclasses can be instantiated via
  * {link {@link Class#newInstance()}}: the class is public, static, and has
@@ -53,7 +48,7 @@
  *   http://stackoverflow.com/questions/8058809/fragment-activity-crashes-on-screen-rotate
  * (and countless duplicates)
  */
-public class FragmentDetector extends Detector implements JavaScanner {
+public class FragmentDetector extends Detector implements JavaPsiScanner {
     /** Are fragment subclasses instantiatable? */
     public static final Issue ISSUE = Issue.create(
         "ValidFragment", //$NON-NLS-1$
@@ -81,12 +76,6 @@
     public FragmentDetector() {
     }
 
-    @NonNull
-    @Override
-    public Speed getSpeed() {
-        return Speed.FAST;
-    }
-
     // ---- Implements JavaScanner ----
 
     @Nullable
@@ -96,81 +85,69 @@
     }
 
     @Override
-    public void checkClass(@NonNull JavaContext context, @Nullable ClassDeclaration node,
-            @NonNull Node declarationOrAnonymous, @NonNull ResolvedClass cls) {
-        if (node == null) {
+    public void checkClass(@NonNull JavaContext context, @NonNull PsiClass node) {
+        if (node instanceof PsiAnonymousClass) {
             String message = "Fragments should be static such that they can be re-instantiated by " +
-                             "the system, and anonymous classes are not static";
-            Node locationNode = declarationOrAnonymous;
-            if (locationNode.getParent() instanceof ConstructorInvocation) {
-                ConstructorInvocation constructor = (ConstructorInvocation)locationNode.getParent();
-                if (constructor.astTypeReference() != null) {
-                    locationNode = constructor.astTypeReference();
-                }
+                    "the system, and anonymous classes are not static";
+            PsiElement locationNode = JavaContext.findNameElement(node);
+            if (locationNode == null) {
+                locationNode = node;
             }
-            context.report(ISSUE, declarationOrAnonymous, context.getLocation(locationNode), message);
+            context.report(ISSUE, locationNode, context.getLocation(locationNode), message);
             return;
         }
 
-        int flags = node.astModifiers().getEffectiveModifierFlags();
-        if ((flags & Modifier.ABSTRACT) != 0) {
+        JavaEvaluator evaluator = context.getEvaluator();
+        if (evaluator.isAbstract(node)) {
             return;
         }
 
-        if ((flags & Modifier.PUBLIC) == 0) {
+        if (!evaluator.isPublic(node)) {
             String message = String.format("This fragment class should be public (%1$s)",
-                    cls.getName());
-            context.report(ISSUE, node, context.getLocation(node.astName()), message);
+                    node.getQualifiedName());
+            context.report(ISSUE, node, context.getNameLocation(node), message);
             return;
         }
 
-        if (cls.getContainingClass() != null && (flags & Modifier.STATIC) == 0) {
+        if (node.getContainingClass() != null && !evaluator.isStatic(node)) {
             String message = String.format(
-                    "This fragment inner class should be static (%1$s)", cls.getName());
-            context.report(ISSUE, node, context.getLocation(node.astName()), message);
+                    "This fragment inner class should be static (%1$s)", node.getQualifiedName());
+            context.report(ISSUE, node, context.getNameLocation(node), message);
             return;
         }
 
         boolean hasDefaultConstructor = false;
         boolean hasConstructor = false;
-        NormalTypeBody body = node.astBody();
-        if (body != null) {
-            for (TypeMember member : body.astMembers()) {
-                if (member instanceof ConstructorDeclaration) {
-                    hasConstructor = true;
-                    ConstructorDeclaration constructor = (ConstructorDeclaration) member;
-                    if (constructor.astParameters().isEmpty()) {
-                        // The constructor must be public
-                        if (constructor.astModifiers().isPublic()) {
-                            hasDefaultConstructor = true;
-                        } else {
-                            Location location = context.getLocation(
-                                    constructor.astTypeName());
-                            context.report(ISSUE, constructor, location,
-                                    "The default constructor must be public");
-                            // Also mark that we have a constructor so we don't complain again
-                            // below since we've already emitted a more specific error related
-                            // to the default constructor
-                            hasDefaultConstructor = true;
-                        }
-                    } else {
-                        Location location = context.getLocation(constructor.astTypeName());
-                        // TODO: Use separate issue for this which isn't an error
-                        String message = "Avoid non-default constructors in fragments: "
-                                + "use a default constructor plus "
-                                + "`Fragment#setArguments(Bundle)` instead";
-                        context.report(ISSUE, constructor, location, message);
-                    }
+        for (PsiMethod constructor : node.getConstructors()) {
+            hasConstructor = true;
+            if (constructor.getParameterList().getParametersCount() == 0) {
+                if (evaluator.isPublic(constructor)) {
+                    hasDefaultConstructor = true;
+                } else {
+                    Location location = context.getNameLocation(constructor);
+                    context.report(ISSUE, constructor, location,
+                            "The default constructor must be public");
+                    // Also mark that we have a constructor so we don't complain again
+                    // below since we've already emitted a more specific error related
+                    // to the default constructor
+                    hasDefaultConstructor = true;
                 }
+            } else {
+                Location location = context.getNameLocation(constructor);
+                // TODO: Use separate issue for this which isn't an error
+                String message = "Avoid non-default constructors in fragments: "
+                        + "use a default constructor plus "
+                        + "`Fragment#setArguments(Bundle)` instead";
+                context.report(ISSUE, constructor, location, message);
             }
         }
 
         if (!hasDefaultConstructor && hasConstructor) {
             String message = String.format(
                     "This fragment should provide a default constructor (a public " +
-                    "constructor with no arguments) (`%1$s`)",
-                    cls.getName());
-            context.report(ISSUE, node, context.getLocation(node.astName()), message);
+                            "constructor with no arguments) (`%1$s`)",
+                    node.getQualifiedName());
+            context.report(ISSUE, node, context.getNameLocation(node), message);
         }
     }
 }
diff --git a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/FullBackupContentDetector.java b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/FullBackupContentDetector.java
index 6780de7..bf40440 100644
--- a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/FullBackupContentDetector.java
+++ b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/FullBackupContentDetector.java
@@ -20,7 +20,6 @@
 import com.android.annotations.Nullable;
 import com.android.resources.ResourceFolderType;
 import com.android.tools.lint.detector.api.Category;
-import com.android.tools.lint.detector.api.Detector.JavaScanner;
 import com.android.tools.lint.detector.api.Implementation;
 import com.android.tools.lint.detector.api.Issue;
 import com.android.tools.lint.detector.api.Location;
@@ -45,7 +44,7 @@
 /**
  * Check which makes sure that a full-backup-content descriptor file is valid and logical
  */
-public class FullBackupContentDetector extends ResourceXmlDetector implements JavaScanner {
+public class FullBackupContentDetector extends ResourceXmlDetector {
     /**
      * Validation of {@code <full-backup-content>} XML elements
      */
diff --git a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/GetSignaturesDetector.java b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/GetSignaturesDetector.java
index 76aec22..472651f 100644
--- a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/GetSignaturesDetector.java
+++ b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/GetSignaturesDetector.java
@@ -18,41 +18,35 @@
 
 import com.android.annotations.NonNull;
 import com.android.annotations.Nullable;
+import com.android.tools.lint.client.api.JavaEvaluator;
 import com.android.tools.lint.client.api.JavaParser;
-import com.android.tools.lint.client.api.JavaParser.ResolvedField;
-import com.android.tools.lint.client.api.JavaParser.ResolvedMethod;
-import com.android.tools.lint.client.api.JavaParser.ResolvedNode;
-import com.android.tools.lint.client.api.JavaParser.TypeDescriptor;
 import com.android.tools.lint.detector.api.Category;
+import com.android.tools.lint.detector.api.ConstantEvaluator;
 import com.android.tools.lint.detector.api.Detector;
+import com.android.tools.lint.detector.api.Detector.JavaPsiScanner;
 import com.android.tools.lint.detector.api.Implementation;
 import com.android.tools.lint.detector.api.Issue;
 import com.android.tools.lint.detector.api.JavaContext;
 import com.android.tools.lint.detector.api.Scope;
 import com.android.tools.lint.detector.api.Severity;
+import com.intellij.psi.JavaElementVisitor;
+import com.intellij.psi.PsiExpression;
+import com.intellij.psi.PsiMethod;
+import com.intellij.psi.PsiMethodCallExpression;
 
 import java.util.Collections;
 import java.util.List;
 
-import lombok.ast.AstVisitor;
-import lombok.ast.BinaryExpression;
-import lombok.ast.BinaryOperator;
-import lombok.ast.Expression;
-import lombok.ast.IntegralLiteral;
-import lombok.ast.MethodInvocation;
-import lombok.ast.StrictListAccessor;
-import lombok.ast.StringLiteral;
-
-public class GetSignaturesDetector extends Detector implements Detector.JavaScanner  {
+public class GetSignaturesDetector extends Detector implements JavaPsiScanner  {
     public static final Issue ISSUE = Issue.create(
             "PackageManagerGetSignatures", //$NON-NLS-1$
             "Potential Multiple Certificate Exploit",
             "Improper validation of app signatures could lead to issues where a malicious app " +
-                "submits itself to the Play Store with both its real certificate and a fake " +
-                "certificate and gains access to functionality or information it shouldn't " +
-                "have due to another application only checking for the fake certificate and " +
-                "ignoring the rest. Please make sure to validate all signatures returned " +
-                "by this method.",
+            "submits itself to the Play Store with both its real certificate and a fake " +
+            "certificate and gains access to functionality or information it shouldn't " +
+            "have due to another application only checking for the fake certificate and " +
+            "ignoring the rest. Please make sure to validate all signatures returned " +
+            "by this method.",
             Category.SECURITY,
             8,
             Severity.INFORMATIONAL,
@@ -74,83 +68,34 @@
     }
 
     @Override
-    public void visitMethod(@NonNull JavaContext context, @Nullable AstVisitor visitor,
-            @NonNull MethodInvocation node) {
-        ResolvedNode resolved = context.resolve(node);
-
-        if (!(resolved instanceof ResolvedMethod) ||
-                !((ResolvedMethod) resolved).getContainingClass()
-                        .isSubclassOf(PACKAGE_MANAGER_CLASS, false)) {
+    public void visitMethod(@NonNull JavaContext context, @Nullable JavaElementVisitor visitor,
+            @NonNull PsiMethodCallExpression node, @NonNull PsiMethod method) {
+        JavaEvaluator evaluator = context.getEvaluator();
+        if (!evaluator.methodMatches(method, PACKAGE_MANAGER_CLASS, true,
+                JavaParser.TYPE_STRING,
+                JavaParser.TYPE_INT)) {
             return;
         }
-        StrictListAccessor<Expression, MethodInvocation> argumentList = node.astArguments();
 
-        // Ignore if the method doesn't fit our description.
-        if (argumentList != null && argumentList.size() == 2) {
-            TypeDescriptor firstParameterType = context.getType(argumentList.first());
-            if (firstParameterType != null
-                && firstParameterType.matchesSignature(JavaParser.TYPE_STRING)) {
-                maybeReportIssue(calculateValue(context, argumentList.last()), context, node);
+        PsiExpression[] arguments = node.getArgumentList().getExpressions();
+        if (arguments.length == 2) {
+            PsiExpression second = arguments[1];
+            Object number = ConstantEvaluator.evaluate(context, second);
+            if (number instanceof Number) {
+                int flagValue = ((Number)number).intValue();
+                maybeReportIssue(flagValue, context, node, second);
             }
         }
     }
 
     private static void maybeReportIssue(
-            int flagValue, JavaContext context, MethodInvocation node) {
+            int flagValue, JavaContext context, PsiMethodCallExpression node,
+            PsiExpression last) {
         if ((flagValue & GET_SIGNATURES_FLAG) != 0) {
-            context.report(ISSUE, node, context.getLocation(node.astArguments().last()),
+            context.report(ISSUE, node, context.getLocation(last),
                 "Reading app signatures from getPackageInfo: The app signatures "
                     + "could be exploited if not validated properly; "
                     + "see issue explanation for details.");
         }
     }
-
-    private static int calculateValue(JavaContext context, Expression expression) {
-        // This function assumes that the only inputs to the expression are static integer
-        // flags that combined via bitwise operands.
-        if (expression instanceof IntegralLiteral) {
-            return ((IntegralLiteral) expression).astIntValue();
-        }
-
-        ResolvedNode resolvedNode = context.resolve(expression);
-        if (resolvedNode instanceof ResolvedField) {
-            Object value = ((ResolvedField) resolvedNode).getValue();
-            if (value instanceof Integer) {
-                return (Integer) value;
-            }
-        }
-        if (expression instanceof BinaryExpression) {
-            BinaryExpression binaryExpression = (BinaryExpression) expression;
-            BinaryOperator operator = binaryExpression.astOperator();
-            int leftValue = calculateValue(context, binaryExpression.astLeft());
-            int rightValue = calculateValue(context, binaryExpression.astRight());
-
-            if (operator == BinaryOperator.BITWISE_OR) {
-                return leftValue | rightValue;
-            }
-            if (operator == BinaryOperator.BITWISE_AND) {
-                return leftValue & rightValue;
-            }
-            if (operator == BinaryOperator.BITWISE_XOR) {
-                return leftValue ^ rightValue;
-            }
-        }
-
-        return 0;
-    }
-
-    private static boolean isStringParameter(
-            @NonNull Expression expression, @NonNull JavaContext context) {
-        if (expression instanceof StringLiteral) {
-            return true;
-        } else {
-            ResolvedNode resolvedNode = context.resolve(expression);
-            if (resolvedNode instanceof ResolvedField) {
-                if (((ResolvedField) resolvedNode).getValue() instanceof String) {
-                    return true;
-                }
-            }
-        }
-        return false;
-    }
 }
diff --git a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/GradleDetector.java b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/GradleDetector.java
index b5a29e9..0a954e1 100644
--- a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/GradleDetector.java
+++ b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/GradleDetector.java
@@ -1071,7 +1071,7 @@
         GradleCoordinate latestPlugin = GradleCoordinate.parseCoordinateString(
                 SdkConstants.GRADLE_PLUGIN_NAME +
                         GRADLE_PLUGIN_MINIMUM_VERSION);
-        if (GradleCoordinate.COMPARE_PLUS_HIGHER.compare(dependency, latestPlugin) < 0) {
+        if (COMPARE_PLUS_HIGHER.compare(dependency, latestPlugin) < 0) {
             String message = "You must use a newer version of the Android Gradle plugin. The "
                     + "minimum supported version is " + GRADLE_PLUGIN_MINIMUM_VERSION +
                     " and the recommended version is " + GRADLE_PLUGIN_RECOMMENDED_VERSION;
@@ -1293,7 +1293,7 @@
       return cookie;
     }
 
-    @SuppressWarnings("MethodMayBeStatic")
+    @SuppressWarnings({"MethodMayBeStatic", "UnusedParameters"})
     protected int getStartOffset(@NonNull Context context, @NonNull Object cookie) {
         return -1;
     }
diff --git a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/HandlerDetector.java b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/HandlerDetector.java
index 3b8a41c..d8ce886 100644
--- a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/HandlerDetector.java
+++ b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/HandlerDetector.java
@@ -16,37 +16,38 @@
 
 package com.android.tools.lint.checks;
 
+import static com.intellij.psi.util.PsiTreeUtil.getParentOfType;
+
 import com.android.annotations.NonNull;
 import com.android.annotations.Nullable;
-import com.android.tools.lint.client.api.JavaParser.ResolvedClass;
-import com.android.tools.lint.client.api.JavaParser.ResolvedMethod;
-import com.android.tools.lint.client.api.JavaParser.TypeDescriptor;
 import com.android.tools.lint.detector.api.Category;
 import com.android.tools.lint.detector.api.Detector;
+import com.android.tools.lint.detector.api.Detector.JavaPsiScanner;
 import com.android.tools.lint.detector.api.Implementation;
 import com.android.tools.lint.detector.api.Issue;
 import com.android.tools.lint.detector.api.JavaContext;
 import com.android.tools.lint.detector.api.Location;
 import com.android.tools.lint.detector.api.Scope;
 import com.android.tools.lint.detector.api.Severity;
-import com.android.tools.lint.detector.api.Speed;
+import com.intellij.psi.PsiAnonymousClass;
+import com.intellij.psi.PsiClass;
+import com.intellij.psi.PsiClassType;
+import com.intellij.psi.PsiElement;
+import com.intellij.psi.PsiExpression;
+import com.intellij.psi.PsiExpressionList;
+import com.intellij.psi.PsiMethod;
+import com.intellij.psi.PsiNewExpression;
+import com.intellij.psi.PsiParameter;
+import com.intellij.psi.PsiType;
 
-import java.lang.reflect.Modifier;
 import java.util.Collections;
 import java.util.List;
 
-import lombok.ast.ClassDeclaration;
-import lombok.ast.ConstructorInvocation;
-import lombok.ast.Expression;
-import lombok.ast.MethodDeclaration;
-import lombok.ast.Node;
-import lombok.ast.NormalTypeBody;
-
 /**
  * Checks that Handler implementations are top level classes or static.
  * See the corresponding check in the android.os.Handler source code.
  */
-public class HandlerDetector extends Detector implements Detector.JavaScanner {
+public class HandlerDetector extends Detector implements JavaPsiScanner {
 
     /** Potentially leaking handlers */
     public static final Issue ISSUE = Issue.create(
@@ -76,12 +77,6 @@
     public HandlerDetector() {
     }
 
-    @NonNull
-    @Override
-    public Speed getSpeed() {
-        return Speed.FAST;
-    }
-
     // ---- Implements JavaScanner ----
 
     @Nullable
@@ -91,84 +86,61 @@
     }
 
     @Override
-    public void checkClass(@NonNull JavaContext context, @Nullable ClassDeclaration declaration,
-            @NonNull Node node, @NonNull ResolvedClass cls) {
-        if (!isInnerClass(declaration)) {
+    public void checkClass(@NonNull JavaContext context, @NonNull PsiClass declaration) {
+        // Only consider static inner classes
+        if (context.getEvaluator().isStatic(declaration)) {
+            return;
+        }
+        boolean isAnonymous = declaration instanceof PsiAnonymousClass;
+        if (declaration.getContainingClass() == null && !isAnonymous) {
             return;
         }
 
-        if (isStaticClass(declaration)) {
-            return;
-        }
-
-        // Only flag handlers using the default looper
-        ConstructorInvocation invocation = null;
-        Node current = node;
-        while (current != null) {
-            if (current instanceof ConstructorInvocation) {
-                invocation = (ConstructorInvocation) current;
-                break;
-            } else if (current instanceof MethodDeclaration ||
-                    current instanceof ClassDeclaration) {
-                break;
-            }
-            current = current.getParent();
-        }
-
+        //// Only flag handlers using the default looper
+        //noinspection unchecked
+        PsiNewExpression invocation = getParentOfType(declaration, PsiNewExpression.class,
+                true, PsiMethod.class);
         if (invocation != null) {
-            for (Expression expression : invocation.astArguments()) {
-                TypeDescriptor type = context.getType(expression);
-                if (type != null && type.matchesName(LOOPER_CLS)) {
-                    return;
+            PsiExpressionList argumentList = invocation.getArgumentList();
+            if (argumentList != null && isAnonymous) {
+                ((PsiAnonymousClass)declaration).getArgumentList();
+                for (PsiExpression expression : argumentList.getExpressions()) {
+                    PsiType type = expression.getType();
+                    if (type instanceof PsiClassType
+                            && LOOPER_CLS.equals(type.getCanonicalText())) {
+                        return;
+                    }
                 }
             }
-        } else if (hasLooperConstructorParameter(cls)) {
+        } else if (hasLooperConstructorParameter(declaration)) {
             // This is an inner class which takes a Looper parameter:
             // possibly used correctly from elsewhere
             return;
         }
 
-        Location location;
-        Node locationNode;
-        if (node instanceof ClassDeclaration) {
-            locationNode = node;
-            location = context.getLocation(((ClassDeclaration) node).astName());
-        } else if (node instanceof NormalTypeBody
-                && node.getParent() instanceof ConstructorInvocation) {
-            ConstructorInvocation parent = (ConstructorInvocation)node.getParent();
-            locationNode = parent;
-            location = context.getRangeLocation(parent, 0, parent.astTypeReference(), 0);
+        PsiElement locationNode = JavaContext.findNameElement(declaration);
+        if (locationNode == null) {
+            locationNode = declaration;
+        }
+        Location location = context.getLocation(locationNode);
+        String name;
+        if (isAnonymous) {
+            name = "anonymous " + ((PsiAnonymousClass)declaration).getBaseClassReference().getQualifiedName();
         } else {
-            locationNode = node;
-            location = context.getLocation(node);;
+            name = declaration.getQualifiedName();
         }
 
         //noinspection VariableNotUsedInsideIf
         context.report(ISSUE, locationNode, location, String.format(
                 "This Handler class should be static or leaks might occur (%1$s)",
-                declaration == null ? "anonymous " + cls.getName() : cls.getName()));
+                name));
     }
 
-    private static boolean isInnerClass(@Nullable ClassDeclaration node) {
-        return node == null || // null class declarations means anonymous inner class
-                JavaContext.getParentOfType(node, ClassDeclaration.class, true) != null;
-    }
-
-    private static boolean isStaticClass(@Nullable ClassDeclaration node) {
-        if (node == null) {
-            // A null class declaration means anonymous inner class, and these can't be static
-            return false;
-        }
-
-        int flags = node.astModifiers().getEffectiveModifierFlags();
-        return (flags & Modifier.STATIC) != 0;
-    }
-
-    private static boolean hasLooperConstructorParameter(@NonNull ResolvedClass cls) {
-        for (ResolvedMethod constructor : cls.getConstructors()) {
-            for (int i = 0, n = constructor.getArgumentCount(); i < n; i++) {
-                TypeDescriptor type = constructor.getArgumentType(i);
-                if (type.matchesSignature(LOOPER_CLS)) {
+    private static boolean hasLooperConstructorParameter(@NonNull PsiClass cls) {
+        for (PsiMethod constructor : cls.getConstructors()) {
+            for (PsiParameter parameter : constructor.getParameterList().getParameters()) {
+                PsiType type = parameter.getType();
+                if (LOOPER_CLS.equals(type.getCanonicalText())) {
                     return true;
                 }
             }
diff --git a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/IconDetector.java b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/IconDetector.java
index 22d562e..18b29b2 100644
--- a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/IconDetector.java
+++ b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/IconDetector.java
@@ -34,9 +34,6 @@
 import static com.android.SdkConstants.DRAWABLE_XHDPI;
 import static com.android.SdkConstants.DRAWABLE_XXHDPI;
 import static com.android.SdkConstants.DRAWABLE_XXXHDPI;
-import static com.android.SdkConstants.MENU_TYPE;
-import static com.android.SdkConstants.R_CLASS;
-import static com.android.SdkConstants.R_DRAWABLE_PREFIX;
 import static com.android.SdkConstants.TAG_ACTIVITY;
 import static com.android.SdkConstants.TAG_APPLICATION;
 import static com.android.SdkConstants.TAG_ITEM;
@@ -49,27 +46,42 @@
 import com.android.annotations.Nullable;
 import com.android.builder.model.ProductFlavor;
 import com.android.builder.model.ProductFlavorContainer;
+import com.android.ide.common.resources.ResourceUrl;
 import com.android.resources.Density;
 import com.android.resources.ResourceFolderType;
+import com.android.resources.ResourceType;
 import com.android.tools.lint.detector.api.Category;
 import com.android.tools.lint.detector.api.Context;
-import com.android.tools.lint.detector.api.Detector;
+import com.android.tools.lint.detector.api.Detector.JavaPsiScanner;
 import com.android.tools.lint.detector.api.Implementation;
 import com.android.tools.lint.detector.api.Issue;
 import com.android.tools.lint.detector.api.JavaContext;
 import com.android.tools.lint.detector.api.LintUtils;
 import com.android.tools.lint.detector.api.Location;
 import com.android.tools.lint.detector.api.Project;
+import com.android.tools.lint.detector.api.ResourceEvaluator;
 import com.android.tools.lint.detector.api.ResourceXmlDetector;
 import com.android.tools.lint.detector.api.Scope;
 import com.android.tools.lint.detector.api.Severity;
-import com.android.tools.lint.detector.api.Speed;
 import com.android.tools.lint.detector.api.XmlContext;
 import com.google.common.collect.ArrayListMultimap;
 import com.google.common.collect.Lists;
 import com.google.common.collect.Maps;
 import com.google.common.collect.Multimap;
 import com.google.common.collect.Sets;
+import com.intellij.psi.JavaElementVisitor;
+import com.intellij.psi.JavaRecursiveElementVisitor;
+import com.intellij.psi.PsiAnonymousClass;
+import com.intellij.psi.PsiClass;
+import com.intellij.psi.PsiElement;
+import com.intellij.psi.PsiExpression;
+import com.intellij.psi.PsiExpressionList;
+import com.intellij.psi.PsiJavaCodeReferenceElement;
+import com.intellij.psi.PsiMethod;
+import com.intellij.psi.PsiMethodCallExpression;
+import com.intellij.psi.PsiNewExpression;
+import com.intellij.psi.PsiReferenceExpression;
+import com.intellij.psi.util.PsiTreeUtil;
 
 import org.w3c.dom.Element;
 
@@ -99,24 +111,11 @@
 import javax.imageio.ImageReader;
 import javax.imageio.stream.ImageInputStream;
 
-import lombok.ast.AstVisitor;
-import lombok.ast.ConstructorInvocation;
-import lombok.ast.Expression;
-import lombok.ast.ForwardingAstVisitor;
-import lombok.ast.MethodDeclaration;
-import lombok.ast.MethodInvocation;
-import lombok.ast.Node;
-import lombok.ast.Select;
-import lombok.ast.StrictListAccessor;
-import lombok.ast.TypeReference;
-import lombok.ast.TypeReferencePart;
-import lombok.ast.VariableReference;
-
 /**
  * Checks for common icon problems, such as wrong icon sizes, placing icons in the
  * density independent drawable folder, etc.
  */
-public class IconDetector extends ResourceXmlDetector implements Detector.JavaScanner {
+public class IconDetector extends ResourceXmlDetector implements JavaPsiScanner {
 
     private static final boolean INCLUDE_LDPI;
     static {
@@ -382,12 +381,6 @@
     public IconDetector() {
     }
 
-    @NonNull
-    @Override
-    public Speed getSpeed() {
-        return Speed.SLOW;
-    }
-
     @Override
     public void beforeCheckProject(@NonNull Context context) {
         mLauncherIcons = null;
@@ -1967,92 +1960,85 @@
 
     // ---- Implements JavaScanner ----
 
-    private static final String NOTIFICATION_CLASS = "Notification";              //$NON-NLS-1$
-    private static final String NOTIFICATION_COMPAT_CLASS = "NotificationCompat"; //$NON-NLS-1$
-    private static final String BUILDER_CLASS = "Builder";                        //$NON-NLS-1$
-    private static final String SET_SMALL_ICON = "setSmallIcon";                  //$NON-NLS-1$
-    private static final String ON_CREATE_OPTIONS_MENU = "onCreateOptionsMenu";   //$NON-NLS-1$
+    private static final String NOTIFICATION_CLASS = "Notification";
+    private static final String NOTIFICATION_BUILDER_CLASS = "Notification.Builder";
+    private static final String NOTIFICATION_COMPAT_BUILDER_CLASS = "NotificationCompat.Builder";
+    private static final String SET_SMALL_ICON = "setSmallIcon";
+    private static final String ON_CREATE_OPTIONS_MENU = "onCreateOptionsMenu";
 
     @Override
-    @Nullable
-    public AstVisitor createJavaVisitor(@NonNull JavaContext context) {
-        return new NotificationFinder();
-    }
-
-    @Override
-    @Nullable
-    public List<Class<? extends Node>> getApplicableNodeTypes() {
-        List<Class<? extends Node>> types = new ArrayList<Class<? extends Node>>(3);
-        types.add(MethodDeclaration.class);
-        types.add(ConstructorInvocation.class);
+    public List<Class<? extends PsiElement>> getApplicablePsiTypes() {
+        List<Class<? extends PsiElement>> types = new ArrayList<Class<? extends PsiElement>>(2);
+        types.add(PsiNewExpression.class);
+        types.add(PsiMethod.class);
         return types;
     }
 
-    private final class NotificationFinder extends ForwardingAstVisitor {
-        @Override
-        public boolean visitMethodDeclaration(MethodDeclaration node) {
-            if (ON_CREATE_OPTIONS_MENU.equals(node.astMethodName().astValue())) {
-                // Gather any R.menu references found in this method
-                node.accept(new MenuFinder());
-            }
+    @Nullable
+    @Override
+    public JavaElementVisitor createPsiVisitor(@NonNull JavaContext context) {
+        return new NotificationFinder();
+    }
 
-            return super.visitMethodDeclaration(node);
+    private final class NotificationFinder extends JavaElementVisitor {
+
+        @Override
+        public void visitMethod(PsiMethod method) {
+            if (ON_CREATE_OPTIONS_MENU.equals(method.getName())) {
+                // Gather any R.menu references found in this method
+                method.accept(new MenuFinder());
+            }
         }
 
         @Override
-        public boolean visitConstructorInvocation(ConstructorInvocation node) {
-            TypeReference reference = node.astTypeReference();
-            StrictListAccessor<TypeReferencePart, TypeReference> parts = reference.astParts();
-            String typeName = parts.last().astIdentifier().astValue();
+        public void visitNewExpression(PsiNewExpression node) {
+            PsiJavaCodeReferenceElement classReference = node.getClassReference();
+            if (classReference == null) {
+                return;
+            }
+            PsiElement resolved = classReference.resolve();
+            if (!(resolved instanceof PsiClass)) {
+                return;
+            }
+            String typeName = ((PsiClass)resolved).getName();
             if (NOTIFICATION_CLASS.equals(typeName)) {
-                StrictListAccessor<Expression, ConstructorInvocation> args = node.astArguments();
-                if (args.size() == 3) {
-                    if (args.first() instanceof Select && handleSelect((Select) args.first())) {
-                        return super.visitConstructorInvocation(node);
+                PsiExpressionList argumentList = node.getArgumentList();
+                PsiExpression[] args = argumentList != null
+                        ? argumentList.getExpressions() : PsiExpression.EMPTY_ARRAY;
+                if (args.length == 3) {
+                    if (args[0] instanceof PsiReferenceExpression && handleSelect(args[0])) {
+                        return;
                     }
 
-                    Node method = StringFormatDetector.getParentMethod(node);
-                    if (method != null) {
-                        // Must track local types
-                        String name = StringFormatDetector.getResourceForFirstArg(method, node);
-                        if (name != null) {
-                            if (mNotificationIcons == null) {
-                                mNotificationIcons = Sets.newHashSet();
-                            }
-                            mNotificationIcons.add(name);
+                    ResourceUrl url = ResourceEvaluator.getResource(null, args[0]);
+                    if (url != null
+                            && (url.type == ResourceType.DRAWABLE
+                            || url.type == ResourceType.COLOR
+                            || url.type == ResourceType.MIPMAP)) {
+                        if (mNotificationIcons == null) {
+                            mNotificationIcons = Sets.newHashSet();
                         }
+                        mNotificationIcons.add(url.name);
                     }
                 }
-            } else if (BUILDER_CLASS.equals(typeName)) {
-                boolean isBuilder = false;
-                if (parts.size() == 1) {
-                    isBuilder = true;
-                } else if (parts.size() == 2) {
-                    String clz = parts.first().astIdentifier().astValue();
-                    if (NOTIFICATION_CLASS.equals(clz) || NOTIFICATION_COMPAT_CLASS.equals(clz)) {
-                        isBuilder = true;
-                    }
-                }
-                if (isBuilder) {
-                    Node method = StringFormatDetector.getParentMethod(node);
-                    if (method != null) {
-                        SetIconFinder finder = new SetIconFinder();
-                        method.accept(finder);
-                    }
+            } else if (NOTIFICATION_BUILDER_CLASS.equals(typeName)
+                    || NOTIFICATION_COMPAT_BUILDER_CLASS.equals(typeName)) {
+                PsiMethod method = PsiTreeUtil.getParentOfType(node, PsiMethod.class, true);
+                if (method != null) {
+                    SetIconFinder finder = new SetIconFinder();
+                    method.accept(finder);
                 }
             }
-
-            return super.visitConstructorInvocation(node);
         }
     }
 
-    private boolean handleSelect(Select select) {
-        if (select.toString().startsWith(R_DRAWABLE_PREFIX)) {
-            String name = select.astIdentifier().astValue();
+    private boolean handleSelect(PsiElement select) {
+        ResourceUrl url = ResourceEvaluator.getResourceConstant(select);
+        if (url != null && url.type == ResourceType.DRAWABLE && !url.framework) {
             if (mNotificationIcons == null) {
                 mNotificationIcons = Sets.newHashSet();
             }
-            mNotificationIcons.add(name);
+            mNotificationIcons.add(url.name);
 
             return true;
         }
@@ -2060,48 +2046,45 @@
         return false;
     }
 
-    private final class SetIconFinder extends ForwardingAstVisitor {
+    private final class SetIconFinder extends JavaRecursiveElementVisitor {
         @Override
-        public boolean visitMethodInvocation(MethodInvocation node) {
-            if (SET_SMALL_ICON.equals(node.astName().astValue())) {
-                StrictListAccessor<Expression,MethodInvocation> arguments = node.astArguments();
-                if (arguments.size() == 1 && arguments.first() instanceof Select) {
-                    handleSelect((Select) arguments.first());
+        public void visitMethodCallExpression(PsiMethodCallExpression expression) {
+            super.visitMethodCallExpression(expression);
+            if (SET_SMALL_ICON.equals(expression.getMethodExpression().getReferenceName())) {
+                PsiExpression[] arguments = expression.getArgumentList().getExpressions();
+                if (arguments.length == 1 && arguments[0] instanceof PsiReferenceExpression) {
+                    handleSelect(arguments[0]);
                 }
             }
-            return super.visitMethodInvocation(node);
+        }
+
+        @Override
+        public void visitAnonymousClass(PsiAnonymousClass aClass) {
         }
     }
 
-    private final class MenuFinder extends ForwardingAstVisitor {
+    private final class MenuFinder extends JavaRecursiveElementVisitor {
         @Override
-        public boolean visitSelect(Select node) {
-            // R.type.name
-            if (node.astOperand() instanceof Select) {
-                Select select = (Select) node.astOperand();
-                if (select.astOperand() instanceof VariableReference) {
-                    VariableReference reference = (VariableReference) select.astOperand();
-                    if (reference.astIdentifier().astValue().equals(R_CLASS)) {
-                        String type = select.astIdentifier().astValue();
+        public void visitReferenceExpression(PsiReferenceExpression node) {
+            super.visitReferenceExpression(node);
 
-                        if (type.equals(MENU_TYPE)) {
-                            String name = node.astIdentifier().astValue();
-                            // Reclassify icons in the given menu as action bar icons
-                            if (mMenuToIcons != null) {
-                                Collection<String> icons = mMenuToIcons.get(name);
-                                if (icons != null) {
-                                    if (mActionBarIcons == null) {
-                                        mActionBarIcons = Sets.newHashSet();
-                                    }
-                                    mActionBarIcons.addAll(icons);
-                                }
-                            }
+            ResourceUrl url = ResourceEvaluator.getResourceConstant(node);
+            if (url != null && url.type == ResourceType.MENU && !url.framework) {
+                // Reclassify icons in the given menu as action bar icons
+                if (mMenuToIcons != null) {
+                    Collection<String> icons = mMenuToIcons.get(url.name);
+                    if (icons != null) {
+                        if (mActionBarIcons == null) {
+                            mActionBarIcons = Sets.newHashSet();
                         }
+                        mActionBarIcons.addAll(icons);
                     }
                 }
             }
+        }
 
-            return super.visitSelect(node);
+        @Override
+        public void visitAnonymousClass(PsiAnonymousClass aClass) {
         }
     }
 }
diff --git a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/JavaPerformanceDetector.java b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/JavaPerformanceDetector.java
index 52c6b6a..f4eff69 100644
--- a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/JavaPerformanceDetector.java
+++ b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/JavaPerformanceDetector.java
@@ -18,55 +18,60 @@
 
 import static com.android.SdkConstants.SUPPORT_LIB_ARTIFACT;
 import static com.android.tools.lint.client.api.JavaParser.TYPE_BOOLEAN;
+import static com.android.tools.lint.client.api.JavaParser.TYPE_BOOLEAN_WRAPPER;
+import static com.android.tools.lint.client.api.JavaParser.TYPE_BYTE_WRAPPER;
+import static com.android.tools.lint.client.api.JavaParser.TYPE_CHARACTER_WRAPPER;
+import static com.android.tools.lint.client.api.JavaParser.TYPE_DOUBLE_WRAPPER;
+import static com.android.tools.lint.client.api.JavaParser.TYPE_FLOAT_WRAPPER;
 import static com.android.tools.lint.client.api.JavaParser.TYPE_INT;
+import static com.android.tools.lint.client.api.JavaParser.TYPE_INTEGER_WRAPPER;
+import static com.android.tools.lint.client.api.JavaParser.TYPE_LONG_WRAPPER;
+import static com.android.tools.lint.detector.api.LintUtils.skipParentheses;
 
 import com.android.annotations.NonNull;
 import com.android.annotations.Nullable;
+import com.android.tools.lint.client.api.JavaEvaluator;
 import com.android.tools.lint.detector.api.Category;
-import com.android.tools.lint.detector.api.Context;
 import com.android.tools.lint.detector.api.Detector;
 import com.android.tools.lint.detector.api.Implementation;
 import com.android.tools.lint.detector.api.Issue;
 import com.android.tools.lint.detector.api.JavaContext;
 import com.android.tools.lint.detector.api.Scope;
 import com.android.tools.lint.detector.api.Severity;
-import com.android.tools.lint.detector.api.Speed;
 import com.android.tools.lint.detector.api.TextFormat;
 import com.google.common.collect.Sets;
 import com.google.common.collect.Sets.SetView;
+import com.intellij.psi.JavaElementVisitor;
+import com.intellij.psi.JavaRecursiveElementVisitor;
+import com.intellij.psi.PsiAssignmentExpression;
+import com.intellij.psi.PsiBinaryExpression;
+import com.intellij.psi.PsiElement;
+import com.intellij.psi.PsiExpression;
+import com.intellij.psi.PsiIdentifier;
+import com.intellij.psi.PsiIfStatement;
+import com.intellij.psi.PsiJavaCodeReferenceElement;
+import com.intellij.psi.PsiMethod;
+import com.intellij.psi.PsiMethodCallExpression;
+import com.intellij.psi.PsiNewExpression;
+import com.intellij.psi.PsiParenthesizedExpression;
+import com.intellij.psi.PsiPrefixExpression;
+import com.intellij.psi.PsiReferenceExpression;
+import com.intellij.psi.PsiSuperExpression;
+import com.intellij.psi.PsiThisExpression;
+import com.intellij.psi.PsiThrowStatement;
+import com.intellij.psi.PsiType;
+import com.intellij.psi.util.PsiTreeUtil;
 
-import java.io.File;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.HashSet;
-import java.util.Iterator;
 import java.util.List;
 
-import lombok.ast.AstVisitor;
-import lombok.ast.BinaryExpression;
-import lombok.ast.BinaryOperator;
-import lombok.ast.ConstructorInvocation;
-import lombok.ast.Expression;
-import lombok.ast.ForwardingAstVisitor;
-import lombok.ast.If;
-import lombok.ast.MethodDeclaration;
-import lombok.ast.MethodInvocation;
-import lombok.ast.Node;
-import lombok.ast.Select;
-import lombok.ast.StrictListAccessor;
-import lombok.ast.This;
-import lombok.ast.Throw;
-import lombok.ast.TypeReference;
-import lombok.ast.TypeReferencePart;
-import lombok.ast.UnaryExpression;
-import lombok.ast.VariableDefinition;
-import lombok.ast.VariableReference;
-
 /**
  * Looks for performance issues in Java files, such as memory allocations during
  * drawing operations and using HashMap instead of SparseArray.
  */
-public class JavaPerformanceDetector extends Detector implements Detector.JavaScanner {
+public class JavaPerformanceDetector extends Detector implements Detector.JavaPsiScanner {
 
     private static final Implementation IMPLEMENTATION = new Implementation(
             JavaPerformanceDetector.class,
@@ -129,53 +134,35 @@
             Severity.WARNING,
             IMPLEMENTATION);
 
-    static final String ON_MEASURE = "onMeasure";                           //$NON-NLS-1$
-    static final String ON_DRAW = "onDraw";                                 //$NON-NLS-1$
-    static final String ON_LAYOUT = "onLayout";                             //$NON-NLS-1$
-    private static final String INTEGER = "Integer";                        //$NON-NLS-1$
-    private static final String BOOLEAN = "Boolean";                        //$NON-NLS-1$
-    private static final String BYTE = "Byte";                              //$NON-NLS-1$
-    private static final String LONG = "Long";                              //$NON-NLS-1$
-    private static final String CHARACTER = "Character";                    //$NON-NLS-1$
-    private static final String DOUBLE = "Double";                          //$NON-NLS-1$
-    private static final String FLOAT = "Float";                            //$NON-NLS-1$
-    private static final String HASH_MAP = "HashMap";                       //$NON-NLS-1$
-    private static final String SPARSE_ARRAY = "SparseArray";               //$NON-NLS-1$
-    private static final String CANVAS = "Canvas";                          //$NON-NLS-1$
-    private static final String LAYOUT = "layout";                          //$NON-NLS-1$
+    static final String ON_MEASURE = "onMeasure";
+    static final String ON_DRAW = "onDraw";
+    static final String ON_LAYOUT = "onLayout";
+    private static final String LAYOUT = "layout";
+    private static final String HASH_MAP = "java.util.HashMap";
+    private static final String SPARSE_ARRAY = "android.util.SparseArray";
+    public static final String CLASS_CANVAS = "android.graphics.Canvas";
 
     /** Constructs a new {@link JavaPerformanceDetector} check */
     public JavaPerformanceDetector() {
     }
 
-    @Override
-    public boolean appliesTo(@NonNull Context context, @NonNull File file) {
-        return true;
-    }
-
-    @NonNull
-    @Override
-    public Speed getSpeed() {
-        return Speed.FAST;
-    }
-
     // ---- Implements JavaScanner ----
 
     @Override
-    public List<Class<? extends Node>> getApplicableNodeTypes() {
-        List<Class<? extends Node>> types = new ArrayList<Class<? extends Node>>(3);
-        types.add(ConstructorInvocation.class);
-        types.add(MethodDeclaration.class);
-        types.add(MethodInvocation.class);
+    public List<Class<? extends PsiElement>> getApplicablePsiTypes() {
+        List<Class<? extends PsiElement>> types = new ArrayList<Class<? extends PsiElement>>(3);
+        types.add(PsiNewExpression.class);
+        types.add(PsiMethod.class);
+        types.add(PsiMethodCallExpression.class);
         return types;
     }
 
     @Override
-    public AstVisitor createJavaVisitor(@NonNull JavaContext context) {
+    public JavaElementVisitor createPsiVisitor(@NonNull JavaContext context) {
         return new PerformanceVisitor(context);
     }
 
-    private static class PerformanceVisitor extends ForwardingAstVisitor {
+    private static class PerformanceVisitor extends JavaElementVisitor {
         private final JavaContext mContext;
         private final boolean mCheckMaps;
         private final boolean mCheckAllocations;
@@ -192,108 +179,106 @@
         }
 
         @Override
-        public boolean visitMethodDeclaration(MethodDeclaration node) {
+        public void visitMethod(PsiMethod node) {
             mFlagAllocations = isBlockedAllocationMethod(node);
-
-            return super.visitMethodDeclaration(node);
         }
 
         @Override
-        public boolean visitConstructorInvocation(ConstructorInvocation node) {
+        public void visitNewExpression(PsiNewExpression node) {
             String typeName = null;
+            PsiJavaCodeReferenceElement classReference = node.getClassReference();
+            if (mCheckMaps || mCheckValueOf) {
+                if (classReference != null) {
+                    typeName = classReference.getQualifiedName();
+                }
+            }
+
             if (mCheckMaps) {
-                TypeReference reference = node.astTypeReference();
-                typeName = reference.astParts().last().astIdentifier().astValue();
                 // TODO: Should we handle factory method constructions of HashMaps as well,
                 // e.g. via Guava? This is a bit trickier since we need to infer the type
                 // arguments from the calling context.
-                if (typeName.equals(HASH_MAP)) {
-                    checkHashMap(node, reference);
-                } else if (typeName.equals(SPARSE_ARRAY)) {
-                    checkSparseArray(node, reference);
+                if (HASH_MAP.equals(typeName)) {
+                    checkHashMap(node, classReference);
+                } else if (SPARSE_ARRAY.equals(typeName)) {
+                    checkSparseArray(node, classReference);
                 }
             }
 
             if (mCheckValueOf) {
-                if (typeName == null) {
-                    TypeReference reference = node.astTypeReference();
-                    typeName = reference.astParts().last().astIdentifier().astValue();
-                }
-                if ((typeName.equals(INTEGER)
-                        || typeName.equals(BOOLEAN)
-                        || typeName.equals(FLOAT)
-                        || typeName.equals(CHARACTER)
-                        || typeName.equals(LONG)
-                        || typeName.equals(DOUBLE)
-                        || typeName.equals(BYTE))
-                        && node.astTypeReference().astParts().size() == 1
-                        && node.astArguments().size() == 1) {
-                    String argument = node.astArguments().first().toString();
+                if (typeName != null
+                        && (typeName.equals(TYPE_INTEGER_WRAPPER)
+                        || typeName.equals(TYPE_BOOLEAN_WRAPPER)
+                        || typeName.equals(TYPE_FLOAT_WRAPPER)
+                        || typeName.equals(TYPE_CHARACTER_WRAPPER)
+                        || typeName.equals(TYPE_LONG_WRAPPER)
+                        || typeName.equals(TYPE_DOUBLE_WRAPPER)
+                        || typeName.equals(TYPE_BYTE_WRAPPER))
+                        //&& node.astTypeReference().astParts().size() == 1
+                        && node.getArgumentList() != null
+                        && node.getArgumentList().getExpressions().length == 1) {
+                    String argument = node.getArgumentList().getExpressions()[0].getText();
                     mContext.report(USE_VALUE_OF, node, mContext.getLocation(node), getUseValueOfErrorMessage(
                             typeName, argument));
                 }
             }
 
-            if (mFlagAllocations && !(node.getParent() instanceof Throw) && mCheckAllocations) {
+            if (mFlagAllocations
+                    && !(skipParentheses(node.getParent()) instanceof PsiThrowStatement)
+                    && mCheckAllocations) {
                 // Make sure we're still inside the method declaration that marked
                 // mInDraw as true, in case we've left it and we're in a static
                 // block or something:
-                Node method = node;
-                while (method != null) {
-                    if (method instanceof MethodDeclaration) {
-                        break;
-                    }
-                    method = method.getParent();
-                }
-                if (method != null && isBlockedAllocationMethod(((MethodDeclaration) method))
+                PsiMethod method = PsiTreeUtil.getParentOfType(node, PsiMethod.class);
+                if (method != null && isBlockedAllocationMethod(method)
                         && !isLazilyInitialized(node)) {
                     reportAllocation(node);
                 }
             }
-
-            return super.visitConstructorInvocation(node);
         }
 
-        private void reportAllocation(Node node) {
+        private void reportAllocation(PsiElement node) {
             mContext.report(PAINT_ALLOC, node, mContext.getLocation(node),
                 "Avoid object allocations during draw/layout operations (preallocate and " +
                 "reuse instead)");
         }
 
         @Override
-        public boolean visitMethodInvocation(MethodInvocation node) {
-            if (mFlagAllocations && node.astOperand() != null) {
-                // Look for forbidden methods
-                String methodName = node.astName().astValue();
-                if (methodName.equals("createBitmap")                              //$NON-NLS-1$
-                        || methodName.equals("createScaledBitmap")) {              //$NON-NLS-1$
-                    String operand = node.astOperand().toString();
-                    if (operand.equals("Bitmap")                                   //$NON-NLS-1$
-                            || operand.equals("android.graphics.Bitmap")) {        //$NON-NLS-1$
-                        if (!isLazilyInitialized(node)) {
-                            reportAllocation(node);
-                        }
-                    }
-                } else if (methodName.startsWith("decode")) {                      //$NON-NLS-1$
-                    // decodeFile, decodeByteArray, ...
-                    String operand = node.astOperand().toString();
-                    if (operand.equals("BitmapFactory")                            //$NON-NLS-1$
-                            || operand.equals("android.graphics.BitmapFactory")) { //$NON-NLS-1$
-                        if (!isLazilyInitialized(node)) {
-                            reportAllocation(node);
-                        }
-                    }
-                } else if (methodName.equals("getClipBounds")) {                   //$NON-NLS-1$
-                    if (node.astArguments().isEmpty()) {
-                        mContext.report(PAINT_ALLOC, node, mContext.getLocation(node),
-                                "Avoid object allocations during draw operations: Use " +
-                                "`Canvas.getClipBounds(Rect)` instead of `Canvas.getClipBounds()` " +
-                                "which allocates a temporary `Rect`");
-                    }
+        public void visitMethodCallExpression(PsiMethodCallExpression node) {
+            if (!mFlagAllocations) {
+                return;
+            }
+            PsiReferenceExpression expression = node.getMethodExpression();
+            PsiElement qualifier = expression.getQualifier();
+            if (qualifier == null) {
+                return;
+            }
+            String methodName = expression.getReferenceName();
+            if (methodName == null) {
+                return;
+            }
+            // Look for forbidden methods
+            if (methodName.equals("createBitmap")                              //$NON-NLS-1$
+                    || methodName.equals("createScaledBitmap")) {              //$NON-NLS-1$
+                PsiMethod method = node.resolveMethod();
+                if (method != null && mContext.getEvaluator().isMemberInClass(method,
+                        "android.graphics.Bitmap") && !isLazilyInitialized(node)) {
+                    reportAllocation(node);
+                }
+            } else if (methodName.startsWith("decode")) {                      //$NON-NLS-1$
+                // decodeFile, decodeByteArray, ...
+                PsiMethod method = node.resolveMethod();
+                if (method != null && mContext.getEvaluator().isMemberInClass(method,
+                        "android.graphics.BitmapFactory") && !isLazilyInitialized(node)) {
+                    reportAllocation(node);
+                }
+            } else if (methodName.equals("getClipBounds")) {                   //$NON-NLS-1$
+                if (node.getArgumentList().getExpressions().length == 0) {
+                    mContext.report(PAINT_ALLOC, node, mContext.getLocation(node),
+                            "Avoid object allocations during draw operations: Use " +
+                            "`Canvas.getClipBounds(Rect)` instead of `Canvas.getClipBounds()` " +
+                            "which allocates a temporary `Rect`");
                 }
             }
-
-            return super.visitMethodInvocation(node);
         }
 
         /**
@@ -316,13 +301,13 @@
          *    }
          * </pre>
          */
-        private static boolean isLazilyInitialized(Node node) {
-            Node curr = node.getParent();
+        private static boolean isLazilyInitialized(PsiElement node) {
+            PsiElement curr = node.getParent();
             while (curr != null) {
-                if (curr instanceof MethodDeclaration) {
+                if (curr instanceof PsiMethod) {
                     return false;
-                } else if (curr instanceof If) {
-                    If ifNode = (If) curr;
+                } else if (curr instanceof PsiIfStatement) {
+                    PsiIfStatement ifNode = (PsiIfStatement) curr;
                     // See if the if block represents a lazy initialization:
                     // compute all variable names seen in the condition
                     // (e.g. for "if (foo == null || bar != foo)" the result is "foo,bar"),
@@ -332,10 +317,12 @@
                     // about.)
                     List<String> assignments = new ArrayList<String>();
                     AssignmentTracker visitor = new AssignmentTracker(assignments);
-                    ifNode.astStatement().accept(visitor);
+                    if (ifNode.getThenBranch() != null) {
+                        ifNode.getThenBranch().accept(visitor);
+                    }
                     if (!assignments.isEmpty()) {
                         List<String> references = new ArrayList<String>();
-                        addReferencedVariables(references, ifNode.astCondition());
+                        addReferencedVariables(references, ifNode.getCondition());
                         if (!references.isEmpty()) {
                             SetView<String> intersection = Sets.intersection(
                                     new HashSet<String>(assignments),
@@ -353,22 +340,32 @@
         }
 
         /** Adds any variables referenced in the given expression into the given list */
-        private static void addReferencedVariables(Collection<String> variables,
-                Expression expression) {
-            if (expression instanceof BinaryExpression) {
-                BinaryExpression binary = (BinaryExpression) expression;
-                addReferencedVariables(variables, binary.astLeft());
-                addReferencedVariables(variables, binary.astRight());
-            } else if (expression instanceof UnaryExpression) {
-                UnaryExpression unary = (UnaryExpression) expression;
-                addReferencedVariables(variables, unary.astOperand());
-            } else if (expression instanceof VariableReference) {
-                VariableReference reference = (VariableReference) expression;
-                variables.add(reference.astIdentifier().astValue());
-            } else if (expression instanceof Select) {
-                Select select = (Select) expression;
-                if (select.astOperand() instanceof This) {
-                    variables.add(select.astIdentifier().astValue());
+        private static void addReferencedVariables(
+                @NonNull Collection<String> variables,
+                @Nullable PsiExpression expression) {
+            if (expression instanceof PsiBinaryExpression) {
+                PsiBinaryExpression binary = (PsiBinaryExpression) expression;
+                addReferencedVariables(variables, binary.getLOperand());
+                addReferencedVariables(variables, binary.getROperand());
+            } else if (expression instanceof PsiPrefixExpression) {
+                PsiPrefixExpression unary = (PsiPrefixExpression) expression;
+                addReferencedVariables(variables, unary.getOperand());
+            } else if (expression instanceof PsiParenthesizedExpression) {
+                PsiParenthesizedExpression exp = (PsiParenthesizedExpression) expression;
+                addReferencedVariables(variables, exp.getExpression());
+            } else if (expression instanceof PsiIdentifier) {
+                PsiIdentifier reference = (PsiIdentifier) expression;
+                variables.add(reference.getText());
+            } else if (expression instanceof PsiReferenceExpression) {
+                PsiReferenceExpression ref = (PsiReferenceExpression) expression;
+                PsiElement qualifier = ref.getQualifier();
+                if (qualifier != null) {
+                    if (qualifier instanceof PsiThisExpression ||
+                            qualifier instanceof PsiSuperExpression) {
+                        variables.add(ref.getReferenceName());
+                    }
+                } else {
+                    variables.add(ref.getReferenceName());
                 }
             }
         }
@@ -377,30 +374,23 @@
          * Returns whether the given method declaration represents a method
          * where allocating objects is not allowed for performance reasons
          */
-        private static boolean isBlockedAllocationMethod(MethodDeclaration node) {
-            return isOnDrawMethod(node) || isOnMeasureMethod(node) || isOnLayoutMethod(node)
-                    || isLayoutMethod(node);
+        private boolean isBlockedAllocationMethod(
+                @NonNull PsiMethod node) {
+            JavaEvaluator evaluator = mContext.getEvaluator();
+            return isOnDrawMethod(evaluator, node)
+                    || isOnMeasureMethod(evaluator, node)
+                    || isOnLayoutMethod(evaluator, node)
+                    || isLayoutMethod(evaluator, node);
         }
 
         /**
          * Returns true if this method looks like it's overriding android.view.View's
          * {@code protected void onDraw(Canvas canvas)}
          */
-        private static boolean isOnDrawMethod(MethodDeclaration node) {
-            if (ON_DRAW.equals(node.astMethodName().astValue())) {
-                StrictListAccessor<VariableDefinition, MethodDeclaration> parameters =
-                        node.astParameters();
-                if (parameters != null && parameters.size() == 1) {
-                    VariableDefinition arg0 = parameters.first();
-                    TypeReferencePart type = arg0.astTypeReference().astParts().last();
-                    String typeName = type.getTypeName();
-                    if (typeName.equals(CANVAS)) {
-                        return true;
-                    }
-                }
-            }
-
-            return false;
+        private static boolean isOnDrawMethod(
+                @NonNull JavaEvaluator evaluator,
+                @NonNull PsiMethod node) {
+            return ON_DRAW.equals(node.getName()) && evaluator.parametersMatch(node, CLASS_CANVAS);
         }
 
         /**
@@ -409,115 +399,66 @@
          * {@code protected void onLayout(boolean changed, int left, int top,
          *      int right, int bottom)}
          */
-        private static boolean isOnLayoutMethod(MethodDeclaration node) {
-            if (ON_LAYOUT.equals(node.astMethodName().astValue())) {
-                StrictListAccessor<VariableDefinition, MethodDeclaration> parameters =
-                        node.astParameters();
-                if (parameters != null && parameters.size() == 5) {
-                    Iterator<VariableDefinition> iterator = parameters.iterator();
-                    if (!iterator.hasNext()) {
-                        return false;
-                    }
-
-                    // Ensure that the argument list matches boolean, int, int, int, int
-                    TypeReferencePart type = iterator.next().astTypeReference().astParts().last();
-                    if (!type.getTypeName().equals(TYPE_BOOLEAN) || !iterator.hasNext()) {
-                        return false;
-                    }
-                    for (int i = 0; i < 4; i++) {
-                        type = iterator.next().astTypeReference().astParts().last();
-                        if (!type.getTypeName().equals(TYPE_INT)) {
-                            return false;
-                        }
-                        if (!iterator.hasNext()) {
-                            return i == 3;
-                        }
-                    }
-                }
-            }
-
-            return false;
+        private static boolean isOnLayoutMethod(
+                @NonNull JavaEvaluator evaluator,
+                @NonNull PsiMethod node) {
+            return ON_LAYOUT.equals(node.getName()) && evaluator.parametersMatch(node,
+                    TYPE_BOOLEAN, TYPE_INT, TYPE_INT, TYPE_INT, TYPE_INT);
         }
 
         /**
          * Returns true if this method looks like it's overriding android.view.View's
          * {@code protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)}
          */
-        private static boolean isOnMeasureMethod(MethodDeclaration node) {
-            if (ON_MEASURE.equals(node.astMethodName().astValue())) {
-                StrictListAccessor<VariableDefinition, MethodDeclaration> parameters =
-                        node.astParameters();
-                if (parameters != null && parameters.size() == 2) {
-                    VariableDefinition arg0 = parameters.first();
-                    VariableDefinition arg1 = parameters.last();
-                    TypeReferencePart type1 = arg0.astTypeReference().astParts().last();
-                    TypeReferencePart type2 = arg1.astTypeReference().astParts().last();
-                    return TYPE_INT.equals(type1.getTypeName())
-                            && TYPE_INT.equals(type2.getTypeName());
-                }
-            }
-
-            return false;
+        private static boolean isOnMeasureMethod(
+                @NonNull JavaEvaluator evaluator,
+                @NonNull PsiMethod node) {
+            return ON_MEASURE.equals(node.getName()) && evaluator.parametersMatch(node,
+                    TYPE_INT, TYPE_INT);
         }
 
         /**
          * Returns true if this method looks like it's overriding android.view.View's
          * {@code public void layout(int l, int t, int r, int b)}
          */
-        private static boolean isLayoutMethod(MethodDeclaration node) {
-            if (LAYOUT.equals(node.astMethodName().astValue())) {
-                StrictListAccessor<VariableDefinition, MethodDeclaration> parameters =
-                        node.astParameters();
-                if (parameters != null && parameters.size() == 4) {
-                    Iterator<VariableDefinition> iterator = parameters.iterator();
-                    for (int i = 0; i < 4; i++) {
-                        if (!iterator.hasNext()) {
-                            return false;
-                        }
-                        VariableDefinition next = iterator.next();
-                        TypeReferencePart type = next.astTypeReference().astParts().last();
-                        if (!TYPE_INT.equals(type.getTypeName())) {
-                            return false;
-                        }
-                    }
-                    return true;
-                }
-            }
-
-            return false;
+        private static boolean isLayoutMethod(
+                @NonNull JavaEvaluator evaluator,
+                @NonNull PsiMethod node) {
+            return LAYOUT.equals(node.getName()) && evaluator.parametersMatch(node,
+                    TYPE_INT, TYPE_INT, TYPE_INT, TYPE_INT);
         }
 
-
         /**
          * Checks whether the given constructor call and type reference refers
          * to a HashMap constructor call that is eligible for replacement by a
          * SparseArray call instead
          */
-        private void checkHashMap(ConstructorInvocation node, TypeReference reference) {
-            // reference.hasTypeArguments returns false where it should not
-            StrictListAccessor<TypeReference, TypeReference> types = reference.getTypeArguments();
-            if (types != null && types.size() == 2) {
-                TypeReference first = types.first();
-                String typeName = first.getTypeName();
+        private void checkHashMap(
+                @NonNull PsiNewExpression node,
+                @NonNull PsiJavaCodeReferenceElement reference) {
+            PsiType[] types = reference.getTypeParameters();
+            if (types.length == 2) {
+                PsiType first = types[0];
+                String typeName = first.getCanonicalText();
                 int minSdk = mContext.getMainProject().getMinSdk();
-                if (typeName.equals(INTEGER) || typeName.equals(BYTE)) {
-                    String valueType = types.last().getTypeName();
-                    if (valueType.equals(INTEGER)) {
+                if (TYPE_INTEGER_WRAPPER.equals(typeName) || TYPE_BYTE_WRAPPER.equals(typeName)) {
+                    String valueType = types[1].getCanonicalText();
+                    if (valueType.equals(TYPE_INTEGER_WRAPPER)) {
                         mContext.report(USE_SPARSE_ARRAY, node, mContext.getLocation(node),
                             "Use new `SparseIntArray(...)` instead for better performance");
-                    } else if (valueType.equals(LONG) && minSdk >= 18) {
+                    } else if (valueType.equals(TYPE_LONG_WRAPPER) && minSdk >= 18) {
                         mContext.report(USE_SPARSE_ARRAY, node, mContext.getLocation(node),
                                 "Use `new SparseLongArray(...)` instead for better performance");
-                    } else if (valueType.equals(BOOLEAN)) {
+                    } else if (valueType.equals(TYPE_BOOLEAN_WRAPPER)) {
                         mContext.report(USE_SPARSE_ARRAY, node, mContext.getLocation(node),
                                 "Use `new SparseBooleanArray(...)` instead for better performance");
                     } else {
                         mContext.report(USE_SPARSE_ARRAY, node, mContext.getLocation(node),
                             String.format(
                                 "Use `new SparseArray<%1$s>(...)` instead for better performance",
-                              valueType));
+                              valueType.substring(valueType.lastIndexOf('.') + 1)));
                     }
-                } else if (typeName.equals(LONG) && (minSdk >= 16 ||
+                } else if (TYPE_LONG_WRAPPER.equals(typeName) && (minSdk >= 16 ||
                         Boolean.TRUE == mContext.getMainProject().dependsOn(
                                 SUPPORT_LIB_ARTIFACT))) {
                     boolean useBuiltin = minSdk >= 16;
@@ -530,16 +471,16 @@
             }
         }
 
-        private void checkSparseArray(ConstructorInvocation node, TypeReference reference) {
-            // reference.hasTypeArguments returns false where it should not
-            StrictListAccessor<TypeReference, TypeReference> types = reference.getTypeArguments();
-            if (types != null && types.size() == 1) {
-                TypeReference first = types.first();
-                String valueType = first.getTypeName();
-                if (valueType.equals(INTEGER)) {
+        private void checkSparseArray(
+                @NonNull PsiNewExpression node,
+                @NonNull PsiJavaCodeReferenceElement reference) {
+            PsiType[] types = reference.getTypeParameters();
+            if (types.length == 1) {
+                String valueType = types[0].getCanonicalText();
+                if (valueType.equals(TYPE_INTEGER_WRAPPER)) {
                     mContext.report(USE_SPARSE_ARRAY, node, mContext.getLocation(node),
                         "Use `new SparseIntArray(...)` instead for better performance");
-                } else if (valueType.equals(BOOLEAN)) {
+                } else if (valueType.equals(TYPE_BOOLEAN_WRAPPER)) {
                     mContext.report(USE_SPARSE_ARRAY, node, mContext.getLocation(node),
                             "Use `new SparseBooleanArray(...)` instead for better performance");
                 }
@@ -549,13 +490,15 @@
 
     private static String getUseValueOfErrorMessage(String typeName, String argument) {
         // Keep in sync with {@link #getReplacedType} below
-        return String.format("Use `%1$s.valueOf(%2$s)` instead", typeName, argument);
+        return String.format("Use `%1$s.valueOf(%2$s)` instead",
+                typeName.substring(typeName.lastIndexOf('.') + 1), argument);
     }
 
     /**
      * For an error message for an {@link #USE_VALUE_OF} issue reported by this detector,
      * returns the type being replaced. Intended to use for IDE quickfix implementations.
      */
+    @SuppressWarnings("unused") // Used by the IDE
     @Nullable
     public static String getReplacedType(@NonNull String message, @NonNull TextFormat format) {
         message = format.toText(message);
@@ -567,7 +510,7 @@
     }
 
     /** Visitor which records variable names assigned into */
-    private static class AssignmentTracker extends ForwardingAstVisitor {
+    private static class AssignmentTracker extends JavaRecursiveElementVisitor {
         private final Collection<String> mVariables;
 
         public AssignmentTracker(Collection<String> variables) {
@@ -575,20 +518,21 @@
         }
 
         @Override
-        public boolean visitBinaryExpression(BinaryExpression node) {
-            BinaryOperator operator = node.astOperator();
-            if (operator == BinaryOperator.ASSIGN || operator == BinaryOperator.OR_ASSIGN) {
-                Expression left = node.astLeft();
-                String variable;
-                if (left instanceof Select && ((Select) left).astOperand() instanceof This) {
-                    variable = ((Select) left).astIdentifier().astValue();
-                } else {
-                    variable = left.toString();
-                }
-                mVariables.add(variable);
-            }
+        public void visitAssignmentExpression(PsiAssignmentExpression node) {
+            super.visitAssignmentExpression(node);
 
-            return super.visitBinaryExpression(node);
+            PsiExpression left = node.getLExpression();
+            if (left instanceof PsiReferenceExpression) {
+                PsiReferenceExpression ref = (PsiReferenceExpression) left;
+                if (ref.getQualifier() instanceof PsiThisExpression ||
+                        ref.getQualifier() instanceof PsiSuperExpression) {
+                    mVariables.add(ref.getReferenceName());
+                } else {
+                    mVariables.add(ref.getText());
+                }
+            } else if (left instanceof PsiIdentifier) {
+                mVariables.add(left.getText());
+            }
         }
     }
 }
diff --git a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/JavaScriptInterfaceDetector.java b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/JavaScriptInterfaceDetector.java
index 9e4a407..4ee3037 100644
--- a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/JavaScriptInterfaceDetector.java
+++ b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/JavaScriptInterfaceDetector.java
@@ -18,45 +18,34 @@
 
 import com.android.annotations.NonNull;
 import com.android.annotations.Nullable;
-import com.android.tools.lint.client.api.JavaParser.ResolvedAnnotation;
-import com.android.tools.lint.client.api.JavaParser.ResolvedClass;
-import com.android.tools.lint.client.api.JavaParser.ResolvedMethod;
-import com.android.tools.lint.client.api.JavaParser.ResolvedNode;
-import com.android.tools.lint.client.api.JavaParser.ResolvedVariable;
-import com.android.tools.lint.client.api.JavaParser.TypeDescriptor;
+import com.android.tools.lint.client.api.JavaEvaluator;
 import com.android.tools.lint.detector.api.Category;
 import com.android.tools.lint.detector.api.Detector;
+import com.android.tools.lint.detector.api.Detector.JavaPsiScanner;
 import com.android.tools.lint.detector.api.Implementation;
 import com.android.tools.lint.detector.api.Issue;
 import com.android.tools.lint.detector.api.JavaContext;
 import com.android.tools.lint.detector.api.Location;
 import com.android.tools.lint.detector.api.Scope;
 import com.android.tools.lint.detector.api.Severity;
-import com.android.tools.lint.detector.api.Speed;
-import com.google.common.collect.Maps;
+import com.android.tools.lint.detector.api.TypeEvaluator;
+import com.intellij.psi.JavaElementVisitor;
+import com.intellij.psi.PsiClass;
+import com.intellij.psi.PsiClassType;
+import com.intellij.psi.PsiExpression;
+import com.intellij.psi.PsiMethod;
+import com.intellij.psi.PsiMethodCallExpression;
+import com.intellij.psi.PsiModifierList;
+import com.intellij.psi.PsiType;
 
 import java.util.Collections;
 import java.util.List;
-import java.util.Map;
-
-import lombok.ast.AstVisitor;
-import lombok.ast.BinaryExpression;
-import lombok.ast.BinaryOperator;
-import lombok.ast.Cast;
-import lombok.ast.ConstructorInvocation;
-import lombok.ast.Expression;
-import lombok.ast.ForwardingAstVisitor;
-import lombok.ast.InlineIfExpression;
-import lombok.ast.MethodInvocation;
-import lombok.ast.Node;
-import lombok.ast.VariableDefinitionEntry;
-import lombok.ast.VariableReference;
 
 /**
  * Looks for addJavascriptInterface calls on interfaces have been properly annotated
  * with {@code @JavaScriptInterface}
  */
-public class JavaScriptInterfaceDetector extends Detector implements Detector.JavaScanner {
+public class JavaScriptInterfaceDetector extends Detector implements JavaPsiScanner {
     /** The main issue discovered by this detector */
     public static final Issue ISSUE = Issue.create(
             "JavascriptInterface", //$NON-NLS-1$
@@ -82,12 +71,6 @@
     public JavaScriptInterfaceDetector() {
     }
 
-    @NonNull
-    @Override
-    public Speed getSpeed() {
-        return Speed.SLOW; // because it relies on class loading referenced javascript interface
-    }
-
     // ---- Implements JavaScanner ----
 
     @Nullable
@@ -97,98 +80,54 @@
     }
 
     @Override
-    public void visitMethod(
-            @NonNull JavaContext context,
-            @Nullable AstVisitor visitor,
-            @NonNull MethodInvocation call) {
+    public void visitMethod(@NonNull JavaContext context, @Nullable JavaElementVisitor visitor,
+            @NonNull PsiMethodCallExpression call, @NonNull PsiMethod method) {
         if (context.getMainProject().getTargetSdk() < 17) {
             return;
         }
 
-        if (call.astArguments().size() != 2) {
+        PsiExpression[] arguments = call.getArgumentList().getExpressions();
+        if (arguments.length != 2) {
             return;
         }
 
-        if (!isCallOnWebView(context, call)) {
+        JavaEvaluator evaluator = context.getEvaluator();
+        if (!evaluator.isMemberInClass(method, WEB_VIEW_CLS)) {
             return;
         }
 
-        Expression first = call.astArguments().first();
-        ResolvedNode resolved = context.resolve(first);
-        if (resolved instanceof ResolvedVariable) {
-            // We're passing in a variable to the addJavaScriptInterface method;
-            // the variable may be of a more generic type than the actual
-            // value assigned to it. For example, we may have a scenario like this:
-            //    Object object = new SpecificType();
-            //    addJavaScriptInterface(object, ...)
-            // Here the type of the variable is Object, but we know that it can
-            // contain objects of type SpecificType, so we should check that type instead.
-            Node method = JavaContext.findSurroundingMethod(call);
-            if (method != null) {
-                ConcreteTypeVisitor v = new ConcreteTypeVisitor(context, call);
-                method.accept(v);
-                resolved = v.getType();
-                if (resolved == null) {
-                    return;
-                }
-            } else {
+        PsiExpression first = arguments[0];
+        PsiType evaluated = TypeEvaluator.evaluate(context, first);
+        if (evaluated instanceof PsiClassType) {
+            PsiClassType classType = (PsiClassType) evaluated;
+            PsiClass cls = classType.resolve();
+            if (cls == null) {
                 return;
             }
-        } else if (resolved instanceof ResolvedMethod) {
-            ResolvedMethod method = (ResolvedMethod) resolved;
-            if (method.isConstructor()) {
-                resolved = method.getContainingClass();
-            } else {
-                TypeDescriptor returnType = method.getReturnType();
-                if (returnType != null) {
-                    resolved = returnType.getTypeClass();
-                }
-            }
-        } else {
-            TypeDescriptor type = context.getType(first);
-            if (type != null) {
-                resolved = type.getTypeClass();
-            }
-        }
-
-        if (resolved instanceof ResolvedClass) {
-            ResolvedClass cls = (ResolvedClass) resolved;
             if (isJavaScriptAnnotated(cls)) {
                 return;
             }
 
-            Location location = context.getLocation(call.astName());
+            Location location = context.getNameLocation(call);
             String message = String.format(
                     "None of the methods in the added interface (%1$s) have been annotated " +
                     "with `@android.webkit.JavascriptInterface`; they will not " +
-                    "be visible in API 17", cls.getSimpleName());
+                    "be visible in API 17", cls.getName());
             context.report(ISSUE, call, location, message);
         }
     }
 
-    private static boolean isCallOnWebView(JavaContext context, MethodInvocation call) {
-        ResolvedNode resolved = context.resolve(call);
-        if (!(resolved instanceof ResolvedMethod)) {
-            return false;
-        }
-        ResolvedMethod method = (ResolvedMethod) resolved;
-        return method.getContainingClass().matches(WEB_VIEW_CLS);
-
-    }
-
-    private static boolean isJavaScriptAnnotated(ResolvedClass clz) {
+    private static boolean isJavaScriptAnnotated(PsiClass clz) {
         while (clz != null) {
-            for (ResolvedAnnotation annotation : clz.getAnnotations()) {
-                if (annotation.getType().matchesSignature(JAVASCRIPT_INTERFACE_CLS)) {
-                    return true;
-                }
+            PsiModifierList modifierList = clz.getModifierList();
+            if (modifierList != null
+                    && modifierList.findAnnotation(JAVASCRIPT_INTERFACE_CLS) != null) {
+                return true;
             }
 
-            for (ResolvedMethod method : clz.getMethods(false)) {
-                for (ResolvedAnnotation annotation : method.getAnnotations()) {
-                    if (annotation.getType().matchesSignature(JAVASCRIPT_INTERFACE_CLS)) {
-                        return true;
-                    }
+            for (PsiMethod method : clz.getMethods()) {
+                if (method.getModifierList().findAnnotation(JAVASCRIPT_INTERFACE_CLS) != null) {
+                    return true;
                 }
             }
 
@@ -197,125 +136,4 @@
 
         return false;
     }
-
-    private static class ConcreteTypeVisitor extends ForwardingAstVisitor {
-        private final JavaContext mContext;
-        private final MethodInvocation mTargetCall;
-        private boolean mFoundCall;
-        private Map<Node, ResolvedClass> mTypes = Maps.newIdentityHashMap();
-        private Map<ResolvedVariable, ResolvedClass> mVariableTypes = Maps.newHashMap();
-
-        public ConcreteTypeVisitor(JavaContext context, MethodInvocation call) {
-            mContext = context;
-            mTargetCall = call;
-        }
-
-        public ResolvedClass getType() {
-            Expression first = mTargetCall.astArguments().first();
-            ResolvedClass resolvedClass = mTypes.get(first);
-            if (resolvedClass == null) {
-                ResolvedNode resolved = mContext.resolve(first);
-                if (resolved instanceof ResolvedVariable) {
-                    resolvedClass = mVariableTypes.get(resolved);
-                    if (resolvedClass == null) {
-                        return ((ResolvedVariable)resolved).getType().getTypeClass();
-                    }
-                }
-            }
-            return resolvedClass;
-        }
-
-        @Override
-        public boolean visitNode(Node node) {
-            return mFoundCall || super.visitNode(node);
-        }
-
-        @Override
-        public void afterVisitMethodInvocation(MethodInvocation node) {
-            if (node == mTargetCall) {
-                mFoundCall = true;
-            }
-        }
-
-        @Override
-        public void afterVisitConstructorInvocation(@NonNull ConstructorInvocation node) {
-            ResolvedNode resolved = mContext.resolve(node);
-            if (resolved instanceof ResolvedMethod) {
-                ResolvedMethod method = (ResolvedMethod) resolved;
-                mTypes.put(node, method.getContainingClass());
-            } else {
-                // Implicit constructor?
-                TypeDescriptor type = mContext.getType(node);
-                if (type != null) {
-                    ResolvedClass typeClass = type.getTypeClass();
-                    if (typeClass != null) {
-                        mTypes.put(node, typeClass);
-                    }
-                }
-            }
-        }
-
-        @Override
-        public void afterVisitVariableReference(VariableReference node) {
-            if (mTypes.get(node) == null) {
-                ResolvedNode resolved = mContext.resolve(node);
-                if (resolved instanceof ResolvedVariable) {
-                    ResolvedClass resolvedClass = mVariableTypes.get(resolved);
-                    if (resolvedClass != null) {
-                        mTypes.put(node, resolvedClass);
-                    }
-                }
-            }
-        }
-
-        @Override
-        public void afterVisitBinaryExpression(BinaryExpression node) {
-            if (node.astOperator() == BinaryOperator.ASSIGN) {
-                Expression rhs = node.astRight();
-                ResolvedClass resolvedClass = mTypes.get(rhs);
-                if (resolvedClass != null) {
-                    Expression lhs = node.astLeft();
-                    mTypes.put(lhs, resolvedClass);
-                    ResolvedNode variable = mContext.resolve(lhs);
-                    if (variable instanceof ResolvedVariable) {
-                        mVariableTypes.put((ResolvedVariable) variable, resolvedClass);
-                    }
-                }
-            }
-        }
-
-        @Override
-        public void afterVisitInlineIfExpression(InlineIfExpression node) {
-            ResolvedClass resolvedClass = mTypes.get(node.astIfTrue());
-            if (resolvedClass == null) {
-                resolvedClass = mTypes.get(node.astIfFalse());
-            }
-            if (resolvedClass != null) {
-                mTypes.put(node, resolvedClass);
-            }
-        }
-
-        @Override
-        public void afterVisitVariableDefinitionEntry(VariableDefinitionEntry node) {
-            Expression initializer = node.astInitializer();
-            if (initializer != null) {
-                ResolvedClass resolvedClass = mTypes.get(initializer);
-                if (resolvedClass != null) {
-                    mTypes.put(node, resolvedClass);
-                    ResolvedNode variable = mContext.resolve(node);
-                    if (variable instanceof ResolvedVariable) {
-                        mVariableTypes.put((ResolvedVariable) variable, resolvedClass);
-                    }
-                }
-            }
-        }
-
-        @Override
-        public void afterVisitCast(Cast node) {
-            ResolvedClass resolvedClass = mTypes.get(node);
-            if (resolvedClass != null) {
-                mTypes.put(node, resolvedClass);
-            }
-        }
-    }
 }
diff --git a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/LayoutConsistencyDetector.java b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/LayoutConsistencyDetector.java
index f67af16..9e91e24 100644
--- a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/LayoutConsistencyDetector.java
+++ b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/LayoutConsistencyDetector.java
@@ -28,7 +28,7 @@
 import com.android.tools.lint.client.api.LintDriver;
 import com.android.tools.lint.detector.api.Category;
 import com.android.tools.lint.detector.api.Context;
-import com.android.tools.lint.detector.api.Detector;
+import com.android.tools.lint.detector.api.Detector.JavaPsiScanner;
 import com.android.tools.lint.detector.api.Implementation;
 import com.android.tools.lint.detector.api.Issue;
 import com.android.tools.lint.detector.api.JavaContext;
@@ -37,12 +37,13 @@
 import com.android.tools.lint.detector.api.Location;
 import com.android.tools.lint.detector.api.Scope;
 import com.android.tools.lint.detector.api.Severity;
-import com.android.tools.lint.detector.api.Speed;
 import com.android.tools.lint.detector.api.XmlContext;
 import com.android.utils.Pair;
 import com.google.common.collect.Lists;
 import com.google.common.collect.Maps;
 import com.google.common.collect.Sets;
+import com.intellij.psi.JavaElementVisitor;
+import com.intellij.psi.PsiElement;
 
 import org.w3c.dom.Attr;
 import org.w3c.dom.Document;
@@ -61,12 +62,10 @@
 import java.util.Map;
 import java.util.Set;
 
-import lombok.ast.AstVisitor;
-
 /**
  * Checks for consistency in layouts across different resource folders
  */
-public class LayoutConsistencyDetector extends LayoutDetector implements Detector.JavaScanner {
+public class LayoutConsistencyDetector extends LayoutDetector implements JavaPsiScanner {
 
     /** Map from layout resource names to a list of files defining that resource,
      * and within each file the value is a map from string ids to the widget type
@@ -124,12 +123,6 @@
         return folderType == ResourceFolderType.LAYOUT;
     }
 
-    @NonNull
-    @Override
-    public Speed getSpeed() {
-        return Speed.NORMAL;
-    }
-
     @Override
     public void visitDocument(@NonNull XmlContext context, @NonNull Document document) {
         Element root = document.getDocumentElement();
@@ -436,10 +429,10 @@
     }
 
     @Override
-    public void visitResourceReference(@NonNull JavaContext context, @Nullable AstVisitor visitor,
-            @NonNull lombok.ast.Node node, @NonNull String type, @NonNull String name,
-            boolean isFramework) {
-        if (!isFramework && type.equals(ResourceType.ID.getName())) {
+    public void visitResourceReference(@NonNull JavaContext context,
+            @Nullable JavaElementVisitor visitor, @NonNull PsiElement node,
+            @NonNull ResourceType type, @NonNull String name, boolean isFramework) {
+        if (!isFramework && type == ResourceType.ID) {
             mRelevantIds.add(name);
         }
     }
diff --git a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/LayoutInflationDetector.java b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/LayoutInflationDetector.java
index 3b4ea12..945bbba 100644
--- a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/LayoutInflationDetector.java
+++ b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/LayoutInflationDetector.java
@@ -31,7 +31,7 @@
 import com.android.tools.lint.client.api.LintClient;
 import com.android.tools.lint.detector.api.Category;
 import com.android.tools.lint.detector.api.Context;
-import com.android.tools.lint.detector.api.Detector;
+import com.android.tools.lint.detector.api.Detector.JavaPsiScanner;
 import com.android.tools.lint.detector.api.Implementation;
 import com.android.tools.lint.detector.api.Issue;
 import com.android.tools.lint.detector.api.JavaContext;
@@ -41,11 +41,16 @@
 import com.android.tools.lint.detector.api.Project;
 import com.android.tools.lint.detector.api.Scope;
 import com.android.tools.lint.detector.api.Severity;
-import com.android.tools.lint.detector.api.Speed;
 import com.android.tools.lint.detector.api.XmlContext;
 import com.android.utils.Pair;
 import com.google.common.collect.Lists;
 import com.google.common.collect.Sets;
+import com.intellij.psi.JavaElementVisitor;
+import com.intellij.psi.PsiElement;
+import com.intellij.psi.PsiExpression;
+import com.intellij.psi.PsiMethod;
+import com.intellij.psi.PsiMethodCallExpression;
+import com.intellij.psi.PsiReferenceExpression;
 
 import org.kxml2.io.KXmlParser;
 import org.w3c.dom.Attr;
@@ -60,21 +65,13 @@
 import java.io.Reader;
 import java.io.StringReader;
 import java.util.Collections;
-import java.util.Iterator;
 import java.util.List;
 import java.util.Set;
 
-import lombok.ast.AstVisitor;
-import lombok.ast.Expression;
-import lombok.ast.MethodInvocation;
-import lombok.ast.NullLiteral;
-import lombok.ast.Select;
-import lombok.ast.StrictListAccessor;
-
 /**
  * Looks for layout inflation calls passing null as the view root
  */
-public class LayoutInflationDetector extends LayoutDetector implements Detector.JavaScanner {
+public class LayoutInflationDetector extends LayoutDetector implements JavaPsiScanner {
 
     @SuppressWarnings("unchecked")
     private static final Implementation IMPLEMENTATION = new Implementation(
@@ -104,12 +101,6 @@
     public LayoutInflationDetector() {
     }
 
-    @NonNull
-    @Override
-    public Speed getSpeed() {
-        return Speed.NORMAL;
-    }
-
     @Override
     public void afterCheckProject(@NonNull Context context) {
         if (mPendingErrors != null) {
@@ -159,33 +150,37 @@
     }
 
     @Override
-    public void visitMethod(@NonNull JavaContext context, @Nullable AstVisitor visitor,
-            @NonNull MethodInvocation node) {
-        assert node.astName().astValue().equals(INFLATE);
-        if (node.astOperand() == null) {
+    public void visitMethod(@NonNull JavaContext context, @Nullable JavaElementVisitor visitor,
+            @NonNull PsiMethodCallExpression call, @NonNull PsiMethod method) {
+        assert method.getName().equals(INFLATE);
+        if (call.getMethodExpression().getQualifier() == null) {
             return;
         }
-        StrictListAccessor<Expression, MethodInvocation> arguments = node.astArguments();
-        if (arguments.size() < 2) {
+        PsiExpression[] arguments = call.getArgumentList().getExpressions();
+        if (arguments.length < 2) {
             return;
         }
-        Iterator<Expression> iterator = arguments.iterator();
-        Expression first = iterator.next();
-        Expression second = iterator.next();
-        if (!(second instanceof NullLiteral) || !(first instanceof Select)) {
+
+        PsiExpression first = arguments[0];
+        if (!(first instanceof PsiReferenceExpression)) {
             return;
         }
-        Select select = (Select) first;
-        Expression operand = select.astOperand();
-        if (operand instanceof Select) {
-            Select rLayout = (Select) operand;
-            if (rLayout.astIdentifier().astValue().equals(ResourceType.LAYOUT.getName()) &&
-                    rLayout.astOperand().toString().endsWith(SdkConstants.R_CLASS)) {
-                String layoutName = select.astIdentifier().astValue();
+        PsiExpression second = arguments[1];
+        if (!LintUtils.isNullLiteral(second)) {
+            return;
+        }
+        PsiReferenceExpression select = (PsiReferenceExpression) first;
+        PsiElement operand = select.getQualifier();
+        if (operand instanceof PsiReferenceExpression) {
+            PsiReferenceExpression rLayout = (PsiReferenceExpression) operand;
+            if (ResourceType.LAYOUT.getName().equals(rLayout.getReferenceName()) &&
+                    rLayout.getQualifier() != null &&
+                    rLayout.getQualifier().getText().endsWith(SdkConstants.R_CLASS)) {
+                String layoutName = select.getReferenceName();
                 if (context.getScope().contains(Scope.RESOURCE_FILE)) {
                     // We're doing a full analysis run: we can gather this information
                     // incrementally
-                    if (!context.getDriver().isSuppressed(context, ISSUE, node)) {
+                    if (!context.getDriver().isSuppressed(context, ISSUE, call)) {
                         if (mPendingErrors == null) {
                             mPendingErrors = Lists.newArrayList();
                         }
@@ -193,12 +188,10 @@
                         mPendingErrors.add(Pair.of(layoutName, location));
                     }
                 } else if (hasLayoutParams(context, layoutName)) {
-                    context.report(ISSUE, node, context.getLocation(second), ERROR_MESSAGE);
+                    context.report(ISSUE, call, context.getLocation(second), ERROR_MESSAGE);
                 }
             }
         }
-
-        super.visitMethod(context, visitor, node);
     }
 
     private static boolean hasLayoutParams(@NonNull JavaContext context, String name) {
diff --git a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/LocaleDetector.java b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/LocaleDetector.java
index 1b16852..b216b05 100644
--- a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/LocaleDetector.java
+++ b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/LocaleDetector.java
@@ -21,34 +21,31 @@
 
 import com.android.annotations.NonNull;
 import com.android.annotations.Nullable;
-import com.android.tools.lint.client.api.JavaParser;
-import com.android.tools.lint.client.api.JavaParser.ResolvedMethod;
-import com.android.tools.lint.client.api.JavaParser.ResolvedNode;
 import com.android.tools.lint.client.api.LintClient;
 import com.android.tools.lint.detector.api.Category;
 import com.android.tools.lint.detector.api.ConstantEvaluator;
 import com.android.tools.lint.detector.api.Detector;
-import com.android.tools.lint.detector.api.Detector.JavaScanner;
+import com.android.tools.lint.detector.api.Detector.JavaPsiScanner;
 import com.android.tools.lint.detector.api.Implementation;
 import com.android.tools.lint.detector.api.Issue;
 import com.android.tools.lint.detector.api.JavaContext;
 import com.android.tools.lint.detector.api.Location;
 import com.android.tools.lint.detector.api.Scope;
 import com.android.tools.lint.detector.api.Severity;
+import com.intellij.psi.JavaElementVisitor;
+import com.intellij.psi.PsiExpression;
+import com.intellij.psi.PsiMethod;
+import com.intellij.psi.PsiMethodCallExpression;
+import com.intellij.psi.util.PsiTreeUtil;
 
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.List;
 
-import lombok.ast.AstVisitor;
-import lombok.ast.Expression;
-import lombok.ast.MethodInvocation;
-import lombok.ast.Node;
-
 /**
  * Checks for errors related to locale handling
  */
-public class LocaleDetector extends Detector implements JavaScanner {
+public class LocaleDetector extends Detector implements JavaPsiScanner {
     private static final Implementation IMPLEMENTATION = new Implementation(
             LocaleDetector.class,
             Scope.JAVA_FILE_SCOPE);
@@ -100,22 +97,18 @@
     }
 
     @Override
-    public void visitMethod(@NonNull JavaContext context, @Nullable AstVisitor visitor,
-            @NonNull MethodInvocation call) {
-        ResolvedNode resolved = context.resolve(call);
-        if (resolved instanceof ResolvedMethod) {
-            ResolvedMethod method = (ResolvedMethod) resolved;
-            if (method.getContainingClass().matches(TYPE_STRING)) {
-                String name = method.getName();
-                if (name.equals(FORMAT_METHOD)) {
-                    checkFormat(context, method, call);
-                } else if (method.getArgumentCount() == 0) {
-                    Location location = context.getNameLocation(call);
-                    String message = String.format(
-                            "Implicitly using the default locale is a common source of bugs: " +
-                                    "Use `%1$s(Locale)` instead", name);
-                    context.report(STRING_LOCALE, call, location, message);
-                }
+    public void visitMethod(@NonNull JavaContext context, @Nullable JavaElementVisitor visitor,
+            @NonNull PsiMethodCallExpression call, @NonNull PsiMethod method) {
+        if (context.getEvaluator().isMemberInClass(method, TYPE_STRING)) {
+            String name = method.getName();
+            if (name.equals(FORMAT_METHOD)) {
+                checkFormat(context, method, call);
+            } else if (method.getParameterList().getParametersCount() == 0) {
+                Location location = context.getNameLocation(call);
+                String message = String.format(
+                        "Implicitly using the default locale is a common source of bugs: " +
+                                "Use `%1$s(Locale)` instead", name);
+                context.report(STRING_LOCALE, call, location, message);
             }
         }
     }
@@ -123,19 +116,14 @@
     /** Returns true if the given node is a parameter to a Logging call */
     private static boolean isLoggingParameter(
             @NonNull JavaContext context,
-            @NonNull MethodInvocation node) {
-        Node parent = node.getParent();
-        if (parent instanceof MethodInvocation) {
-            MethodInvocation call = (MethodInvocation)parent;
-            String name = call.astName().astValue();
-            if (name.length() == 1) { // "d", "i", "e" etc in Log
-                ResolvedNode resolved = context.resolve(call);
-                if (resolved instanceof ResolvedMethod) {
-                    ResolvedMethod method = (ResolvedMethod) resolved;
-                    if (method.getContainingClass().matches(LogDetector.LOG_CLS)) {
-                        return true;
-                    }
-                }
+            @NonNull PsiMethodCallExpression node) {
+        PsiMethodCallExpression parentCall =
+                PsiTreeUtil.getParentOfType(node, PsiMethodCallExpression.class, true);
+        if (parentCall != null) {
+            String name = parentCall.getMethodExpression().getReferenceName();
+            if (name != null && name.length() == 1) { // "d", "i", "e" etc in Log
+                PsiMethod method = parentCall.resolveMethod();
+                return context.getEvaluator().isMemberInClass(method, LogDetector.LOG_CLS);
             }
         }
 
@@ -144,17 +132,21 @@
 
     private static void checkFormat(
             @NonNull JavaContext context,
-            @NonNull ResolvedMethod method,
-            @NonNull MethodInvocation call) {
+            @NonNull PsiMethod method,
+            @NonNull PsiMethodCallExpression call) {
         // Only check the non-locale version of String.format
-        if (method.getArgumentCount() == 0
-                || !method.getArgumentType(0).matchesName(TYPE_STRING)
-                || call.astArguments().isEmpty()) {
+        if (method.getParameterList().getParametersCount() == 0
+                || !context.getEvaluator().parameterHasType(method, 0, TYPE_STRING)) {
+            return;
+        }
+
+        PsiExpression[] expressions = call.getArgumentList().getExpressions();
+        if (expressions.length == 0) {
             return;
         }
 
         // Find the formatting string
-        Expression first = call.astArguments().first();
+        PsiExpression first = expressions[0];
         Object value = ConstantEvaluator.evaluate(context, first);
         if (!(value instanceof String)) {
             return;
diff --git a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/LocaleFolderDetector.java b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/LocaleFolderDetector.java
index 48444df..012f3c6 100644
--- a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/LocaleFolderDetector.java
+++ b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/LocaleFolderDetector.java
@@ -37,7 +37,6 @@
 import com.android.tools.lint.detector.api.ResourceContext;
 import com.android.tools.lint.detector.api.Scope;
 import com.android.tools.lint.detector.api.Severity;
-import com.android.tools.lint.detector.api.Speed;
 import com.google.common.base.Joiner;
 import com.google.common.collect.ArrayListMultimap;
 import com.google.common.collect.Lists;
@@ -154,12 +153,6 @@
     public LocaleFolderDetector() {
     }
 
-    @NonNull
-    @Override
-    public Speed getSpeed() {
-        return Speed.FAST;
-    }
-
     // ---- Implements ResourceFolderScanner ----
 
     @Override
diff --git a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/LogDetector.java b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/LogDetector.java
index 0530b4d..73dc051 100644
--- a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/LogDetector.java
+++ b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/LogDetector.java
@@ -20,37 +20,40 @@
 
 import com.android.annotations.NonNull;
 import com.android.annotations.Nullable;
-import com.android.tools.lint.client.api.JavaParser.ResolvedMethod;
-import com.android.tools.lint.client.api.JavaParser.ResolvedNode;
+import com.android.tools.lint.client.api.JavaEvaluator;
 import com.android.tools.lint.detector.api.Category;
 import com.android.tools.lint.detector.api.ConstantEvaluator;
 import com.android.tools.lint.detector.api.Detector;
+import com.android.tools.lint.detector.api.Detector.JavaPsiScanner;
 import com.android.tools.lint.detector.api.Implementation;
 import com.android.tools.lint.detector.api.Issue;
 import com.android.tools.lint.detector.api.JavaContext;
 import com.android.tools.lint.detector.api.Location;
 import com.android.tools.lint.detector.api.Scope;
 import com.android.tools.lint.detector.api.Severity;
+import com.intellij.psi.JavaElementVisitor;
+import com.intellij.psi.PsiBinaryExpression;
+import com.intellij.psi.PsiClass;
+import com.intellij.psi.PsiElement;
+import com.intellij.psi.PsiExpression;
+import com.intellij.psi.PsiExpressionList;
+import com.intellij.psi.PsiField;
+import com.intellij.psi.PsiIfStatement;
+import com.intellij.psi.PsiLiteral;
+import com.intellij.psi.PsiLocalVariable;
+import com.intellij.psi.PsiMethod;
+import com.intellij.psi.PsiMethodCallExpression;
+import com.intellij.psi.PsiParameter;
+import com.intellij.psi.PsiParameterList;
+import com.intellij.psi.PsiReferenceExpression;
 
 import java.util.Arrays;
-import java.util.Iterator;
 import java.util.List;
 
-import lombok.ast.AstVisitor;
-import lombok.ast.BinaryExpression;
-import lombok.ast.ClassDeclaration;
-import lombok.ast.Expression;
-import lombok.ast.If;
-import lombok.ast.MethodInvocation;
-import lombok.ast.Node;
-import lombok.ast.Select;
-import lombok.ast.StringLiteral;
-import lombok.ast.VariableReference;
-
 /**
  * Detector for finding inefficiencies and errors in logging calls.
  */
-public class LogDetector extends Detector implements Detector.JavaScanner {
+public class LogDetector extends Detector implements JavaPsiScanner {
     private static final Implementation IMPLEMENTATION = new Implementation(
           LogDetector.class, Scope.JAVA_FILE_SCOPE);
 
@@ -121,18 +124,14 @@
     }
 
     @Override
-    public void visitMethod(@NonNull JavaContext context, @Nullable AstVisitor visitor, @NonNull MethodInvocation node) {
-        ResolvedNode resolved = context.resolve(node);
-        if (!(resolved instanceof ResolvedMethod)) {
+    public void visitMethod(@NonNull JavaContext context, @Nullable JavaElementVisitor visitor,
+            @NonNull PsiMethodCallExpression node, @NonNull PsiMethod method) {
+        JavaEvaluator evaluator = context.getEvaluator();
+        if (!evaluator.isMemberInClass(method, LOG_CLS)) {
             return;
         }
 
-        ResolvedMethod method = (ResolvedMethod) resolved;
-        if (!method.getContainingClass().matches(LOG_CLS)) {
-            return;
-        }
-
-        String name = node.astName().astValue();
+        String name = method.getName();
         boolean withinConditional = IS_LOGGABLE.equals(name) ||
                 checkWithinConditional(context, node.getParent(), node);
 
@@ -145,26 +144,23 @@
             String message = String.format("The log call Log.%1$s(...) should be " +
                             "conditional: surround with `if (Log.isLoggable(...))` or " +
                             "`if (BuildConfig.DEBUG) { ... }`",
-                    node.astName().toString());
+                    node.getMethodExpression().getReferenceName());
             context.report(CONDITIONAL, node, context.getLocation(node), message);
         }
 
         // Check tag length
         if (context.isEnabled(LONG_TAG)) {
             int tagArgumentIndex = PRINTLN.equals(name) ? 1 : 0;
-            if (method.getArgumentCount() > tagArgumentIndex
-                    && method.getArgumentType(tagArgumentIndex).matchesSignature(TYPE_STRING)
-                    && node.astArguments().size() == method.getArgumentCount()) {
-                Iterator<Expression> iterator = node.astArguments().iterator();
-                if (tagArgumentIndex == 1) {
-                    iterator.next();
-                }
-                Node argument = iterator.next();
+            PsiParameterList parameterList = method.getParameterList();
+            PsiExpressionList argumentList = node.getArgumentList();
+            if (evaluator.parameterHasType(method, tagArgumentIndex, TYPE_STRING)
+                    && parameterList.getParametersCount() == argumentList.getExpressions().length) {
+                PsiExpression argument = argumentList.getExpressions()[tagArgumentIndex];
                 String tag = ConstantEvaluator.evaluateString(context, argument, true);
                 if (tag != null && tag.length() > 23) {
                     String message = String.format(
-                        "The logging tag can be at most 23 characters, was %1$d (%2$s)",
-                        tag.length(), tag);
+                            "The logging tag can be at most 23 characters, was %1$d (%2$s)",
+                            tag.length(), tag);
                     context.report(LONG_TAG, node, context.getLocation(node), message);
                 }
             }
@@ -174,32 +170,43 @@
     /** Returns true if the given logging call performs "work" to compute the message */
     private static boolean performsWork(
             @NonNull JavaContext context,
-            @NonNull MethodInvocation node) {
-        int messageArgumentIndex = PRINTLN.equals(node.astName().astValue()) ? 2 : 1;
-        if (node.astArguments().size() >= messageArgumentIndex) {
-            Iterator<Expression> iterator = node.astArguments().iterator();
-            Node argument = null;
-            for (int i = 0; i <= messageArgumentIndex; i++) {
-                argument = iterator.next();
-            }
+            @NonNull PsiMethodCallExpression node) {
+        String referenceName = node.getMethodExpression().getReferenceName();
+        if (referenceName == null) {
+            return false;
+        }
+        int messageArgumentIndex = PRINTLN.equals(referenceName) ? 2 : 1;
+        PsiExpression[] arguments = node.getArgumentList().getExpressions();
+        if (arguments.length > messageArgumentIndex) {
+            PsiExpression argument = arguments[messageArgumentIndex];
             if (argument == null) {
                 return false;
             }
-            if (argument instanceof StringLiteral || argument instanceof VariableReference) {
+            if (argument instanceof PsiLiteral) {
                 return false;
             }
-            if (argument instanceof BinaryExpression) {
+            if (argument instanceof PsiBinaryExpression) {
                 String string = ConstantEvaluator.evaluateString(context, argument, false);
                 //noinspection VariableNotUsedInsideIf
                 if (string != null) { // does it resolve to a constant?
                     return false;
                 }
-            } else if (argument instanceof Select) {
+            } else if (argument instanceof PsiReferenceExpression) {
+                if (((PsiReferenceExpression) argument).getQualifier() == null) {
+                    // Just a simple local variable/field reference
+                    return false;
+                }
                 String string = ConstantEvaluator.evaluateString(context, argument, false);
                 //noinspection VariableNotUsedInsideIf
                 if (string != null) {
                     return false;
                 }
+                PsiElement resolved = context.getEvaluator().resolve(argument);
+                if (resolved instanceof PsiField || resolved instanceof PsiLocalVariable ||
+                        resolved instanceof PsiParameter) {
+                    // Just a reference to a field, parameter or variable
+                    return false;
+                }
             }
 
             // Method invocations etc
@@ -211,21 +218,22 @@
 
     private static boolean checkWithinConditional(
             @NonNull JavaContext context,
-            @Nullable Node curr,
-            @NonNull MethodInvocation logCall) {
+            @Nullable PsiElement curr,
+            @NonNull PsiMethodCallExpression logCall) {
         while (curr != null) {
-            if (curr instanceof If) {
-                If ifNode = (If) curr;
-                if (ifNode.astCondition() instanceof MethodInvocation) {
-                    MethodInvocation call = (MethodInvocation) ifNode.astCondition();
-                    if (IS_LOGGABLE.equals(call.astName().astValue())) {
+            if (curr instanceof PsiIfStatement) {
+                PsiIfStatement ifNode = (PsiIfStatement) curr;
+                if (ifNode.getCondition() instanceof PsiMethodCallExpression) {
+                    PsiMethodCallExpression call = (PsiMethodCallExpression) ifNode.getCondition();
+                    if (IS_LOGGABLE.equals(call.getMethodExpression().getReferenceName())) {
                         checkTagConsistent(context, logCall, call);
                     }
                 }
 
                 return true;
-            } else if (curr instanceof MethodInvocation
-                    || curr instanceof ClassDeclaration) { // static block
+            } else if (curr instanceof PsiMethodCallExpression
+                    || curr instanceof PsiMethod
+                    || curr instanceof PsiClass) { // static block
                 break;
             }
             curr = curr.getParent();
@@ -234,48 +242,48 @@
     }
 
     /** Checks that the tag passed to Log.s and Log.isLoggable match */
-    private static void checkTagConsistent(JavaContext context, MethodInvocation logCall,
-            MethodInvocation call) {
-        Iterator<Expression> isLogIterator = call.astArguments().iterator();
-        Iterator<Expression> logIterator = logCall.astArguments().iterator();
-        if (!isLogIterator.hasNext() || !logIterator.hasNext()) {
+    private static void checkTagConsistent(JavaContext context, PsiMethodCallExpression logCall,
+            PsiMethodCallExpression isLoggableCall) {
+        PsiExpression[] isLoggableArguments = isLoggableCall.getArgumentList().getExpressions();
+        PsiExpression[] logArguments = logCall.getArgumentList().getExpressions();
+        if (isLoggableArguments.length == 0 || logArguments.length == 0) {
             return;
         }
-        Expression isLoggableTag = isLogIterator.next();
-        Expression logTag = logIterator.next();
+        PsiExpression isLoggableTag = isLoggableArguments[0];
+        PsiExpression logTag = logArguments[0];
 
-        //String callName = logCall.astName().astValue();
-        String logCallName = logCall.astName().astValue();
+        String logCallName = logCall.getMethodExpression().getReferenceName();
+        if (logCallName == null) {
+            return;
+        }
         boolean isPrintln = PRINTLN.equals(logCallName);
-        if (isPrintln) {
-            if (!logIterator.hasNext()) {
-                return;
-            }
-            logTag = logIterator.next();
+        if (isPrintln && logArguments.length > 1) {
+            logTag = logArguments[1];
         }
 
         if (logTag != null) {
-            if (!isLoggableTag.toString().equals(logTag.toString())) {
-                ResolvedNode resolved1 = context.resolve(isLoggableTag);
-                ResolvedNode resolved2 = context.resolve(logTag);
+            if (!isLoggableTag.getText().equals(logTag.getText())) {
+                PsiElement resolved1 = context.getEvaluator().resolve(isLoggableTag);
+                PsiElement resolved2 = context.getEvaluator().resolve(logTag);
                 if ((resolved1 == null || resolved2 == null || !resolved1.equals(resolved2))
                         && context.isEnabled(WRONG_TAG)) {
                     Location location = context.getLocation(logTag);
                     Location alternate = context.getLocation(isLoggableTag);
                     alternate.setMessage("Conflicting tag");
                     location.setSecondary(alternate);
-                    String isLoggableDescription = resolved1 != null ? resolved1
-                            .getName()
-                            : isLoggableTag.toString();
-                    String logCallDescription = resolved2 != null ? resolved2.getName()
-                            : logTag.toString();
+                    String isLoggableDescription = resolved1 instanceof PsiMethod
+                            ? ((PsiMethod)resolved1).getName()
+                            : isLoggableTag.getText();
+                    String logCallDescription = resolved2 instanceof PsiMethod
+                            ? ((PsiMethod)resolved2).getName()
+                            : logTag.getText();
                     String message = String.format(
                             "Mismatched tags: the `%1$s()` and `isLoggable()` calls typically " +
                                     "should pass the same tag: `%2$s` versus `%3$s`",
                             logCallName,
                             isLoggableDescription,
                             logCallDescription);
-                    context.report(WRONG_TAG, call, location, message);
+                    context.report(WRONG_TAG, isLoggableCall, location, message);
                 }
             }
         }
@@ -283,18 +291,18 @@
         // Check log level versus the actual log call type (e.g. flag
         //    if (Log.isLoggable(TAG, Log.DEBUG) Log.info(TAG, "something")
 
-        if (logCallName.length() != 1 || !isLogIterator.hasNext()) { // e.g. println
+        if (logCallName.length() != 1 || isLoggableArguments.length < 2) { // e.g. println
             return;
         }
-        Expression isLoggableLevel = isLogIterator.next();
+        PsiExpression isLoggableLevel = isLoggableArguments[1];
         if (isLoggableLevel == null) {
             return;
         }
-        String levelString = isLoggableLevel.toString();
-        if (isLoggableLevel instanceof Select) {
-            levelString = ((Select)isLoggableLevel).astIdentifier().astValue();
+        String levelString = isLoggableLevel.getText();
+        if (isLoggableLevel instanceof PsiReferenceExpression) {
+            levelString = ((PsiReferenceExpression)isLoggableLevel).getReferenceName();
         }
-        if (levelString.isEmpty()) {
+        if (levelString == null || levelString.isEmpty()) {
             return;
         }
         char levelChar = Character.toLowerCase(levelString.charAt(0));
@@ -318,10 +326,10 @@
                 "Mismatched logging levels: when checking `isLoggable` level `%1$s`, the " +
                 "corresponding log call should be `Log.%2$s`, not `Log.%3$s`",
                 levelString, expectedCall, logCallName);
-        Location location = context.getLocation(logCall.astName());
+        Location location = context.getNameLocation(logCall);
         Location alternate = context.getLocation(isLoggableLevel);
         alternate.setMessage("Conflicting tag");
         location.setSecondary(alternate);
-        context.report(WRONG_TAG, call, location, message);
+        context.report(WRONG_TAG, isLoggableCall, location, message);
     }
 }
diff --git a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/ManifestResourceDetector.java b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/ManifestResourceDetector.java
index 63adda0..6028a3c 100644
--- a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/ManifestResourceDetector.java
+++ b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/ManifestResourceDetector.java
@@ -47,7 +47,6 @@
 import com.android.tools.lint.detector.api.Scope;
 import com.android.tools.lint.detector.api.Severity;
 import com.android.tools.lint.detector.api.XmlContext;
-import com.android.xml.AndroidManifest;
 import com.google.common.base.Joiner;
 import com.google.common.collect.ArrayListMultimap;
 import com.google.common.collect.Lists;
diff --git a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/MathDetector.java b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/MathDetector.java
index 6f627a0..c91ecbd 100644
--- a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/MathDetector.java
+++ b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/MathDetector.java
@@ -18,29 +18,29 @@
 
 import com.android.annotations.NonNull;
 import com.android.annotations.Nullable;
-import com.android.tools.lint.client.api.JavaParser.ResolvedMethod;
-import com.android.tools.lint.client.api.JavaParser.ResolvedNode;
 import com.android.tools.lint.detector.api.Category;
 import com.android.tools.lint.detector.api.Detector;
+import com.android.tools.lint.detector.api.Detector.JavaPsiScanner;
 import com.android.tools.lint.detector.api.Implementation;
 import com.android.tools.lint.detector.api.Issue;
 import com.android.tools.lint.detector.api.JavaContext;
 import com.android.tools.lint.detector.api.Location;
 import com.android.tools.lint.detector.api.Scope;
 import com.android.tools.lint.detector.api.Severity;
+import com.intellij.psi.JavaElementVisitor;
+import com.intellij.psi.PsiElement;
+import com.intellij.psi.PsiMethod;
+import com.intellij.psi.PsiMethodCallExpression;
+import com.intellij.psi.PsiReferenceExpression;
 
 import java.util.Arrays;
 import java.util.List;
 
-import lombok.ast.AstVisitor;
-import lombok.ast.Expression;
-import lombok.ast.MethodInvocation;
-
 /**
- * Looks for usages of {@link java.lang.Math} methods which can be replaced with
+ * Looks for usages of {@link Math} methods which can be replaced with
  * {@code android.util.FloatMath} methods to avoid casting.
  */
-public class MathDetector extends Detector implements Detector.JavaScanner {
+public class MathDetector extends Detector implements JavaPsiScanner {
     /** The main issue discovered by this detector */
     public static final Issue ISSUE = Issue.create(
             "FloatMath", //$NON-NLS-1$
@@ -81,22 +81,19 @@
     }
 
     @Override
-    public void visitMethod(@NonNull JavaContext context, @Nullable AstVisitor visitor,
-            @NonNull MethodInvocation call) {
-        Expression operand = call.astOperand();
-        if (operand == null || !operand.toString().equals("Math")) {
-            ResolvedNode resolved = context.resolve(call);
-            if (resolved instanceof ResolvedMethod &&
-                    ((ResolvedMethod)resolved).getContainingClass().matches("android.util.FloatMath")
+    public void visitMethod(@NonNull JavaContext context, @Nullable JavaElementVisitor visitor,
+            @NonNull PsiMethodCallExpression call, @NonNull PsiMethod method) {
+        if (context.getEvaluator().isMemberInClass(method, "android.util.FloatMath")
                     && context.getProject().getMinSdk() >= 8) {
-                String message = String.format(
-                        "Use `java.lang.Math#%1$s` instead of `android.util.FloatMath#%1$s()` " +
-                                "since it is faster as of API 8", call.astName().astValue());
-                Location location = operand != null
-                        ? context.getRangeLocation(operand, 0, call.astName(), 0)
-                        : context.getLocation(call);
-                context.report(ISSUE, call, location, message);
-            }
+            String message = String.format(
+                    "Use `java.lang.Math#%1$s` instead of `android.util.FloatMath#%1$s()` " +
+                            "since it is faster as of API 8", method.getName());
+            PsiReferenceExpression expression = call.getMethodExpression();
+            PsiElement operand = expression.getQualifier();
+            Location location = operand != null && expression.getReferenceNameElement() != null
+                    ? context.getRangeLocation(operand, 0, expression.getReferenceNameElement(), 0)
+                    : context.getLocation(call);
+            context.report(ISSUE, call, location, message);
         }
     }
 }
diff --git a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/MergeRootFrameLayoutDetector.java b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/MergeRootFrameLayoutDetector.java
index ffc617b..d5952d7 100644
--- a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/MergeRootFrameLayoutDetector.java
+++ b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/MergeRootFrameLayoutDetector.java
@@ -21,17 +21,16 @@
 import static com.android.SdkConstants.ATTR_FOREGROUND;
 import static com.android.SdkConstants.ATTR_LAYOUT;
 import static com.android.SdkConstants.ATTR_LAYOUT_GRAVITY;
-import static com.android.SdkConstants.DOT_JAVA;
 import static com.android.SdkConstants.FRAME_LAYOUT;
 import static com.android.SdkConstants.LAYOUT_RESOURCE_PREFIX;
-import static com.android.SdkConstants.R_LAYOUT_RESOURCE_PREFIX;
 import static com.android.SdkConstants.VIEW_INCLUDE;
 
 import com.android.annotations.NonNull;
 import com.android.annotations.Nullable;
+import com.android.resources.ResourceType;
 import com.android.tools.lint.detector.api.Category;
 import com.android.tools.lint.detector.api.Context;
-import com.android.tools.lint.detector.api.Detector;
+import com.android.tools.lint.detector.api.Detector.JavaPsiScanner;
 import com.android.tools.lint.detector.api.Implementation;
 import com.android.tools.lint.detector.api.Issue;
 import com.android.tools.lint.detector.api.JavaContext;
@@ -41,14 +40,17 @@
 import com.android.tools.lint.detector.api.Location.Handle;
 import com.android.tools.lint.detector.api.Scope;
 import com.android.tools.lint.detector.api.Severity;
-import com.android.tools.lint.detector.api.Speed;
 import com.android.tools.lint.detector.api.XmlContext;
 import com.android.utils.Pair;
+import com.intellij.psi.JavaElementVisitor;
+import com.intellij.psi.PsiExpression;
+import com.intellij.psi.PsiMethod;
+import com.intellij.psi.PsiMethodCallExpression;
+import com.intellij.psi.PsiReferenceExpression;
 
 import org.w3c.dom.Element;
 import org.w3c.dom.Node;
 
-import java.io.File;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
@@ -58,16 +60,10 @@
 import java.util.List;
 import java.util.Set;
 
-import lombok.ast.AstVisitor;
-import lombok.ast.Expression;
-import lombok.ast.MethodInvocation;
-import lombok.ast.Select;
-import lombok.ast.StrictListAccessor;
-
 /**
  * Checks whether a root FrameLayout can be replaced with a {@code <merge>} tag.
  */
-public class MergeRootFrameLayoutDetector extends LayoutDetector implements Detector.JavaScanner {
+public class MergeRootFrameLayoutDetector extends LayoutDetector implements JavaPsiScanner {
     /**
      * Set of layouts that we want to enable the warning for. We only warn for
      * {@code <FrameLayout>}'s that are the root of a layout included from
@@ -107,17 +103,6 @@
     }
 
     @Override
-    @NonNull
-    public Speed getSpeed() {
-        return Speed.FAST;
-    }
-
-    @Override
-    public boolean appliesTo(@NonNull Context context, @NonNull File file) {
-        return LintUtils.isXmlFile(file) || LintUtils.endsWith(file.getName(), DOT_JAVA);
-    }
-
-    @Override
     public void afterCheckProject(@NonNull Context context) {
         if (mPending != null && mWhitelistedLayouts != null) {
             // Process all the root FrameLayouts that are eligible, and generate
@@ -199,17 +184,18 @@
     }
 
     @Override
-    public void visitMethod(
-            @NonNull JavaContext context,
-            @Nullable AstVisitor visitor,
-            @NonNull MethodInvocation node) {
-        StrictListAccessor<Expression, MethodInvocation> argumentList = node.astArguments();
-        if (argumentList != null && argumentList.size() == 1) {
-            Expression argument = argumentList.first();
-            if (argument instanceof Select) {
-                String expression = argument.toString();
-                if (expression.startsWith(R_LAYOUT_RESOURCE_PREFIX)) {
-                    whiteListLayout(expression.substring(R_LAYOUT_RESOURCE_PREFIX.length()));
+    public void visitMethod(@NonNull JavaContext context, @Nullable JavaElementVisitor visitor,
+            @NonNull PsiMethodCallExpression call, @NonNull PsiMethod method) {
+        PsiExpression[] expressions = call.getArgumentList().getExpressions();
+        if (expressions.length == 1 && expressions[0] instanceof PsiReferenceExpression) {
+            PsiReferenceExpression expression = (PsiReferenceExpression)expressions[0];
+            if (expression.getQualifier() instanceof PsiReferenceExpression) {
+                PsiReferenceExpression inner = (PsiReferenceExpression)expression.getQualifier();
+                if (ResourceType.LAYOUT.getName().equals(inner.getReferenceName())) {
+                    String layoutName = expression.getReferenceName();
+                    if (layoutName != null) {
+                        whiteListLayout(layoutName);
+                    }
                 }
             }
         }
diff --git a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/NamespaceDetector.java b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/NamespaceDetector.java
index 50bfc21..07a3256 100644
--- a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/NamespaceDetector.java
+++ b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/NamespaceDetector.java
@@ -24,7 +24,6 @@
 import static com.android.SdkConstants.URI_PREFIX;
 import static com.android.SdkConstants.XMLNS_PREFIX;
 
-import com.android.SdkConstants;
 import com.android.annotations.NonNull;
 import com.android.tools.lint.detector.api.Category;
 import com.android.tools.lint.detector.api.Implementation;
diff --git a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/NfcTechListDetector.java b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/NfcTechListDetector.java
index 28a98c8..e03886d 100644
--- a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/NfcTechListDetector.java
+++ b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/NfcTechListDetector.java
@@ -20,13 +20,11 @@
 import com.android.annotations.Nullable;
 import com.android.resources.ResourceFolderType;
 import com.android.tools.lint.detector.api.Category;
-import com.android.tools.lint.detector.api.Detector.JavaScanner;
 import com.android.tools.lint.detector.api.Implementation;
 import com.android.tools.lint.detector.api.Issue;
 import com.android.tools.lint.detector.api.ResourceXmlDetector;
 import com.android.tools.lint.detector.api.Scope;
 import com.android.tools.lint.detector.api.Severity;
-import com.android.tools.lint.detector.api.Speed;
 import com.android.tools.lint.detector.api.XmlContext;
 
 import org.w3c.dom.Element;
@@ -40,7 +38,7 @@
  * Check which makes sure NFC tech lists do not include spaces around {@code <tech>} values
  * since that's not handled correctly by the inflater
  */
-public class NfcTechListDetector extends ResourceXmlDetector implements JavaScanner {
+public class NfcTechListDetector extends ResourceXmlDetector {
     /** The main issue discovered by this detector */
     public static final Issue ISSUE = Issue.create(
             "NfcTechWhitespace", //$NON-NLS-1$
@@ -71,12 +69,6 @@
         return folderType == ResourceFolderType.XML;
     }
 
-    @NonNull
-    @Override
-    public Speed getSpeed() {
-        return Speed.FAST;
-    }
-
     @Override
     @Nullable
     public Collection<String> getApplicableElements() {
diff --git a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/NonInternationalizedSmsDetector.java b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/NonInternationalizedSmsDetector.java
index 0ad022b..85749fd 100644
--- a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/NonInternationalizedSmsDetector.java
+++ b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/NonInternationalizedSmsDetector.java
@@ -19,26 +19,24 @@
 import com.android.annotations.NonNull;
 import com.android.annotations.Nullable;
 import com.android.tools.lint.detector.api.Category;
-import com.android.tools.lint.detector.api.Context;
 import com.android.tools.lint.detector.api.Detector;
+import com.android.tools.lint.detector.api.Detector.JavaPsiScanner;
 import com.android.tools.lint.detector.api.Implementation;
 import com.android.tools.lint.detector.api.Issue;
 import com.android.tools.lint.detector.api.JavaContext;
 import com.android.tools.lint.detector.api.Scope;
 import com.android.tools.lint.detector.api.Severity;
+import com.intellij.psi.JavaElementVisitor;
+import com.intellij.psi.PsiExpression;
+import com.intellij.psi.PsiLiteral;
+import com.intellij.psi.PsiMethod;
+import com.intellij.psi.PsiMethodCallExpression;
 
-import java.io.File;
 import java.util.ArrayList;
 import java.util.List;
 
-import lombok.ast.AstVisitor;
-import lombok.ast.Expression;
-import lombok.ast.MethodInvocation;
-import lombok.ast.StrictListAccessor;
-import lombok.ast.StringLiteral;
-
 /** Detector looking for text messages sent to an unlocalized phone number. */
-public class NonInternationalizedSmsDetector extends Detector implements Detector.JavaScanner {
+public class NonInternationalizedSmsDetector extends Detector implements JavaPsiScanner {
     /** The main issue discovered by this detector */
     public static final Issue ISSUE = Issue.create(
             "UnlocalizedSms", //$NON-NLS-1$
@@ -60,12 +58,6 @@
     public NonInternationalizedSmsDetector() {
     }
 
-    @Override
-    public boolean appliesTo(@NonNull Context context, @NonNull File file) {
-        return true;
-    }
-
-
     // ---- Implements JavaScanner ----
 
     @Override
@@ -77,28 +69,32 @@
     }
 
     @Override
-    public void visitMethod(@NonNull JavaContext context, @Nullable AstVisitor visitor,
-            @NonNull MethodInvocation node) {
-        assert node.astName().astValue().equals("sendTextMessage") ||  //$NON-NLS-1$
-            node.astName().astValue().equals("sendMultipartTextMessage");  //$NON-NLS-1$
-        if (node.astOperand() == null) {
+    public void visitMethod(@NonNull JavaContext context, @Nullable JavaElementVisitor visitor,
+            @NonNull PsiMethodCallExpression call, @NonNull PsiMethod method) {
+        if (call.getMethodExpression().getQualifier() == null) {
             // "sendTextMessage"/"sendMultipartTextMessage" in the code with no operand
             return;
         }
 
-        StrictListAccessor<Expression, MethodInvocation> args = node.astArguments();
-        if (args.size() == 5) {
-            Expression destinationAddress = args.first();
-            if (destinationAddress instanceof StringLiteral) {
-                String number = ((StringLiteral) destinationAddress).astValue();
-
-                if (!number.startsWith("+")) {  //$NON-NLS-1$
-                   context.report(ISSUE, node, context.getLocation(destinationAddress),
-                       "To make sure the SMS can be sent by all users, please start the SMS number " +
-                       "with a + and a country code or restrict the code invocation to people in the country " +
-                       "you are targeting.");
-                }
-            }
+        PsiExpression[] args = call.getArgumentList().getExpressions();
+        if (args.length != 5) {
+            return;
         }
+        PsiExpression destinationAddress = args[0];
+        if (!(destinationAddress instanceof PsiLiteral)) {
+            return;
+        }
+        Object literal = ((PsiLiteral)destinationAddress).getValue();
+        if (!(literal instanceof String)) {
+            return;
+        }
+        String number = (String) literal;
+        if (number.startsWith("+")) {  //$NON-NLS-1$
+            return;
+        }
+        context.report(ISSUE, call, context.getLocation(destinationAddress),
+            "To make sure the SMS can be sent by all users, please start the SMS number " +
+            "with a + and a country code or restrict the code invocation to people in the " +
+            "country you are targeting.");
     }
 }
\ No newline at end of file
diff --git a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/OverdrawDetector.java b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/OverdrawDetector.java
index 0603d87..f54ad3e 100644
--- a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/OverdrawDetector.java
+++ b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/OverdrawDetector.java
@@ -23,10 +23,10 @@
 import static com.android.SdkConstants.ATTR_PARENT;
 import static com.android.SdkConstants.ATTR_THEME;
 import static com.android.SdkConstants.ATTR_TILE_MODE;
-import static com.android.SdkConstants.DOT_JAVA;
 import static com.android.SdkConstants.DOT_XML;
 import static com.android.SdkConstants.DRAWABLE_PREFIX;
 import static com.android.SdkConstants.NULL_RESOURCE;
+import static com.android.SdkConstants.R_CLASS;
 import static com.android.SdkConstants.STYLE_RESOURCE_PREFIX;
 import static com.android.SdkConstants.TAG_ACTIVITY;
 import static com.android.SdkConstants.TAG_APPLICATION;
@@ -39,10 +39,12 @@
 import static com.android.utils.SdkUtils.getResourceFieldName;
 
 import com.android.annotations.NonNull;
+import com.android.annotations.Nullable;
 import com.android.resources.ResourceFolderType;
+import com.android.resources.ResourceType;
 import com.android.tools.lint.detector.api.Category;
 import com.android.tools.lint.detector.api.Context;
-import com.android.tools.lint.detector.api.Detector;
+import com.android.tools.lint.detector.api.Detector.JavaPsiScanner;
 import com.android.tools.lint.detector.api.Implementation;
 import com.android.tools.lint.detector.api.Issue;
 import com.android.tools.lint.detector.api.JavaContext;
@@ -52,9 +54,15 @@
 import com.android.tools.lint.detector.api.Project;
 import com.android.tools.lint.detector.api.Scope;
 import com.android.tools.lint.detector.api.Severity;
-import com.android.tools.lint.detector.api.Speed;
 import com.android.tools.lint.detector.api.XmlContext;
 import com.android.utils.Pair;
+import com.intellij.psi.JavaRecursiveElementVisitor;
+import com.intellij.psi.PsiAnonymousClass;
+import com.intellij.psi.PsiClass;
+import com.intellij.psi.PsiElement;
+import com.intellij.psi.PsiExpression;
+import com.intellij.psi.PsiMethodCallExpression;
+import com.intellij.psi.PsiReferenceExpression;
 
 import org.w3c.dom.Attr;
 import org.w3c.dom.Element;
@@ -68,28 +76,14 @@
 import java.util.Collections;
 import java.util.EnumSet;
 import java.util.HashMap;
-import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
-import java.util.Set;
-
-import lombok.ast.AstVisitor;
-import lombok.ast.ClassDeclaration;
-import lombok.ast.CompilationUnit;
-import lombok.ast.Expression;
-import lombok.ast.ForwardingAstVisitor;
-import lombok.ast.MethodInvocation;
-import lombok.ast.PackageDeclaration;
-import lombok.ast.Select;
-import lombok.ast.StrictListAccessor;
-import lombok.ast.VariableReference;
 
 /**
  * Check which looks for overdraw problems where view areas are painted and then
  * painted over, meaning that the bottom paint operation is a waste of time.
  */
-public class OverdrawDetector extends LayoutDetector implements Detector.JavaScanner {
-    private static final String R_STYLE_PREFIX = "R.style.";    //$NON-NLS-1$
+public class OverdrawDetector extends LayoutDetector implements JavaPsiScanner {
     private static final String SET_THEME = "setTheme";         //$NON-NLS-1$
 
     /** The main issue discovered by this detector */
@@ -136,10 +130,6 @@
     /** List of theme names registered in the project which have blank backgrounds */
     private List<String> mBlankThemes;
 
-    /** Set of activities registered in the manifest. We will limit the Java analysis to
-     * these. */
-    private Set<String> mActivities;
-
     /** List of drawable resources that are not flagged for overdraw (XML drawables
      * except for {@code <bitmap>} drawables without tiling) */
     private List<String> mValidDrawables;
@@ -165,17 +155,6 @@
                 || folderType == ResourceFolderType.DRAWABLE;
     }
 
-    @Override
-    public boolean appliesTo(@NonNull Context context, @NonNull File file) {
-        return LintUtils.isXmlFile(file) || LintUtils.endsWith(file.getName(), DOT_JAVA);
-    }
-
-    @NonNull
-    @Override
-    public Speed getSpeed() {
-        return Speed.FAST;
-    }
-
     /** Is the given theme a "blank" theme (one not painting its background) */
     private boolean isBlankTheme(String name) {
         if (name.startsWith("@android:style/Theme_")) {               //$NON-NLS-1$
@@ -409,11 +388,6 @@
             }
         }
 
-        if (mActivities == null) {
-            mActivities = new HashSet<String>();
-        }
-        mActivities.add(name);
-
         String theme = element.getAttributeNS(ANDROID_URI, ATTR_THEME);
         if (theme != null && !theme.isEmpty()) {
             if (mActivityToTheme == null) {
@@ -495,92 +469,105 @@
 
     // ---- Implements JavaScanner ----
 
+
+    @Nullable
     @Override
-    public List<Class<? extends lombok.ast.Node>> getApplicableNodeTypes() {
-        // This detector does not specify specific node types; this means
-        // that the infrastructure will run the full visitor on the compilation
-        // unit rather than on individual nodes. This is important since this
-        // detector relies on pruning (if it gets to a class declaration that is
-        // not an activity, it skips everything inside).
-        return null;
+    public List<String> applicableSuperClasses() {
+        return Collections.singletonList("android.app.Activity");
     }
 
     @Override
-    public AstVisitor createJavaVisitor(@NonNull JavaContext context) {
+    public List<Class<? extends PsiElement>> getApplicablePsiTypes() {
+        return Collections.<Class<? extends PsiElement>>singletonList(PsiClass.class);
+    }
+
+    @Override
+    public void checkClass(@NonNull JavaContext context, @NonNull PsiClass declaration) {
         if (!context.getProject().getReportIssues()) {
-            return null;
+            return;
         }
-        return new OverdrawVisitor();
+        String name = declaration.getQualifiedName();
+        if (name != null) {
+            declaration.accept(new OverdrawVisitor(name, declaration));
+        }
     }
 
-    private class OverdrawVisitor extends ForwardingAstVisitor {
-        private static final String ACTIVITY = "Activity"; //$NON-NLS-1$
-        private String mClassFqn;
+    private class OverdrawVisitor extends JavaRecursiveElementVisitor {
+        private final String mName;
+        private final PsiClass mCls;
 
-        @Override
-        public boolean visitClassDeclaration(ClassDeclaration node) {
-            String name = node.astName().astValue();
-            if (mActivities != null && mActivities.contains(mClassFqn) || name.endsWith(ACTIVITY)
-                    || node.astExtending() != null &&
-                        node.astExtending().getTypeName().endsWith(ACTIVITY)) {
-                String packageName = "";
-                if (node.getParent() instanceof CompilationUnit) {
-                    CompilationUnit compilationUnit = (CompilationUnit) node.getParent();
-                    PackageDeclaration packageDeclaration = compilationUnit.astPackageDeclaration();
-                    if (packageDeclaration == null) {
-                        // No package declaration: ignore this one
-                        return true;
-                    }
-                    packageName = packageDeclaration.getPackageName();
-                }
-                mClassFqn = (!packageName.isEmpty() ? (packageName + '.') : "") + name;
-
-                return false;
-            }
-
-            return true; // Done: No need to look inside this class
+        public OverdrawVisitor(String name, PsiClass cls) {
+            mName = name;
+            mCls = cls;
         }
 
-        // Store R.layout references in activity classes in a map mapping back layouts
-        // to activities
         @Override
-        public boolean visitSelect(Select node) {
-            if (node.astIdentifier().astValue().equals("layout") //$NON-NLS-1$
-                    && node.astOperand() instanceof VariableReference
-                    && ((VariableReference) node.astOperand()).astIdentifier().astValue()
-                        .equals("R")                             //$NON-NLS-1$
-                    && node.getParent() instanceof Select) {
-                String layout = ((Select) node.getParent()).astIdentifier().astValue();
-                registerLayoutActivity(layout, mClassFqn);
+        public void visitAnonymousClass(PsiAnonymousClass aClass) {
+            // Don't go into inner classes
+            if (mCls == aClass) {
+                super.visitAnonymousClass(aClass);
             }
-
-            return false;
         }
 
-
-        // Look for setTheme(R.style.whatever) and register as a theme registration
-        // for the current activity
         @Override
-        public boolean visitMethodInvocation(MethodInvocation node) {
-            if (node.astName().astValue().equals(SET_THEME)) {
-                // Look at argument
-                StrictListAccessor<Expression, MethodInvocation> args = node.astArguments();
-                if (args.size() == 1) {
-                    Expression arg = args.first();
-                    if (arg instanceof Select) {
-                        String resource = arg.toString();
-                        if (resource.startsWith(R_STYLE_PREFIX)) {
-                            if (mActivityToTheme == null) {
-                                mActivityToTheme = new HashMap<String, String>();
+        public void visitClass(PsiClass aClass) {
+            // Don't go into inner classes
+            if (mCls == aClass) {
+                super.visitClass(aClass);
+            }
+        }
+
+        @Override
+        public void visitReferenceExpression(PsiReferenceExpression expression) {
+            if (expression.getQualifier() instanceof PsiReferenceExpression) {
+                PsiReferenceExpression type = (PsiReferenceExpression) expression.getQualifier();
+                if (ResourceType.LAYOUT.getName().equals(type.getReferenceName())) {
+                    if (type.getQualifier() instanceof PsiReferenceExpression) {
+                        PsiReferenceExpression rClass = (PsiReferenceExpression) type.getQualifier();
+                        if (rClass.getQualifier() == null && R_CLASS.equals(rClass.getReferenceName())) {
+                            String layout = expression.getReferenceName();
+                            if (layout != null) {
+                                registerLayoutActivity(layout, mName);
                             }
-                            String name = ((Select) arg).astIdentifier().astValue();
-                            mActivityToTheme.put(mClassFqn, STYLE_RESOURCE_PREFIX + name);
+                        }
+                    }
+                }
+            }
+            super.visitReferenceExpression(expression);
+        }
+
+        @Override
+        public void visitMethodCallExpression(PsiMethodCallExpression expression) {
+            if (SET_THEME.equals(expression.getMethodExpression().getReferenceName())) {
+                // Look at argument
+                PsiExpression[] args = expression.getArgumentList().getExpressions();
+                if (args.length == 1) {
+                    PsiExpression arg = args[0];
+                    if (arg instanceof PsiReferenceExpression) {
+                        PsiReferenceExpression resource = (PsiReferenceExpression) arg;
+                        if (resource.getQualifier() instanceof PsiReferenceExpression) {
+                            PsiReferenceExpression type = (PsiReferenceExpression) resource.getQualifier();
+                            if (ResourceType.STYLE.getName().equals(type.getReferenceName())) {
+                                if (type.getQualifier() instanceof PsiReferenceExpression) {
+                                    PsiReferenceExpression rClass = (PsiReferenceExpression) type.getQualifier();
+                                    if (rClass.getQualifier() == null && R_CLASS.equals(rClass.getReferenceName())) {
+                                        String style = resource.getReferenceName();
+                                        if (style != null) {
+                                            if (mActivityToTheme == null) {
+                                                mActivityToTheme = new HashMap<String, String>();
+                                            }
+                                            mActivityToTheme.put(mName, STYLE_RESOURCE_PREFIX +
+                                                    style);
+                                        }
+                                    }
+                                }
+                            }
                         }
                     }
                 }
             }
 
-            return false;
+            super.visitMethodCallExpression(expression);
         }
     }
 }
diff --git a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/OverrideConcreteDetector.java b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/OverrideConcreteDetector.java
index eb95cc5..53dc042 100644
--- a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/OverrideConcreteDetector.java
+++ b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/OverrideConcreteDetector.java
@@ -16,33 +16,30 @@
 
 package com.android.tools.lint.checks;
 
-import static com.android.tools.lint.client.api.JavaParser.ResolvedClass;
-
 import com.android.annotations.NonNull;
 import com.android.annotations.Nullable;
-import com.android.tools.lint.client.api.JavaParser.ResolvedMethod;
+import com.android.tools.lint.client.api.JavaEvaluator;
 import com.android.tools.lint.detector.api.Category;
 import com.android.tools.lint.detector.api.Detector;
-import com.android.tools.lint.detector.api.Detector.JavaScanner;
+import com.android.tools.lint.detector.api.Detector.JavaPsiScanner;
 import com.android.tools.lint.detector.api.Implementation;
 import com.android.tools.lint.detector.api.Issue;
 import com.android.tools.lint.detector.api.JavaContext;
 import com.android.tools.lint.detector.api.Scope;
 import com.android.tools.lint.detector.api.Severity;
+import com.intellij.psi.PsiClass;
+import com.intellij.psi.PsiMethod;
+import com.intellij.psi.util.PsiTreeUtil;
 
-import java.lang.reflect.Modifier;
 import java.util.Collections;
 import java.util.List;
 
-import lombok.ast.ClassDeclaration;
-import lombok.ast.Node;
-
 /**
  * Checks that subclasses of certain APIs are overriding all methods that were abstract
  * in one or more earlier API levels that are still targeted by the minSdkVersion
  * of this project.
  */
-public class OverrideConcreteDetector extends Detector implements JavaScanner {
+public class OverrideConcreteDetector extends Detector implements JavaPsiScanner {
     /** Are previously-abstract methods all overridden? */
     public static final Issue ISSUE = Issue.create(
         "OverrideAbstract", //$NON-NLS-1$
@@ -92,17 +89,13 @@
     }
 
     @Override
-    public void checkClass(@NonNull JavaContext context, @Nullable ClassDeclaration node,
-            @NonNull Node declarationOrAnonymous, @NonNull ResolvedClass resolvedClass) {
-        if (node == null) {
-            return;
-        }
-        int flags = node.astModifiers().getEffectiveModifierFlags();
-        if ((flags & Modifier.ABSTRACT) != 0) {
+    public void checkClass(@NonNull JavaContext context, @NonNull PsiClass declaration) {
+        JavaEvaluator evaluator = context.getEvaluator();
+        if (evaluator.isAbstract(declaration)) {
             return;
         }
 
-        int minSdk = Math.max(context.getProject().getMinSdk(), getTargetApi(node));
+        int minSdk = Math.max(context.getProject().getMinSdk(), getTargetApi(declaration));
         if (minSdk >= CONCRETE_IN) {
             return;
         }
@@ -110,23 +103,26 @@
         String[] methodNames = {ON_NOTIFICATION_POSTED, ON_NOTIFICATION_REMOVED};
         for (String methodName : methodNames) {
             boolean found = false;
-            for (ResolvedMethod method : resolvedClass.getMethods(methodName, true)) {
+            for (PsiMethod method : declaration.findMethodsByName(methodName, true)) {
                 // Make sure it's not the base method, but that it's been defined
                 // in a subclass, concretely
-                ResolvedClass containingClass = method.getContainingClass();
-                if (containingClass.matches(NOTIFICATION_LISTENER_SERVICE_FQN)) {
+                PsiClass containingClass = method.getContainingClass();
+                if (containingClass == null) {
+                    continue;
+                }
+                if (NOTIFICATION_LISTENER_SERVICE_FQN.equals(containingClass.getQualifiedName())) {
                     continue;
                 }
                 // Make sure subclass isn't just defining another abstract definition
                 // of the method
-                if ((method.getModifiers() & Modifier.ABSTRACT) != 0) {
+                if (evaluator.isAbstract(method)) {
                     continue;
                 }
                 // Make sure it has the exact right signature
-                if (method.getArgumentCount() != 1) {
+                if (method.getParameterList().getParametersCount() != 1) {
                     continue; // Wrong signature
                 }
-                if (!method.getArgumentType(0).matchesName(STATUS_BAR_NOTIFICATION_FQN)) {
+                if (!evaluator.parameterHasType(method, 0, STATUS_BAR_NOTIFICATION_FQN)) {
                     continue;
                 }
 
@@ -139,23 +135,21 @@
                         "Must override `%1$s.%2$s(%3$s)`: Method was abstract until %4$d, and your `minSdkVersion` is %5$d",
                         NOTIFICATION_LISTENER_SERVICE_FQN, methodName,
                         STATUS_BAR_NOTIFICATION_FQN, CONCRETE_IN, minSdk);
-                Node nameNode = node.astName();
-                context.report(ISSUE, node, context.getLocation(nameNode),
-                        message);
+                context.report(ISSUE, declaration, context.getNameLocation(declaration), message);
                 break;
             }
 
         }
     }
 
-    private static int getTargetApi(ClassDeclaration node) {
+    private static int getTargetApi(@NonNull PsiClass node) {
         while (node != null) {
-            int targetApi = ApiDetector.getTargetApi(node.astModifiers());
+            int targetApi = ApiDetector.getTargetApi(node.getModifierList());
             if (targetApi != -1) {
                 return targetApi;
             }
 
-            node = JavaContext.findSurroundingClass(node.getParent());
+            node = PsiTreeUtil.getParentOfType(node, PsiClass.class, true);
         }
 
         return -1;
diff --git a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/ParcelDetector.java b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/ParcelDetector.java
index f6fbb13..cb9412b 100644
--- a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/ParcelDetector.java
+++ b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/ParcelDetector.java
@@ -15,32 +15,31 @@
  */
 package com.android.tools.lint.checks;
 
-import static com.android.tools.lint.client.api.JavaParser.ResolvedClass;
-import static com.android.tools.lint.client.api.JavaParser.ResolvedField;
+import static com.android.SdkConstants.CLASS_PARCELABLE;
 
 import com.android.annotations.NonNull;
 import com.android.annotations.Nullable;
 import com.android.tools.lint.detector.api.Category;
 import com.android.tools.lint.detector.api.Detector;
+import com.android.tools.lint.detector.api.Detector.JavaPsiScanner;
 import com.android.tools.lint.detector.api.Implementation;
 import com.android.tools.lint.detector.api.Issue;
 import com.android.tools.lint.detector.api.JavaContext;
 import com.android.tools.lint.detector.api.Location;
 import com.android.tools.lint.detector.api.Scope;
 import com.android.tools.lint.detector.api.Severity;
-
-import org.objectweb.asm.Opcodes;
+import com.intellij.psi.PsiAnonymousClass;
+import com.intellij.psi.PsiClass;
+import com.intellij.psi.PsiField;
+import com.intellij.psi.PsiModifier;
 
 import java.util.Collections;
 import java.util.List;
 
-import lombok.ast.ClassDeclaration;
-import lombok.ast.Node;
-
 /**
  * Looks for Parcelable classes that are missing a CREATOR field
  */
-public class ParcelDetector extends Detector implements Detector.JavaScanner {
+public class ParcelDetector extends Detector implements JavaPsiScanner {
 
     /** The main issue discovered by this detector */
     public static final Issue ISSUE = Issue.create(
@@ -60,7 +59,7 @@
                     Scope.JAVA_FILE_SCOPE))
             .addMoreInfo("http://developer.android.com/reference/android/os/Parcelable.html");
 
-    /** Constructs a new {@link com.android.tools.lint.checks.ParcelDetector} check */
+    /** Constructs a new {@link ParcelDetector} check */
     public ParcelDetector() {
     }
 
@@ -69,31 +68,36 @@
     @Nullable
     @Override
     public List<String> applicableSuperClasses() {
-        return Collections.singletonList("android.os.Parcelable");
+        return Collections.singletonList(CLASS_PARCELABLE);
     }
 
     @Override
-    public void checkClass(@NonNull JavaContext context, @Nullable ClassDeclaration node,
-            @NonNull Node declarationOrAnonymous, @NonNull ResolvedClass cls) {
-        if (node == null) {
+    public void checkClass(@NonNull JavaContext context, @NonNull PsiClass declaration) {
+        if (declaration instanceof PsiAnonymousClass) {
             // Anonymous classes aren't parcelable
             return;
         }
+
         // Only applies to concrete classes
-        int flags = node.astModifiers().getExplicitModifierFlags();
-        if ((flags & (Opcodes.ACC_INTERFACE | Opcodes.ACC_ABSTRACT)) != 0) {
+        if (declaration.isInterface()) {
+            return;
+        }
+        if (declaration.hasModifierProperty(PsiModifier.ABSTRACT)) {
             return;
         }
 
         // Parceling spans is handled in TextUtils#CHAR_SEQUENCE_CREATOR
-        if (!cls.isImplementing("android.text.ParcelableSpan", false)) {
-            ResolvedField field = cls.getField("CREATOR", false);
-            if (field == null) {
-                Location location = context.getLocation(node.astName());
-                context.report(ISSUE, node, location,
-                        "This class implements `Parcelable` but does not "
-                                + "provide a `CREATOR` field");
-            }
+        if (context.getEvaluator().implementsInterface(declaration,
+                "android.text.ParcelableSpan", false)) {
+            return;
+        }
+
+        PsiField field = declaration.findFieldByName("CREATOR", false);
+        if (field == null) {
+            Location location = context.getNameLocation(declaration);
+            context.report(ISSUE, declaration, location,
+                    "This class implements `Parcelable` but does not "
+                            + "provide a `CREATOR` field");
         }
     }
 }
diff --git a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/PermissionFinder.java b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/PermissionFinder.java
index 439f0e5..de5f504 100644
--- a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/PermissionFinder.java
+++ b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/PermissionFinder.java
@@ -19,34 +19,31 @@
 import static com.android.tools.lint.checks.SupportAnnotationDetector.PERMISSION_ANNOTATION;
 import static com.android.tools.lint.checks.SupportAnnotationDetector.PERMISSION_ANNOTATION_READ;
 import static com.android.tools.lint.checks.SupportAnnotationDetector.PERMISSION_ANNOTATION_WRITE;
-import static com.android.tools.lint.detector.api.JavaContext.getParentOfType;
 
 import com.android.annotations.NonNull;
 import com.android.annotations.Nullable;
-import com.android.tools.lint.client.api.JavaParser;
-import com.android.tools.lint.client.api.JavaParser.ResolvedAnnotation;
-import com.android.tools.lint.client.api.JavaParser.ResolvedClass;
-import com.android.tools.lint.client.api.JavaParser.ResolvedField;
-import com.android.tools.lint.client.api.JavaParser.ResolvedNode;
 import com.android.tools.lint.detector.api.JavaContext;
-
-import java.util.ListIterator;
-
-import lombok.ast.BinaryExpression;
-import lombok.ast.BinaryOperator;
-import lombok.ast.Cast;
-import lombok.ast.ConstructorInvocation;
-import lombok.ast.Expression;
-import lombok.ast.ExpressionStatement;
-import lombok.ast.InlineIfExpression;
-import lombok.ast.Node;
-import lombok.ast.NullLiteral;
-import lombok.ast.Select;
-import lombok.ast.Statement;
-import lombok.ast.VariableDeclaration;
-import lombok.ast.VariableDefinition;
-import lombok.ast.VariableDefinitionEntry;
-import lombok.ast.VariableReference;
+import com.intellij.psi.PsiAnnotation;
+import com.intellij.psi.PsiAssignmentExpression;
+import com.intellij.psi.PsiClass;
+import com.intellij.psi.PsiConditionalExpression;
+import com.intellij.psi.PsiDeclarationStatement;
+import com.intellij.psi.PsiElement;
+import com.intellij.psi.PsiExpression;
+import com.intellij.psi.PsiExpressionList;
+import com.intellij.psi.PsiExpressionStatement;
+import com.intellij.psi.PsiField;
+import com.intellij.psi.PsiJavaCodeReferenceElement;
+import com.intellij.psi.PsiLiteral;
+import com.intellij.psi.PsiLocalVariable;
+import com.intellij.psi.PsiModifierList;
+import com.intellij.psi.PsiNameValuePair;
+import com.intellij.psi.PsiNewExpression;
+import com.intellij.psi.PsiParenthesizedExpression;
+import com.intellij.psi.PsiReferenceExpression;
+import com.intellij.psi.PsiStatement;
+import com.intellij.psi.PsiTypeCastExpression;
+import com.intellij.psi.util.PsiTreeUtil;
 
 /**
  * Utility for locating permissions required by an intent or content resolver
@@ -103,7 +100,7 @@
     public static Result findRequiredPermissions(
             @NonNull Operation operation,
             @NonNull JavaContext context,
-            @NonNull Node parameter) {
+            @NonNull PsiElement parameter) {
 
         // To find the permission required by an intent, we proceed in 3 steps:
         // (1) Locate the parameter in the start call that corresponds to
@@ -127,56 +124,77 @@
     @NonNull private final Operation mOperation;
 
     @Nullable
-    public Result search(@NonNull Node node) {
-        if (node instanceof NullLiteral) {
+    public Result search(@NonNull PsiElement node) {
+        if (node instanceof PsiLiteral && "null".equals(node.getText())) {
             return null;
-        } else if (node instanceof InlineIfExpression) {
-            InlineIfExpression expression = (InlineIfExpression) node;
-            if (expression.astIfTrue() != null) {
-                Result result = search(expression.astIfTrue());
+        } else if (node instanceof PsiConditionalExpression) {
+            PsiConditionalExpression expression = (PsiConditionalExpression) node;
+            if (expression.getThenExpression() != null) {
+                Result result = search(expression.getThenExpression());
                 if (result != null) {
                     return result;
                 }
             }
-            if (expression.astIfFalse() != null) {
-                Result result = search(expression.astIfFalse());
+            if (expression.getElseExpression() != null) {
+                Result result = search(expression.getElseExpression());
                 if (result != null) {
                     return result;
                 }
             }
-        } else if (node instanceof Cast) {
-            Cast cast = (Cast) node;
-            return search(cast.astOperand());
-        } else if (node instanceof ConstructorInvocation && mOperation == Operation.ACTION) {
+        } else if (node instanceof PsiTypeCastExpression) {
+            PsiTypeCastExpression cast = (PsiTypeCastExpression) node;
+            PsiExpression operand = cast.getOperand();
+            if (operand != null) {
+                return search(operand);
+            }
+        } else if (node instanceof PsiParenthesizedExpression) {
+            PsiParenthesizedExpression parens = (PsiParenthesizedExpression) node;
+            PsiExpression expression = parens.getExpression();
+            if (expression != null) {
+                return search(expression);
+            }
+        } else if (node instanceof PsiNewExpression && mOperation == Operation.ACTION) {
             // Identifies "new Intent(argument)" calls and, if found, continues
             // resolving the argument instead looking for the action definition
-            ConstructorInvocation call = (ConstructorInvocation) node;
-            String type = call.astTypeReference().getTypeName();
-            if (type.equals("Intent") || type.equals(CLASS_INTENT)) {
-                Expression action = call.astArguments().first();
-                if (action != null) {
-                    return search(action);
+            PsiNewExpression call = (PsiNewExpression) node;
+            PsiJavaCodeReferenceElement classReference = call.getClassReference();
+            String type = classReference != null ? classReference.getQualifiedName() : null;
+            if (CLASS_INTENT.equals(type)) {
+                PsiExpressionList argumentList = call.getArgumentList();
+                if (argumentList != null) {
+                    PsiExpression[] expressions = argumentList.getExpressions();
+                    if (expressions.length > 0) {
+                        PsiExpression action = expressions[0];
+                        if (action != null) {
+                            return search(action);
+                        }
+                    }
                 }
             }
             return null;
-        } else if ((node instanceof VariableReference || node instanceof Select)) {
-            ResolvedNode resolved = mContext.resolve(node);
-            if (resolved instanceof ResolvedField) {
-                ResolvedField field = (ResolvedField) resolved;
+        } else if (node instanceof PsiReferenceExpression) {
+            PsiElement resolved = ((PsiReferenceExpression) node).resolve();
+            if (resolved instanceof PsiField) {
+                PsiField field = (PsiField) resolved;
                 if (mOperation == Operation.ACTION) {
-                    ResolvedAnnotation annotation = field.getAnnotation(PERMISSION_ANNOTATION);
+                    PsiModifierList modifierList = field.getModifierList();
+                    PsiAnnotation annotation = modifierList != null
+                            ? modifierList.findAnnotation(PERMISSION_ANNOTATION) : null;
                     if (annotation != null) {
                         return getPermissionRequirement(field, annotation);
                     }
                 } else if (mOperation == Operation.READ || mOperation == Operation.WRITE) {
                     String fqn = mOperation == Operation.READ
                             ? PERMISSION_ANNOTATION_READ : PERMISSION_ANNOTATION_WRITE;
-                    ResolvedAnnotation annotation = field.getAnnotation(fqn);
+                    PsiModifierList modifierList = field.getModifierList();
+                    PsiAnnotation annotation = modifierList != null
+                            ? modifierList.findAnnotation(fqn) : null;
                     if (annotation != null) {
-                        Object o = annotation.getValue();
-                        if (o instanceof ResolvedAnnotation) {
-                            annotation = (ResolvedAnnotation) o;
-                            if (annotation.matches(PERMISSION_ANNOTATION)) {
+                        PsiNameValuePair[] attributes = annotation.getParameterList().getAttributes();
+                        PsiNameValuePair o = attributes.length == 1 ? attributes[0] : null;
+                        if (o != null && o.getValue() instanceof PsiAnnotation) {
+                            annotation = (PsiAnnotation) o.getValue();
+                            if (PERMISSION_ANNOTATION.equals(annotation.getQualifiedName())) {
                                 return getPermissionRequirement(field, annotation);
                             }
                         } else {
@@ -192,46 +210,48 @@
                 } else {
                     assert false : mOperation;
                 }
-            } else if (node instanceof VariableReference) {
-                Statement statement = getParentOfType(node, Statement.class, false);
-                if (statement != null) {
-                    ListIterator<Node> iterator =
-                            statement.getParent().getChildren().listIterator();
-                    while (iterator.hasNext()) {
-                        if (iterator.next() == statement) {
-                            if (iterator.hasPrevious()) { // should always be true
-                                iterator.previous();
-                            }
-                            break;
-                        }
-                    }
+            } else if (resolved instanceof PsiLocalVariable) {
+                PsiLocalVariable variable = (PsiLocalVariable) resolved;
+                String targetName = variable.getName();
+                PsiStatement statement = PsiTreeUtil.getParentOfType(node, PsiStatement.class, false);
+                if (statement != null && targetName != null) {
+                    PsiStatement prev = PsiTreeUtil.getPrevSiblingOfType(statement,
+                            PsiStatement.class);
 
-                    String targetName = ((VariableReference)node).astIdentifier().astValue();
-                    while (iterator.hasPrevious()) {
-                        Node previous = iterator.previous();
-                        if (previous instanceof VariableDeclaration) {
-                            VariableDeclaration declaration = (VariableDeclaration) previous;
-                            VariableDefinition definition = declaration.astDefinition();
-                            for (VariableDefinitionEntry entry : definition
-                                    .astVariables()) {
-                                if (entry.astInitializer() != null
-                                        && entry.astName().astValue().equals(targetName)) {
-                                    return search(entry.astInitializer());
+                    while (prev != null) {
+                        if (prev instanceof PsiDeclarationStatement) {
+                            for (PsiElement element : ((PsiDeclarationStatement) prev)
+                                    .getDeclaredElements()) {
+                                if (variable.equals(element)) {
+                                    if (variable.getInitializer() != null) {
+                                        return search(variable.getInitializer());
+                                    } else {
+                                        break;
+                                    }
                                 }
                             }
-                        } else if (previous instanceof ExpressionStatement) {
-                            ExpressionStatement expressionStatement =
-                                    (ExpressionStatement) previous;
-                            Expression expression = expressionStatement.astExpression();
-                            if (expression instanceof BinaryExpression &&
-                                    ((BinaryExpression) expression).astOperator()
-                                            == BinaryOperator.ASSIGN) {
-                                BinaryExpression binaryExpression = (BinaryExpression) expression;
-                                if (targetName.equals(binaryExpression.astLeft().toString())) {
-                                    return search(binaryExpression.astRight());
+                        } else if (prev instanceof PsiExpressionStatement) {
+                            PsiExpression expression = ((PsiExpressionStatement) prev)
+                                    .getExpression();
+                            if (expression instanceof PsiAssignmentExpression) {
+                                PsiAssignmentExpression assign
+                                        = (PsiAssignmentExpression) expression;
+                                PsiExpression lhs = assign.getLExpression();
+                                if (lhs instanceof PsiReferenceExpression) {
+                                    PsiReferenceExpression reference = (PsiReferenceExpression) lhs;
+                                    if (targetName.equals(reference.getReferenceName()) &&
+                                            reference.getQualifier() == null) {
+                                        if (assign.getRExpression() != null) {
+                                            return search(assign.getRExpression());
+                                        } else {
+                                            break;
+                                        }
+                                    }
                                 }
                             }
                         }
+                        prev = PsiTreeUtil.getPrevSiblingOfType(prev,
+                                PsiStatement.class);
                     }
                 }
             }
@@ -242,13 +262,14 @@
 
     @NonNull
     private Result getPermissionRequirement(
-            @NonNull ResolvedField field,
-            @NonNull ResolvedAnnotation annotation) {
+            @NonNull PsiField field,
+            @NonNull PsiAnnotation annotation) {
         PermissionRequirement requirement = PermissionRequirement.create(mContext, annotation);
-        ResolvedClass containingClass = field.getContainingClass();
+        PsiClass containingClass = field.getContainingClass();
         String name = containingClass != null
-                ? containingClass.getSimpleName() + "." + field.getName()
+                ? containingClass.getName() + "." + field.getName()
                 : field.getName();
+        assert name != null;
         return new Result(mOperation, requirement, name);
     }
 }
diff --git a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/PermissionHolder.java b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/PermissionHolder.java
index 632748c..232979d 100644
--- a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/PermissionHolder.java
+++ b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/PermissionHolder.java
@@ -46,10 +46,10 @@
      * A convenience implementation of {@link PermissionHolder} backed by a set
      */
     class SetPermissionLookup implements PermissionHolder {
-        private Set<String> mGrantedPermissions;
-        private Set<String> mRevocablePermissions;
-        private AndroidVersion mMinSdkVersion;
-        private AndroidVersion mTargetSdkVersion;
+        private final Set<String> mGrantedPermissions;
+        private final Set<String> mRevocablePermissions;
+        private final AndroidVersion mMinSdkVersion;
+        private final AndroidVersion mTargetSdkVersion;
 
         public SetPermissionLookup(
                 @NonNull Set<String> grantedPermissions,
diff --git a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/PermissionRequirement.java b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/PermissionRequirement.java
index 821c9a3..39b1f77 100644
--- a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/PermissionRequirement.java
+++ b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/PermissionRequirement.java
@@ -25,27 +25,20 @@
 import com.android.annotations.Nullable;
 import com.android.annotations.VisibleForTesting;
 import com.android.sdklib.AndroidVersion;
-import com.android.tools.lint.client.api.JavaParser;
-import com.android.tools.lint.client.api.JavaParser.ResolvedAnnotation;
-import com.android.tools.lint.client.api.JavaParser.ResolvedField;
-import com.android.tools.lint.detector.api.Context;
+import com.android.tools.lint.detector.api.ConstantEvaluator;
 import com.android.tools.lint.detector.api.JavaContext;
 import com.google.common.collect.Lists;
 import com.google.common.collect.Sets;
+import com.intellij.psi.JavaTokenType;
+import com.intellij.psi.PsiAnnotation;
+import com.intellij.psi.PsiAnnotationMemberValue;
+import com.intellij.psi.PsiArrayInitializerMemberValue;
+import com.intellij.psi.tree.IElementType;
 
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.List;
 import java.util.Set;
-import java.util.concurrent.atomic.AtomicReference;
-
-import lombok.ast.BinaryExpression;
-import lombok.ast.BinaryOperator;
-import lombok.ast.Expression;
-import lombok.ast.ForwardingAstVisitor;
-import lombok.ast.Node;
-import lombok.ast.Select;
-import lombok.ast.VariableDefinitionEntry;
 
 /**
  * A permission requirement is a boolean expression of permission names that a
@@ -55,7 +48,7 @@
     public static final String ATTR_PROTECTION_LEVEL = "protectionLevel"; //$NON-NLS-1$
     public static final String VALUE_DANGEROUS = "dangerous"; //$NON-NLS-1$
 
-    protected final ResolvedAnnotation annotation;
+    protected final PsiAnnotation annotation;
     private int firstApi;
     private int lastApi;
 
@@ -98,7 +91,7 @@
 
         @Nullable
         @Override
-        public BinaryOperator getOperator() {
+        public IElementType getOperator() {
             return null;
         }
 
@@ -109,42 +102,33 @@
         }
     };
 
-    private PermissionRequirement(@NonNull ResolvedAnnotation annotation) {
+    private PermissionRequirement(@NonNull PsiAnnotation annotation) {
         this.annotation = annotation;
     }
 
     @NonNull
     public static PermissionRequirement create(
-            @Nullable Context context,
-            @NonNull ResolvedAnnotation annotation) {
-        String value = (String)annotation.getValue(ATTR_VALUE);
-        if (value != null && !value.isEmpty()) {
-            for (int i = 0, n = value.length(); i < n; i++) {
-                char c = value.charAt(i);
-                // See if it's a complex expression and if so build it up
-                if (c == '&' || c == '|' || c == '^') {
-                    return Complex.parse(annotation, context, value);
-                }
-            }
+            @NonNull JavaContext context,
+            @NonNull PsiAnnotation annotation) {
 
+        String value = getAnnotationStringValue(annotation, ATTR_VALUE);
+        if (value != null && !value.isEmpty()) {
             return new Single(annotation, value);
         }
 
-        Object v = annotation.getValue(ATTR_ANY_OF);
-        String[] anyOf = getAnnotationStrings(v);
+        String[] anyOf = getAnnotationStringValues(annotation, ATTR_ANY_OF);
         if (anyOf != null) {
             if (anyOf.length > 1) {
-                return new Many(annotation, BinaryOperator.LOGICAL_OR, anyOf);
+                return new Many(annotation, JavaTokenType.OROR, anyOf);
             } else if (anyOf.length == 1) {
                 return new Single(annotation, anyOf[0]);
             }
         }
 
-        v = annotation.getValue(ATTR_ALL_OF);
-        String[] allOf = getAnnotationStrings(v);
+        String[] allOf = getAnnotationStringValues(annotation, ATTR_ALL_OF);
         if (allOf != null) {
             if (allOf.length > 1) {
-                return new Many(annotation, BinaryOperator.LOGICAL_AND, allOf);
+                return new Many(annotation, JavaTokenType.ANDAND, allOf);
             } else if (allOf.length == 1) {
                 return new Single(annotation, allOf[0]);
             }
@@ -154,25 +138,128 @@
     }
 
     @Nullable
-    static String[] getAnnotationStrings(@Nullable Object v) {
-        if (v != null) {
-            if (v instanceof String[]) {
-                return (String[])v;
-            } else if (v instanceof String) {
-                return new String[] { (String)v };
-            } else if (v instanceof Object[]) {
-                List<String> strings = Lists.newArrayList();
-                for (Object o : (Object[])v) {
-                    if (o instanceof ResolvedField) {
-                        Object vs = ((ResolvedField)o).getValue();
-                        if (vs instanceof String) {
-                            strings.add((String)vs);
-                        }
-                    } else if (o instanceof String) {
-                        strings.add((String)o);
+    public static Boolean getAnnotationBooleanValue(@Nullable PsiAnnotation annotation,
+            @NonNull String name) {
+        if (annotation != null) {
+            PsiAnnotationMemberValue attributeValue = annotation.findAttributeValue(name);
+            if (attributeValue == null && ATTR_VALUE.equals(name)) {
+                attributeValue = annotation.findAttributeValue(null);
+            }
+            // Use constant evaluator since we want to resolve field references as well
+            if (attributeValue != null) {
+                Object o = ConstantEvaluator.evaluate(null, attributeValue);
+                if (o instanceof Boolean) {
+                    return (Boolean) o;
+                }
+            }
+        }
+
+        return null;
+    }
+
+    @Nullable
+    public static Long getAnnotationLongValue(@Nullable PsiAnnotation annotation,
+            @NonNull String name) {
+        if (annotation != null) {
+            PsiAnnotationMemberValue attributeValue = annotation.findAttributeValue(name);
+            if (attributeValue == null && ATTR_VALUE.equals(name)) {
+                attributeValue = annotation.findAttributeValue(null);
+            }
+            // Use constant evaluator since we want to resolve field references as well
+            if (attributeValue != null) {
+                Object o = ConstantEvaluator.evaluate(null, attributeValue);
+                if (o instanceof Number) {
+                    return ((Number)o).longValue();
+                }
+            }
+        }
+
+        return null;
+    }
+
+    @Nullable
+    public static Double getAnnotationDoubleValue(@Nullable PsiAnnotation annotation,
+            @NonNull String name) {
+        if (annotation != null) {
+            PsiAnnotationMemberValue attributeValue = annotation.findAttributeValue(name);
+            if (attributeValue == null && ATTR_VALUE.equals(name)) {
+                attributeValue = annotation.findAttributeValue(null);
+            }
+            // Use constant evaluator since we want to resolve field references as well
+            if (attributeValue != null) {
+                Object o = ConstantEvaluator.evaluate(null, attributeValue);
+                if (o instanceof Number) {
+                    return ((Number)o).doubleValue();
+                }
+            }
+        }
+
+        return null;
+    }
+
+    @Nullable
+    public static String getAnnotationStringValue(@Nullable PsiAnnotation annotation,
+            @NonNull String name) {
+        if (annotation != null) {
+            PsiAnnotationMemberValue attributeValue = annotation.findAttributeValue(name);
+            if (attributeValue == null && ATTR_VALUE.equals(name)) {
+                attributeValue = annotation.findAttributeValue(null);
+            }
+            // Use constant evaluator since we want to resolve field references as well
+            if (attributeValue != null) {
+                Object o = ConstantEvaluator.evaluate(null, attributeValue);
+                if (o instanceof String) {
+                    return (String) o;
+                }
+            }
+        }
+
+        return null;
+    }
+
+    @Nullable
+    public static String[] getAnnotationStringValues(@Nullable PsiAnnotation annotation,
+            @NonNull String name) {
+        if (annotation != null) {
+            PsiAnnotationMemberValue attributeValue = annotation.findAttributeValue(name);
+            if (attributeValue == null && ATTR_VALUE.equals(name)) {
+                attributeValue = annotation.findAttributeValue(null);
+            }
+            if (attributeValue instanceof PsiArrayInitializerMemberValue) {
+                PsiAnnotationMemberValue[] initializers =
+                        ((PsiArrayInitializerMemberValue) attributeValue).getInitializers();
+                List<String> result = Lists.newArrayListWithCapacity(initializers.length);
+                ConstantEvaluator constantEvaluator = new ConstantEvaluator(null);
+                for (PsiAnnotationMemberValue element : initializers) {
+                    Object o = constantEvaluator.evaluate(element);
+                    if (o instanceof String) {
+                        result.add((String)o);
                     }
                 }
-                return strings.toArray(new String[strings.size()]);
+                if (result.isEmpty()) {
+                    return null;
+                } else {
+                    return result.toArray(new String[0]);
+                }
+            } else {
+                // Use constant evaluator since we want to resolve field references as well
+                if (attributeValue != null) {
+                    Object o = ConstantEvaluator.evaluate(null, attributeValue);
+                    if (o instanceof String) {
+                        return new String[]{(String) o};
+                    } else if (o instanceof String[]) {
+                        return (String[])o;
+                    } else if (o instanceof Object[]) {
+                        Object[] array = (Object[]) o;
+                        List<String> strings = Lists.newArrayListWithCapacity(array.length);
+                        for (Object element : array) {
+                            if (element instanceof String) {
+                                strings.add((String) element);
+                            }
+                        }
+                        return strings.toArray(new String[0]);
+                    }
+                }
             }
         }
 
@@ -199,9 +286,8 @@
             firstApi = -1; // initialized, not specified
 
             // Not initialized
-            Object o = annotation.getValue("apis");
-            if (o instanceof String) {
-                String range = (String)o;
+            String range = getAnnotationStringValue(annotation, "apis");
+            if (range != null) {
                 // Currently only support the syntax "a..b" where a and b are inclusive end points
                 // and where "a" and "b" are optional
                 int index = range.indexOf("..");
@@ -255,14 +341,9 @@
      * @return true if this requirement is conditional
      */
     public boolean isConditional() {
-        Object o = annotation.getValue(ATTR_CONDITIONAL);
-        if (o instanceof Boolean) {
-            return (Boolean)o;
-        } else if (o instanceof ResolvedField) {
-            o = ((ResolvedField)o).getValue();
-            if (o instanceof Boolean) {
-                return (Boolean)o;
-            }
+        Boolean o = getAnnotationBooleanValue(annotation, ATTR_CONDITIONAL);
+        if (o != null) {
+            return o;
         }
         return false;
     }
@@ -323,7 +404,7 @@
      * for leaf nodes
      */
     @Nullable
-    public abstract BinaryOperator getOperator();
+    public abstract IElementType getOperator();
 
     /**
      * Returns nested requirements, combined via {@link #getOperator()}
@@ -335,7 +416,7 @@
     private static class Single extends PermissionRequirement {
         public final String name;
 
-        public Single(@NonNull ResolvedAnnotation annotation, @NonNull String name) {
+        public Single(@NonNull PsiAnnotation annotation, @NonNull String name) {
             super(annotation);
             this.name = name;
         }
@@ -347,7 +428,7 @@
 
         @Nullable
         @Override
-        public BinaryOperator getOperator() {
+        public IElementType getOperator() {
             return null;
         }
 
@@ -394,14 +475,14 @@
         }
     }
 
-    protected static void appendOperator(StringBuilder sb, BinaryOperator operator) {
+    protected static void appendOperator(StringBuilder sb, IElementType operator) {
         sb.append(' ');
-        if (operator == BinaryOperator.LOGICAL_AND) {
+        if (operator == JavaTokenType.ANDAND) {
             sb.append("and");
-        } else if (operator == BinaryOperator.LOGICAL_OR) {
+        } else if (operator == JavaTokenType.OROR) {
             sb.append("or");
         } else {
-            assert operator == BinaryOperator.BITWISE_XOR : operator;
+            assert operator == JavaTokenType.XOR : operator;
             sb.append("xor");
         }
         sb.append(' ');
@@ -411,16 +492,16 @@
      * Require a series of permissions, all with the same operator.
      */
     private static class Many extends PermissionRequirement {
-        public final BinaryOperator operator;
+        public final IElementType operator;
         public final List<PermissionRequirement> permissions;
 
         public Many(
-                @NonNull ResolvedAnnotation annotation,
-                BinaryOperator operator,
+                @NonNull PsiAnnotation annotation,
+                IElementType operator,
                 String[] names) {
             super(annotation);
-            assert operator == BinaryOperator.LOGICAL_OR
-                    || operator == BinaryOperator.LOGICAL_AND : operator;
+            assert operator == JavaTokenType.OROR
+                    || operator == JavaTokenType.ANDAND : operator;
             assert names.length >= 2;
             this.operator = operator;
             this.permissions = Lists.newArrayListWithExpectedSize(names.length);
@@ -450,7 +531,7 @@
 
         @Override
         public boolean isSatisfied(@NonNull PermissionHolder available) {
-            if (operator == BinaryOperator.LOGICAL_AND) {
+            if (operator == JavaTokenType.ANDAND) {
                 for (PermissionRequirement requirement : permissions) {
                     if (!requirement.isSatisfied(available) && requirement.appliesTo(available)) {
                         return false;
@@ -458,7 +539,7 @@
                 }
                 return true;
             } else {
-                assert operator == BinaryOperator.LOGICAL_OR : operator;
+                assert operator == JavaTokenType.OROR : operator;
                 for (PermissionRequirement requirement : permissions) {
                     if (requirement.isSatisfied(available) || !requirement.appliesTo(available)) {
                         return true;
@@ -506,7 +587,7 @@
         @Override
         public boolean isRevocable(@NonNull PermissionHolder revocable) {
             // TODO: Pass in the available set of permissions here, and if
-            // the operator is BinaryOperator.LOGICAL_OR, only return revocable=true
+            // the operator is JavaTokenType.OROR, only return revocable=true
             // if an unsatisfied permission is also revocable. In other words,
             // if multiple permissions are allowed, and some of them are satisfied and
             // not revocable the overall permission requirement is not revocable.
@@ -520,7 +601,7 @@
 
         @Nullable
         @Override
-        public BinaryOperator getOperator() {
+        public IElementType getOperator() {
             return operator;
         }
 
@@ -532,217 +613,6 @@
     }
 
     /**
-     * Require multiple permissions. This is a group of permissions with some
-     * associated boolean logic, such as "B or (C and (D or E))".
-     */
-    private static class Complex extends PermissionRequirement {
-        public final BinaryOperator operator;
-        public final PermissionRequirement left;
-        public final PermissionRequirement right;
-
-        public Complex(
-                @NonNull ResolvedAnnotation annotation,
-                BinaryOperator operator,
-                PermissionRequirement left,
-                PermissionRequirement right) {
-            super(annotation);
-            this.operator = operator;
-            this.left = left;
-            this.right = right;
-        }
-
-        @Override
-        public boolean isSingle() {
-            return false;
-        }
-
-        @Override
-        public String toString() {
-            StringBuilder sb = new StringBuilder();
-
-            boolean needsParentheses = left instanceof Complex &&
-                    ((Complex) left).operator != BinaryOperator.LOGICAL_AND;
-            if (needsParentheses) {
-                sb.append('(');
-            }
-            sb.append(left.toString());
-            if (needsParentheses) {
-                sb.append(')');
-            }
-
-            appendOperator(sb, operator);
-
-            needsParentheses = right instanceof Complex &&
-                    ((Complex) right).operator != BinaryOperator.LOGICAL_AND;
-            if (needsParentheses) {
-                sb.append('(');
-            }
-            sb.append(right.toString());
-            if (needsParentheses) {
-                sb.append(')');
-            }
-
-            return sb.toString();
-        }
-
-        @Override
-        public boolean isSatisfied(@NonNull PermissionHolder available) {
-            boolean satisfiedLeft = left.isSatisfied(available) || !left.appliesTo(available);
-            boolean satisfiedRight = right.isSatisfied(available) || !right.appliesTo(available);
-            if (operator == BinaryOperator.LOGICAL_AND) {
-                return satisfiedLeft && satisfiedRight;
-            } else if (operator == BinaryOperator.LOGICAL_OR) {
-                return satisfiedLeft || satisfiedRight;
-            } else {
-                assert operator == BinaryOperator.BITWISE_XOR : operator;
-                return satisfiedLeft ^ satisfiedRight;
-            }
-        }
-
-        @Override
-        public String describeMissingPermissions(@NonNull PermissionHolder available) {
-            boolean satisfiedLeft = left.isSatisfied(available);
-            boolean satisfiedRight = right.isSatisfied(available);
-            if (operator == BinaryOperator.LOGICAL_AND || operator == BinaryOperator.LOGICAL_OR) {
-                if (satisfiedLeft) {
-                    if (satisfiedRight) {
-                        return "";
-                    }
-                    return right.describeMissingPermissions(available);
-                } else if (satisfiedRight) {
-                    return left.describeMissingPermissions(available);
-                } else {
-                    StringBuilder sb = new StringBuilder();
-                    sb.append(left.describeMissingPermissions(available));
-                    appendOperator(sb, operator);
-                    sb.append(right.describeMissingPermissions(available));
-                    return sb.toString();
-                }
-            } else {
-                assert operator == BinaryOperator.BITWISE_XOR : operator;
-                return toString();
-            }
-        }
-
-        @Override
-        protected void addMissingPermissions(@NonNull PermissionHolder available,
-          @NonNull Set<String> missing) {
-            boolean satisfiedLeft = left.isSatisfied(available);
-            boolean satisfiedRight = right.isSatisfied(available);
-            if (operator == BinaryOperator.LOGICAL_AND || operator == BinaryOperator.LOGICAL_OR) {
-                if (satisfiedLeft) {
-                    if (satisfiedRight) {
-                        return;
-                    }
-                    right.addMissingPermissions(available, missing);
-                } else if (satisfiedRight) {
-                    left.addMissingPermissions(available, missing);
-                } else {
-                    left.addMissingPermissions(available, missing);
-                    right.addMissingPermissions(available, missing);
-                }
-            } else {
-                assert operator == BinaryOperator.BITWISE_XOR : operator;
-                left.addMissingPermissions(available, missing);
-                right.addMissingPermissions(available, missing);
-            }
-        }
-
-        @Override
-        protected void addRevocablePermissions(@NonNull Set<String> result,
-                @NonNull PermissionHolder revocable) {
-            left.addRevocablePermissions(result, revocable);
-            right.addRevocablePermissions(result, revocable);
-        }
-
-        @Override
-        public boolean isRevocable(@NonNull PermissionHolder revocable) {
-            // TODO: If operator == BinaryOperator.LOGICAL_OR only return
-            // revocable the there isn't a non-revocable term which is also satisfied.
-            return left.isRevocable(revocable) || right.isRevocable(revocable);
-        }
-
-        @NonNull
-        public static PermissionRequirement parse(@NonNull ResolvedAnnotation annotation,
-                @Nullable Context context, @NonNull final String value) {
-            // Parse an expression of the form (A op1 B op2 C) op3 (D op4 E) etc.
-            // We'll just use the Java parser to handle this to ensure that operator
-            // precedence etc is correct.
-            if (context == null) {
-                return NONE;
-            }
-            JavaParser javaParser = context.getClient().getJavaParser(null);
-            if (javaParser == null) {
-                return NONE;
-            }
-            try {
-                JavaContext javaContext = new JavaContext(context.getDriver(),
-                        context.getProject(), context.getMainProject(), context.file,
-                        javaParser) {
-                    @Nullable
-                    @Override
-                    public String getContents() {
-                        return ""
-                                + "class Test { void test() {\n"
-                                + "boolean result=" + value
-                                + ";\n}\n}";
-                    }
-                };
-                Node node = javaParser.parseJava(javaContext);
-                if (node != null) {
-                    final AtomicReference<Expression> reference = new AtomicReference<Expression>();
-                    node.accept(new ForwardingAstVisitor() {
-                        @Override
-                        public boolean visitVariableDefinitionEntry(VariableDefinitionEntry node) {
-                            reference.set(node.astInitializer());
-                            return true;
-                        }
-                    });
-                    Expression expression = reference.get();
-                    if (expression != null) {
-                        return parse(annotation, expression);
-                    }
-                }
-
-                return NONE;
-            } finally {
-                javaParser.dispose();
-            }
-        }
-
-        private static PermissionRequirement parse(
-                @NonNull ResolvedAnnotation annotation,
-                @NonNull Expression expression) {
-            if (expression instanceof Select) {
-                return new Single(annotation, expression.toString());
-            } else if (expression instanceof BinaryExpression) {
-                BinaryExpression binaryExpression = (BinaryExpression) expression;
-                BinaryOperator operator = binaryExpression.astOperator();
-                if (operator == BinaryOperator.LOGICAL_AND
-                        || operator == BinaryOperator.LOGICAL_OR
-                        || operator == BinaryOperator.BITWISE_XOR) {
-                    PermissionRequirement left = parse(annotation, binaryExpression.astLeft());
-                    PermissionRequirement right = parse(annotation, binaryExpression.astRight());
-                    return new Complex(annotation, operator, left, right);
-                }
-            }
-            return NONE;
-        }
-
-        @Nullable
-        @Override
-        public BinaryOperator getOperator() {
-            return operator;
-        }
-
-        @NonNull
-        @Override
-        public Iterable<PermissionRequirement> getChildren() {
-            return Arrays.asList(left, right);
-        }
-    }
-
-    /**
      * Returns true if the given permission name is a revocable permission for
      * targetSdkVersion >= 23
      *
diff --git a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/PluralsDatabase.java b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/PluralsDatabase.java
index cb40ad7..91ff76b 100644
--- a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/PluralsDatabase.java
+++ b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/PluralsDatabase.java
@@ -33,14 +33,14 @@
 import java.util.Map;
 
 /**
- * Database used by the {@link com.android.tools.lint.checks.PluralsDetector} to get information
+ * Database used by the {@link PluralsDetector} to get information
  * about plural forms for a given language
  */
 public class PluralsDatabase {
     private static final EnumSet<Quantity> NONE = EnumSet.noneOf(Quantity.class);
 
     private static final PluralsDatabase sInstance = new PluralsDatabase();
-    private Map<String, EnumSet<Quantity>> mPlurals = Maps.newHashMap();
+    private final Map<String, EnumSet<Quantity>> mPlurals = Maps.newHashMap();
 
     /** Bit set if this language uses quantity zero */
     @SuppressWarnings("PointlessBitwiseExpression")
diff --git a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/PreferenceActivityDetector.java b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/PreferenceActivityDetector.java
index 749902e..b4770d6 100644
--- a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/PreferenceActivityDetector.java
+++ b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/PreferenceActivityDetector.java
@@ -24,18 +24,20 @@
 
 import com.android.annotations.NonNull;
 import com.android.annotations.Nullable;
-import com.android.tools.lint.client.api.JavaParser.ResolvedClass;
-import com.android.tools.lint.client.api.JavaParser.ResolvedMethod;
+import com.android.tools.lint.client.api.JavaEvaluator;
 import com.android.tools.lint.detector.api.Category;
 import com.android.tools.lint.detector.api.Detector;
+import com.android.tools.lint.detector.api.Detector.JavaPsiScanner;
+import com.android.tools.lint.detector.api.Detector.XmlScanner;
 import com.android.tools.lint.detector.api.Implementation;
 import com.android.tools.lint.detector.api.Issue;
 import com.android.tools.lint.detector.api.JavaContext;
 import com.android.tools.lint.detector.api.Location;
 import com.android.tools.lint.detector.api.Scope;
 import com.android.tools.lint.detector.api.Severity;
-import com.android.tools.lint.detector.api.Speed;
 import com.android.tools.lint.detector.api.XmlContext;
+import com.intellij.psi.PsiClass;
+import com.intellij.psi.PsiMethod;
 
 import org.w3c.dom.Element;
 
@@ -46,14 +48,11 @@
 import java.util.List;
 import java.util.Map;
 
-import lombok.ast.ClassDeclaration;
-import lombok.ast.Node;
-
 /**
  * Ensures that PreferenceActivity and its subclasses are never exported.
  */
 public class PreferenceActivityDetector extends Detector
-        implements Detector.XmlScanner, Detector.JavaScanner {
+        implements XmlScanner, JavaPsiScanner {
     public static final Issue ISSUE = Issue.create(
             "ExportedPreferenceActivity", //$NON-NLS-1$
             "PreferenceActivity should not be exported",
@@ -73,13 +72,8 @@
     private final Map<String, Location.Handle> mExportedActivities =
             new HashMap<String, Location.Handle>();
 
-    @NonNull
-    @Override
-    public Speed getSpeed() {
-        return Speed.FAST;
-    }
-
     // ---- Implements XmlScanner ----
+
     @Override
     public Collection<String> getApplicableElements() {
         return Collections.singletonList(TAG_ACTIVITY);
@@ -131,20 +125,19 @@
     }
 
     @Override
-    public void checkClass(@NonNull JavaContext context, @Nullable ClassDeclaration node,
-            @NonNull Node declarationOrAnonymous, @NonNull ResolvedClass resolvedClass) {
+    public void checkClass(@NonNull JavaContext context, @NonNull PsiClass declaration) {
         if (!context.getProject().getReportIssues()) {
             return;
         }
-        String className = resolvedClass.getName();
-        if (resolvedClass.isSubclassOf(PREFERENCE_ACTIVITY, false)
+        JavaEvaluator evaluator = context.getEvaluator();
+        String className = declaration.getQualifiedName();
+        if (evaluator.extendsClass(declaration, PREFERENCE_ACTIVITY, false)
                 && mExportedActivities.containsKey(className)) {
-
             // Ignore the issue if we target an API greater than 19 and the class in
             // question specifically overrides isValidFragment() and thus knowingly white-lists
             // valid fragments.
             if (context.getMainProject().getTargetSdk() >= 19
-                    && overridesIsValidFragment(resolvedClass)) {
+                    && overridesIsValidFragment(evaluator, declaration)) {
                 return;
             }
 
@@ -152,16 +145,15 @@
                     "`PreferenceActivity` subclass `%1$s` should not be exported",
                     className);
             Location location = mExportedActivities.get(className).resolve();
-            context.report(ISSUE, declarationOrAnonymous, location, message);
+            context.report(ISSUE, declaration, location, message);
         }
     }
 
-    private static boolean overridesIsValidFragment(ResolvedClass resolvedClass) {
-        Iterable<ResolvedMethod> resolvedMethods = resolvedClass.getMethods(IS_VALID_FRAGMENT,
-                false);
-        for (ResolvedMethod resolvedMethod : resolvedMethods) {
-            if (resolvedMethod.getArgumentCount() == 1
-                    && resolvedMethod.getArgumentType(0).getName().equals(TYPE_STRING)) {
+    private static boolean overridesIsValidFragment(
+            @NonNull JavaEvaluator evaluator,
+            @NonNull PsiClass resolvedClass) {
+        for (PsiMethod method : resolvedClass.findMethodsByName(IS_VALID_FRAGMENT, false)) {
+            if (evaluator.parametersMatch(method, TYPE_STRING)) {
                 return true;
             }
         }
diff --git a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/PrivateResourceDetector.java b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/PrivateResourceDetector.java
index a4a8bab..6ed318f 100644
--- a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/PrivateResourceDetector.java
+++ b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/PrivateResourceDetector.java
@@ -46,7 +46,7 @@
 import com.android.resources.ResourceType;
 import com.android.tools.lint.detector.api.Category;
 import com.android.tools.lint.detector.api.Context;
-import com.android.tools.lint.detector.api.Detector.JavaScanner;
+import com.android.tools.lint.detector.api.Detector.JavaPsiScanner;
 import com.android.tools.lint.detector.api.Implementation;
 import com.android.tools.lint.detector.api.Issue;
 import com.android.tools.lint.detector.api.JavaContext;
@@ -56,11 +56,13 @@
 import com.android.tools.lint.detector.api.ResourceXmlDetector;
 import com.android.tools.lint.detector.api.Scope;
 import com.android.tools.lint.detector.api.Severity;
-import com.android.tools.lint.detector.api.Speed;
 import com.android.tools.lint.detector.api.XmlContext;
+import com.intellij.psi.JavaElementVisitor;
+import com.intellij.psi.PsiElement;
 
 import org.w3c.dom.Attr;
 import org.w3c.dom.Element;
+import org.w3c.dom.Node;
 import org.w3c.dom.NodeList;
 
 import java.io.File;
@@ -68,13 +70,11 @@
 import java.util.Collection;
 import java.util.List;
 
-import lombok.ast.AstVisitor;
-import lombok.ast.Node;
-
 /**
  * Check which looks for access of private resources.
  */
-public class PrivateResourceDetector extends ResourceXmlDetector implements JavaScanner {
+public class PrivateResourceDetector extends ResourceXmlDetector implements
+        JavaPsiScanner {
     /** Attribute for overriding a resource */
     private static final String ATTR_OVERRIDE = "override";
 
@@ -104,12 +104,6 @@
     public PrivateResourceDetector() {
     }
 
-    @NonNull
-    @Override
-    public Speed getSpeed() {
-        return Speed.FAST;
-    }
-
     // ---- Implements JavaScanner ----
 
     @Override
@@ -118,18 +112,13 @@
     }
 
     @Override
-    public void visitResourceReference(
-            @NonNull JavaContext context,
-            @Nullable AstVisitor visitor,
-            @NonNull Node node,
-            @NonNull String type,
-            @NonNull String name,
-            boolean isFramework) {
+    public void visitResourceReference(@NonNull JavaContext context,
+            @Nullable JavaElementVisitor visitor, @NonNull PsiElement node,
+            @NonNull ResourceType resourceType, @NonNull String name, boolean isFramework) {
         if (context.getProject().isGradleProject() && !isFramework) {
             Project project = context.getProject();
             if (project.getGradleProjectModel() != null && project.getCurrentVariant() != null) {
-                ResourceType resourceType = ResourceType.getEnum(type);
-                if (resourceType != null && isPrivate(context, resourceType, name)) {
+                if (isPrivate(context, resourceType, name)) {
                     String message = createUsageErrorMessage(context, resourceType, name);
                     context.report(ISSUE, node, context.getLocation(node), message);
                 }
@@ -236,8 +225,8 @@
         // Look for ?attr/ and @dimen/foo etc references in the item children
         NodeList childNodes = item.getChildNodes();
         for (int i = 0, n = childNodes.getLength(); i < n; i++) {
-            org.w3c.dom.Node child = childNodes.item(i);
-            if (child.getNodeType() == org.w3c.dom.Node.TEXT_NODE) {
+            Node child = childNodes.item(i);
+            if (child.getNodeType() == Node.TEXT_NODE) {
                 String text = child.getNodeValue();
 
                 int index = text.indexOf(ATTR_REF_PREFIX);
diff --git a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/ReadParcelableDetector.java b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/ReadParcelableDetector.java
index c6884af..a5b0b0b 100644
--- a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/ReadParcelableDetector.java
+++ b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/ReadParcelableDetector.java
@@ -15,35 +15,35 @@
  */
 package com.android.tools.lint.checks;
 
+import static com.android.SdkConstants.CLASS_PARCEL;
+
 import com.android.annotations.NonNull;
 import com.android.annotations.Nullable;
-import com.android.tools.lint.client.api.JavaParser;
-import com.android.tools.lint.client.api.JavaParser.ResolvedMethod;
-import com.android.tools.lint.client.api.JavaParser.ResolvedNode;
 import com.android.tools.lint.detector.api.Category;
 import com.android.tools.lint.detector.api.Detector;
+import com.android.tools.lint.detector.api.Detector.JavaPsiScanner;
 import com.android.tools.lint.detector.api.Implementation;
 import com.android.tools.lint.detector.api.Issue;
 import com.android.tools.lint.detector.api.JavaContext;
+import com.android.tools.lint.detector.api.LintUtils;
 import com.android.tools.lint.detector.api.Location;
 import com.android.tools.lint.detector.api.Scope;
 import com.android.tools.lint.detector.api.Severity;
-import com.android.tools.lint.detector.api.Speed;
+import com.intellij.psi.JavaElementVisitor;
+import com.intellij.psi.PsiClass;
+import com.intellij.psi.PsiElement;
+import com.intellij.psi.PsiExpression;
+import com.intellij.psi.PsiExpressionList;
+import com.intellij.psi.PsiMethod;
+import com.intellij.psi.PsiMethodCallExpression;
 
 import java.util.Arrays;
-import java.util.Collections;
 import java.util.List;
 
-import lombok.ast.AstVisitor;
-import lombok.ast.Expression;
-import lombok.ast.Identifier;
-import lombok.ast.MethodInvocation;
-import lombok.ast.NullLiteral;
-
 /**
  * Looks for Parcelable classes that are missing a CREATOR field
  */
-public class ReadParcelableDetector extends Detector implements Detector.JavaScanner {
+public class ReadParcelableDetector extends Detector implements JavaPsiScanner {
 
     /** The main issue discovered by this detector */
     public static final Issue ISSUE = Issue.create(
@@ -74,7 +74,6 @@
 
     // ---- Implements JavaScanner ----
 
-
     @Override
     public List<String> getApplicableMethodNames() {
         return Arrays.asList(
@@ -89,31 +88,39 @@
     }
 
     @Override
-    public void visitMethod(@NonNull JavaContext context, @Nullable AstVisitor visitor,
-            @NonNull MethodInvocation node) {
-        ResolvedNode resolved = context.resolve(node);
-        if (resolved instanceof ResolvedMethod) {
-            ResolvedMethod method = (ResolvedMethod) resolved;
-            if (method.getContainingClass().matches("android.os.Parcel")) {
-                int argumentCount = method.getArgumentCount();
-                if (argumentCount == 0) {
-                    Identifier name = node.astName();
-                    String message = String.format("Using the default class loader "
+    public void visitMethod(@NonNull JavaContext context, @Nullable JavaElementVisitor visitor,
+            @NonNull PsiMethodCallExpression node, @NonNull PsiMethod method) {
+        PsiClass containingClass = method.getContainingClass();
+        if (containingClass == null) {
+            return;
+        }
+        if (!(CLASS_PARCEL.equals(containingClass.getQualifiedName()))) {
+            return;
+        }
+
+        PsiExpressionList argumentList = node.getArgumentList();
+        PsiExpression[] expressions = argumentList.getExpressions();
+        int argumentCount = expressions.length;
+        if (argumentCount == 0) {
+            PsiElement name = node.getMethodExpression().getReferenceNameElement();
+            assert name != null;
+            String message = String.format("Using the default class loader "
                             + "will not work if you are restoring your own classes. Consider "
                             + "using for example `%1$s(getClass().getClassLoader())` instead.",
-                            name.astValue());
-                    Location location = context.getRangeLocation(name, 0, name, 2);
-                    context.report(ISSUE, node, location, message);
-                } else if (argumentCount == 1) {
-                    Expression parameter = node.astArguments().first();
-                    if (parameter instanceof NullLiteral) {
-                        String message = "Passing null here (to use the default class loader) "
-                                + "will not work if you are restoring your own classes. Consider "
-                                + "using for example `getClass().getClassLoader()` instead.";
-                        Location location = context.getRangeLocation(node.astName(), 0, parameter, 1);
-                        context.report(ISSUE, node, location, message);
-                    }
-                }
+                    name.getText());
+            Location location = context.getRangeLocation(name, 0, name, 2);
+            context.report(ISSUE, node, location, message);
+        } else if (argumentCount == 1) {
+            PsiExpression parameter = expressions[0];
+            if (LintUtils.isNullLiteral(parameter)) {
+                String message = "Passing null here (to use the default class loader) "
+                        + "will not work if you are restoring your own classes. Consider "
+                        + "using for example `getClass().getClassLoader()` instead.";
+                PsiElement name = node.getMethodExpression().getReferenceNameElement();
+                assert name != null;
+
+                Location location = context.getRangeLocation(name, 0, parameter, 1);
+                context.report(ISSUE, node, location, message);
             }
         }
     }
diff --git a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/RecyclerViewDetector.java b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/RecyclerViewDetector.java
index a4c5461..2fb8607 100644
--- a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/RecyclerViewDetector.java
+++ b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/RecyclerViewDetector.java
@@ -17,46 +17,41 @@
 package com.android.tools.lint.checks;
 
 
-import static com.android.tools.lint.detector.api.JavaContext.findSurroundingClass;
-
 import com.android.annotations.NonNull;
 import com.android.annotations.Nullable;
-import com.android.tools.lint.client.api.JavaParser.ResolvedClass;
-import com.android.tools.lint.client.api.JavaParser.ResolvedField;
-import com.android.tools.lint.client.api.JavaParser.ResolvedNode;
-import com.android.tools.lint.client.api.JavaParser.ResolvedVariable;
+import com.android.tools.lint.client.api.JavaEvaluator;
 import com.android.tools.lint.detector.api.Category;
 import com.android.tools.lint.detector.api.Detector;
+import com.android.tools.lint.detector.api.Detector.JavaPsiScanner;
 import com.android.tools.lint.detector.api.Implementation;
 import com.android.tools.lint.detector.api.Issue;
 import com.android.tools.lint.detector.api.JavaContext;
 import com.android.tools.lint.detector.api.Scope;
 import com.android.tools.lint.detector.api.Severity;
 import com.google.common.collect.Lists;
+import com.intellij.psi.JavaRecursiveElementVisitor;
+import com.intellij.psi.PsiAssignmentExpression;
+import com.intellij.psi.PsiClass;
+import com.intellij.psi.PsiElement;
+import com.intellij.psi.PsiExpression;
+import com.intellij.psi.PsiField;
+import com.intellij.psi.PsiLocalVariable;
+import com.intellij.psi.PsiMethod;
+import com.intellij.psi.PsiNewExpression;
+import com.intellij.psi.PsiParameter;
+import com.intellij.psi.PsiReference;
+import com.intellij.psi.PsiReferenceExpression;
+import com.intellij.psi.PsiVariable;
+import com.intellij.psi.util.PsiTreeUtil;
 
 import java.util.Collections;
-import java.util.Iterator;
 import java.util.List;
 
-import lombok.ast.BinaryExpression;
-import lombok.ast.BinaryOperator;
-import lombok.ast.ClassDeclaration;
-import lombok.ast.ClassLiteral;
-import lombok.ast.ConstructorInvocation;
-import lombok.ast.Expression;
-import lombok.ast.ForwardingAstVisitor;
-import lombok.ast.MethodDeclaration;
-import lombok.ast.Node;
-import lombok.ast.NormalTypeBody;
-import lombok.ast.VariableDefinition;
-import lombok.ast.VariableDefinitionEntry;
-import lombok.ast.VariableReference;
-
 /**
  * Checks related to RecyclerView usage.
  * // https://code.google.com/p/android/issues/detail?id=172335
  */
-public class RecyclerViewDetector extends Detector implements Detector.JavaScanner {
+public class RecyclerViewDetector extends Detector implements JavaPsiScanner {
     public static final Issue ISSUE = Issue.create(
             "RecyclerView", //$NON-NLS-1$
             "RecyclerView Problems",
@@ -90,63 +85,33 @@
     }
 
     @Override
-    public void checkClass(@NonNull JavaContext context, @Nullable ClassDeclaration declaration,
-            @NonNull Node node, @NonNull ResolvedClass resolvedClass) {
-        NormalTypeBody body;
-        if (declaration != null) {
-            body = declaration.astBody();
-        } else if (node instanceof NormalTypeBody) {
-            // anonymous inner class
-            body = (NormalTypeBody) node;
-        } else {
-            return;
-        }
-
-        for (Node child : body.astMembers()) {
-            if (child instanceof MethodDeclaration) {
-                MethodDeclaration method = (MethodDeclaration) child;
-                if (method.astMethodName().astValue().equals(ON_BIND_VIEW_HOLDER)) {
-                    int size = method.astParameters().size();
-                    if (size == 2 || size == 3) {
-                        checkMethod(context, method);
-                    }
-                }
+    public void checkClass(@NonNull JavaContext context, @NonNull PsiClass declaration) {
+        JavaEvaluator evaluator = context.getEvaluator();
+        for (PsiMethod method : declaration.findMethodsByName(ON_BIND_VIEW_HOLDER, false)) {
+            int size = evaluator.getParameterCount(method);
+            if (size == 2 || size == 3) {
+                checkMethod(context, method, declaration);
             }
         }
     }
 
     private static void checkMethod(@NonNull JavaContext context,
-            @NonNull MethodDeclaration declaration) {
-        Iterator<VariableDefinition> iterator = declaration.astParameters().iterator();
-        if (!iterator.hasNext()) {
-            return;
-        }
-        VariableDefinition viewHolder = iterator.next();
-        if (!iterator.hasNext()) {
-            return;
-        }
-        VariableDefinition parameter = iterator.next();
-        ResolvedNode reference = context.resolve(parameter);
+            @NonNull PsiMethod declaration, @NonNull PsiClass cls) {
+        PsiParameter[] parameters = declaration.getParameterList().getParameters();
+        PsiParameter viewHolder = parameters[0];
+        PsiParameter parameter = parameters[1];
 
-        if (reference instanceof ResolvedVariable) {
-            ParameterEscapesVisitor visitor = new ParameterEscapesVisitor(context, declaration,
-                    (ResolvedVariable) reference);
-            declaration.accept(visitor);
-            if (visitor.variableEscapes()) {
-                reportError(context, viewHolder, parameter);
-            }
-        } else if (parameter.astModifiers().isFinal()) {
+        ParameterEscapesVisitor visitor = new ParameterEscapesVisitor(context, cls, parameter);
+        declaration.accept(visitor);
+        if (visitor.variableEscapes()) {
             reportError(context, viewHolder, parameter);
         }
     }
 
-    private static void reportError(@NonNull JavaContext context, VariableDefinition viewHolder,
-            VariableDefinition parameter) {
-        String variablePrefix;
-        VariableDefinitionEntry first = viewHolder.astVariables().first();
-        if (first != null) {
-            variablePrefix = first.astName().astValue();
-        } else {
+    private static void reportError(@NonNull JavaContext context, PsiParameter viewHolder,
+            PsiParameter parameter) {
+        String variablePrefix = viewHolder.getName();
+        if (variablePrefix == null) {
             variablePrefix = "ViewHolder";
         }
         String message = String.format("Do not treat position as fixed; only use immediately "
@@ -160,19 +125,19 @@
      * Determines whether a given variable "escapes" either to a field or to a nested
      * runnable. (We deliberately ignore variables that escape via method calls.)
      */
-    private static class ParameterEscapesVisitor extends ForwardingAstVisitor {
+    private static class ParameterEscapesVisitor extends JavaRecursiveElementVisitor {
         protected final JavaContext mContext;
-        protected final List<ResolvedVariable> mVariables;
-        private final ClassDeclaration mBindClass;
+        protected final List<PsiVariable> mVariables;
+        private final PsiClass mBindClass;
         private boolean mEscapes;
         private boolean mFoundInnerClass;
 
         public ParameterEscapesVisitor(JavaContext context,
-                @NonNull MethodDeclaration onBindMethod,
-                @NonNull ResolvedVariable variable) {
+                @NonNull PsiClass bindClass,
+                @NonNull PsiParameter variable) {
             mContext = context;
-            mVariables = Lists.newArrayList(variable);
-            mBindClass = findSurroundingClass(onBindMethod);
+            mVariables = Lists.<PsiVariable>newArrayList(variable);
+            mBindClass = bindClass;
         }
 
         public boolean variableEscapes() {
@@ -180,100 +145,80 @@
         }
 
         @Override
-        public boolean visitNode(Node node) {
-            return mEscapes || super.visitNode(node);
-        }
-
-        @Override
-        public boolean visitVariableDefinitionEntry(VariableDefinitionEntry node) {
-            Expression initializer = node.astInitializer();
-            if (initializer instanceof VariableReference) {
-                ResolvedNode resolved = mContext.resolve(initializer);
+        public void visitLocalVariable(PsiLocalVariable variable) {
+            PsiExpression initializer = variable.getInitializer();
+            if (initializer instanceof PsiReference) {
+                PsiElement resolved = ((PsiReference) initializer).resolve();
                 //noinspection SuspiciousMethodCalls
                 if (resolved != null && mVariables.contains(resolved)) {
-                    ResolvedNode resolvedVariable = mContext.resolve(node);
-                    if (resolvedVariable instanceof ResolvedVariable) {
-                        ResolvedVariable variable = (ResolvedVariable) resolvedVariable;
+                    if (resolved instanceof PsiLocalVariable) {
                         mVariables.add(variable);
-                    } else if (resolvedVariable instanceof ResolvedField) {
+                    } else if (resolved instanceof PsiField) {
                         mEscapes = true;
                     }
                 }
             }
-            return super.visitVariableDefinitionEntry(node);
+
+            super.visitLocalVariable(variable);
         }
 
         @Override
-        public boolean visitBinaryExpression(BinaryExpression node) {
-            if (node.astOperator() == BinaryOperator.ASSIGN) {
-                Expression rhs = node.astRight();
-                boolean clearLhs = true;
-                if (rhs instanceof VariableReference) {
-                    ResolvedNode resolved = mContext.resolve(rhs);
-                    //noinspection SuspiciousMethodCalls
-                    if (resolved != null && mVariables.contains(resolved)) {
-                        clearLhs = false;
-                        ResolvedNode resolvedLhs = mContext.resolve(node.astLeft());
-                        if (resolvedLhs instanceof ResolvedVariable) {
-                            ResolvedVariable variable = (ResolvedVariable) resolvedLhs;
-                            mVariables.add(variable);
-                        } else if (resolvedLhs instanceof ResolvedField) {
-                            mEscapes = true;
-                        }
-                    }
-                }
-                if (clearLhs) {
-                    // If we reassign one of the variables, clear it out
-                    ResolvedNode resolved = mContext.resolve(node.astLeft());
-                    //noinspection SuspiciousMethodCalls
-                    if (resolved != null && mVariables.contains(resolved)) {
-                        //noinspection SuspiciousMethodCalls
-                        mVariables.remove(resolved);
+        public void visitAssignmentExpression(PsiAssignmentExpression node) {
+            PsiExpression rhs = node.getRExpression();
+            boolean clearLhs = true;
+            if (rhs instanceof PsiReferenceExpression) {
+                PsiElement resolved = ((PsiReferenceExpression)rhs).resolve();
+                //noinspection SuspiciousMethodCalls
+                if (resolved != null && mVariables.contains(resolved)) {
+                    clearLhs = false;
+                    PsiElement resolvedLhs = mContext.getEvaluator().resolve(node.getLExpression());
+                    if (resolvedLhs instanceof PsiLocalVariable) {
+                        PsiLocalVariable variable = (PsiLocalVariable) resolvedLhs;
+                        mVariables.add(variable);
+                    } else if (resolvedLhs instanceof PsiField) {
+                        mEscapes = true;
                     }
                 }
             }
-            return super.visitBinaryExpression(node);
+            if (clearLhs) {
+                // If we reassign one of the variables, clear it out
+                PsiElement resolved = mContext.getEvaluator().resolve(node.getLExpression());
+                //noinspection SuspiciousMethodCalls
+                if (resolved != null && mVariables.contains(resolved)) {
+                    //noinspection SuspiciousMethodCalls
+                    mVariables.remove(resolved);
+                }
+            }
+
+            super.visitAssignmentExpression(node);
         }
 
         @Override
-        public boolean visitVariableReference(VariableReference node) {
+        public void visitReferenceExpression(PsiReferenceExpression node) {
             if (mFoundInnerClass) {
                 // Check to see if this reference is inside the same class as the original
                 // onBind (e.g. is this a reference from an inner class, or a reference
                 // to a variable assigned from there)
-                ResolvedNode resolved = mContext.resolve(node);
+                PsiElement resolved = mContext.getEvaluator().resolve(node);
                 //noinspection SuspiciousMethodCalls
                 if (resolved != null && mVariables.contains(resolved)) {
-                    Node scope = node.getParent();
-                    while (scope != null) {
-                        if (scope instanceof NormalTypeBody) {
-                            if (scope != mBindClass.astBody()) {
-                                mEscapes = true;
-                            }
-                            break;
-                        }
-                        scope = scope.getParent();
+                    PsiClass outer = PsiTreeUtil.getParentOfType(node, PsiClass.class, true);
+                    if (!mBindClass.equals(outer)) {
+                        mEscapes = true;
                     }
                 }
             }
-            return super.visitVariableReference(node);
+
+            super.visitReferenceExpression(node);
         }
 
         @Override
-        public boolean visitClassLiteral(ClassLiteral node) {
-            mFoundInnerClass = true;
-
-            return super.visitClassLiteral(node);
-        }
-
-        @Override
-        public boolean visitConstructorInvocation(ConstructorInvocation node) {
-            NormalTypeBody anonymous = node.astAnonymousClassBody();
-            if (anonymous != null) {
+        public void visitNewExpression(PsiNewExpression expression) {
+            if (expression.getAnonymousClass() != null) {
                 mFoundInnerClass = true;
             }
 
-            return super.visitConstructorInvocation(node);
+            super.visitNewExpression(expression);
         }
     }
 }
diff --git a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/RegistrationDetector.java b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/RegistrationDetector.java
index a46ee0f..5d52615 100644
--- a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/RegistrationDetector.java
+++ b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/RegistrationDetector.java
@@ -32,12 +32,11 @@
 import com.android.annotations.NonNull;
 import com.android.annotations.Nullable;
 import com.android.builder.model.AndroidProject;
-import com.android.builder.model.BuildTypeContainer;
 import com.android.builder.model.ProductFlavorContainer;
 import com.android.builder.model.SourceProviderContainer;
-import com.android.tools.lint.client.api.JavaParser.ResolvedClass;
+import com.android.tools.lint.client.api.JavaEvaluator;
 import com.android.tools.lint.detector.api.Category;
-import com.android.tools.lint.detector.api.Detector.JavaScanner;
+import com.android.tools.lint.detector.api.Detector.JavaPsiScanner;
 import com.android.tools.lint.detector.api.Implementation;
 import com.android.tools.lint.detector.api.Issue;
 import com.android.tools.lint.detector.api.JavaContext;
@@ -45,10 +44,10 @@
 import com.android.tools.lint.detector.api.Location;
 import com.android.tools.lint.detector.api.Scope;
 import com.android.tools.lint.detector.api.Severity;
-import com.android.tools.lint.detector.api.Speed;
 import com.android.tools.lint.detector.api.XmlContext;
 import com.android.utils.SdkUtils;
 import com.google.common.collect.Maps;
+import com.intellij.psi.PsiClass;
 
 import org.w3c.dom.Element;
 
@@ -59,15 +58,11 @@
 import java.util.List;
 import java.util.Map;
 
-import lombok.ast.ClassDeclaration;
-import lombok.ast.Modifiers;
-import lombok.ast.Node;
-
 /**
  * Checks for missing manifest registrations for activities, services etc
  * and also makes sure that they are registered with the correct tag
  */
-public class RegistrationDetector extends LayoutDetector implements JavaScanner {
+public class RegistrationDetector extends LayoutDetector implements JavaPsiScanner {
     /** Unregistered activities and services */
     public static final Issue ISSUE = Issue.create(
             "Registered", //$NON-NLS-1$
@@ -94,12 +89,6 @@
     public RegistrationDetector() {
     }
 
-    @NonNull
-    @Override
-    public Speed getSpeed() {
-        return Speed.FAST;
-    }
-
     // ---- Implements XmlScanner ----
 
     @Override
@@ -167,45 +156,43 @@
     }
 
     @Override
-    public void checkClass(@NonNull JavaContext context, @Nullable ClassDeclaration node,
-            @NonNull Node declarationOrAnonymous, @NonNull ResolvedClass cls) {
-        if (node == null) {
+    public void checkClass(@NonNull JavaContext context, @NonNull PsiClass cls) {
+        if (cls.getName() == null) {
             // anonymous class; can't be registered
             return;
         }
 
-        Modifiers modifiers = node.astModifiers();
-        if (modifiers.isAbstract()) {
-            // Abstract classes do not need to be registered
+        JavaEvaluator evaluator = context.getEvaluator();
+        if (evaluator.isAbstract(cls) || evaluator.isPrivate(cls)) {
+            // Abstract classes do not need to be registered, and
+            // private classes are clearly not intended to be registered
             return;
         }
 
-        if (modifiers.isPrivate()) {
-            // Private classes are clearly not intended to be registered
-            return;
-        }
-
-        String rightTag = getTag(cls);
+        String rightTag = getTag(evaluator, cls);
         if (rightTag == null) {
             // some non-registered Context, such as a BackupAgent
             return;
         }
-        String className = cls.getName();
+        String className = cls.getQualifiedName();
+        if (className == null) {
+            return;
+        }
         if (mManifestRegistrations != null) {
             String framework = mManifestRegistrations.get(className);
             if (framework == null) {
-                reportMissing(context, node, className, rightTag);
-            } else if (!cls.isSubclassOf(framework, false)) {
-                reportWrongTag(context, node, rightTag, className, framework);
+                reportMissing(context, cls, className, rightTag);
+            } else if (!evaluator.extendsClass(cls, framework, false)) {
+                reportWrongTag(context, cls, rightTag, className, framework);
             }
         } else {
-            reportMissing(context, node, className, rightTag);
+            reportMissing(context, cls, className, rightTag);
         }
     }
 
     private static void reportWrongTag(
             @NonNull JavaContext context,
-            @NonNull ClassDeclaration node,
+            @NonNull PsiClass node,
             @NonNull String rightTag,
             @NonNull String className,
             @NonNull String framework) {
@@ -227,7 +214,7 @@
 
     private static void reportMissing(
             @NonNull JavaContext context,
-            @NonNull ClassDeclaration node,
+            @NonNull PsiClass node,
             @NonNull String className,
             @NonNull String tag) {
         if (tag.equals(TAG_RECEIVER)) {
@@ -274,10 +261,10 @@
         context.report(ISSUE, location, message);
     }
 
-    private static String getTag(@NonNull ResolvedClass cls) {
+    private static String getTag(@NonNull JavaEvaluator evaluator, @NonNull PsiClass cls) {
         String tag = null;
         for (String s : sClasses) {
-            if (cls.isSubclassOf(s, false)) {
+            if (evaluator.extendsClass(cls, s, false)) {
                 tag = classToTag(s);
                 break;
             }
diff --git a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/RelativeOverlapDetector.java b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/RelativeOverlapDetector.java
index caab887..0eec4a9 100644
--- a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/RelativeOverlapDetector.java
+++ b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/RelativeOverlapDetector.java
@@ -90,9 +90,9 @@
             TOP, BOTTOM, SKIP
         }
 
-        private int mIndex;
+        private final int mIndex;
         private boolean mProcessed;
-        private Element mNode;
+        private final Element mNode;
         private Bucket mBucket;
         private LayoutNode mToLeft;
         private LayoutNode mToRight;
diff --git a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/RequiredAttributeDetector.java b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/RequiredAttributeDetector.java
index eae9918..d4a0ee0 100644
--- a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/RequiredAttributeDetector.java
+++ b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/RequiredAttributeDetector.java
@@ -27,7 +27,6 @@
 import static com.android.SdkConstants.ATTR_STYLE;
 import static com.android.SdkConstants.AUTO_URI;
 import static com.android.SdkConstants.FD_RES_LAYOUT;
-import static com.android.SdkConstants.FN_RESOURCE_BASE;
 import static com.android.SdkConstants.FQCN_GRID_LAYOUT_V7;
 import static com.android.SdkConstants.GRID_LAYOUT;
 import static com.android.SdkConstants.LAYOUT_RESOURCE_PREFIX;
@@ -46,24 +45,31 @@
 import static com.android.resources.ResourceFolderType.LAYOUT;
 import static com.android.resources.ResourceFolderType.VALUES;
 import static com.android.tools.lint.detector.api.LintUtils.getLayoutName;
+import static com.android.tools.lint.detector.api.LintUtils.isNullLiteral;
 
 import com.android.annotations.NonNull;
 import com.android.annotations.Nullable;
 import com.android.annotations.VisibleForTesting;
+import com.android.ide.common.resources.ResourceUrl;
 import com.android.resources.ResourceFolderType;
+import com.android.resources.ResourceType;
 import com.android.tools.lint.detector.api.Category;
 import com.android.tools.lint.detector.api.Context;
-import com.android.tools.lint.detector.api.Detector;
+import com.android.tools.lint.detector.api.Detector.JavaPsiScanner;
 import com.android.tools.lint.detector.api.Implementation;
 import com.android.tools.lint.detector.api.Issue;
 import com.android.tools.lint.detector.api.JavaContext;
 import com.android.tools.lint.detector.api.LayoutDetector;
+import com.android.tools.lint.detector.api.ResourceEvaluator;
 import com.android.tools.lint.detector.api.Scope;
 import com.android.tools.lint.detector.api.Severity;
-import com.android.tools.lint.detector.api.Speed;
 import com.android.tools.lint.detector.api.XmlContext;
 import com.google.common.collect.Maps;
 import com.google.common.collect.Sets;
+import com.intellij.psi.JavaElementVisitor;
+import com.intellij.psi.PsiExpression;
+import com.intellij.psi.PsiMethod;
+import com.intellij.psi.PsiMethodCallExpression;
 
 import org.w3c.dom.Element;
 import org.w3c.dom.Node;
@@ -72,23 +78,14 @@
 import java.util.Collection;
 import java.util.Collections;
 import java.util.EnumSet;
-import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
 
-import lombok.ast.AstVisitor;
-import lombok.ast.Expression;
-import lombok.ast.MethodInvocation;
-import lombok.ast.NullLiteral;
-import lombok.ast.Select;
-import lombok.ast.StrictListAccessor;
-import lombok.ast.VariableReference;
-
 /**
  * Ensures that layout width and height attributes are specified
  */
-public class RequiredAttributeDetector extends LayoutDetector implements Detector.JavaScanner {
+public class RequiredAttributeDetector extends LayoutDetector implements JavaPsiScanner {
     /** The main issue discovered by this detector */
     public static final Issue ISSUE = Issue.create(
             "RequiredSize", //$NON-NLS-1$
@@ -147,12 +144,6 @@
     public RequiredAttributeDetector() {
     }
 
-    @NonNull
-    @Override
-    public Speed getSpeed() {
-        return Speed.FAST;
-    }
-
     @Override
     public boolean appliesTo(@NonNull ResourceFolderType folderType) {
         return folderType == LAYOUT || folderType == VALUES;
@@ -582,77 +573,42 @@
     }
 
     @Override
-    public void visitMethod(
-            @NonNull JavaContext context,
-            @Nullable AstVisitor visitor,
-            @NonNull MethodInvocation call) {
+    public void visitMethod(@NonNull JavaContext context, @Nullable JavaElementVisitor visitor,
+            @NonNull PsiMethodCallExpression call, @NonNull PsiMethod method) {
         // Handle
         //    View#inflate(Context context, int resource, ViewGroup root)
         //    LayoutInflater#inflate(int resource, ViewGroup root)
         //    LayoutInflater#inflate(int resource, ViewGroup root, boolean attachToRoot)
-        StrictListAccessor<Expression, MethodInvocation> args = call.astArguments();
+        PsiExpression[] args = call.getArgumentList().getExpressions();
 
         String layout = null;
         int index = 0;
-        for (Iterator<Expression> iterator = args.iterator(); iterator.hasNext(); index++) {
-            Expression expression = iterator.next();
-            if (expression instanceof Select) {
-                Select outer = (Select) expression;
-                Expression operand = outer.astOperand();
-                if (operand instanceof Select) {
-                    Select inner = (Select) operand;
-                    if (inner.astOperand() instanceof VariableReference) {
-                        VariableReference reference = (VariableReference) inner.astOperand();
-                        if (FN_RESOURCE_BASE.equals(reference.astIdentifier().astValue())
-                                // TODO: constant
-                                && "layout".equals(inner.astIdentifier().astValue())) {
-                            layout = LAYOUT_RESOURCE_PREFIX + outer.astIdentifier().astValue();
-                            break;
-                        }
-                    }
-                }
+        ResourceEvaluator evaluator = new ResourceEvaluator(context.getEvaluator());
+        for (PsiExpression expression : args) {
+            ResourceUrl url = evaluator.getResource(expression);
+            if (url != null && url.type == ResourceType.LAYOUT) {
+                layout = url.toString();
+                break;
             }
+            index++;
         }
 
         if (layout == null) {
-            lombok.ast.Node method = StringFormatDetector.getParentMethod(call);
-            if (method != null) {
-                // Must track local types
-                index = 0;
-                String name = StringFormatDetector.getResourceArg(method, call, index);
-                if (name == null) {
-                    index = 1;
-                    name = StringFormatDetector.getResourceArg(method, call, index);
-                }
-                if (name != null) {
-                    layout = LAYOUT_RESOURCE_PREFIX + name;
-                }
-            }
-            if (layout == null) {
-                // Flow analysis didn't succeed
-                return;
-            }
+            // Flow analysis didn't succeed
+            return;
         }
 
         // In all the applicable signatures, the view root argument is immediately after
         // the layout resource id.
         int viewRootPos = index + 1;
-        if (viewRootPos < args.size()) {
-            int i = 0;
-            Iterator<Expression> iterator = args.iterator();
-            while (iterator.hasNext() && i < viewRootPos) {
-                iterator.next();
-                i++;
-            }
-            if (iterator.hasNext()) {
-                Expression viewRoot = iterator.next();
-                if (viewRoot instanceof NullLiteral) {
-                    // Yep, this one inflates the given view with a null parent:
-                    // Tag it as such. For now just use the include data structure since
-                    // it has the same net effect
-                    recordIncludeWidth(layout, true);
-                    recordIncludeHeight(layout, true);
-                }
+        if (viewRootPos < args.length) {
+            PsiExpression viewRoot = args[viewRootPos];
+            if (isNullLiteral(viewRoot)) {
+                // Yep, this one inflates the given view with a null parent:
+                // Tag it as such. For now just use the include data structure since
+                // it has the same net effect
+                recordIncludeWidth(layout, true);
+                recordIncludeHeight(layout, true);
             }
         }
     }
diff --git a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/ResourcePrefixDetector.java b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/ResourcePrefixDetector.java
index bac39af..0d29f22 100644
--- a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/ResourcePrefixDetector.java
+++ b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/ResourcePrefixDetector.java
@@ -73,7 +73,7 @@
                     Scope.RESOURCE_FILE_SCOPE,
                     Scope.BINARY_RESOURCE_FILE_SCOPE));
 
-    /** Constructs a new {@link com.android.tools.lint.checks.ResourcePrefixDetector} */
+    /** Constructs a new {@link ResourcePrefixDetector} */
     public ResourcePrefixDetector() {
     }
 
diff --git a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/ResourceUsageModel.java b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/ResourceUsageModel.java
index 355b657..11eab6e 100644
--- a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/ResourceUsageModel.java
+++ b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/ResourceUsageModel.java
@@ -80,12 +80,12 @@
     private static final int TYPICAL_RESOURCE_COUNT = 200;
 
     /** List of all known resources (parsed from R.java) */
-    private List<Resource> mResources = Lists.newArrayListWithExpectedSize(TYPICAL_RESOURCE_COUNT);
+    private final List<Resource> mResources = Lists.newArrayListWithExpectedSize(TYPICAL_RESOURCE_COUNT);
     /** Map from resource type to map from resource name to resource object */
-    private Map<ResourceType, Map<String, Resource>> mTypeToName =
+    private final Map<ResourceType, Map<String, Resource>> mTypeToName =
             Maps.newEnumMap(ResourceType.class);
     /** Map from R field value to corresponding resource */
-    private Map<Integer, Resource> mValueToResource =
+    private final Map<Integer, Resource> mValueToResource =
             Maps.newHashMapWithExpectedSize(TYPICAL_RESOURCE_COUNT);
 
     public static String getFieldName(Element element) {
@@ -884,8 +884,8 @@
                     NodeList children = node.getChildNodes();
                     for (int i = 0, n = children.getLength(); i < n; i++) {
                         Node child = children.item(i);
-                        if (child.getNodeType() == Element.TEXT_NODE
-                                || child.getNodeType() == Element.CDATA_SECTION_NODE) {
+                        if (child.getNodeType() == Node.TEXT_NODE
+                                || child.getNodeType() == Node.CDATA_SECTION_NODE) {
                             sb.append(child.getNodeValue());
                         }
                     }
@@ -1587,7 +1587,7 @@
                             if (index > begin) {
                                 String name = s.substring(begin, index);
                                 Resource resource = addResource(type, name, null);
-                                ResourceUsageModel.markReachable(resource);
+                                markReachable(resource);
                             }
                         }
                         index--;
diff --git a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/RestrictionsDetector.java b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/RestrictionsDetector.java
index 381e7c4..b827bab 100644
--- a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/RestrictionsDetector.java
+++ b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/RestrictionsDetector.java
@@ -27,7 +27,6 @@
 import com.android.annotations.VisibleForTesting;
 import com.android.resources.ResourceFolderType;
 import com.android.tools.lint.detector.api.Category;
-import com.android.tools.lint.detector.api.Detector.JavaScanner;
 import com.android.tools.lint.detector.api.Implementation;
 import com.android.tools.lint.detector.api.Issue;
 import com.android.tools.lint.detector.api.LintUtils;
@@ -52,7 +51,7 @@
  * The rules are specified in
  * https://developer.android.com/reference/android/content/RestrictionsManager.html
  */
-public class RestrictionsDetector extends ResourceXmlDetector implements JavaScanner {
+public class RestrictionsDetector extends ResourceXmlDetector {
 
     // Copied from Google Play store's AppRestrictionBuilder
     @VisibleForTesting static final int MAX_NESTING_DEPTH = 20;
diff --git a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/RtlDetector.java b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/RtlDetector.java
index a004cd0..979453f 100644
--- a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/RtlDetector.java
+++ b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/RtlDetector.java
@@ -58,12 +58,9 @@
 import com.android.annotations.NonNull;
 import com.android.annotations.Nullable;
 import com.android.annotations.VisibleForTesting;
-import com.android.tools.lint.client.api.JavaParser.ResolvedClass;
-import com.android.tools.lint.client.api.JavaParser.ResolvedField;
-import com.android.tools.lint.client.api.JavaParser.ResolvedNode;
 import com.android.tools.lint.detector.api.Category;
 import com.android.tools.lint.detector.api.Context;
-import com.android.tools.lint.detector.api.Detector;
+import com.android.tools.lint.detector.api.Detector.JavaPsiScanner;
 import com.android.tools.lint.detector.api.Implementation;
 import com.android.tools.lint.detector.api.Issue;
 import com.android.tools.lint.detector.api.JavaContext;
@@ -73,8 +70,11 @@
 import com.android.tools.lint.detector.api.Project;
 import com.android.tools.lint.detector.api.Scope;
 import com.android.tools.lint.detector.api.Severity;
-import com.android.tools.lint.detector.api.Speed;
 import com.android.tools.lint.detector.api.XmlContext;
+import com.intellij.psi.JavaElementVisitor;
+import com.intellij.psi.PsiElement;
+import com.intellij.psi.PsiField;
+import com.intellij.psi.PsiReferenceExpression;
 
 import org.w3c.dom.Attr;
 import org.w3c.dom.Element;
@@ -87,20 +87,10 @@
 import java.util.List;
 import java.util.Locale;
 
-import lombok.ast.AstVisitor;
-import lombok.ast.EnumConstant;
-import lombok.ast.ForwardingAstVisitor;
-import lombok.ast.Identifier;
-import lombok.ast.ImportDeclaration;
-import lombok.ast.Node;
-import lombok.ast.Select;
-import lombok.ast.VariableDefinitionEntry;
-import lombok.ast.VariableReference;
-
 /**
  * Check which looks for RTL issues (right-to-left support) in layouts
  */
-public class RtlDetector extends LayoutDetector implements Detector.JavaScanner {
+public class RtlDetector extends LayoutDetector implements JavaPsiScanner {
 
     @SuppressWarnings("unchecked")
     private static final Implementation IMPLEMENTATION = new Implementation(
@@ -194,9 +184,7 @@
 
     private static final String RIGHT_FIELD = "RIGHT";                          //$NON-NLS-1$
     private static final String LEFT_FIELD = "LEFT";                            //$NON-NLS-1$
-    private static final String GRAVITY_CLASS = "Gravity";                      //$NON-NLS-1$
     private static final String FQCN_GRAVITY = "android.view.Gravity";          //$NON-NLS-1$
-    private static final String FQCN_GRAVITY_PREFIX = "android.view.Gravity.";  //$NON-NLS-1$
     private static final String ATTR_TEXT_ALIGNMENT = "textAlignment";          //$NON-NLS-1$
     static final String ATTR_SUPPORTS_RTL = "supportsRtl";                      //$NON-NLS-1$
 
@@ -215,12 +203,6 @@
     public RtlDetector() {
     }
 
-    @Override
-    @NonNull
-    public  Speed getSpeed() {
-        return Speed.NORMAL;
-    }
-
     private boolean rtlApplies(@NonNull Context context) {
         Project project = context.getMainProject();
         if  (project.getTargetSdk() < RTL_API) {
@@ -567,25 +549,21 @@
     // ---- Implements JavaScanner ----
 
     @Override
-    public boolean appliesTo(@NonNull Context context, @NonNull File file) {
-        return true;
+    public List<Class<? extends PsiElement>> getApplicablePsiTypes() {
+        return Collections.<Class<? extends PsiElement>>singletonList(PsiReferenceExpression.class);
     }
 
+    @Nullable
     @Override
-    public List<Class<? extends Node>> getApplicableNodeTypes() {
-        return Collections.<Class<? extends Node>>singletonList(Identifier.class);
-    }
-
-    @Override
-    public AstVisitor createJavaVisitor(@NonNull JavaContext context) {
+    public JavaElementVisitor createPsiVisitor(@NonNull JavaContext context) {
         if (rtlApplies(context)) {
             return new IdentifierChecker(context);
         }
 
-        return new ForwardingAstVisitor() { };
+        return null;
     }
 
-    private static class IdentifierChecker extends ForwardingAstVisitor {
+    private static class IdentifierChecker extends JavaElementVisitor {
         private final JavaContext mContext;
 
         public IdentifierChecker(JavaContext context) {
@@ -593,44 +571,22 @@
         }
 
         @Override
-        public boolean visitIdentifier(Identifier node) {
-            String identifier = node.astValue();
+        public void visitReferenceExpression(PsiReferenceExpression node) {
+            String identifier = node.getReferenceName();
             boolean isLeft = LEFT_FIELD.equals(identifier);
             boolean isRight = RIGHT_FIELD.equals(identifier);
             if (!isLeft && !isRight) {
-                return false;
+                return;
             }
 
-            Node parent = node.getParent();
-            if (parent instanceof ImportDeclaration || parent instanceof EnumConstant
-                    || parent instanceof VariableDefinitionEntry) {
-                return false;
-            }
 
-            ResolvedNode resolved = mContext.resolve(node);
-            if (resolved != null) {
-                if (!(resolved instanceof ResolvedField)) {
-                    return false;
-                } else {
-                    ResolvedField field = (ResolvedField) resolved;
-                    ResolvedClass containingClass = field.getContainingClass();
-                    if (containingClass == null || !containingClass.matches(FQCN_GRAVITY)) {
-                        return false;
-                    }
-                }
+            PsiElement resolved = node.resolve();
+            if (!(resolved instanceof PsiField)) {
+                return;
             } else {
-                // Can't resolve types (for example while editing code with errors):
-                // rely on heuristics like import statements and class qualifiers
-                if (parent instanceof Select &&
-                        !(GRAVITY_CLASS.equals(((Select) parent).astOperand().toString()))) {
-                    return false;
-                }
-                if (parent instanceof VariableReference) {
-                    // No operand: make sure it's statically imported
-                    if (!LintUtils.isImported(mContext.getCompilationUnit(),
-                            FQCN_GRAVITY_PREFIX + identifier)) {
-                        return false;
-                    }
+                PsiField field = (PsiField) resolved;
+                if (!mContext.getEvaluator().isMemberInClass(field, FQCN_GRAVITY)) {
+                    return;
                 }
             }
 
@@ -639,10 +595,12 @@
                             + "behavior in right-to-left locales",
                     (isLeft ? GRAVITY_VALUE_START : GRAVITY_VALUE_END).toUpperCase(Locale.US),
                     (isLeft ? GRAVITY_VALUE_LEFT : GRAVITY_VALUE_RIGHT).toUpperCase(Locale.US));
-            Location location = mContext.getLocation(node);
+            PsiElement locationNode = node.getReferenceNameElement();
+            if (locationNode == null) {
+                locationNode = node;
+            }
+            Location location = mContext.getLocation(locationNode);
             mContext.report(USE_START, node, location, message);
-
-            return true;
         }
     }
 }
diff --git a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/SQLiteDetector.java b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/SQLiteDetector.java
index 4197a88..d136661 100644
--- a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/SQLiteDetector.java
+++ b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/SQLiteDetector.java
@@ -20,30 +20,28 @@
 
 import com.android.annotations.NonNull;
 import com.android.annotations.Nullable;
-import com.android.tools.lint.client.api.JavaParser.ResolvedMethod;
-import com.android.tools.lint.client.api.JavaParser.ResolvedNode;
+import com.android.tools.lint.client.api.JavaEvaluator;
 import com.android.tools.lint.detector.api.Category;
 import com.android.tools.lint.detector.api.ConstantEvaluator;
 import com.android.tools.lint.detector.api.Detector;
+import com.android.tools.lint.detector.api.Detector.JavaPsiScanner;
 import com.android.tools.lint.detector.api.Implementation;
 import com.android.tools.lint.detector.api.Issue;
 import com.android.tools.lint.detector.api.JavaContext;
 import com.android.tools.lint.detector.api.Scope;
 import com.android.tools.lint.detector.api.Severity;
+import com.intellij.psi.JavaElementVisitor;
+import com.intellij.psi.PsiExpression;
+import com.intellij.psi.PsiMethod;
+import com.intellij.psi.PsiMethodCallExpression;
 
 import java.util.Collections;
-import java.util.Iterator;
 import java.util.List;
 
-import lombok.ast.AstVisitor;
-import lombok.ast.Expression;
-import lombok.ast.MethodInvocation;
-import lombok.ast.Node;
-
 /**
  * Detector which looks for problems related to SQLite usage
  */
-public class SQLiteDetector extends Detector implements Detector.JavaScanner {
+public class SQLiteDetector extends Detector implements JavaPsiScanner {
     private static final Implementation IMPLEMENTATION = new Implementation(
           SQLiteDetector.class, Scope.JAVA_FILE_SCOPE);
 
@@ -84,33 +82,30 @@
     }
 
     @Override
-    public void visitMethod(@NonNull JavaContext context, @Nullable AstVisitor visitor,
-            @NonNull MethodInvocation node) {
-        ResolvedNode resolved = context.resolve(node);
-        if (!(resolved instanceof ResolvedMethod)) {
+    public void visitMethod(@NonNull JavaContext context, @Nullable JavaElementVisitor visitor,
+            @NonNull PsiMethodCallExpression node, @NonNull PsiMethod method) {
+        JavaEvaluator evaluator = context.getEvaluator();
+        if (!evaluator.isMemberInClass(method, "android.database.sqlite.SQLiteDatabase")) {
             return;
         }
 
-        ResolvedMethod method = (ResolvedMethod) resolved;
-        if (!method.getContainingClass().matches("android.database.sqlite.SQLiteDatabase")) {
+        int parameterCount = evaluator.getParameterCount(method);
+        if (parameterCount == 0) {
             return;
         }
-
+        if (!evaluator.parameterHasType(method, 0, TYPE_STRING)) {
+            return;
+        }
         // Try to resolve the String and look for STRING keys
-        if (method.getArgumentCount() > 0
-                && method.getArgumentType(0).matchesSignature(TYPE_STRING)
-                && node.astArguments().size() == method.getArgumentCount()) {
-            Iterator<Expression> iterator = node.astArguments().iterator();
-            Node argument = iterator.next();
-            String sql = ConstantEvaluator.evaluateString(context, argument, true);
-            if (sql != null && (sql.startsWith("CREATE TABLE") || sql.startsWith("ALTER TABLE"))
-                    && sql.matches(".*\\bSTRING\\b.*")) {
-                String message = "Using column type STRING; did you mean to use TEXT? "
-                        + "(STRING is a numeric type and its value can be adjusted; for example,"
-                        + "strings that look like integers can drop leading zeroes. See issue "
-                        + "explanation for details.)";
-                context.report(ISSUE, node, context.getLocation(node), message);
-            }
+        PsiExpression argument = node.getArgumentList().getExpressions()[0];
+        String sql = ConstantEvaluator.evaluateString(context, argument, true);
+        if (sql != null && (sql.startsWith("CREATE TABLE") || sql.startsWith("ALTER TABLE"))
+                && sql.matches(".*\\bSTRING\\b.*")) {
+            String message = "Using column type STRING; did you mean to use TEXT? "
+                    + "(STRING is a numeric type and its value can be adjusted; for example, "
+                    + "strings that look like integers can drop leading zeroes. See issue "
+                    + "explanation for details.)";
+            context.report(ISSUE, node, context.getLocation(node), message);
         }
     }
 }
diff --git a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/SdCardDetector.java b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/SdCardDetector.java
index 5cd803f..ea3e320 100644
--- a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/SdCardDetector.java
+++ b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/SdCardDetector.java
@@ -18,7 +18,6 @@
 
 import com.android.annotations.NonNull;
 import com.android.tools.lint.detector.api.Category;
-import com.android.tools.lint.detector.api.Context;
 import com.android.tools.lint.detector.api.Detector;
 import com.android.tools.lint.detector.api.Implementation;
 import com.android.tools.lint.detector.api.Issue;
@@ -26,21 +25,19 @@
 import com.android.tools.lint.detector.api.Location;
 import com.android.tools.lint.detector.api.Scope;
 import com.android.tools.lint.detector.api.Severity;
-import com.android.tools.lint.detector.api.Speed;
+import com.intellij.psi.CommonClassNames;
+import com.intellij.psi.JavaElementVisitor;
+import com.intellij.psi.PsiElement;
+import com.intellij.psi.PsiLiteralExpression;
+import com.intellij.psi.PsiType;
 
-import java.io.File;
 import java.util.Collections;
 import java.util.List;
 
-import lombok.ast.AstVisitor;
-import lombok.ast.ForwardingAstVisitor;
-import lombok.ast.Node;
-import lombok.ast.StringLiteral;
-
 /**
  * Looks for hardcoded references to /sdcard/.
  */
-public class SdCardDetector extends Detector implements Detector.JavaScanner {
+public class SdCardDetector extends Detector implements Detector.JavaPsiScanner {
     /** Hardcoded /sdcard/ references */
     public static final Issue ISSUE = Issue.create(
             "SdCardPath", //$NON-NLS-1$
@@ -66,30 +63,20 @@
     public SdCardDetector() {
     }
 
-    @Override
-    public boolean appliesTo(@NonNull Context context, @NonNull File file) {
-        return true;
-    }
-
-    @NonNull
-    @Override
-    public Speed getSpeed() {
-        return Speed.FAST;
-    }
 
     // ---- Implements JavaScanner ----
 
     @Override
-    public List<Class<? extends Node>> getApplicableNodeTypes() {
-        return Collections.<Class<? extends Node>>singletonList(StringLiteral.class);
+    public List<Class<? extends PsiElement>> getApplicablePsiTypes() {
+        return Collections.<Class<? extends PsiElement>>singletonList(PsiLiteralExpression.class);
     }
 
     @Override
-    public AstVisitor createJavaVisitor(@NonNull JavaContext context) {
+    public JavaElementVisitor createPsiVisitor(@NonNull JavaContext context) {
         return new StringChecker(context);
     }
 
-    private static class StringChecker extends ForwardingAstVisitor {
+    private static class StringChecker extends JavaElementVisitor {
         private final JavaContext mContext;
 
         public StringChecker(JavaContext context) {
@@ -97,34 +84,35 @@
         }
 
         @Override
-        public boolean visitStringLiteral(StringLiteral node) {
-            String s = node.astValue();
-            if (s.isEmpty()) {
-                return false;
-            }
-            char c = s.charAt(0);
-            if (c != '/' && c != 'f') {
-                return false;
-            }
+        public void visitLiteralExpression(PsiLiteralExpression node) {
+            PsiType type = node.getType();
+            if (type != null && type.getCanonicalText().equals(CommonClassNames.JAVA_LANG_STRING)) {
+                String s = (String)node.getValue();
+                if (s == null || s.isEmpty()) {
+                    return;
+                }
+                char c = s.charAt(0);
+                if (c != '/' && c != 'f') {
+                    return;
+                }
 
-            if (s.startsWith("/sdcard")                        //$NON-NLS-1$
-                    || s.startsWith("/mnt/sdcard/")            //$NON-NLS-1$
-                    || s.startsWith("/system/media/sdcard")    //$NON-NLS-1$
-                    || s.startsWith("file://sdcard/")          //$NON-NLS-1$
-                    || s.startsWith("file:///sdcard/")) {      //$NON-NLS-1$
-                String message = "Do not hardcode \"/sdcard/\"; " +
-                    "use `Environment.getExternalStorageDirectory().getPath()` instead";
-                Location location = mContext.getLocation(node);
-                mContext.report(ISSUE, node, location, message);
-            } else if (s.startsWith("/data/data/")    //$NON-NLS-1$
-                    || s.startsWith("/data/user/")) { //$NON-NLS-1$
-                String message = "Do not hardcode \"`/data/`\"; " +
-                        "use `Context.getFilesDir().getPath()` instead";
+                if (s.startsWith("/sdcard")                        //$NON-NLS-1$
+                        || s.startsWith("/mnt/sdcard/")            //$NON-NLS-1$
+                        || s.startsWith("/system/media/sdcard")    //$NON-NLS-1$
+                        || s.startsWith("file://sdcard/")          //$NON-NLS-1$
+                        || s.startsWith("file:///sdcard/")) {      //$NON-NLS-1$
+                    String message = "Do not hardcode \"/sdcard/\"; " +
+                            "use `Environment.getExternalStorageDirectory().getPath()` instead";
                     Location location = mContext.getLocation(node);
                     mContext.report(ISSUE, node, location, message);
+                } else if (s.startsWith("/data/data/")    //$NON-NLS-1$
+                        || s.startsWith("/data/user/")) { //$NON-NLS-1$
+                    String message = "Do not hardcode \"`/data/`\"; " +
+                            "use `Context.getFilesDir().getPath()` instead";
+                    Location location = mContext.getLocation(node);
+                    mContext.report(ISSUE, node, location, message);
+                }
             }
-
-            return false;
         }
     }
 }
diff --git a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/SecureRandomDetector.java b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/SecureRandomDetector.java
index 7a0b729..77289c2 100644
--- a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/SecureRandomDetector.java
+++ b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/SecureRandomDetector.java
@@ -18,33 +18,31 @@
 
 import com.android.annotations.NonNull;
 import com.android.annotations.Nullable;
-import com.android.tools.lint.client.api.JavaParser.ResolvedClass;
-import com.android.tools.lint.client.api.JavaParser.ResolvedMethod;
-import com.android.tools.lint.client.api.JavaParser.ResolvedNode;
-import com.android.tools.lint.client.api.JavaParser.TypeDescriptor;
+import com.android.tools.lint.client.api.JavaEvaluator;
 import com.android.tools.lint.detector.api.Category;
 import com.android.tools.lint.detector.api.ConstantEvaluator;
 import com.android.tools.lint.detector.api.Detector;
-import com.android.tools.lint.detector.api.Detector.JavaScanner;
+import com.android.tools.lint.detector.api.Detector.JavaPsiScanner;
 import com.android.tools.lint.detector.api.Implementation;
 import com.android.tools.lint.detector.api.Issue;
 import com.android.tools.lint.detector.api.JavaContext;
 import com.android.tools.lint.detector.api.Scope;
 import com.android.tools.lint.detector.api.Severity;
 import com.android.tools.lint.detector.api.TypeEvaluator;
+import com.intellij.psi.JavaElementVisitor;
+import com.intellij.psi.PsiElement;
+import com.intellij.psi.PsiExpression;
+import com.intellij.psi.PsiMethod;
+import com.intellij.psi.PsiMethodCallExpression;
+import com.intellij.psi.PsiType;
 
 import java.util.Collections;
 import java.util.List;
 
-import lombok.ast.AstVisitor;
-import lombok.ast.Expression;
-import lombok.ast.MethodInvocation;
-import lombok.ast.Node;
-
 /**
  * Checks for hardcoded seeds with random numbers.
  */
-public class SecureRandomDetector extends Detector implements JavaScanner {
+public class SecureRandomDetector extends Detector implements JavaPsiScanner {
     /** Unregistered activities and services */
     public static final Issue ISSUE = Issue.create(
             "SecureRandom", //$NON-NLS-1$
@@ -78,20 +76,16 @@
     }
 
     @Override
-    public void visitMethod(@NonNull JavaContext context, @Nullable AstVisitor visitor,
-            @NonNull MethodInvocation call) {
-        ResolvedNode resolved = context.resolve(call);
-        if (!(resolved instanceof ResolvedMethod)) {
+    public void visitMethod(@NonNull JavaContext context, @Nullable JavaElementVisitor visitor,
+            @NonNull PsiMethodCallExpression call, @NonNull PsiMethod method) {
+        PsiExpression[] arguments = call.getArgumentList().getExpressions();
+        if (arguments.length == 0) {
             return;
         }
-        ResolvedMethod method = (ResolvedMethod) resolved;
-        Expression seedArgument = call.astArguments().first();
-        if (seedArgument == null) {
-            return;
-        }
-        ResolvedClass containingClass = method.getContainingClass();
-        if (containingClass.matches(JAVA_SECURITY_SECURE_RANDOM) ||
-                containingClass.isSubclassOf(JAVA_UTIL_RANDOM, false)
+        PsiExpression seedArgument = arguments[0];
+        JavaEvaluator evaluator = context.getEvaluator();
+        if (evaluator.isMemberInClass(method, JAVA_SECURITY_SECURE_RANDOM)
+                || evaluator.isMemberInSubClassOf(method, JAVA_UTIL_RANDOM, false)
                         && isSecureRandomReceiver(context, call)) {
             // Called with a fixed seed?
             Object seed = ConstantEvaluator.evaluate(context, seedArgument);
@@ -102,12 +96,12 @@
                                 "it is not secure. Use `getSeed()`.");
             } else {
                 // Called with a simple System.currentTimeMillis() seed or something like that?
-                ResolvedNode resolvedArgument = context.resolve(seedArgument);
-                if (resolvedArgument instanceof ResolvedMethod) {
-                    ResolvedMethod seedMethod = (ResolvedMethod) resolvedArgument;
+                PsiElement resolvedArgument = evaluator.resolve(seedArgument);
+                if (resolvedArgument instanceof PsiMethod) {
+                    PsiMethod seedMethod = (PsiMethod) resolvedArgument;
                     String methodName = seedMethod.getName();
-                    if (methodName.equals("currentTimeMillis") || methodName
-                            .equals("nanoTime")) {
+                    if (methodName.equals("currentTimeMillis")
+                            || methodName.equals("nanoTime")) {
                         context.report(ISSUE, call, context.getLocation(call),
                                 "It is dangerous to seed `SecureRandom` with the current "
                                         + "time because that value is more predictable to "
@@ -121,18 +115,20 @@
     /**
      * Returns true if the given invocation is assigned a SecureRandom type
      */
-    private static boolean isSecureRandomReceiver(@NonNull JavaContext context,
-            @NonNull MethodInvocation call) {
-        Expression operand = call.astOperand();
+    private static boolean isSecureRandomReceiver(
+            @NonNull JavaContext context,
+            @NonNull PsiMethodCallExpression call) {
+        PsiElement operand = call.getMethodExpression().getQualifier();
         return operand != null && isSecureRandomType(context, operand);
     }
 
     /**
      * Returns true if the node evaluates to an instance of type SecureRandom
      */
-    private static boolean isSecureRandomType(@NonNull JavaContext context, @NonNull Node node) {
-        TypeDescriptor type = TypeEvaluator.evaluate(context, node);
-        return type != null && type.matchesName(JAVA_SECURITY_SECURE_RANDOM);
-
+    private static boolean isSecureRandomType(
+            @NonNull JavaContext context,
+            @NonNull PsiElement node) {
+        PsiType type = TypeEvaluator.evaluate(context, node);
+        return type != null && JAVA_SECURITY_SECURE_RANDOM.equals(type.getCanonicalText());
     }
 }
diff --git a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/SecurityDetector.java b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/SecurityDetector.java
index 35a5334..6b75c9a 100644
--- a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/SecurityDetector.java
+++ b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/SecurityDetector.java
@@ -16,7 +16,6 @@
 
 package com.android.tools.lint.checks;
 
-import static com.android.SdkConstants.ANDROID_MANIFEST_XML;
 import static com.android.SdkConstants.ANDROID_URI;
 import static com.android.SdkConstants.ATTR_EXPORTED;
 import static com.android.SdkConstants.ATTR_NAME;
@@ -38,13 +37,11 @@
 
 import com.android.annotations.NonNull;
 import com.android.annotations.Nullable;
-import com.android.tools.lint.client.api.JavaParser.ResolvedClass;
-import com.android.tools.lint.client.api.JavaParser.ResolvedMethod;
-import com.android.tools.lint.client.api.JavaParser.ResolvedNode;
 import com.android.tools.lint.detector.api.Category;
 import com.android.tools.lint.detector.api.ConstantEvaluator;
-import com.android.tools.lint.detector.api.Context;
 import com.android.tools.lint.detector.api.Detector;
+import com.android.tools.lint.detector.api.Detector.JavaPsiScanner;
+import com.android.tools.lint.detector.api.Detector.XmlScanner;
 import com.android.tools.lint.detector.api.Implementation;
 import com.android.tools.lint.detector.api.Issue;
 import com.android.tools.lint.detector.api.JavaContext;
@@ -52,31 +49,26 @@
 import com.android.tools.lint.detector.api.Location;
 import com.android.tools.lint.detector.api.Scope;
 import com.android.tools.lint.detector.api.Severity;
-import com.android.tools.lint.detector.api.Speed;
 import com.android.tools.lint.detector.api.XmlContext;
+import com.intellij.psi.JavaElementVisitor;
+import com.intellij.psi.PsiExpression;
+import com.intellij.psi.PsiMethod;
+import com.intellij.psi.PsiMethodCallExpression;
+import com.intellij.psi.PsiReferenceExpression;
 
 import org.w3c.dom.Attr;
 import org.w3c.dom.Element;
 import org.w3c.dom.Node;
 
-import java.io.File;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.List;
 
-import lombok.ast.AstVisitor;
-import lombok.ast.Expression;
-import lombok.ast.ForwardingAstVisitor;
-import lombok.ast.Identifier;
-import lombok.ast.MethodInvocation;
-import lombok.ast.StrictListAccessor;
-
 /**
  * Checks that exported services request a permission.
  */
-public class SecurityDetector extends Detector implements Detector.XmlScanner,
-        Detector.JavaScanner {
+public class SecurityDetector extends Detector implements XmlScanner, JavaPsiScanner {
 
     private static final Implementation IMPLEMENTATION_MANIFEST = new Implementation(
             SecurityDetector.class,
@@ -198,17 +190,6 @@
     public SecurityDetector() {
     }
 
-    @NonNull
-    @Override
-    public Speed getSpeed() {
-        return Speed.FAST;
-    }
-
-    @Override
-    public boolean appliesTo(@NonNull Context context, @NonNull File file) {
-        return file.getName().equals(ANDROID_MANIFEST_XML);
-    }
-
     // ---- Implements Detector.XmlScanner ----
 
     @Override
@@ -406,52 +387,47 @@
     }
 
     @Override
-    public void visitMethod(@NonNull JavaContext context, @Nullable AstVisitor visitor,
-            @NonNull MethodInvocation node) {
-        ResolvedNode resolved = context.resolve(node);
-        if (resolved instanceof ResolvedMethod) {
-            ResolvedClass resolvedClass = ((ResolvedMethod) resolved).getContainingClass();
-            String methodName = node.astName().astValue();
-            if (resolvedClass.isSubclassOf(FILE_CLASS, false)) {
-                // Report calls to java.io.File.setReadable(true, false) or
-                // java.io.File.setWritable(true, false)
-                if ("setReadable".equals(methodName)) {
-                    if (node.astArguments().size() == 2 &&
-                            Boolean.TRUE.equals(ConstantEvaluator.evaluate(context,
-                                    node.astArguments().first())) &&
-                            Boolean.FALSE.equals(ConstantEvaluator.evaluate(context,
-                                    node.astArguments().last()))) {
-                        context.report(SET_READABLE, node, context.getLocation(node),
-                                "Setting file permissions to world-readable can be " +
-                                        "risky, review carefully");
-                    }
-                    return;
-                } else if ("setWritable".equals(methodName)) {
-                    if (node.astArguments().size() == 2 &&
-                            Boolean.TRUE.equals(ConstantEvaluator.evaluate(context,
-                                    node.astArguments().first())) &&
-                            Boolean.FALSE.equals(ConstantEvaluator.evaluate(context,
-                                    node.astArguments().last()))) {
-                        context.report(SET_WRITABLE, node, context.getLocation(node),
-                                "Setting file permissions to world-writable can be " +
-                                        "risky, review carefully");
-                    }
-                    return;
+    public void visitMethod(@NonNull JavaContext context, @Nullable JavaElementVisitor visitor,
+            @NonNull PsiMethodCallExpression node, @NonNull PsiMethod method) {
+        PsiExpression[] args = node.getArgumentList().getExpressions();
+        String methodName = node.getMethodExpression().getReferenceName();
+        if (context.getEvaluator().isMemberInSubClassOf(method, FILE_CLASS, false)) {
+            // Report calls to java.io.File.setReadable(true, false) or
+            // java.io.File.setWritable(true, false)
+            if ("setReadable".equals(methodName)) {
+                if (args.length == 2 &&
+                        Boolean.TRUE.equals(ConstantEvaluator.evaluate(context, args[0])) &&
+                        Boolean.FALSE.equals(ConstantEvaluator.evaluate(context, args[1]))) {
+                    context.report(SET_READABLE, node, context.getLocation(node),
+                            "Setting file permissions to world-readable can be " +
+                                    "risky, review carefully");
                 }
+                return;
+            } else if ("setWritable".equals(methodName)) {
+                if (args.length == 2 &&
+                        Boolean.TRUE.equals(ConstantEvaluator.evaluate(context, args[0])) &&
+                        Boolean.FALSE.equals(ConstantEvaluator.evaluate(context, args[1]))) {
+                    context.report(SET_WRITABLE, node, context.getLocation(node),
+                            "Setting file permissions to world-writable can be " +
+                                    "risky, review carefully");
+                }
+                return;
             }
         }
-        StrictListAccessor<Expression,MethodInvocation> args = node.astArguments();
-        for (Expression arg : args) {
+
+        assert visitor != null;
+        for (PsiExpression arg : args) {
             arg.accept(visitor);
         }
     }
 
+    @Nullable
     @Override
-    public AstVisitor createJavaVisitor(@NonNull JavaContext context) {
+    public JavaElementVisitor createPsiVisitor(@NonNull JavaContext context) {
         return new IdentifierVisitor(context);
     }
 
-    private static class IdentifierVisitor extends ForwardingAstVisitor {
+    private static class IdentifierVisitor extends JavaElementVisitor {
         private final JavaContext mContext;
 
         public IdentifierVisitor(JavaContext context) {
@@ -460,20 +436,21 @@
         }
 
         @Override
-        public boolean visitIdentifier(Identifier node) {
-            if ("MODE_WORLD_WRITEABLE".equals(node.astValue())) { //$NON-NLS-1$
+        public void visitReferenceExpression(PsiReferenceExpression node) {
+            super.visitReferenceExpression(node);
+
+            String name = node.getReferenceName();
+            if ("MODE_WORLD_WRITEABLE".equals(name)) { //$NON-NLS-1$
                 Location location = mContext.getLocation(node);
                 mContext.report(WORLD_WRITEABLE, node, location,
                         "Using `MODE_WORLD_WRITEABLE` when creating files can be " +
                                 "risky, review carefully");
-            } else if ("MODE_WORLD_READABLE".equals(node.astValue())) { //$NON-NLS-1$
+            } else if ("MODE_WORLD_READABLE".equals(name)) { //$NON-NLS-1$
                 Location location = mContext.getLocation(node);
                 mContext.report(WORLD_READABLE, node, location,
                         "Using `MODE_WORLD_READABLE` when creating files can be " +
                                 "risky, review carefully");
             }
-
-            return false;
         }
     }
 }
diff --git a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/ServiceCastDetector.java b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/ServiceCastDetector.java
index 71541e8..b8ec69b 100644
--- a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/ServiceCastDetector.java
+++ b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/ServiceCastDetector.java
@@ -19,30 +19,31 @@
 import com.android.annotations.NonNull;
 import com.android.annotations.Nullable;
 import com.android.tools.lint.detector.api.Category;
-import com.android.tools.lint.detector.api.Context;
 import com.android.tools.lint.detector.api.Detector;
+import com.android.tools.lint.detector.api.Detector.JavaPsiScanner;
 import com.android.tools.lint.detector.api.Implementation;
 import com.android.tools.lint.detector.api.Issue;
 import com.android.tools.lint.detector.api.JavaContext;
+import com.android.tools.lint.detector.api.LintUtils;
 import com.android.tools.lint.detector.api.Scope;
 import com.android.tools.lint.detector.api.Severity;
 import com.google.common.collect.Maps;
+import com.intellij.psi.JavaElementVisitor;
+import com.intellij.psi.PsiElement;
+import com.intellij.psi.PsiExpression;
+import com.intellij.psi.PsiMethod;
+import com.intellij.psi.PsiMethodCallExpression;
+import com.intellij.psi.PsiReferenceExpression;
+import com.intellij.psi.PsiTypeCastExpression;
 
-import java.io.File;
 import java.util.Collections;
 import java.util.List;
 import java.util.Map;
 
-import lombok.ast.AstVisitor;
-import lombok.ast.Cast;
-import lombok.ast.Expression;
-import lombok.ast.MethodInvocation;
-import lombok.ast.StrictListAccessor;
-
 /**
  * Detector looking for casts on th result of context.getSystemService which are suspect
  */
-public class ServiceCastDetector extends Detector implements Detector.JavaScanner {
+public class ServiceCastDetector extends Detector implements JavaPsiScanner {
     /** The main issue discovered by this detector */
     public static final Issue ISSUE = Issue.create(
             "ServiceCast", //$NON-NLS-1$
@@ -63,11 +64,6 @@
     public ServiceCastDetector() {
     }
 
-    @Override
-    public boolean appliesTo(@NonNull Context context, @NonNull File file) {
-        return true;
-    }
-
     // ---- Implements JavaScanner ----
 
     @Override
@@ -76,16 +72,18 @@
     }
 
     @Override
-    public void visitMethod(@NonNull JavaContext context, @Nullable AstVisitor visitor,
-            @NonNull MethodInvocation node) {
-        if (node.getParent() instanceof Cast) {
-            Cast cast = (Cast) node.getParent();
-            StrictListAccessor<Expression, MethodInvocation> args = node.astArguments();
-            if (args.size() == 1) {
-                String name = stripPackage(args.first().toString());
+    public void visitMethod(@NonNull JavaContext context, @Nullable JavaElementVisitor visitor,
+            @NonNull PsiMethodCallExpression call, @NonNull PsiMethod method) {
+        PsiElement parent = LintUtils.skipParentheses(call.getParent());
+        if (parent instanceof PsiTypeCastExpression) {
+            PsiTypeCastExpression cast = (PsiTypeCastExpression) parent;
+
+            PsiExpression[] args = call.getArgumentList().getExpressions();
+            if (args.length == 1 && args[0] instanceof PsiReferenceExpression) {
+                String name = ((PsiReferenceExpression)args[0]).getReferenceName();
                 String expectedClass = getExpectedType(name);
-                if (expectedClass != null) {
-                    String castType = cast.astTypeReference().getTypeName();
+                if (expectedClass != null && cast.getCastType() != null) {
+                    String castType = cast.getCastType().getType().getCanonicalText();
                     if (castType.indexOf('.') == -1) {
                         expectedClass = stripPackage(expectedClass);
                     }
@@ -99,7 +97,7 @@
                         String message = String.format(
                                 "Suspicious cast to `%1$s` for a `%2$s`: expected `%3$s`",
                                 stripPackage(castType), name, stripPackage(expectedClass));
-                        context.report(ISSUE, node, context.getLocation(cast), message);
+                        context.report(ISSUE, call, context.getLocation(cast), message);
                     }
                 }
 
@@ -122,8 +120,8 @@
     }
 
     @Nullable
-    private static String getExpectedType(@NonNull String value) {
-        return getServiceMap().get(value);
+    private static String getExpectedType(@Nullable String value) {
+        return value != null ? getServiceMap().get(value) : null;
     }
 
     @NonNull
@@ -183,7 +181,7 @@
             sServiceMap.put("USB_SERVICE", "android.hardware.usb.UsbManager");
             sServiceMap.put("USER_SERVICE", "android.os.UserManager");
             sServiceMap.put("VIBRATOR_SERVICE", "android.os.Vibrator");
-            sServiceMap.put("WALLPAPER_SERVICE", "com.android.server.WallpaperService");
+            sServiceMap.put("WALLPAPER_SERVICE", "android.service.wallpaper.WallpaperService");
             sServiceMap.put("WIFI_P2P_SERVICE", "android.net.wifi.p2p.WifiP2pManager");
             sServiceMap.put("WIFI_SERVICE", "android.net.wifi.WifiManager");
             sServiceMap.put("WINDOW_SERVICE", "android.view.WindowManager");
diff --git a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/SetJavaScriptEnabledDetector.java b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/SetJavaScriptEnabledDetector.java
index f1533be..9147221 100644
--- a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/SetJavaScriptEnabledDetector.java
+++ b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/SetJavaScriptEnabledDetector.java
@@ -19,23 +19,26 @@
 import com.android.annotations.NonNull;
 import com.android.annotations.Nullable;
 import com.android.tools.lint.detector.api.Category;
+import com.android.tools.lint.detector.api.ConstantEvaluator;
 import com.android.tools.lint.detector.api.Detector;
+import com.android.tools.lint.detector.api.Detector.JavaPsiScanner;
 import com.android.tools.lint.detector.api.Implementation;
 import com.android.tools.lint.detector.api.Issue;
 import com.android.tools.lint.detector.api.JavaContext;
 import com.android.tools.lint.detector.api.Scope;
 import com.android.tools.lint.detector.api.Severity;
+import com.intellij.psi.JavaElementVisitor;
+import com.intellij.psi.PsiExpression;
+import com.intellij.psi.PsiMethod;
+import com.intellij.psi.PsiMethodCallExpression;
 
 import java.util.Collections;
 import java.util.List;
 
-import lombok.ast.AstVisitor;
-import lombok.ast.MethodInvocation;
-
 /**
  * Looks for invocations of android.webkit.WebSettings.setJavaScriptEnabled.
  */
-public class SetJavaScriptEnabledDetector extends Detector implements Detector.JavaScanner {
+public class SetJavaScriptEnabledDetector extends Detector implements JavaPsiScanner {
     /** Invocations of setJavaScriptEnabled */
     public static final Issue ISSUE = Issue.create("SetJavaScriptEnabled", //$NON-NLS-1$
             "Using `setJavaScriptEnabled`",
@@ -59,13 +62,16 @@
     // ---- Implements JavaScanner ----
 
     @Override
-    public void visitMethod(@NonNull JavaContext context, @Nullable AstVisitor visitor,
-            @NonNull MethodInvocation node) {
-        if (node.astArguments().size() == 1
-                && !node.astArguments().first().toString().equals("false")) { //$NON-NLS-1$
-            context.report(ISSUE, node, context.getLocation(node),
-                    "Using `setJavaScriptEnabled` can introduce XSS vulnerabilities " +
-                            "into you application, review carefully.");
+    public void visitMethod(@NonNull JavaContext context, @Nullable JavaElementVisitor visitor,
+            @NonNull PsiMethodCallExpression call, @NonNull PsiMethod method) {
+        PsiExpression[] arguments = call.getArgumentList().getExpressions();
+        if (arguments.length == 1) {
+            Object constant = ConstantEvaluator.evaluate(context, arguments[0]);
+            if (constant != null && !Boolean.FALSE.equals(constant)) {
+                context.report(ISSUE, call, context.getLocation(call),
+                        "Using `setJavaScriptEnabled` can introduce XSS vulnerabilities " +
+                                "into you application, review carefully.");
+            }
         }
     }
 
diff --git a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/SetTextDetector.java b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/SetTextDetector.java
index fc55166..ed775b0 100644
--- a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/SetTextDetector.java
+++ b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/SetTextDetector.java
@@ -18,31 +18,31 @@
 
 import com.android.annotations.NonNull;
 import com.android.annotations.Nullable;
-import com.android.tools.lint.client.api.JavaParser.ResolvedClass;
-import com.android.tools.lint.client.api.JavaParser.ResolvedMethod;
+import com.android.tools.lint.client.api.JavaEvaluator;
 import com.android.tools.lint.detector.api.Category;
 import com.android.tools.lint.detector.api.Detector;
-import com.android.tools.lint.detector.api.Detector.JavaScanner;
+import com.android.tools.lint.detector.api.Detector.JavaPsiScanner;
 import com.android.tools.lint.detector.api.Implementation;
 import com.android.tools.lint.detector.api.Issue;
 import com.android.tools.lint.detector.api.JavaContext;
 import com.android.tools.lint.detector.api.Scope;
 import com.android.tools.lint.detector.api.Severity;
+import com.intellij.psi.JavaElementVisitor;
+import com.intellij.psi.JavaTokenType;
+import com.intellij.psi.PsiBinaryExpression;
+import com.intellij.psi.PsiClass;
+import com.intellij.psi.PsiElement;
+import com.intellij.psi.PsiLiteral;
+import com.intellij.psi.PsiMethod;
+import com.intellij.psi.PsiMethodCallExpression;
 
 import java.util.Collections;
 import java.util.List;
 
-import lombok.ast.AstVisitor;
-import lombok.ast.BinaryExpression;
-import lombok.ast.BinaryOperator;
-import lombok.ast.MethodInvocation;
-import lombok.ast.Node;
-import lombok.ast.StringLiteral;
-
 /**
  * Checks for errors related to TextView#setText and internationalization
  */
-public class SetTextDetector extends Detector implements JavaScanner {
+public class SetTextDetector extends Detector implements JavaPsiScanner {
 
     private static final Implementation IMPLEMENTATION = new Implementation(
             SetTextDetector.class,
@@ -93,43 +93,49 @@
     }
 
     @Override
-    public void visitMethod(@NonNull JavaContext context, @Nullable AstVisitor visitor,
-            @NonNull MethodInvocation call) {
-        ResolvedMethod method = (ResolvedMethod) context.resolve(call);
-        if (method != null && method.getContainingClass().matches(TEXT_VIEW_CLS)
-                && method.matches(METHOD_NAME)
-                && method.getArgumentCount() > 0
-                && method.getArgumentType(0).matchesSignature(CHAR_SEQUENCE_CLS)) {
-            checkNode(context, call.astArguments().first());
+    public void visitMethod(@NonNull JavaContext context, @Nullable JavaElementVisitor visitor,
+            @NonNull PsiMethodCallExpression call, @NonNull PsiMethod method) {
+        JavaEvaluator evaluator = context.getEvaluator();
+        if (!evaluator.isMemberInSubClassOf(method, TEXT_VIEW_CLS, false)) {
+            return;
+        }
+        if (method.getParameterList().getParametersCount() > 0 &&
+                evaluator.parameterHasType(method, 0, CHAR_SEQUENCE_CLS)) {
+            checkNode(context, call.getArgumentList().getExpressions()[0]);
         }
     }
 
-    private static void checkNode(JavaContext context, Node node) {
-        if (node instanceof StringLiteral) {
-            if (((StringLiteral) node).astValue().matches(WORD_PATTERN)) {
+    private static void checkNode(@NonNull JavaContext context, @Nullable PsiElement node) {
+        if (node instanceof PsiLiteral) {
+            Object value = ((PsiLiteral)node).getValue();
+            if (value instanceof String && value.toString().matches(WORD_PATTERN)) {
                 context.report(SET_TEXT_I18N, node, context.getLocation(node),
                         "String literal in `setText` can not be translated. Use Android "
                                 + "resources instead.");
             }
-        } else if (node instanceof MethodInvocation) {
-            ResolvedMethod rm = (ResolvedMethod) context.resolve(node);
-            if (rm != null && rm.getName().matches(TO_STRING_NAME)) {
-                ResolvedClass superClass = rm.getContainingClass().getSuperClass();
-                if (superClass != null && superClass.matches(NUMBER_CLS)) {
+        } else if (node instanceof PsiMethodCallExpression) {
+            PsiMethod calledMethod = ((PsiMethodCallExpression) node).resolveMethod();
+            if (calledMethod != null && TO_STRING_NAME.equals(calledMethod.getName())) {
+                PsiClass containingClass = calledMethod.getContainingClass();
+                if (containingClass == null) {
+                    return;
+                }
+                PsiClass superClass = containingClass.getSuperClass();
+                if (superClass != null && NUMBER_CLS.equals(superClass.getQualifiedName())) {
                     context.report(SET_TEXT_I18N, node, context.getLocation(node),
                             "Number formatting does not take into account locale settings. " +
                                     "Consider using `String.format` instead.");
                 }
             }
-        } else if (node instanceof BinaryExpression) {
-            BinaryExpression expression = (BinaryExpression) node;
-            if (expression.astOperator() == BinaryOperator.PLUS) {
+        } else if (node instanceof PsiBinaryExpression) {
+            PsiBinaryExpression expression = (PsiBinaryExpression) node;
+            if (expression.getOperationTokenType() == JavaTokenType.PLUS) {
                 context.report(SET_TEXT_I18N, node, context.getLocation(node),
                     "Do not concatenate text displayed with `setText`. "
                             + "Use resource string with placeholders.");
             }
-            checkNode(context, expression.astLeft());
-            checkNode(context, expression.astRight());
+            checkNode(context, expression.getLOperand());
+            checkNode(context, expression.getROperand());
         }
     }
 }
diff --git a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/SharedPrefsDetector.java b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/SharedPrefsDetector.java
deleted file mode 100644
index 3b0bd09..0000000
--- a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/SharedPrefsDetector.java
+++ /dev/null
@@ -1,349 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * 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.android.tools.lint.checks;
-
-import static com.android.tools.lint.client.api.JavaParser.ResolvedMethod;
-import static com.android.tools.lint.client.api.JavaParser.ResolvedNode;
-import static com.android.tools.lint.client.api.JavaParser.TypeDescriptor;
-
-import com.android.annotations.NonNull;
-import com.android.annotations.Nullable;
-import com.android.tools.lint.client.api.JavaParser.ResolvedClass;
-import com.android.tools.lint.detector.api.Category;
-import com.android.tools.lint.detector.api.Context;
-import com.android.tools.lint.detector.api.Detector;
-import com.android.tools.lint.detector.api.Implementation;
-import com.android.tools.lint.detector.api.Issue;
-import com.android.tools.lint.detector.api.JavaContext;
-import com.android.tools.lint.detector.api.LintUtils;
-import com.android.tools.lint.detector.api.Scope;
-import com.android.tools.lint.detector.api.Severity;
-
-import java.io.File;
-import java.util.Collections;
-import java.util.List;
-
-import lombok.ast.Assert;
-import lombok.ast.AstVisitor;
-import lombok.ast.Block;
-import lombok.ast.Case;
-import lombok.ast.ClassDeclaration;
-import lombok.ast.ConstructorDeclaration;
-import lombok.ast.DoWhile;
-import lombok.ast.Expression;
-import lombok.ast.ExpressionStatement;
-import lombok.ast.For;
-import lombok.ast.ForwardingAstVisitor;
-import lombok.ast.If;
-import lombok.ast.MethodDeclaration;
-import lombok.ast.MethodInvocation;
-import lombok.ast.Node;
-import lombok.ast.NormalTypeBody;
-import lombok.ast.Return;
-import lombok.ast.Statement;
-import lombok.ast.VariableDeclaration;
-import lombok.ast.VariableDefinition;
-import lombok.ast.VariableReference;
-import lombok.ast.While;
-
-/**
- * Detector looking for SharedPreferences.edit() calls without a corresponding
- * commit() or apply() call
- */
-public class SharedPrefsDetector extends Detector implements Detector.JavaScanner {
-    /** The main issue discovered by this detector */
-    public static final Issue ISSUE = Issue.create(
-            "CommitPrefEdits", //$NON-NLS-1$
-            "Missing `commit()` on `SharedPreference` editor",
-
-            "After calling `edit()` on a `SharedPreference`, you must call `commit()` " +
-            "or `apply()` on the editor to save the results.",
-
-            Category.CORRECTNESS,
-            6,
-            Severity.WARNING,
-            new Implementation(
-                    SharedPrefsDetector.class,
-                    Scope.JAVA_FILE_SCOPE));
-
-    public static final String ANDROID_CONTENT_SHARED_PREFERENCES =
-            "android.content.SharedPreferences"; //$NON-NLS-1$
-    private static final String ANDROID_CONTENT_SHARED_PREFERENCES_EDITOR =
-            "android.content.SharedPreferences.Editor"; //$NON-NLS-1$
-
-    /** Constructs a new {@link SharedPrefsDetector} check */
-    public SharedPrefsDetector() {
-    }
-
-    @Override
-    public boolean appliesTo(@NonNull Context context, @NonNull File file) {
-        return true;
-    }
-
-
-    // ---- Implements JavaScanner ----
-
-    @Override
-    public List<String> getApplicableMethodNames() {
-        return Collections.singletonList("edit"); //$NON-NLS-1$
-    }
-
-    @Nullable
-    private static NormalTypeBody findSurroundingTypeBody(Node scope) {
-        while (scope != null) {
-            Class<? extends Node> type = scope.getClass();
-            // The Lombok AST uses a flat hierarchy of node type implementation classes
-            // so no need to do instanceof stuff here.
-            if (type == NormalTypeBody.class) {
-                return (NormalTypeBody) scope;
-            }
-
-            scope = scope.getParent();
-        }
-
-        return null;
-    }
-
-
-    @Override
-    public void visitMethod(@NonNull JavaContext context, @Nullable AstVisitor visitor,
-            @NonNull MethodInvocation node) {
-        assert node.astName().astValue().equals("edit");
-
-        boolean verifiedType = false;
-        ResolvedNode resolve = context.resolve(node);
-        if (resolve instanceof ResolvedMethod) {
-            ResolvedMethod method = (ResolvedMethod) resolve;
-            TypeDescriptor returnType = method.getReturnType();
-            if (returnType == null ||
-                    !returnType.matchesName(ANDROID_CONTENT_SHARED_PREFERENCES_EDITOR)) {
-                return;
-            }
-            verifiedType = true;
-        }
-
-        Expression operand = node.astOperand();
-        if (operand == null) {
-            return;
-        }
-
-        // Looking for the specific pattern where you assign the edit() result
-        // to a local variable; this means we won't recognize some other usages
-        // of the API (e.g. assigning it to a previously declared variable) but
-        // is needed until we have type attribution in the AST itself.
-        Node parent = node.getParent();
-
-        VariableDefinition definition = getLhs(parent);
-        boolean allowCommitBeforeTarget;
-        if (definition == null) {
-            if (operand instanceof VariableReference) {
-                if (!verifiedType) {
-                    NormalTypeBody body = findSurroundingTypeBody(parent);
-                    if (body == null) {
-                        return;
-                    }
-                    String variableName = ((VariableReference) operand).astIdentifier().astValue();
-                    String type = getFieldType(body, variableName);
-                    if (type == null || !type.equals("SharedPreferences")) { //$NON-NLS-1$
-                        return;
-                    }
-                }
-                allowCommitBeforeTarget = true;
-            } else {
-                allowCommitBeforeTarget = false;
-            }
-        } else {
-            if (!verifiedType) {
-                String type = definition.astTypeReference().toString();
-                if (!type.endsWith("SharedPreferences.Editor")) {  //$NON-NLS-1$
-                    if (!type.equals("Editor") ||                  //$NON-NLS-1$
-                            !LintUtils.isImported(context.getCompilationUnit(),
-                                    ANDROID_CONTENT_SHARED_PREFERENCES_EDITOR)) {
-                        return;
-                    }
-                }
-            }
-            allowCommitBeforeTarget = false;
-        }
-
-        Node method = JavaContext.findSurroundingMethod(parent);
-        if (method == null) {
-            return;
-        }
-
-        CommitFinder finder = new CommitFinder(context, node, allowCommitBeforeTarget);
-        method.accept(finder);
-        if (!finder.isCommitCalled()) {
-            context.report(ISSUE, method, context.getLocation(node),
-                "`SharedPreferences.edit()` without a corresponding `commit()` or `apply()` call");
-        }
-    }
-
-    @Nullable
-    private static String getFieldType(@NonNull NormalTypeBody cls, @NonNull String name) {
-        List<Node> children = cls.getChildren();
-        for (Node child : children) {
-            if (child.getClass() == VariableDeclaration.class) {
-                VariableDeclaration declaration = (VariableDeclaration) child;
-                VariableDefinition definition = declaration.astDefinition();
-                return definition.astTypeReference().toString();
-            }
-        }
-
-        return null;
-    }
-
-    @Nullable
-    private static VariableDefinition getLhs(@NonNull Node node) {
-        while (node != null) {
-            Class<? extends Node> type = node.getClass();
-            // The Lombok AST uses a flat hierarchy of node type implementation classes
-            // so no need to do instanceof stuff here.
-            if (type == MethodDeclaration.class || type == ConstructorDeclaration.class) {
-                return null;
-            }
-            if (type == VariableDefinition.class) {
-                return (VariableDefinition) node;
-            }
-
-            node = node.getParent();
-        }
-
-        return null;
-    }
-
-    private static class CommitFinder extends ForwardingAstVisitor {
-        /** The target edit call */
-        private final MethodInvocation mTarget;
-        /** whether it allows the commit call to be seen before the target node */
-        private final boolean mAllowCommitBeforeTarget;
-
-        private final JavaContext mContext;
-
-        /** Whether we've found one of the commit/cancel methods */
-        private boolean mFound;
-        /** Whether we've seen the target edit node yet */
-        private boolean mSeenTarget;
-
-        private CommitFinder(JavaContext context, MethodInvocation target,
-                boolean allowCommitBeforeTarget) {
-            mContext = context;
-            mTarget = target;
-            mAllowCommitBeforeTarget = allowCommitBeforeTarget;
-        }
-
-        @Override
-        public boolean visitMethodInvocation(MethodInvocation node) {
-            if (node == mTarget) {
-                mSeenTarget = true;
-
-                // Look for chained calls on the target node
-                Node parent = node.getParent();
-                while (parent instanceof MethodInvocation) {
-                    MethodInvocation methodInvocation = (MethodInvocation) parent;
-                    String name = methodInvocation.astName().astValue();
-                    boolean isCommit = "commit".equals(name);
-                    if (isCommit || "apply".equals(name)) {
-                        mFound = true;
-                        if (isCommit) {
-                            suggestApplyIfApplicable(node);
-                        }
-                        break;
-                    }
-
-                    parent = parent.getParent();
-                }
-
-            } else if (mAllowCommitBeforeTarget || mSeenTarget || node.astOperand() == mTarget) {
-                String name = node.astName().astValue();
-                boolean isCommit = "commit".equals(name);
-                if (isCommit || "apply".equals(name)) { //$NON-NLS-1$ //$NON-NLS-2$
-                    // TODO: Do more flow analysis to see whether we're really calling commit/apply
-                    // on the right type of object?
-                    mFound = true;
-
-                    if (isCommit) {
-                        suggestApplyIfApplicable(node);
-                    }
-                }
-            }
-
-            return super.visitMethodInvocation(node);
-        }
-
-        private void suggestApplyIfApplicable(MethodInvocation node) {
-            ResolvedNode resolved = mContext.resolve(node);
-            if (resolved instanceof ResolvedMethod) {
-                ResolvedMethod method = (ResolvedMethod) resolved;
-                ResolvedClass clz = method.getContainingClass();
-                if (clz.isSubclassOf("android.content.SharedPreferences.Editor", false)
-                        && mContext.getProject().getMinSdkVersion().getApiLevel() >= 9) {
-                    // See if the return value is read: can only replace commit with
-                    // apply if the return value is not considered
-                    Node parent = node.getParent();
-                    boolean returnValueIgnored = false;
-                    if (parent instanceof MethodDeclaration ||
-                            parent instanceof ConstructorDeclaration ||
-                            parent instanceof ClassDeclaration ||
-                            parent instanceof Block ||
-                            parent instanceof ExpressionStatement) {
-                        returnValueIgnored = true;
-                    } else if (parent instanceof Statement) {
-                        if (parent instanceof If) {
-                            returnValueIgnored = ((If) parent).astCondition() != node;
-                        } else if (parent instanceof Return) {
-                            returnValueIgnored = false;
-                        } else if (parent instanceof VariableDeclaration) {
-                            returnValueIgnored = false;
-                        } else if (parent instanceof For) {
-                            returnValueIgnored = ((For) parent).astCondition() != node;
-                        } else if (parent instanceof While) {
-                            returnValueIgnored = ((While) parent).astCondition() != node;
-                        } else if (parent instanceof DoWhile) {
-                            returnValueIgnored = ((DoWhile) parent).astCondition() != node;
-                        } else if (parent instanceof Case) {
-                            returnValueIgnored = ((Case) parent).astCondition() != node;
-                        } else if (parent instanceof Assert) {
-                            returnValueIgnored = ((Assert) parent).astAssertion() != node;
-                        } else {
-                            returnValueIgnored = true;
-                        }
-                    }
-                    if (returnValueIgnored) {
-                        String message = "Consider using `apply()` instead; `commit` writes "
-                                + "its data to persistent storage immediately, whereas "
-                                + "`apply` will handle it in the background";
-                        mContext.report(ISSUE, node, mContext.getLocation(node), message);
-                    }
-                }
-            }
-        }
-
-        @Override
-        public boolean visitReturn(Return node) {
-            if (node.astValue() == mTarget) {
-                // If you just do "return editor.commit() don't warn
-                mFound = true;
-            }
-            return super.visitReturn(node);
-        }
-
-        boolean isCommitCalled() {
-            return mFound;
-        }
-    }
-}
diff --git a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/SslCertificateSocketFactoryDetector.java b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/SslCertificateSocketFactoryDetector.java
index 393458a..21b582a 100644
--- a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/SslCertificateSocketFactoryDetector.java
+++ b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/SslCertificateSocketFactoryDetector.java
@@ -18,29 +18,26 @@
 
 import com.android.annotations.NonNull;
 import com.android.annotations.Nullable;
-import com.android.tools.lint.client.api.JavaParser.ResolvedClass;
-import com.android.tools.lint.client.api.JavaParser.ResolvedMethod;
-import com.android.tools.lint.client.api.JavaParser.ResolvedNode;
-import com.android.tools.lint.client.api.JavaParser.TypeDescriptor;
 import com.android.tools.lint.detector.api.Category;
 import com.android.tools.lint.detector.api.Detector;
+import com.android.tools.lint.detector.api.Detector.JavaPsiScanner;
 import com.android.tools.lint.detector.api.Implementation;
 import com.android.tools.lint.detector.api.Issue;
 import com.android.tools.lint.detector.api.JavaContext;
 import com.android.tools.lint.detector.api.Scope;
 import com.android.tools.lint.detector.api.Severity;
-import com.android.tools.lint.detector.api.Speed;
+import com.intellij.psi.JavaElementVisitor;
+import com.intellij.psi.PsiClassType;
+import com.intellij.psi.PsiExpression;
+import com.intellij.psi.PsiMethod;
+import com.intellij.psi.PsiMethodCallExpression;
+import com.intellij.psi.PsiType;
 
 import java.util.Arrays;
 import java.util.List;
 
-import lombok.ast.AstVisitor;
-import lombok.ast.Expression;
-import lombok.ast.MethodInvocation;
-import lombok.ast.StrictListAccessor;
-
 public class SslCertificateSocketFactoryDetector extends Detector
-        implements Detector.JavaScanner {
+        implements JavaPsiScanner {
 
     private static final Implementation IMPLEMENTATION_JAVA = new Implementation(
             SslCertificateSocketFactoryDetector.class,
@@ -83,13 +80,7 @@
     private static final String SSL_CERTIFICATE_SOCKET_FACTORY_CLASS =
             "android.net.SSLCertificateSocketFactory";
 
-    @NonNull
-    @Override
-    public Speed getSpeed() {
-        return Speed.FAST;
-    }
-
-    // ---- Implements Detector.JavaScanner ----
+    // ---- Implements JavaScanner ----
 
     @Override
     public List<String> getApplicableMethodNames() {
@@ -98,36 +89,31 @@
     }
 
     @Override
-    public void visitMethod(@NonNull JavaContext context, @Nullable AstVisitor visitor,
-            @NonNull MethodInvocation node) {
-        ResolvedNode resolved = context.resolve(node);
-        if (resolved instanceof ResolvedMethod) {
-            String methodName = node.astName().astValue();
-            ResolvedClass resolvedClass = ((ResolvedMethod) resolved).getContainingClass();
-            if (resolvedClass.isSubclassOf(SSL_CERTIFICATE_SOCKET_FACTORY_CLASS, false)) {
-                if ("createSocket".equals(methodName)) {
-                    StrictListAccessor<Expression, MethodInvocation> argumentList =
-                            node.astArguments();
-                    if (argumentList != null) {
-                        TypeDescriptor firstParameterType = context.getType(argumentList.first());
-                        if (firstParameterType != null) {
-                            ResolvedClass firstParameterClass = firstParameterType.getTypeClass();
-                            if (firstParameterClass != null &&
-                                    firstParameterClass.isSubclassOf(INET_ADDRESS_CLASS, false)) {
-                                context.report(CREATE_SOCKET, node, context.getLocation(node),
-                                        "Use of `SSLCertificateSocketFactory.createSocket()` " +
-                                        "with an InetAddress parameter can cause insecure " +
-                                        "network traffic due to trusting arbitrary hostnames in " +
-                                        "TLS/SSL certificates presented by peers");
-                            }
-                        }
+    public void visitMethod(@NonNull JavaContext context, @Nullable JavaElementVisitor visitor,
+            @NonNull PsiMethodCallExpression call, @NonNull PsiMethod method) {
+        if (context.getEvaluator().isMemberInSubClassOf(method,
+                SSL_CERTIFICATE_SOCKET_FACTORY_CLASS, false)) {
+            String methodName = method.getName();
+            if ("createSocket".equals(methodName)) {
+                PsiExpression[] args = call.getArgumentList().getExpressions();
+                if (args.length > 0) {
+                    PsiType type = args[0].getType();
+                    if (type != null
+                            && (INET_ADDRESS_CLASS.equals(type.getCanonicalText())
+                            || context.getEvaluator().extendsClass(((PsiClassType)type).resolve(),
+                            INET_ADDRESS_CLASS, false))) {
+                        context.report(CREATE_SOCKET, call, context.getLocation(call),
+                                "Use of `SSLCertificateSocketFactory.createSocket()` " +
+                                "with an InetAddress parameter can cause insecure " +
+                                "network traffic due to trusting arbitrary hostnames in " +
+                                "TLS/SSL certificates presented by peers");
                     }
-                } else if ("getInsecure".equals(methodName)) {
-                    context.report(GET_INSECURE, node, context.getLocation(node),
-                            "Use of `SSLCertificateSocketFactory.getInsecure()` can cause " +
-                            "insecure network traffic due to trusting arbitrary TLS/SSL " +
-                            "certificates presented by peers");
                 }
+            } else if ("getInsecure".equals(methodName)) {
+                context.report(GET_INSECURE, call, context.getLocation(call),
+                        "Use of `SSLCertificateSocketFactory.getInsecure()` can cause " +
+                        "insecure network traffic due to trusting arbitrary TLS/SSL " +
+                        "certificates presented by peers");
             }
         }
     }
diff --git a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/StateListDetector.java b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/StateListDetector.java
index 36f53c0..0cb4f12 100644
--- a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/StateListDetector.java
+++ b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/StateListDetector.java
@@ -28,7 +28,6 @@
 import com.android.tools.lint.detector.api.ResourceXmlDetector;
 import com.android.tools.lint.detector.api.Scope;
 import com.android.tools.lint.detector.api.Severity;
-import com.android.tools.lint.detector.api.Speed;
 import com.android.tools.lint.detector.api.XmlContext;
 
 import org.w3c.dom.Attr;
@@ -71,12 +70,6 @@
         return folderType == ResourceFolderType.DRAWABLE;
     }
 
-    @NonNull
-    @Override
-    public Speed getSpeed() {
-        return Speed.FAST;
-    }
-
     @Override
     public void visitDocument(@NonNull XmlContext context, @NonNull Document document) {
         // TODO: Look for views that don't specify
@@ -90,24 +83,24 @@
             Map<Element, Set<String>> states =
                     new HashMap<Element, Set<String>>(children.size());
 
-            for (int i = 0; i < children.size(); i++) {
-                Element child = children.get(i);
+            for (Element child : children) {
                 NamedNodeMap attributes = child.getAttributes();
                 Set<String> stateNames = new HashSet<String>(attributes.getLength());
                 states.put(child, stateNames);
 
                 for (int j = 0; j < attributes.getLength(); j++) {
-                    Attr attribute = (Attr) attributes.item(j);
+                    Attr attribute = (Attr)attributes.item(j);
                     String name = attribute.getLocalName();
                     if (name == null) {
                         continue;
                     }
                     if (name.startsWith(STATE_PREFIX)) {
                         stateNames.add(name + '=' + attribute.getValue());
-                    } else {
+                    }
+                    else {
                         String namespaceUri = attribute.getNamespaceURI();
                         if (namespaceUri != null && !namespaceUri.isEmpty() &&
-                                !ANDROID_URI.equals(namespaceUri)) {
+                            !ANDROID_URI.equals(namespaceUri)) {
                             // There is a custom attribute on this item.
                             // This could be a state, see
                             //   http://code.google.com/p/android/issues/detail?id=22339
diff --git a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/StringFormatDetector.java b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/StringFormatDetector.java
index e8397fc..a838f56 100644
--- a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/StringFormatDetector.java
+++ b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/StringFormatDetector.java
@@ -24,20 +24,16 @@
 import static com.android.SdkConstants.DOT_JAVA;
 import static com.android.SdkConstants.FORMAT_METHOD;
 import static com.android.SdkConstants.GET_STRING_METHOD;
-import static com.android.SdkConstants.R_CLASS;
 import static com.android.SdkConstants.TAG_STRING;
-import static com.android.tools.lint.client.api.JavaParser.TYPE_BOOLEAN;
-import static com.android.tools.lint.client.api.JavaParser.TYPE_BYTE;
-import static com.android.tools.lint.client.api.JavaParser.TYPE_CHAR;
-import static com.android.tools.lint.client.api.JavaParser.TYPE_DOUBLE;
-import static com.android.tools.lint.client.api.JavaParser.TYPE_FLOAT;
-import static com.android.tools.lint.client.api.JavaParser.TYPE_INT;
-import static com.android.tools.lint.client.api.JavaParser.TYPE_LONG;
-import static com.android.tools.lint.client.api.JavaParser.TYPE_NULL;
-import static com.android.tools.lint.client.api.JavaParser.TYPE_OBJECT;
-import static com.android.tools.lint.client.api.JavaParser.TYPE_SHORT;
+import static com.android.tools.lint.client.api.JavaParser.TYPE_BOOLEAN_WRAPPER;
+import static com.android.tools.lint.client.api.JavaParser.TYPE_BYTE_WRAPPER;
+import static com.android.tools.lint.client.api.JavaParser.TYPE_CHARACTER_WRAPPER;
+import static com.android.tools.lint.client.api.JavaParser.TYPE_DOUBLE_WRAPPER;
+import static com.android.tools.lint.client.api.JavaParser.TYPE_FLOAT_WRAPPER;
+import static com.android.tools.lint.client.api.JavaParser.TYPE_INTEGER_WRAPPER;
+import static com.android.tools.lint.client.api.JavaParser.TYPE_LONG_WRAPPER;
+import static com.android.tools.lint.client.api.JavaParser.TYPE_SHORT_WRAPPER;
 import static com.android.tools.lint.client.api.JavaParser.TYPE_STRING;
-import static com.android.tools.lint.client.api.JavaParser.TypeDescriptor;
 
 import com.android.annotations.NonNull;
 import com.android.annotations.Nullable;
@@ -45,15 +41,14 @@
 import com.android.ide.common.rendering.api.ResourceValue;
 import com.android.ide.common.res2.AbstractResourceRepository;
 import com.android.ide.common.res2.ResourceItem;
+import com.android.ide.common.resources.ResourceUrl;
 import com.android.resources.ResourceFolderType;
 import com.android.resources.ResourceType;
-import com.android.tools.lint.client.api.JavaParser.ResolvedClass;
-import com.android.tools.lint.client.api.JavaParser.ResolvedMethod;
-import com.android.tools.lint.client.api.JavaParser.ResolvedNode;
+import com.android.tools.lint.client.api.JavaEvaluator;
 import com.android.tools.lint.client.api.LintClient;
 import com.android.tools.lint.detector.api.Category;
 import com.android.tools.lint.detector.api.Context;
-import com.android.tools.lint.detector.api.Detector;
+import com.android.tools.lint.detector.api.Detector.JavaPsiScanner;
 import com.android.tools.lint.detector.api.Implementation;
 import com.android.tools.lint.detector.api.Issue;
 import com.android.tools.lint.detector.api.JavaContext;
@@ -61,6 +56,7 @@
 import com.android.tools.lint.detector.api.Location;
 import com.android.tools.lint.detector.api.Location.Handle;
 import com.android.tools.lint.detector.api.Position;
+import com.android.tools.lint.detector.api.ResourceEvaluator;
 import com.android.tools.lint.detector.api.ResourceXmlDetector;
 import com.android.tools.lint.detector.api.Scope;
 import com.android.tools.lint.detector.api.Severity;
@@ -70,6 +66,19 @@
 import com.google.common.collect.Lists;
 import com.google.common.collect.Maps;
 import com.google.common.collect.Sets;
+import com.intellij.psi.JavaElementVisitor;
+import com.intellij.psi.PsiArrayInitializerExpression;
+import com.intellij.psi.PsiClassType;
+import com.intellij.psi.PsiElement;
+import com.intellij.psi.PsiExpression;
+import com.intellij.psi.PsiLiteral;
+import com.intellij.psi.PsiMethod;
+import com.intellij.psi.PsiMethodCallExpression;
+import com.intellij.psi.PsiNewExpression;
+import com.intellij.psi.PsiParameterList;
+import com.intellij.psi.PsiReference;
+import com.intellij.psi.PsiType;
+import com.intellij.psi.PsiVariable;
 
 import org.w3c.dom.Element;
 import org.w3c.dom.Node;
@@ -83,42 +92,19 @@
 import java.util.EnumSet;
 import java.util.HashMap;
 import java.util.HashSet;
-import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
-import lombok.ast.ArrayCreation;
-import lombok.ast.ArrayDimension;
-import lombok.ast.ArrayInitializer;
-import lombok.ast.AstVisitor;
-import lombok.ast.BooleanLiteral;
-import lombok.ast.CharLiteral;
-import lombok.ast.ConstructorDeclaration;
-import lombok.ast.ConstructorInvocation;
-import lombok.ast.Expression;
-import lombok.ast.FloatingPointLiteral;
-import lombok.ast.ForwardingAstVisitor;
-import lombok.ast.IntegralLiteral;
-import lombok.ast.MethodDeclaration;
-import lombok.ast.MethodInvocation;
-import lombok.ast.NullLiteral;
-import lombok.ast.Select;
-import lombok.ast.StrictListAccessor;
-import lombok.ast.StringLiteral;
-import lombok.ast.VariableDefinitionEntry;
-import lombok.ast.VariableReference;
-
 /**
  * Check which looks for problems with formatting strings such as inconsistencies between
  * translations or between string declaration and string usage in Java.
  * <p>
- * TODO: Verify booleans!
  * TODO: Handle Resources.getQuantityString as well
  */
-public class StringFormatDetector extends ResourceXmlDetector implements Detector.JavaScanner {
+public class StringFormatDetector extends ResourceXmlDetector implements JavaPsiScanner {
     private static final Implementation IMPLEMENTATION_XML = new Implementation(
             StringFormatDetector.class,
             Scope.ALL_RESOURCES_SCOPE);
@@ -836,7 +822,7 @@
             // Argument Index
             "(\\d+\\$)?" +                                                      //$NON-NLS-1$
             // Flags
-            "([-+#, 0(\\<]*)?" +                                                //$NON-NLS-1$
+            "([-+#, 0(<]*)?" +                                                  //$NON-NLS-1$
             // Width
             "(\\d+)?" +                                                         //$NON-NLS-1$
             // Precision
@@ -910,7 +896,6 @@
      * {@code seenArguments} parameter is not null, put the indices of any
      * observed arguments into it.
      */
-    @VisibleForTesting
     static int getFormatArgumentCount(@NonNull String s, @Nullable Set<Integer> seenArguments) {
         Matcher matcher = FORMAT.matcher(s);
         int index = 0;
@@ -1032,24 +1017,21 @@
     }
 
     @Override
-    public void visitMethod(@NonNull JavaContext context, @Nullable AstVisitor visitor,
-            @NonNull MethodInvocation node) {
+    public void visitMethod(@NonNull JavaContext context, @Nullable JavaElementVisitor visitor,
+            @NonNull PsiMethodCallExpression node, @NonNull PsiMethod method) {
         if (mFormatStrings == null && !context.getClient().supportsProjectResources()) {
             return;
         }
 
-        ResolvedNode resolved = context.resolve(node);
-        if (!(resolved instanceof ResolvedMethod)) {
-            return;
-        }
-        ResolvedMethod method = (ResolvedMethod) resolved;
-        String methodName = node.astName().astValue();
+        JavaEvaluator evaluator = context.getEvaluator();
+        String methodName = method.getName();
         if (methodName.equals(FORMAT_METHOD)) {
-            if (method.getContainingClass().matches(TYPE_STRING)) {
+            if (evaluator.isMemberInClass(method, TYPE_STRING)) {
                 // Check formatting parameters for
                 //   java.lang.String#format(String format, Object... formatArgs)
                 //   java.lang.String#format(Locale locale, String format, Object... formatArgs)
-                checkFormatCall(context, node, method.getArgumentCount() == 3);
+                checkStringFormatCall(context, method, node,
+                        method.getParameterList().getParametersCount() == 3);
 
                 // TODO: Consider also enforcing
                 // java.util.Formatter#format(String string, Object... formatArgs)
@@ -1067,16 +1049,15 @@
             // also possible that they're looking up strings that are not intended to be used
             // for formatting so while we may want to warn about this it's not necessarily
             // an error.
-            if (method.getArgumentCount() < 2) {
+            if (method.getParameterList().getParametersCount() < 2) {
                 return;
             }
 
-            ResolvedClass containingClass = method.getContainingClass();
-            if (containingClass.isSubclassOf(CLASS_RESOURCES, false) ||
-                    containingClass.isSubclassOf(CLASS_CONTEXT, false) ||
-                    containingClass.isSubclassOf(CLASS_FRAGMENT, false) ||
-                    containingClass.isSubclassOf(CLASS_V4_FRAGMENT, false)) {
-                checkFormatCall(context, node, false);
+            if (evaluator.isMemberInSubClassOf(method, CLASS_RESOURCES, false) ||
+                    evaluator.isMemberInSubClassOf(method, CLASS_CONTEXT, false) ||
+                    evaluator.isMemberInSubClassOf(method, CLASS_FRAGMENT, false) ||
+                    evaluator.isMemberInSubClassOf(method, CLASS_V4_FRAGMENT, false)) {
+                checkStringFormatCall(context, method, node, false);
             }
 
             // TODO: Consider also looking up
@@ -1087,14 +1068,6 @@
         }
     }
 
-    private void checkFormatCall(JavaContext context, MethodInvocation node,
-            boolean specifiesLocale) {
-        lombok.ast.Node current = getParentMethod(node);
-        if (current != null) {
-            checkStringFormatCall(context, current, node, specifiesLocale);
-        }
-    }
-
     /**
      * Checks a String.format call that is using a string that doesn't contain format placeholders.
      * @param context the context to report errors to
@@ -1104,7 +1077,7 @@
      */
     private static void checkNotFormattedHandle(
             JavaContext context,
-            MethodInvocation call,
+            PsiMethodCallExpression call,
             String name,
             Handle handle) {
         Object clientData = handle.getClientData();
@@ -1127,37 +1100,38 @@
     /**
      * Check the given String.format call (with the given arguments) to see if the string format is
      * being used correctly
-     *
-     * @param context         the context to report errors to
-     * @param method          the method containing the {@link String#format} call
-     * @param call            the AST node for the {@link String#format}
-     * @param specifiesLocale whether the first parameter is a locale string, shifting the
-     *                        formatting string to the second argument
+     *  @param context           the context to report errors to
+     * @param calledMethod      the method being called
+     * @param call              the AST node for the {@link String#format}
+     * @param specifiesLocale   whether the first parameter is a locale string, shifting the
      */
     private void checkStringFormatCall(
             JavaContext context,
-            lombok.ast.Node method,
-            MethodInvocation call,
+            PsiMethod calledMethod,
+            PsiMethodCallExpression call,
             boolean specifiesLocale) {
 
-        StrictListAccessor<Expression, MethodInvocation> args = call.astArguments();
-        if (args.isEmpty()) {
+        int argIndex = specifiesLocale ? 1 : 0;
+        PsiExpression[] args = call.getArgumentList().getExpressions();
+
+        if (args.length <= argIndex) {
             return;
         }
 
-        StringTracker tracker = new StringTracker(context, method, call, specifiesLocale ? 1 : 0);
-        method.accept(tracker);
-        String name = tracker.getFormatStringName();
-        if (name == null) {
+        PsiExpression argument = args[argIndex];
+        ResourceUrl resource = ResourceEvaluator.getResource(context.getEvaluator(), argument);
+        if (resource == null || resource.framework || resource.type != ResourceType.STRING) {
             return;
         }
 
+        String name = resource.name;
         if (mIgnoreStrings != null && mIgnoreStrings.contains(name)) {
             return;
         }
 
         boolean passingVarArgsArray = false;
-        int callCount = args.size() - 1 - (specifiesLocale ? 1 : 0);
+        int callCount = args.length - 1 - argIndex;
+
         if (callCount == 1) {
             // If instead of a varargs call like
             //    getString(R.string.foo, arg1, arg2, arg3)
@@ -1165,29 +1139,55 @@
             //    getString(R.string.foo, new Object[] { arg1, arg2, arg3 })
             // we'll need to handle that such that we don't think this is a single
             // argument
-            TypeDescriptor type = context.getType(args.last());
-            if (type != null && type.isArray() && !type.isPrimitive()) {
+
+            PsiExpression lastArg = args[args.length - 1];
+            PsiParameterList parameterList = calledMethod.getParameterList();
+            int parameterCount = parameterList.getParametersCount();
+            if (parameterCount > 0 && parameterList.getParameters()[parameterCount - 1].isVarArgs()) {
                 boolean knownArity = false;
-                if (args.last() instanceof ArrayCreation) {
-                    ArrayCreation creation = (ArrayCreation) args.last();
-                    ArrayInitializer initializer = creation.astInitializer();
-                    if (initializer != null) {
-                        callCount = initializer.astExpressions().size();
-                        knownArity = true;
-                    } else if (creation.astDimensions() != null
-                            && creation.astDimensions().size() == 1) {
-                        ArrayDimension first = creation.astDimensions().first();
-                        Expression expression = first.astDimension();
-                        if (expression instanceof IntegralLiteral) {
-                            callCount = ((IntegralLiteral) expression).astIntValue();
-                            knownArity = true;
+
+                boolean argWasReference = false;
+                if (lastArg instanceof PsiReference) {
+                    PsiElement resolved = ((PsiReference) lastArg).resolve();
+
+
+                    if (resolved instanceof PsiVariable) {
+                        PsiExpression initializer = ((PsiVariable) resolved).getInitializer();
+                        if (initializer instanceof PsiNewExpression) {
+                            argWasReference = true;
+                            // Now handled by check below
+                            lastArg = initializer;
                         }
                     }
                 }
-                if (!knownArity) {
-                    return;
+
+                if (lastArg instanceof PsiNewExpression) {
+                    PsiNewExpression newExpression = (PsiNewExpression) lastArg;
+                    PsiArrayInitializerExpression initializer = newExpression.getArrayInitializer();
+                    if (initializer != null) {
+                        callCount = initializer.getInitializers().length;
+                        knownArity = true;
+                    } else {
+                        PsiExpression[] arrayDimensions = newExpression.getArrayDimensions();
+                        if (arrayDimensions.length == 1) {
+                            PsiExpression first = arrayDimensions[0];
+                            if (first instanceof PsiLiteral) {
+                                Object o = ((PsiLiteral)first).getValue();
+                                if (o instanceof Integer) {
+                                    callCount = (Integer)o;
+                                    knownArity = true;
+                                }
+                            }
+                        }
+                    }
+                    if (!knownArity) {
+                        if (!argWasReference) {
+                            return;
+                        }
+                    } else {
+                        passingVarArgsArray = true;
+                    }
                 }
-                passingVarArgsArray = true;
             }
         }
 
@@ -1285,7 +1285,7 @@
                             "Wrong argument count, format string `%1$s` requires `%2$d` but format " +
                             "call supplies `%3$d`",
                             name, count, callCount);
-                    context.report(ARG_TYPES, method, location, message);
+                    context.report(ARG_TYPES, call, location, message);
                     if (reported == null) {
                         reported = Sets.newHashSet();
                     }
@@ -1297,8 +1297,8 @@
                         return;
                     }
                     for (int i = 1; i <= count; i++) {
-                        int argumentIndex = i + (specifiesLocale ? 1 : 0);
-                        Class<?> type = tracker.getArgumentType(argumentIndex);
+                        int argumentIndex = i + argIndex;
+                        PsiType type = args[argumentIndex].getType();
                         if (type != null) {
                             boolean valid = true;
                             String formatType = getFormatArgumentType(s, i);
@@ -1319,7 +1319,7 @@
                                 // unusual and probably not intended.
                                 case 'b':
                                 case 'B':
-                                    valid = type == Boolean.TYPE;
+                                    valid = isBooleanType(type);
                                     break;
 
                                 // Numeric: integer and floats in various formats
@@ -1334,17 +1334,12 @@
                                 case 'G':
                                 case 'a':
                                 case 'A':
-                                    valid = type == Integer.TYPE
-                                            || type == Float.TYPE
-                                            || type == Double.TYPE
-                                            || type == Long.TYPE
-                                            || type == Byte.TYPE
-                                            || type == Short.TYPE;
+                                    valid = isNumericType(type, true);
                                     break;
                                 case 'c':
                                 case 'C':
                                     // Unicode character
-                                    valid = type == Character.TYPE;
+                                    valid = isCharacterType(type);
                                     break;
                                 case 'h':
                                 case 'H': // Hex print of hash code of objects
@@ -1354,25 +1349,28 @@
                                     // numbers since you may have meant more
                                     // specific formatting. Use special issue
                                     // explanation for this?
-                                    valid = type != Boolean.TYPE &&
-                                        !Number.class.isAssignableFrom(type);
+                                    valid = !isBooleanType(type) &&
+                                            !isNumericType(type, false);
                                     break;
                             }
 
                             if (!valid) {
-                                Expression argument = tracker.getArgument(argumentIndex);
-                                Location location = context.getLocation(argument);
+                                Location location = context.getLocation(args[argumentIndex]);
                                 Location secondary = handle.resolve();
                                 secondary.setMessage("Conflicting argument declaration here");
                                 location.setSecondary(secondary);
 
+                                String canonicalText = type.getCanonicalText();
+                                canonicalText = canonicalText.substring(
+                                        canonicalText.lastIndexOf('.') + 1);
+
                                 String message = String.format(
                                         "Wrong argument type for formatting argument '#%1$d' " +
                                         "in `%2$s`: conversion is '`%3$s`', received `%4$s` " +
                                         "(argument #%5$d in method call)",
-                                        i, name, formatType, type.getSimpleName(),
+                                        i, name, formatType, canonicalText,
                                         argumentIndex + 1);
-                                context.report(ARG_TYPES, method, location, message);
+                                context.report(ARG_TYPES, call, location, message);
                                 if (reported == null) {
                                     reported = Sets.newHashSet();
                                 }
@@ -1385,409 +1383,61 @@
         }
     }
 
-    private static boolean isLocaleReference(@Nullable TypeDescriptor reference) {
-        return reference != null && isLocaleReference(reference.getName());
+    private static boolean isCharacterType(PsiType type) {
+        //return PsiType.CHAR.isAssignableFrom(type);
+        if (type == PsiType.CHAR) {
+            return true;
+        }
+        if (type instanceof PsiClassType) {
+            String fqn = type.getCanonicalText();
+            return TYPE_CHARACTER_WRAPPER.equals(fqn);
+        }
+
+        return false;
     }
 
-    private static boolean isLocaleReference(@Nullable String typeName) {
-        return typeName != null && (typeName.equals("Locale")            //$NON-NLS-1$
-                || typeName.equals("java.util.Locale"));                 //$NON-NLS-1$
+    private static boolean isBooleanType(PsiType type) {
+        //return PsiType.BOOLEAN.isAssignableFrom(type);
+        if (type == PsiType.BOOLEAN) {
+            return true;
+        }
+        if (type instanceof PsiClassType) {
+            String fqn = type.getCanonicalText();
+            return TYPE_BOOLEAN_WRAPPER.equals(fqn);
+        }
+
+        return false;
     }
 
-    /** Returns the parent method of the given AST node */
-    @Nullable
-    public static lombok.ast.Node getParentMethod(@NonNull lombok.ast.Node node) {
-        lombok.ast.Node current = node.getParent();
-        while (current != null
-                && !(current instanceof MethodDeclaration)
-                && !(current instanceof ConstructorDeclaration)) {
-            current = current.getParent();
+    //PsiType:java.lang.Boolean
+    private static boolean isNumericType(@NonNull PsiType type, boolean allowBigNumbers) {
+        if (PsiType.INT.equals(type)
+                || PsiType.FLOAT.equals(type)
+                || PsiType.DOUBLE.equals(type)
+                || PsiType.LONG.equals(type)
+                || PsiType.BYTE.equals(type)
+                || PsiType.SHORT.equals(type)) {
+            return true;
         }
 
-        return current;
-    }
-
-    /** Returns the resource name corresponding to the first argument in the given call */
-    @Nullable
-    public static String getResourceForFirstArg(
-            @NonNull lombok.ast.Node method,
-            @NonNull lombok.ast.Node call) {
-        assert call instanceof MethodInvocation || call instanceof ConstructorInvocation;
-        StringTracker tracker = new StringTracker(null, method, call, 0);
-        method.accept(tracker);
-
-        return tracker.getFormatStringName();
-    }
-
-    /** Returns the resource name corresponding to the given argument in the given call */
-    @Nullable
-    public static String getResourceArg(
-            @NonNull lombok.ast.Node method,
-            @NonNull lombok.ast.Node call,
-            int argIndex) {
-        assert call instanceof MethodInvocation || call instanceof ConstructorInvocation;
-        StringTracker tracker = new StringTracker(null, method, call, argIndex);
-        method.accept(tracker);
-
-        return tracker.getFormatStringName();
-    }
-
-    /**
-     * Given a variable reference, finds the original R.string value corresponding to it.
-     * For example:
-     * <pre>
-     * {@code
-     *  String target = "World";
-     *  String hello = getResources().getString(R.string.hello);
-     *  String output = String.format(hello, target);
-     * }
-     * </pre>
-     *
-     * Given the {@code String.format} call, we want to find out what R.string resource
-     * corresponds to the first argument, in this case {@code R.string.hello}.
-     * To do this, we look for R.string references, and track those through assignments
-     * until we reach the target node.
-     * <p>
-     * In addition, it also does some primitive type tracking such that it (in some cases)
-     * can answer questions about the types of variables. This allows it to check whether
-     * certain argument types are valid. Note however that it does not do full-blown
-     * type analysis by checking method call signatures and so on.
-     */
-    private static class StringTracker extends ForwardingAstVisitor {
-        /** Method we're searching within */
-        private final lombok.ast.Node mTop;
-        /** The argument index in the method we're targeting */
-        private final int mArgIndex;
-        /** Map from variable name to corresponding string resource name */
-        private final Map<String, String> mMap = new HashMap<String, String>();
-        /** Map from variable name to corresponding type */
-        private final Map<String, Class<?>> mTypes = new HashMap<String, Class<?>>();
-        /** The AST node for the String.format we're interested in */
-        private final lombok.ast.Node mTargetNode;
-        private boolean mDone;
-        @Nullable
-        private JavaContext mContext;
-
-        /**
-         * Result: the name of the string resource being passed to the
-         * String.format, if any
-         */
-        private String mName;
-
-        public StringTracker(@Nullable JavaContext context, lombok.ast.Node top, lombok.ast.Node targetNode, int argIndex) {
-            mContext = context;
-            mTop = top;
-            mArgIndex = argIndex;
-            mTargetNode = targetNode;
-        }
-
-        public String getFormatStringName() {
-            return mName;
-        }
-
-        /** Returns the argument type of the given formatting argument of the
-         * target node. Note: This is in the formatting string, which is one higher
-         * than the String.format parameter number, since the first argument is the
-         * formatting string itself.
-         *
-         * @param argument the argument number
-         * @return the class (such as {@link Integer#TYPE} etc) or null if not known
-         */
-        public Class<?> getArgumentType(int argument) {
-            Expression arg = getArgument(argument);
-            if (arg != null) {
-                // Look up type based on the source code literals
-                Class<?> type = getType(arg);
-                if (type != null) {
-                    return type;
-                }
-
-                // If the AST supports type resolution, use that for other types
-                // of expressions
-                if (mContext != null) {
-                    return getTypeClass(mContext.getType(arg));
+        if (type instanceof PsiClassType) {
+            String fqn = type.getCanonicalText();
+            if (TYPE_INTEGER_WRAPPER.equals(fqn)
+                    || TYPE_FLOAT_WRAPPER.equals(fqn)
+                    || TYPE_DOUBLE_WRAPPER.equals(fqn)
+                    || TYPE_LONG_WRAPPER.equals(fqn)
+                    || TYPE_BYTE_WRAPPER.equals(fqn)
+                    || TYPE_SHORT_WRAPPER.equals(fqn)) {
+                return true;
+            }
+            if (allowBigNumbers) {
+                if ("java.math.BigInteger".equals(fqn) ||
+                        "java.math.BigDecimal".equals(fqn)) {
+                    return true;
                 }
             }
-
-            return null;
         }
 
-        private static Class<?> getTypeClass(@Nullable TypeDescriptor type) {
-            if (type != null) {
-                return getTypeClass(type.getName());
-            }
-            return null;
-        }
-
-        private static Class<?> getTypeClass(@Nullable String fqcn) {
-            if (fqcn == null) {
-                return null;
-            } else if (fqcn.equals(TYPE_STRING) || fqcn.equals("String")) {   //$NON-NLS-1$
-                return String.class;
-            } else if (fqcn.equals(TYPE_INT)) {
-                return Integer.TYPE;
-            } else if (fqcn.equals(TYPE_BOOLEAN)) {
-                return Boolean.TYPE;
-            } else if (fqcn.equals(TYPE_NULL)) {
-                return Object.class;
-            } else if (fqcn.equals(TYPE_LONG)) {
-                return Long.TYPE;
-            } else if (fqcn.equals(TYPE_FLOAT)) {
-                return Float.TYPE;
-            } else if (fqcn.equals(TYPE_DOUBLE)) {
-                return Double.TYPE;
-            } else if (fqcn.equals(TYPE_CHAR)) {
-                return Character.TYPE;
-            } else if (fqcn.equals("BigDecimal")                //$NON-NLS-1$
-                    || fqcn.equals("java.math.BigDecimal")) {   //$NON-NLS-1$
-                return Float.TYPE;
-            } else if (fqcn.equals("BigInteger")                //$NON-NLS-1$
-                    || fqcn.equals("java.math.BigInteger")) {   //$NON-NLS-1$
-                return Integer.TYPE;
-            } else if (fqcn.equals(TYPE_OBJECT)) {
-              return null;
-            } else if (fqcn.startsWith("java.lang.")) {
-              if (fqcn.equals("java.lang.Integer")
-                  || fqcn.equals("java.lang.Short")
-                  || fqcn.equals("java.lang.Byte")
-                  || fqcn.equals("java.lang.Long")) {
-                return Integer.TYPE;
-              } else if (fqcn.equals("java.lang.Float")
-                || fqcn.equals("java.lang.Double")) {
-                return Float.TYPE;
-              } else {
-                return null;
-              }
-            } else if (fqcn.equals(TYPE_BYTE)) {
-              return Byte.TYPE;
-            } else if (fqcn.equals(TYPE_SHORT)) {
-                return Short.TYPE;
-            } else {
-                return null;
-            }
-        }
-
-        public Expression getArgument(int argument) {
-            if (!(mTargetNode instanceof MethodInvocation)) {
-                return null;
-            }
-            MethodInvocation call = (MethodInvocation) mTargetNode;
-            StrictListAccessor<Expression, MethodInvocation> args = call.astArguments();
-            if (argument >= args.size()) {
-                return null;
-            }
-
-            Iterator<Expression> iterator = args.iterator();
-            int index = 0;
-            while (iterator.hasNext()) {
-                Expression arg = iterator.next();
-                if (index++ == argument) {
-                    return arg;
-                }
-            }
-
-            return null;
-        }
-
-        @Override
-        public boolean visitNode(lombok.ast.Node node) {
-            return mDone || super.visitNode(node);
-
-        }
-
-        @Override
-        public boolean visitVariableReference(VariableReference node) {
-            if (node.astIdentifier().astValue().equals(R_CLASS) &&   //$NON-NLS-1$
-                    node.getParent() instanceof Select &&
-                    node.getParent().getParent() instanceof Select) {
-
-                // See if we're on the right hand side of an assignment
-                lombok.ast.Node current = node.getParent().getParent();
-                String reference = ((Select) current).astIdentifier().astValue();
-                lombok.ast.Node prev = current;
-                while (current != mTop && !(current instanceof VariableDefinitionEntry)) {
-                    if (current == mTargetNode) {
-                        // Make sure the reference we found was part of the
-                        // target parameter (e.g. for a string format check,
-                        // the actual formatting string, not one of the arguments
-                        // supplied to the formatting string)
-                        boolean isParameterArg = false;
-                        Iterator<Expression> iterator = null;
-                        if (mTargetNode instanceof MethodInvocation) {
-                            MethodInvocation call = (MethodInvocation) mTargetNode;
-                            iterator = call.astArguments().iterator();
-                        } else if (mTargetNode instanceof ConstructorInvocation) {
-                            ConstructorInvocation call = (ConstructorInvocation) mTargetNode;
-                            iterator = call.astArguments().iterator();
-                        }
-                        if (iterator != null) {
-                            Expression arg = null;
-                            for (int i = 0; i <= mArgIndex; i++) {
-                                if (iterator.hasNext()) {
-                                    arg = iterator.next();
-                                } else {
-                                    arg = null;
-                                }
-                            }
-                            if (arg == prev) {
-                                isParameterArg = true;
-                            }
-                        } else {
-                            // Constructor?
-                            isParameterArg = true;
-                        }
-                        if (isParameterArg) {
-                            mName = reference;
-                            mDone = true;
-                            return false;
-                        }
-                    }
-                    prev = current;
-                    current = current.getParent();
-                }
-                if (current instanceof VariableDefinitionEntry) {
-                    VariableDefinitionEntry entry = (VariableDefinitionEntry) current;
-                    String variable = entry.astName().astValue();
-                    mMap.put(variable, reference);
-                }
-            }
-
-            return false;
-        }
-
-        @Nullable
-        private Expression getTargetArgument() {
-            Iterator<Expression> iterator;
-            if (mTargetNode instanceof MethodInvocation) {
-                iterator = ((MethodInvocation) mTargetNode).astArguments().iterator();
-            } else if (mTargetNode instanceof ConstructorInvocation) {
-                iterator = ((ConstructorInvocation) mTargetNode).astArguments().iterator();
-            } else {
-                return null;
-            }
-            int i = 0;
-            while (i < mArgIndex && iterator.hasNext()) {
-                iterator.next();
-                i++;
-            }
-            if (iterator.hasNext()) {
-                Expression next = iterator.next();
-                if (next != null && mContext != null && iterator.hasNext()) {
-                    TypeDescriptor type = mContext.getType(next);
-                    if (isLocaleReference(type)) {
-                        next = iterator.next();
-                    } else if (type == null
-                            && next.toString().startsWith("Locale.")) { //$NON-NLS-1$
-                        next = iterator.next();
-                    }
-                }
-                return next;
-            }
-
-            return null;
-        }
-
-        @Override
-        public boolean visitMethodInvocation(MethodInvocation node) {
-            if (node == mTargetNode) {
-                Expression arg = getTargetArgument();
-                if (arg instanceof VariableReference) {
-                      VariableReference reference = (VariableReference) arg;
-                      String variable = reference.astIdentifier().astValue();
-                      mName = mMap.get(variable);
-                      mDone = true;
-                      return true;
-                }
-            }
-
-            // Is this a getString() call? On a resource object? If so,
-            // promote the resource argument up to the left hand side
-            return super.visitMethodInvocation(node);
-        }
-
-        @Override
-        public boolean visitConstructorInvocation(ConstructorInvocation node) {
-            if (node == mTargetNode) {
-                Expression arg = getTargetArgument();
-                if (arg instanceof VariableReference) {
-                      VariableReference reference = (VariableReference) arg;
-                      String variable = reference.astIdentifier().astValue();
-                      mName = mMap.get(variable);
-                      mDone = true;
-                      return true;
-                }
-            }
-
-            // Is this a getString() call? On a resource object? If so,
-            // promote the resource argument up to the left hand side
-            return super.visitConstructorInvocation(node);
-        }
-
-        @Override
-        public boolean visitVariableDefinitionEntry(VariableDefinitionEntry node) {
-            String name = node.astName().astValue();
-            Expression rhs = node.astInitializer();
-            Class<?> type = getType(rhs);
-            if (type != null) {
-                mTypes.put(name, type);
-            } else {
-                // Make sure we're not visiting the String.format node itself. If you have
-                //    msg = String.format("%1$s", msg)
-                // then we'd be wiping out the type of "msg" before visiting the
-                // String.format call!
-                if (rhs != mTargetNode) {
-                    mTypes.remove(name);
-                }
-            }
-
-            return super.visitVariableDefinitionEntry(node);
-        }
-
-        private Class<?> getType(Expression expression) {
-            if (expression == null) {
-              return null;
-            }
-
-            if (expression instanceof VariableReference) {
-                VariableReference reference = (VariableReference) expression;
-                String variable = reference.astIdentifier().astValue();
-                Class<?> type = mTypes.get(variable);
-                if (type != null) {
-                    return type;
-                }
-            } else if (expression instanceof MethodInvocation) {
-                MethodInvocation method = (MethodInvocation) expression;
-                String methodName = method.astName().astValue();
-                if (methodName.equals(GET_STRING_METHOD)) {
-                    return String.class;
-                }
-            } else if (expression instanceof StringLiteral) {
-                return String.class;
-            } else if (expression instanceof IntegralLiteral) {
-                return Integer.TYPE;
-            } else if (expression instanceof FloatingPointLiteral) {
-                return Float.TYPE;
-            } else if (expression instanceof CharLiteral) {
-                return Character.TYPE;
-            } else if (expression instanceof BooleanLiteral) {
-                return Boolean.TYPE;
-            } else if (expression instanceof NullLiteral) {
-                return Object.class;
-            }
-
-            if (mContext != null) {
-                TypeDescriptor type = mContext.getType(expression);
-                if (type != null) {
-                    Class<?> typeClass = getTypeClass(type);
-                    if (typeClass != null) {
-                        return typeClass;
-                    } else {
-                        return Object.class;
-                    }
-                }
-            }
-
-            return null;
-        }
+        return false;
     }
 }
diff --git a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/SupportAnnotationDetector.java b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/SupportAnnotationDetector.java
index 8056895..93b93e6 100644
--- a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/SupportAnnotationDetector.java
+++ b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/SupportAnnotationDetector.java
@@ -21,7 +21,6 @@
 import static com.android.SdkConstants.ATTR_VALUE;
 import static com.android.SdkConstants.CLASS_INTENT;
 import static com.android.SdkConstants.INT_DEF_ANNOTATION;
-import static com.android.SdkConstants.R_CLASS;
 import static com.android.SdkConstants.STRING_DEF_ANNOTATION;
 import static com.android.SdkConstants.SUPPORT_ANNOTATIONS_PREFIX;
 import static com.android.SdkConstants.TAG_PERMISSION;
@@ -37,10 +36,11 @@
 import static com.android.tools.lint.checks.PermissionFinder.Operation.WRITE;
 import static com.android.tools.lint.checks.PermissionRequirement.ATTR_PROTECTION_LEVEL;
 import static com.android.tools.lint.checks.PermissionRequirement.VALUE_DANGEROUS;
-import static com.android.tools.lint.client.api.JavaParser.TYPE_INT;
-import static com.android.tools.lint.client.api.JavaParser.TYPE_LONG;
-import static com.android.tools.lint.detector.api.JavaContext.findSurroundingMethod;
-import static com.android.tools.lint.detector.api.JavaContext.getParentOfType;
+import static com.android.tools.lint.checks.PermissionRequirement.getAnnotationBooleanValue;
+import static com.android.tools.lint.checks.PermissionRequirement.getAnnotationDoubleValue;
+import static com.android.tools.lint.checks.PermissionRequirement.getAnnotationLongValue;
+import static com.android.tools.lint.checks.PermissionRequirement.getAnnotationStringValue;
+import static com.android.tools.lint.detector.api.LintUtils.skipParentheses;
 
 import com.android.annotations.NonNull;
 import com.android.annotations.Nullable;
@@ -49,74 +49,79 @@
 import com.android.tools.lint.checks.PermissionFinder.Operation;
 import com.android.tools.lint.checks.PermissionFinder.Result;
 import com.android.tools.lint.checks.PermissionHolder.SetPermissionLookup;
-import com.android.tools.lint.client.api.JavaParser;
-import com.android.tools.lint.client.api.JavaParser.ResolvedAnnotation;
-import com.android.tools.lint.client.api.JavaParser.ResolvedClass;
-import com.android.tools.lint.client.api.JavaParser.ResolvedField;
-import com.android.tools.lint.client.api.JavaParser.ResolvedMethod;
-import com.android.tools.lint.client.api.JavaParser.ResolvedNode;
-import com.android.tools.lint.client.api.JavaParser.TypeDescriptor;
+import com.android.tools.lint.client.api.JavaEvaluator;
 import com.android.tools.lint.client.api.LintClient;
 import com.android.tools.lint.detector.api.Category;
 import com.android.tools.lint.detector.api.ConstantEvaluator;
 import com.android.tools.lint.detector.api.Detector;
+import com.android.tools.lint.detector.api.Detector.JavaPsiScanner;
 import com.android.tools.lint.detector.api.Implementation;
 import com.android.tools.lint.detector.api.Issue;
 import com.android.tools.lint.detector.api.JavaContext;
 import com.android.tools.lint.detector.api.Project;
+import com.android.tools.lint.detector.api.ResourceEvaluator;
 import com.android.tools.lint.detector.api.Scope;
 import com.android.tools.lint.detector.api.Severity;
 import com.android.tools.lint.detector.api.TextFormat;
 import com.android.utils.XmlUtils;
 import com.google.common.base.Joiner;
 import com.google.common.collect.Sets;
+import com.intellij.psi.JavaElementVisitor;
+import com.intellij.psi.JavaRecursiveElementVisitor;
+import com.intellij.psi.JavaTokenType;
+import com.intellij.psi.PsiAnnotation;
+import com.intellij.psi.PsiAnnotationMemberValue;
+import com.intellij.psi.PsiArrayInitializerExpression;
+import com.intellij.psi.PsiArrayInitializerMemberValue;
+import com.intellij.psi.PsiArrayType;
+import com.intellij.psi.PsiAssignmentExpression;
+import com.intellij.psi.PsiBinaryExpression;
+import com.intellij.psi.PsiCall;
+import com.intellij.psi.PsiCallExpression;
+import com.intellij.psi.PsiCatchSection;
+import com.intellij.psi.PsiClass;
+import com.intellij.psi.PsiClassType;
+import com.intellij.psi.PsiConditionalExpression;
+import com.intellij.psi.PsiDeclarationStatement;
+import com.intellij.psi.PsiDisjunctionType;
+import com.intellij.psi.PsiElement;
+import com.intellij.psi.PsiEnumConstant;
+import com.intellij.psi.PsiExpression;
+import com.intellij.psi.PsiExpressionList;
+import com.intellij.psi.PsiExpressionStatement;
+import com.intellij.psi.PsiField;
+import com.intellij.psi.PsiJavaCodeReferenceElement;
+import com.intellij.psi.PsiLiteral;
+import com.intellij.psi.PsiLocalVariable;
+import com.intellij.psi.PsiMethod;
+import com.intellij.psi.PsiMethodCallExpression;
+import com.intellij.psi.PsiModifierList;
+import com.intellij.psi.PsiNameValuePair;
+import com.intellij.psi.PsiNewExpression;
+import com.intellij.psi.PsiParameter;
+import com.intellij.psi.PsiParameterList;
+import com.intellij.psi.PsiParenthesizedExpression;
+import com.intellij.psi.PsiPrefixExpression;
+import com.intellij.psi.PsiReference;
+import com.intellij.psi.PsiReferenceExpression;
+import com.intellij.psi.PsiStatement;
+import com.intellij.psi.PsiTryStatement;
+import com.intellij.psi.PsiType;
+import com.intellij.psi.tree.IElementType;
+import com.intellij.psi.util.PsiTreeUtil;
 
 import org.w3c.dom.Document;
 import org.w3c.dom.Element;
+import org.w3c.dom.Node;
 import org.w3c.dom.NodeList;
 
 import java.io.File;
-import java.lang.reflect.Modifier;
 import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
 import java.util.EnumSet;
-import java.util.Iterator;
 import java.util.List;
-import java.util.ListIterator;
 import java.util.Locale;
 import java.util.Set;
 
-import lombok.ast.ArrayCreation;
-import lombok.ast.ArrayInitializer;
-import lombok.ast.AstVisitor;
-import lombok.ast.BinaryExpression;
-import lombok.ast.BinaryOperator;
-import lombok.ast.Catch;
-import lombok.ast.ConstructorInvocation;
-import lombok.ast.EnumConstant;
-import lombok.ast.Expression;
-import lombok.ast.ExpressionStatement;
-import lombok.ast.FloatingPointLiteral;
-import lombok.ast.ForwardingAstVisitor;
-import lombok.ast.InlineIfExpression;
-import lombok.ast.IntegralLiteral;
-import lombok.ast.MethodDeclaration;
-import lombok.ast.MethodInvocation;
-import lombok.ast.Node;
-import lombok.ast.NullLiteral;
-import lombok.ast.Select;
-import lombok.ast.Statement;
-import lombok.ast.StringLiteral;
-import lombok.ast.Try;
-import lombok.ast.TypeReference;
-import lombok.ast.UnaryExpression;
-import lombok.ast.UnaryOperator;
-import lombok.ast.VariableDeclaration;
-import lombok.ast.VariableDefinition;
-import lombok.ast.VariableDefinitionEntry;
-import lombok.ast.VariableReference;
-
 /**
  * Looks up annotations on method calls and enforces the various things they
  * express, e.g. for {@code @CheckReturn} it makes sure the return value is used,
@@ -126,7 +131,7 @@
  * specifying toInclusive without setting to, combining @ColorInt with any @ResourceTypeRes,
  * using @CheckResult on a void method, etc.
  */
-public class SupportAnnotationDetector extends Detector implements Detector.JavaScanner {
+public class SupportAnnotationDetector extends Detector implements JavaPsiScanner {
 
     public static final Implementation IMPLEMENTATION
             = new Implementation(SupportAnnotationDetector.class, Scope.JAVA_FILE_SCOPE);
@@ -278,14 +283,6 @@
     public static final String ATTR_CONDITIONAL = "conditional";
 
     /**
-     * Marker ResourceType used to signify that an expression is of type {@code @ColorInt},
-     * which isn't actually a ResourceType but one we want to specifically compare with.
-     * We're using {@link ResourceType#PUBLIC} because that one won't appear in the R
-     * class (and ResourceType is an enum we can't just create new constants for.)
-     */
-    public static final ResourceType COLOR_INT_MARKER_TYPE = ResourceType.PUBLIC;
-
-    /**
      * Constructs a new {@link SupportAnnotationDetector} check
      */
     public SupportAnnotationDetector() {
@@ -293,31 +290,38 @@
 
     private void checkMethodAnnotation(
             @NonNull JavaContext context,
-            @NonNull ResolvedMethod method,
-            @NonNull Node node,
-            @NonNull ResolvedAnnotation annotation) {
-        String signature = annotation.getSignature();
+            @NonNull PsiMethod method,
+            @NonNull PsiElement call,
+            @NonNull PsiAnnotation annotation) {
+        String signature = annotation.getQualifiedName();
+        if (signature == null) {
+            return;
+        }
         if (CHECK_RESULT_ANNOTATION.equals(signature)
-                || signature.endsWith(".CheckReturnValue")) { // support findbugs annotation too
-            checkResult(context, node, annotation);
+                // support findbugs annotation too
+                || signature.endsWith(".CheckReturnValue")) {
+            checkResult(context, call, annotation);
         } else if (signature.equals(PERMISSION_ANNOTATION)) {
             PermissionRequirement requirement = PermissionRequirement.create(context, annotation);
-            checkPermission(context, node, method, null, requirement);
+            checkPermission(context, call, method, null, requirement);
         } else if (signature.endsWith(THREAD_SUFFIX)
                 && signature.startsWith(SUPPORT_ANNOTATIONS_PREFIX)) {
-            checkThreading(context, node, method, signature);
+            checkThreading(context, call, method, signature);
         }
     }
 
     private void checkParameterAnnotations(
             @NonNull JavaContext context,
-            @NonNull Node argument,
-            @NonNull Node call,
-            @NonNull ResolvedMethod method,
-            @NonNull Iterable<ResolvedAnnotation> annotations) {
+            @NonNull PsiExpression argument,
+            @NonNull PsiElement call,
+            @NonNull PsiMethod method,
+            @NonNull PsiAnnotation[] annotations) {
         boolean handledResourceTypes = false;
-        for (ResolvedAnnotation annotation : annotations) {
-            String signature = annotation.getSignature();
+        for (PsiAnnotation annotation : annotations) {
+            String signature = annotation.getQualifiedName();
+            if (signature == null) {
+                continue;
+            }
 
             if (COLOR_INT_ANNOTATION.equals(signature)) {
                 checkColor(context, argument);
@@ -340,7 +344,7 @@
                 // don't want to (a) create redundant warnings or (b) work harder than we
                 // have to
                 if (signature.equals(INT_DEF_ANNOTATION)) {
-                    boolean flag = annotation.getValue(TYPE_DEF_FLAG_ATTRIBUTE) == Boolean.TRUE;
+                    boolean flag = getAnnotationBooleanValue(annotation, TYPE_DEF_FLAG_ATTRIBUTE) == Boolean.TRUE;
                     checkTypeDefConstant(context, annotation, argument, null, flag,
                             annotations);
                 } else if (signature.equals(STRING_DEF_ANNOTATION)) {
@@ -355,9 +359,9 @@
                     // Handle all resource type annotations in one go: there could be multiple
                     // resource type annotations specified on the same element; we need to
                     // know about them all up front.
-                    for (ResolvedAnnotation a : annotations) {
-                        String s = a.getSignature();
-                        if (s.endsWith(RES_SUFFIX)) {
+                    for (PsiAnnotation a : annotations) {
+                        String s = a.getQualifiedName();
+                        if (s != null && s.endsWith(RES_SUFFIX)) {
                             String typeString = s.substring(SUPPORT_ANNOTATIONS_PREFIX.length(),
                                     s.length() - RES_SUFFIX.length()).toLowerCase(Locale.US);
                             ResourceType type = ResourceType.getEnum(typeString);
@@ -384,27 +388,24 @@
 
     private static EnumSet<ResourceType> getAnyRes() {
         EnumSet<ResourceType> types = EnumSet.allOf(ResourceType.class);
-        types.remove(COLOR_INT_MARKER_TYPE);
+        types.remove(ResourceEvaluator.COLOR_INT_MARKER_TYPE);
         return types;
     }
 
     private void checkParameterPermission(
             @NonNull JavaContext context,
             @NonNull String signature,
-            @NonNull Node call,
-            @NonNull ResolvedMethod method,
-            @NonNull Node argument) {
+            @NonNull PsiElement call,
+            @NonNull PsiMethod method,
+            @NonNull PsiExpression argument) {
         Operation operation = null;
         if (signature.equals(PERMISSION_ANNOTATION_READ)) {
             operation = READ;
         } else if (signature.equals(PERMISSION_ANNOTATION_WRITE)) {
             operation = WRITE;
         } else {
-            TypeDescriptor type = context.getType(argument);
-            if (type == null) {
-                return;
-            }
-            if (type.matchesSignature(CLASS_INTENT)) {
+            PsiType type = argument.getType();
+            if (type != null && CLASS_INTENT.equals(type.getCanonicalText())) {
                 operation = ACTION;
             }
         }
@@ -417,27 +418,32 @@
         }
     }
 
-    private static void checkColor(@NonNull JavaContext context, @NonNull Node argument) {
-        if (argument instanceof InlineIfExpression) {
-            InlineIfExpression expression = (InlineIfExpression) argument;
-            checkColor(context, expression.astIfTrue());
-            checkColor(context, expression.astIfFalse());
+    private static void checkColor(@NonNull JavaContext context, @NonNull PsiElement argument) {
+        if (argument instanceof PsiConditionalExpression) {
+            PsiConditionalExpression expression = (PsiConditionalExpression) argument;
+            if (expression.getThenExpression() != null) {
+                checkColor(context, expression.getThenExpression());
+            }
+            if (expression.getElseExpression() != null) {
+                checkColor(context, expression.getElseExpression());
+            }
             return;
         }
 
-        EnumSet<ResourceType> types = getResourceTypes(context, argument);
+        EnumSet<ResourceType> types = ResourceEvaluator.getResourceTypes(context.getEvaluator(),
+                argument);
 
-        if (types != null && types.contains(ResourceType.COLOR)
+        if (types != null && types.contains(COLOR)
                 && !isIgnoredInIde(COLOR_USAGE, context, argument)) {
             String message = String.format(
                     "Should pass resolved color instead of resource id here: " +
-                            "`getResources().getColor(%1$s)`", argument.toString());
+                            "`getResources().getColor(%1$s)`", argument.getText());
             context.report(COLOR_USAGE, argument, context.getLocation(argument), message);
         }
     }
 
     private static boolean isIgnoredInIde(@NonNull Issue issue, @NonNull JavaContext context,
-            @NonNull Node node) {
+            @NonNull PsiElement node) {
         // Historically, the IDE would treat *all* support annotation warnings as
         // handled by the id "ResourceType", so look for that id too for issues
         // deliberately suppressed prior to Android Studio 2.0.
@@ -449,8 +455,8 @@
 
     private void checkPermission(
             @NonNull JavaContext context,
-            @NonNull Node node,
-            @Nullable ResolvedMethod method,
+            @NonNull PsiElement node,
+            @Nullable PsiMethod method,
             @Nullable Result result,
             @NonNull PermissionRequirement requirement) {
         if (requirement.isConditional()) {
@@ -472,7 +478,12 @@
                     operation = result.operation;
                 } else {
                     assert method != null;
-                    name = method.getContainingClass().getSimpleName() + "." + method.getName();
+                    PsiClass containingClass = method.getContainingClass();
+                    if (containingClass != null) {
+                        name = containingClass.getName() + "." + method.getName();
+                    } else {
+                        name = method.getName();
+                    }
                     operation = Operation.CALL;
                 }
                 String message = getMissingPermissionMessage(requirement, name, permissions,
@@ -481,48 +492,13 @@
             }
         } else if (requirement.isRevocable(permissions) &&
                 context.getMainProject().getTargetSdkVersion().getFeatureLevel() >= 23) {
-            // Ensure that the caller is handling a security exception
-            // First check to see if we're inside a try/catch which catches a SecurityException
-            // (or some wider exception than that). Check for nested try/catches too.
-            boolean handlesMissingPermission = false;
-            Node parent = node;
-            while (true) {
-                Try tryCatch = getParentOfType(parent, Try.class);
-                if (tryCatch == null) {
-                    break;
-                } else {
-                    JavaParser parser = context.getParser();
-                    for (Catch aCatch : tryCatch.astCatches()) {
-                        for (TypeDescriptor catchType : parser.getCatchTypes(context, aCatch)) {
-                            if (isSecurityException(context,
-                                    catchType)) {
-                                handlesMissingPermission = true;
-                                break;
-                            }
-                        }
-                    }
-                    parent = tryCatch;
-                }
-            }
 
-            // If not, check to see if the method itself declares that it throws a
-            // SecurityException or something wider.
-            if (!handlesMissingPermission) {
-                MethodDeclaration declaration = getParentOfType(parent, MethodDeclaration.class);
-                if (declaration != null) {
-                    for (TypeReference typeReference : declaration.astThrownTypeReferences()) {
-                        if (isSecurityException(context, context.getType(typeReference))) {
-                            handlesMissingPermission = true;
-                            break;
-                        }
-                    }
-                }
-            }
+            boolean handlesMissingPermission = handlesSecurityException(node);
 
             // If not, check to see if the code is deliberately checking to see if the
             // given permission is available.
             if (!handlesMissingPermission) {
-                Node methodNode = JavaContext.findSurroundingMethod(node);
+                PsiMethod methodNode = PsiTreeUtil.getParentOfType(node, PsiMethod.class, true);
                 if (methodNode != null) {
                     CheckPermissionVisitor visitor = new CheckPermissionVisitor(node);
                     methodNode.accept(visitor);
@@ -537,25 +513,63 @@
         }
     }
 
+    private static boolean handlesSecurityException(@NonNull PsiElement node) {
+        // Ensure that the caller is handling a security exception
+        // First check to see if we're inside a try/catch which catches a SecurityException
+        // (or some wider exception than that). Check for nested try/catches too.
+        PsiElement parent = node;
+        while (true) {
+            PsiTryStatement tryCatch = PsiTreeUtil
+                    .getParentOfType(parent, PsiTryStatement.class, true);
+            if (tryCatch == null) {
+                break;
+            } else {
+                for (PsiCatchSection psiCatchSection : tryCatch.getCatchSections()) {
+                    PsiType type = psiCatchSection.getCatchType();
+                    if (isSecurityException(type)) {
+                        return true;
+                    }
+                }
+
+                parent = tryCatch;
+            }
+        }
+
+        // If not, check to see if the method itself declares that it throws a
+        // SecurityException or something wider.
+        PsiMethod declaration = PsiTreeUtil.getParentOfType(parent, PsiMethod.class, false);
+        if (declaration != null) {
+            for (PsiClassType type : declaration.getThrowsList().getReferencedTypes()) {
+                if (isSecurityException(type)) {
+                    return true;
+                }
+            }
+        }
+
+        return false;
+    }
+
     @NonNull
     private static PermissionHolder addLocalPermissions(
             @NonNull JavaContext context,
             @NonNull PermissionHolder permissions,
-            @NonNull Node node) {
+            @NonNull PsiElement node) {
         // Accumulate @RequirePermissions available in the local context
-        Node methodNode = JavaContext.findSurroundingMethod(node);
-        if (methodNode == null) {
+        PsiMethod method = PsiTreeUtil.getParentOfType(node, PsiMethod.class, true);
+        if (method == null) {
             return permissions;
         }
-        ResolvedNode resolved = context.resolve(methodNode);
-        if (!(resolved instanceof ResolvedMethod)) {
-            return permissions;
+        PsiAnnotation annotation = method.getModifierList().findAnnotation(PERMISSION_ANNOTATION);
+        permissions = mergeAnnotationPermissions(context, permissions, annotation);
+
+        PsiClass containingClass = method.getContainingClass();
+        if (containingClass != null) {
+            PsiModifierList modifierList = containingClass.getModifierList();
+            if (modifierList != null) {
+                annotation = modifierList.findAnnotation(PERMISSION_ANNOTATION);
+                permissions = mergeAnnotationPermissions(context, permissions, annotation);
+            }
         }
-        ResolvedMethod method = (ResolvedMethod) resolved;
-        ResolvedAnnotation annotation = method.getAnnotation(PERMISSION_ANNOTATION);
-        permissions = mergeAnnotationPermissions(context, permissions, annotation);
-        annotation = method.getContainingClass().getAnnotation(PERMISSION_ANNOTATION);
-        permissions = mergeAnnotationPermissions(context, permissions, annotation);
         return permissions;
     }
 
@@ -563,7 +577,7 @@
     private static PermissionHolder mergeAnnotationPermissions(
             @NonNull JavaContext context,
             @NonNull PermissionHolder permissions,
-            @Nullable ResolvedAnnotation annotation) {
+            @Nullable PsiAnnotation annotation) {
         if (annotation != null) {
             PermissionRequirement requirement = PermissionRequirement.create(context, annotation);
             permissions = SetPermissionLookup.join(permissions, requirement);
@@ -599,33 +613,35 @@
      * or whether the check return value (== PERMISSION_GRANTED vs != PERMISSION_GRANTED)
      * is handled correctly, etc.
      */
-    private static class CheckPermissionVisitor extends ForwardingAstVisitor {
+    private static class CheckPermissionVisitor extends JavaRecursiveElementVisitor {
         private boolean mChecksPermission;
         private boolean mDone;
-        private final Node mTarget;
+        private final PsiElement mTarget;
 
-        public CheckPermissionVisitor(@NonNull Node target) {
+        public CheckPermissionVisitor(@NonNull PsiElement target) {
             mTarget = target;
         }
 
         @Override
-        public boolean visitNode(Node node) {
-            return mDone;
+        public void visitElement(PsiElement element) {
+            if (!mDone) {
+                super.visitElement(element);
+            }
         }
 
         @Override
-        public boolean visitMethodInvocation(MethodInvocation node) {
+        public void visitMethodCallExpression(PsiMethodCallExpression node) {
             if (node == mTarget) {
                 mDone = true;
             }
 
-            String name = node.astName().astValue();
-            if ((name.startsWith("check") || name.startsWith("enforce"))
+            String name = node.getMethodExpression().getReferenceName();
+            if (name != null
+                    && (name.startsWith("check") || name.startsWith("enforce"))
                     && name.endsWith("Permission")) {
                 mChecksPermission = true;
                 mDone = true;
             }
-            return super.visitMethodInvocation(node);
         }
 
         public boolean checksPermission() {
@@ -634,12 +650,23 @@
     }
 
     private static boolean isSecurityException(
-            @NonNull JavaContext context,
-            @Nullable TypeDescriptor type) {
-        // In earlier versions we checked not just for java.lang.SecurityException but
-        // any super type as well, however that probably hides warnings in cases where
-        // users don't want that; see http://b.android.com/182165
-        return type != null && type.matchesSignature("java.lang.SecurityException");
+            @Nullable PsiType type) {
+        if (type instanceof PsiClassType) {
+            PsiClass cls = ((PsiClassType) type).resolve();
+            // In earlier versions we checked not just for java.lang.SecurityException but
+            // any super type as well, however that probably hides warnings in cases where
+            // users don't want that; see http://b.android.com/182165
+            //return context.getEvaluator().extendsClass(cls, "java.lang.SecurityException", false);
+            return cls != null && "java.lang.SecurityException".equals(cls.getQualifiedName());
+        } else if (type instanceof PsiDisjunctionType) {
+            for (PsiType disjunction : ((PsiDisjunctionType)type).getDisjunctions()) {
+                if (isSecurityException(disjunction)) {
+                    return true;
+                }
+            }
+        }
+
+        return false;
     }
 
     private PermissionHolder mPermissions;
@@ -685,8 +712,8 @@
         }
         NodeList children = root.getChildNodes();
         for (int i = 0, n = children.getLength(); i < n; i++) {
-            org.w3c.dom.Node item = children.item(i);
-            if (item.getNodeType() != org.w3c.dom.Node.ELEMENT_NODE) {
+            Node item = children.item(i);
+            if (item.getNodeType() != Node.ELEMENT_NODE) {
                 continue;
             }
             String nodeName = item.getNodeName();
@@ -712,11 +739,11 @@
         }
     }
 
-    private static void checkResult(@NonNull JavaContext context, @NonNull Node node,
-            @NonNull ResolvedAnnotation annotation) {
-        if (node.getParent() instanceof ExpressionStatement) {
+    private static void checkResult(@NonNull JavaContext context, @NonNull PsiElement node,
+            @NonNull PsiAnnotation annotation) {
+        if (skipParentheses(node.getParent()) instanceof PsiExpressionStatement) {
             String methodName = JavaContext.getMethodName(node);
-            Object suggested = annotation.getValue(ATTR_SUGGEST);
+            String suggested = getAnnotationStringValue(annotation, ATTR_SUGGEST);
 
             // Failing to check permissions is a potential security issue (and had an existing
             // dedicated issue id before which people may already have configured with a
@@ -739,7 +766,7 @@
                 // with "#" etc?
                 message = String.format(
                         "The result of `%1$s` is not used; did you mean to call `%2$s`?",
-                        methodName, suggested.toString());
+                        methodName, suggested);
             }
             context.report(issue, node, context.getLocation(node), message);
         }
@@ -747,13 +774,14 @@
 
     private static void checkThreading(
             @NonNull JavaContext context,
-            @NonNull Node node,
-            @NonNull ResolvedMethod method,
+            @NonNull PsiElement node,
+            @NonNull PsiMethod method,
             @NonNull String annotation) {
         String threadContext = getThreadContext(context, node);
         if (threadContext != null && !isCompatibleThread(threadContext, annotation)
                 && !isIgnoredInIde(THREAD, context, node)) {
-            String message = String.format("Method %1$s must be called from the `%2$s` thread, currently inferred thread is `%3$s` thread",
+            String message = String.format("%1$s %2$s must be called from the `%3$s` thread, currently inferred thread is `%4$s` thread",
+                    method.isConstructor() ? "Constructor" : "Method",
                     method.getName(), describeThread(annotation), describeThread(threadContext));
             context.report(THREAD, node, context.getLocation(node), message);
         }
@@ -800,36 +828,35 @@
     /** Attempts to infer the current thread context at the site of the given method call */
     @Nullable
     private static String getThreadContext(@NonNull JavaContext context,
-            @NonNull Node methodCall) {
-        Node node = findSurroundingMethod(methodCall);
-        if (node != null) {
-            ResolvedNode resolved = context.resolve(node);
-            if (resolved instanceof ResolvedMethod) {
-                ResolvedMethod method = (ResolvedMethod) resolved;
-                ResolvedClass cls = method.getContainingClass();
+            @NonNull PsiElement methodCall) {
+        PsiMethod method = PsiTreeUtil.getParentOfType(methodCall, PsiMethod.class, true);
+        if (method != null) {
+            PsiClass cls = method.getContainingClass();
 
-                while (method != null) {
-                    for (ResolvedAnnotation annotation : method.getAnnotations()) {
-                        String name = annotation.getSignature();
-                        if (name.startsWith(SUPPORT_ANNOTATIONS_PREFIX)
+            while (method != null) {
+                for (PsiAnnotation annotation : method.getModifierList().getAnnotations()) {
+                    String name = annotation.getQualifiedName();
+                    if (name != null && name.startsWith(SUPPORT_ANNOTATIONS_PREFIX)
+                            && name.endsWith(THREAD_SUFFIX)) {
+                        return name;
+                    }
+                }
+                method = context.getEvaluator().getSuperMethod(method);
+            }
+
+            // See if we're extending a class with a known threading context
+            while (cls != null) {
+                PsiModifierList modifierList = cls.getModifierList();
+                if (modifierList != null) {
+                    for (PsiAnnotation annotation : modifierList.getAnnotations()) {
+                        String name = annotation.getQualifiedName();
+                        if (name != null && name.startsWith(SUPPORT_ANNOTATIONS_PREFIX)
                                 && name.endsWith(THREAD_SUFFIX)) {
                             return name;
                         }
                     }
-                    method = method.getSuperMethod();
                 }
-
-                // See if we're extending a class with a known threading context
-                while (cls != null) {
-                    for (ResolvedAnnotation annotation : cls.getAnnotations()) {
-                        String name = annotation.getSignature();
-                        if (name.startsWith(SUPPORT_ANNOTATIONS_PREFIX)
-                                && name.endsWith(THREAD_SUFFIX)) {
-                            return name;
-                        }
-                    }
-                    cls = cls.getSuperClass();
-                }
+                cls = cls.getSuperClass();
             }
         }
 
@@ -842,30 +869,50 @@
         return null;
     }
 
-    private static boolean isNumber(@NonNull Node argument) {
-        return argument instanceof IntegralLiteral || argument instanceof UnaryExpression
-                && ((UnaryExpression) argument).astOperator() == UnaryOperator.UNARY_MINUS
-                && ((UnaryExpression) argument).astOperand() instanceof IntegralLiteral;
+    private static boolean isNumber(@NonNull PsiElement argument) {
+        if (argument instanceof PsiLiteral) {
+            Object value = ((PsiLiteral) argument).getValue();
+            return value instanceof Number;
+        } else if (argument instanceof PsiPrefixExpression) {
+            PsiPrefixExpression expression = (PsiPrefixExpression) argument;
+            PsiExpression operand = expression.getOperand();
+            return operand != null && isNumber(operand);
+        } else {
+            return false;
+        }
     }
 
-    private static boolean isZero(@NonNull Node argument) {
-        return argument instanceof IntegralLiteral
-                && ((IntegralLiteral) argument).astIntValue() == 0;
+    private static boolean isZero(@NonNull PsiElement argument) {
+        if (argument instanceof PsiLiteral) {
+            Object value = ((PsiLiteral) argument).getValue();
+            return value instanceof Number && ((Number)value).intValue() == 0;
+        }
+        return false;
     }
 
-    private static boolean isMinusOne(@NonNull Node argument) {
-        return argument instanceof UnaryExpression
-                && ((UnaryExpression) argument).astOperator() == UnaryOperator.UNARY_MINUS
-                && ((UnaryExpression) argument).astOperand() instanceof IntegralLiteral
-                && ((IntegralLiteral) ((UnaryExpression) argument).astOperand()).astIntValue()
-                == 1;
+    private static boolean isMinusOne(@NonNull PsiElement argument) {
+        if (argument instanceof PsiPrefixExpression) {
+            PsiPrefixExpression expression = (PsiPrefixExpression) argument;
+            PsiExpression operand = expression.getOperand();
+            if (operand instanceof PsiLiteral &&
+                    expression.getOperationTokenType() == JavaTokenType.MINUS) {
+                Object value = ((PsiLiteral) operand).getValue();
+                return value instanceof Number && ((Number) value).intValue() == 1;
+            } else {
+                return false;
+            }
+        } else {
+            return false;
+        }
     }
 
     private static void checkResourceType(
             @NonNull JavaContext context,
-            @NonNull Node argument,
+            @NonNull PsiElement argument,
             @NonNull EnumSet<ResourceType> expectedType) {
-        EnumSet<ResourceType> actual = getResourceTypes(context, argument);
+        EnumSet<ResourceType> actual = ResourceEvaluator.getResourceTypes(context.getEvaluator(),
+                argument);
+
         if (actual == null && (!isNumber(argument) || isZero(argument) || isMinusOne(argument)) ) {
             return;
         } else if (actual != null && (!Sets.intersection(actual, expectedType).isEmpty()
@@ -879,11 +926,12 @@
         }
 
         String message;
-        if (actual != null && actual.size() == 1 && actual.contains(COLOR_INT_MARKER_TYPE)) {
+        if (actual != null && actual.size() == 1 && actual.contains(
+                ResourceEvaluator.COLOR_INT_MARKER_TYPE)) {
             message = "Expected a color resource id (`R.color.`) but received an RGB integer";
-        } else if (expectedType.contains(COLOR_INT_MARKER_TYPE)) {
+        } else if (expectedType.contains(ResourceEvaluator.COLOR_INT_MARKER_TYPE)) {
             message = String.format("Should pass resolved color instead of resource id here: " +
-                    "`getResources().getColor(%1$s)`", argument.toString());
+                    "`getResources().getColor(%1$s)`", argument.getText());
         } else if (expectedType.size() < ResourceType.getNames().length - 1) {
             message = String.format("Expected resource of type %1$s",
                     Joiner.on(" or ").join(expectedType));
@@ -893,119 +941,11 @@
         context.report(RESOURCE_TYPE, argument, context.getLocation(argument), message);
     }
 
-    @Nullable
-    private static EnumSet<ResourceType> getResourceTypes(@NonNull JavaContext context,
-            @NonNull Node argument) {
-        if (argument instanceof Select) {
-            Select node = (Select) argument;
-            if (node.astOperand() instanceof Select) {
-                Select select = (Select) node.astOperand();
-                if (select.astOperand() instanceof Select) { // android.R....
-                    Select innerSelect = (Select) select.astOperand();
-                    if (innerSelect.astIdentifier().astValue().equals(R_CLASS)) {
-                        String typeName = select.astIdentifier().astValue();
-                        ResourceType type = ResourceType.getEnum(typeName);
-                        return type != null ? EnumSet.of(type) : null;
-                    }
-                }
-                if (select.astOperand() instanceof VariableReference) {
-                    VariableReference reference = (VariableReference) select.astOperand();
-                    if (reference.astIdentifier().astValue().equals(R_CLASS)) {
-                        String typeName = select.astIdentifier().astValue();
-                        ResourceType type = ResourceType.getEnum(typeName);
-                        return type != null ? EnumSet.of(type) : null;
-                    }
-                }
-            }
-
-            // Arbitrary packages -- android.R.type.name, foo.bar.R.type.name
-            if (node.astIdentifier().astValue().equals(R_CLASS)) {
-                Node parent = node.getParent();
-                if (parent instanceof Select) {
-                    Node grandParent = parent.getParent();
-                    if (grandParent instanceof Select) {
-                        Select select = (Select) grandParent;
-                        Expression typeOperand = select.astOperand();
-                        if (typeOperand instanceof Select) {
-                            Select typeSelect = (Select) typeOperand;
-                            String typeName = typeSelect.astIdentifier().astValue();
-                            ResourceType type = ResourceType.getEnum(typeName);
-                            return type != null ? EnumSet.of(type) : null;
-                        }
-                    }
-                }
-            }
-        } else if (argument instanceof VariableReference) {
-            Statement statement = getParentOfType(argument, Statement.class, false);
-            if (statement != null) {
-                ListIterator<Node> iterator = statement.getParent().getChildren().listIterator();
-                while (iterator.hasNext()) {
-                    if (iterator.next() == statement) {
-                        if (iterator.hasPrevious()) { // should always be true
-                            iterator.previous();
-                        }
-                        break;
-                    }
-                }
-
-                String targetName = ((VariableReference)argument).astIdentifier().astValue();
-                while (iterator.hasPrevious()) {
-                    Node previous = iterator.previous();
-                    if (previous instanceof VariableDeclaration) {
-                        VariableDeclaration declaration = (VariableDeclaration) previous;
-                        VariableDefinition definition = declaration.astDefinition();
-                        for (VariableDefinitionEntry entry : definition
-                                .astVariables()) {
-                            if (entry.astInitializer() != null
-                                    && entry.astName().astValue().equals(targetName)) {
-                                return getResourceTypes(context, entry.astInitializer());
-                            }
-                        }
-                    } else if (previous instanceof ExpressionStatement) {
-                        ExpressionStatement expressionStatement = (ExpressionStatement) previous;
-                        Expression expression = expressionStatement.astExpression();
-                        if (expression instanceof BinaryExpression &&
-                                ((BinaryExpression) expression).astOperator()
-                                        == BinaryOperator.ASSIGN) {
-                            BinaryExpression binaryExpression = (BinaryExpression) expression;
-                            if (targetName.equals(binaryExpression.astLeft().toString())) {
-                                return getResourceTypes(context, binaryExpression.astRight());
-                            }
-                        }
-                    }
-                }
-            }
-        } else if (argument instanceof MethodInvocation) {
-            ResolvedNode resolved = context.resolve(argument);
-            if (resolved != null) {
-                for (ResolvedAnnotation annotation : resolved.getAnnotations()) {
-                    String signature = annotation.getSignature();
-                    if (signature.equals(COLOR_INT_ANNOTATION)) {
-                        return EnumSet.of(COLOR_INT_MARKER_TYPE);
-                    }
-                    if (signature.endsWith(RES_SUFFIX)
-                            && signature.startsWith(SUPPORT_ANNOTATIONS_PREFIX)) {
-                        String typeString = signature.substring(SUPPORT_ANNOTATIONS_PREFIX.length(),
-                                signature.length() - RES_SUFFIX.length()).toLowerCase(Locale.US);
-                        ResourceType type = ResourceType.getEnum(typeString);
-                        if (type != null) {
-                            return EnumSet.of(type);
-                        } else if (typeString.equals("any")) { // @AnyRes
-                            return getAnyRes();
-                        }
-                    }
-                }
-            }
-        }
-
-        return null;
-    }
-
     private static void checkIntRange(
             @NonNull JavaContext context,
-            @NonNull ResolvedAnnotation annotation,
-            @NonNull Node argument,
-            @NonNull Iterable<ResolvedAnnotation> allAnnotations) {
+            @NonNull PsiAnnotation annotation,
+            @NonNull PsiElement argument,
+            @NonNull PsiAnnotation[] allAnnotations) {
         String message = getIntRangeError(context, annotation, argument);
         if (message != null) {
             if (findIntDef(allAnnotations) != null) {
@@ -1026,22 +966,21 @@
     @Nullable
     private static String getIntRangeError(
             @NonNull JavaContext context,
-            @NonNull ResolvedAnnotation annotation,
-            @NonNull Node argument) {
-        if (argument instanceof ArrayCreation) {
-            ArrayCreation creation = (ArrayCreation)argument;
-            ArrayInitializer initializer = creation.astInitializer();
+            @NonNull PsiAnnotation annotation,
+            @NonNull PsiElement argument) {
+        if (argument instanceof PsiNewExpression) {
+            PsiNewExpression newExpression = (PsiNewExpression) argument;
+            PsiArrayInitializerExpression initializer = newExpression.getArrayInitializer();
             if (initializer != null) {
-                for (Expression expression : initializer.astExpressions()) {
+                for (PsiExpression expression : initializer.getInitializers()) {
                     String error = getIntRangeError(context, annotation, expression);
                     if (error != null) {
                         return error;
                     }
                 }
             }
-
-            return null;
         }
+
         Object object = ConstantEvaluator.evaluate(context, argument);
         if (!(object instanceof Number)) {
             return null;
@@ -1077,8 +1016,8 @@
 
     private static void checkFloatRange(
             @NonNull JavaContext context,
-            @NonNull ResolvedAnnotation annotation,
-            @NonNull Node argument) {
+            @NonNull PsiAnnotation annotation,
+            @NonNull PsiElement argument) {
         Object object = ConstantEvaluator.evaluate(context, argument);
         if (!(object instanceof Number)) {
             return;
@@ -1101,7 +1040,7 @@
      */
     @Nullable
     private static String getFloatRangeError(double value, double from, double to,
-            boolean fromInclusive, boolean toInclusive, @NonNull Node node) {
+            boolean fromInclusive, boolean toInclusive, @NonNull PsiElement node) {
         if (!((fromInclusive && value >= from || !fromInclusive && value > from) &&
                 (toInclusive && value <= to || !toInclusive && value < to))) {
             StringBuilder sb = new StringBuilder(20);
@@ -1148,12 +1087,12 @@
                 sb.append(Double.toString(to));
             }
             sb.append(" (was ");
-            if (node instanceof FloatingPointLiteral || node instanceof IntegralLiteral) {
+            if (node instanceof PsiLiteral) {
                 // Use source text instead to avoid rounding errors involved in conversion, e.g
                 //    Error: Value must be > 2.5 (was 2.490000009536743) [Range]
                 //    printAtLeastExclusive(2.49f); // ERROR
                 //                          ~~~~~
-                String str = node.toString();
+                String str = node.getText();
                 if (str.endsWith("f") || str.endsWith("F")) {
                     str = str.substring(0, str.length() - 1);
                 }
@@ -1169,26 +1108,33 @@
 
     private static void checkSize(
             @NonNull JavaContext context,
-            @NonNull ResolvedAnnotation annotation,
-            @NonNull Node argument) {
+            @NonNull PsiAnnotation annotation,
+            @NonNull PsiElement argument) {
         int actual;
-        if (argument instanceof StringLiteral) {
-            // Check string length
-            StringLiteral literal = (StringLiteral) argument;
-            String s = literal.astValue();
-            actual = s.length();
-        } else if (argument instanceof ArrayCreation) {
-            ArrayCreation literal = (ArrayCreation) argument;
-            ArrayInitializer initializer = literal.astInitializer();
-            if (initializer == null) {
+        boolean isString = false;
+
+        // TODO: Collections syntax, e.g. Arrays.asList => param count, emptyList=0, singleton=1, etc
+        // TODO: Flow analysis
+        // No flow analysis for this check yet, only checking literals passed in as parameters
+
+        if (argument instanceof PsiNewExpression) {
+            PsiNewExpression newExpression = (PsiNewExpression) argument;
+            PsiArrayInitializerExpression initializer = newExpression.getArrayInitializer();
+            if (initializer != null) {
+                PsiExpression[] initializers = initializer.getInitializers();
+                actual = initializers.length;
+            } else {
                 return;
             }
-            actual = initializer.astExpressions().size();
         } else {
-            // TODO: Collections syntax, e.g. Arrays.asList => param count, emptyList=0, singleton=1, etc
-            // TODO: Flow analysis
-            // No flow analysis for this check yet, only checking literals passed in as parameters
-            return;
+            Object object = ConstantEvaluator.evaluate(context, argument);
+            // Check string length
+            if (object instanceof String) {
+                actual = ((String)object).length();
+                isString = true;
+            } else {
+                return;
+            }
         }
         long exact = getLongAttribute(annotation, ATTR_VALUE, -1);
         long min = getLongAttribute(annotation, ATTR_MIN, Long.MIN_VALUE);
@@ -1196,7 +1142,6 @@
         long multiple = getLongAttribute(annotation, ATTR_MULTIPLE, 1);
 
         String unit;
-        boolean isString = argument instanceof StringLiteral;
         if (isString) {
             unit = "length";
         } else {
@@ -1242,10 +1187,10 @@
     }
 
     @Nullable
-    private static ResolvedAnnotation findIntRange(
-            @NonNull Iterable<ResolvedAnnotation> annotations) {
-        for (ResolvedAnnotation annotation : annotations) {
-            if (INT_RANGE_ANNOTATION.equals(annotation.getName())) {
+    private static PsiAnnotation findIntRange(
+            @NonNull PsiAnnotation[] annotations) {
+        for (PsiAnnotation annotation : annotations) {
+            if (INT_RANGE_ANNOTATION.equals(annotation.getQualifiedName())) {
                 return annotation;
             }
         }
@@ -1254,10 +1199,9 @@
     }
 
     @Nullable
-    static ResolvedAnnotation findIntDef(
-            @NonNull Iterable<ResolvedAnnotation> annotations) {
-        for (ResolvedAnnotation annotation : annotations) {
-            if (INT_DEF_ANNOTATION.equals(annotation.getName())) {
+    static PsiAnnotation findIntDef(@NonNull PsiAnnotation[] annotations) {
+        for (PsiAnnotation annotation : annotations) {
+            if (INT_DEF_ANNOTATION.equals(annotation.getQualifiedName())) {
                 return annotation;
             }
         }
@@ -1267,97 +1211,84 @@
 
     private static void checkTypeDefConstant(
             @NonNull JavaContext context,
-            @NonNull ResolvedAnnotation annotation,
-            @NonNull Node argument,
-            @Nullable Node errorNode,
+            @NonNull PsiAnnotation annotation,
+            @Nullable PsiElement argument,
+            @Nullable PsiElement errorNode,
             boolean flag,
-            @NonNull Iterable<ResolvedAnnotation> allAnnotations) {
-        if (argument instanceof NullLiteral) {
-            // Accepted for @StringDef
+            @NonNull PsiAnnotation[] allAnnotations) {
+        if (argument == null) {
             return;
         }
-
-        if (argument instanceof StringLiteral) {
-            StringLiteral string = (StringLiteral) argument;
-            checkTypeDefConstant(context, annotation, argument, errorNode, false, string.astValue(),
-                    allAnnotations);
-        } else if (argument instanceof IntegralLiteral) {
-            IntegralLiteral literal = (IntegralLiteral) argument;
-            int value = literal.astIntValue();
-            if (flag && value == 0) {
-                // Accepted for a flag @IntDef
+        if (argument instanceof PsiLiteral) {
+            Object value = ((PsiLiteral) argument).getValue();
+            if (value == null) {
+                // Accepted for @StringDef
+                //noinspection UnnecessaryReturnStatement
                 return;
-            }
-
-            ResolvedAnnotation rangeAnnotation = findIntRange(allAnnotations);
-            if (rangeAnnotation != null) {
-                // Allow @IntRange on this number
-                if (getIntRangeError(context, rangeAnnotation, literal) == null) {
+            } else if (value instanceof String) {
+                String string = (String) value;
+                checkTypeDefConstant(context, annotation, argument, errorNode, false, string,
+                        allAnnotations);
+            } else if (value instanceof Integer || value instanceof Long) {
+                long v = value instanceof Long ? ((Long) value) : ((Integer) value).longValue();
+                if (flag && v == 0) {
+                    // Accepted for a flag @IntDef
                     return;
                 }
-            }
 
-            checkTypeDefConstant(context, annotation, argument, errorNode, flag, value,
-                    allAnnotations);
+                checkTypeDefConstant(context, annotation, argument, errorNode, flag, value,
+                        allAnnotations);
+            }
         } else if (isMinusOne(argument)) {
             // -1 is accepted unconditionally for flags
             if (!flag) {
-                ResolvedAnnotation rangeAnnotation = findIntRange(allAnnotations);
-                if (rangeAnnotation != null) {
-                    // Allow @IntRange on this number
-                    if (getIntRangeError(context, rangeAnnotation, argument) == null) {
-                        return;
-                    }
-                }
-
                 reportTypeDef(context, annotation, argument, errorNode, allAnnotations);
             }
-        } else if (argument instanceof InlineIfExpression) {
-            InlineIfExpression expression = (InlineIfExpression) argument;
-            if (expression.astIfTrue() != null) {
-                checkTypeDefConstant(context, annotation, expression.astIfTrue(), errorNode, flag,
-                        allAnnotations);
-            }
-            if (expression.astIfFalse() != null) {
-                checkTypeDefConstant(context, annotation, expression.astIfFalse(), errorNode, flag,
-                        allAnnotations);
-            }
-        } else if (argument instanceof UnaryExpression) {
-            UnaryExpression expression = (UnaryExpression) argument;
-            UnaryOperator operator = expression.astOperator();
+        } else if (argument instanceof PsiPrefixExpression) {
+            PsiPrefixExpression expression = (PsiPrefixExpression) argument;
             if (flag) {
-                checkTypeDefConstant(context, annotation, expression.astOperand(), errorNode, true,
-                        allAnnotations);
-            } else if (operator == UnaryOperator.BINARY_NOT) {
-                if (isIgnoredInIde(TYPE_DEF, context, expression)) {
-                    return;
-                }
-                context.report(TYPE_DEF, expression, context.getLocation(expression),
-                        "Flag not allowed here");
-            } else if (operator == UnaryOperator.UNARY_MINUS) {
-                ResolvedAnnotation rangeAnnotation = findIntRange(allAnnotations);
-                if (rangeAnnotation != null) {
-                    // Allow @IntRange on this number
-                    if (getIntRangeError(context, rangeAnnotation, argument) == null) {
+                checkTypeDefConstant(context, annotation, expression.getOperand(),
+                        errorNode, true, allAnnotations);
+            } else {
+                IElementType operator = expression.getOperationTokenType();
+                if (operator == JavaTokenType.TILDE) {
+                    if (isIgnoredInIde(TYPE_DEF, context, expression)) {
                         return;
                     }
+                    context.report(TYPE_DEF, expression, context.getLocation(expression),
+                            "Flag not allowed here");
+                } else if (operator == JavaTokenType.MINUS) {
+                    reportTypeDef(context, annotation, argument, errorNode, allAnnotations);
                 }
-
-                reportTypeDef(context, annotation, argument, errorNode, allAnnotations);
             }
-        } else if (argument instanceof BinaryExpression) {
+        } else if (argument instanceof PsiParenthesizedExpression) {
+            PsiExpression expression = ((PsiParenthesizedExpression) argument).getExpression();
+            if (expression != null) {
+                checkTypeDefConstant(context, annotation, expression, errorNode, flag, allAnnotations);
+            }
+        } else if (argument instanceof PsiConditionalExpression) {
+            PsiConditionalExpression expression = (PsiConditionalExpression) argument;
+            if (expression.getThenExpression() != null) {
+                checkTypeDefConstant(context, annotation, expression.getThenExpression(), errorNode, flag,
+                        allAnnotations);
+            }
+            if (expression.getElseExpression() != null) {
+                checkTypeDefConstant(context, annotation, expression.getElseExpression(), errorNode, flag,
+                        allAnnotations);
+            }
+        } else if (argument instanceof PsiBinaryExpression) {
             // If it's ?: then check both the if and else clauses
-            BinaryExpression expression = (BinaryExpression) argument;
+            PsiBinaryExpression expression = (PsiBinaryExpression) argument;
             if (flag) {
-                checkTypeDefConstant(context, annotation, expression.astLeft(), errorNode, true,
+                checkTypeDefConstant(context, annotation, expression.getLOperand(), errorNode, true,
                         allAnnotations);
-                checkTypeDefConstant(context, annotation, expression.astRight(), errorNode, true,
+                checkTypeDefConstant(context, annotation, expression.getROperand(), errorNode, true,
                         allAnnotations);
             } else {
-                BinaryOperator operator = expression.astOperator();
-                if (operator == BinaryOperator.BITWISE_AND
-                        || operator == BinaryOperator.BITWISE_OR
-                        || operator == BinaryOperator.BITWISE_XOR) {
+                IElementType operator = expression.getOperationTokenType();
+                if (operator == JavaTokenType.AND
+                        || operator == JavaTokenType.OR
+                        || operator == JavaTokenType.XOR) {
                     if (isIgnoredInIde(TYPE_DEF, context, expression)) {
                         return;
                     }
@@ -1365,82 +1296,86 @@
                             "Flag not allowed here");
                 }
             }
-        } else if (argument instanceof ArrayCreation) {
-            ArrayCreation creation = (ArrayCreation) argument;
-            TypeReference typeReference = creation.astComponentTypeReference();
-            ArrayInitializer initializer = creation.astInitializer();
-            if (initializer != null && (TYPE_INT.equals(typeReference.getTypeName())
-                    || TYPE_LONG.equals(typeReference.getTypeName()))) {
-                for (Expression expression : initializer.astExpressions()) {
-                    checkTypeDefConstant(context, annotation, expression, errorNode, flag,
-                            allAnnotations);
-                }
-            }
-        } else {
-            ResolvedNode resolved = context.resolve(argument);
-            if (resolved instanceof ResolvedField) {
-                ResolvedField field = (ResolvedField) resolved;
-                if (field.getType().isArray()) {
+        } else if (argument instanceof PsiReference) {
+            PsiElement resolved = ((PsiReference) argument).resolve();
+            if (resolved instanceof PsiField) {
+                PsiField field = (PsiField) resolved;
+                if (field.getType() instanceof PsiArrayType) {
                     // It's pointing to an array reference; we can't check these individual
                     // elements (because we can't jump from ResolvedNodes to AST elements; this
                     // is part of the motivation for the PSI change in lint 2.0), but we also
                     // don't want to flag it as invalid.
                     return;
                 }
-                int modifiers = field.getModifiers();
+
                 // If it's a constant (static/final) check that it's one of the allowed ones
-                if ((modifiers & (Modifier.FINAL|Modifier.STATIC))
-                        == (Modifier.FINAL|Modifier.STATIC)) {
+                if (context.getEvaluator().isStatic(field) &&
+                        context.getEvaluator().isFinal(field)) {
                     checkTypeDefConstant(context, annotation, argument,
                             errorNode != null ? errorNode : argument,
                             flag, resolved, allAnnotations);
-                }
-            } else if (argument instanceof VariableReference) {
-                Statement statement = getParentOfType(argument, Statement.class, false);
-                if (statement != null) {
-                    ListIterator<Node> iterator = statement.getParent().getChildren().listIterator();
-                    while (iterator.hasNext()) {
-                        if (iterator.next() == statement) {
-                            if (iterator.hasPrevious()) { // should always be true
-                                iterator.previous();
-                            }
-                            break;
-                        }
-                    }
 
-                    String targetName = ((VariableReference)argument).astIdentifier().astValue();
-                    while (iterator.hasPrevious()) {
-                        Node previous = iterator.previous();
-                        if (previous instanceof VariableDeclaration) {
-                            VariableDeclaration declaration = (VariableDeclaration) previous;
-                            VariableDefinition definition = declaration.astDefinition();
-                            for (VariableDefinitionEntry entry : definition
-                                    .astVariables()) {
-                                if (entry.astInitializer() != null
-                                        && entry.astName().astValue().equals(targetName)) {
+                }
+            } else if (resolved instanceof PsiLocalVariable) {
+                PsiLocalVariable variable = (PsiLocalVariable) resolved;
+                PsiStatement statement = PsiTreeUtil.getParentOfType(argument, PsiStatement.class,
+                        false);
+                if (statement != null) {
+                    PsiStatement prev = PsiTreeUtil.getPrevSiblingOfType(statement,
+                            PsiStatement.class);
+                    String targetName = variable.getName();
+                    if (targetName == null) {
+                        return;
+                    }
+                    while (prev != null) {
+                        if (prev instanceof PsiDeclarationStatement) {
+                            for (PsiElement element : ((PsiDeclarationStatement) prev)
+                                    .getDeclaredElements()) {
+                                if (variable.equals(element)) {
                                     checkTypeDefConstant(context, annotation,
-                                            entry.astInitializer(),
+                                            variable.getInitializer(),
                                             errorNode != null ? errorNode : argument, flag,
                                             allAnnotations);
                                     return;
                                 }
                             }
-                        } else if (previous instanceof ExpressionStatement) {
-                            ExpressionStatement expressionStatement = (ExpressionStatement) previous;
-                            Expression expression = expressionStatement.astExpression();
-                            if (expression instanceof BinaryExpression &&
-                                    ((BinaryExpression) expression).astOperator()
-                                            == BinaryOperator.ASSIGN) {
-                                BinaryExpression binaryExpression = (BinaryExpression) expression;
-                                if (targetName.equals(binaryExpression.astLeft().toString())) {
-                                    checkTypeDefConstant(context, annotation,
-                                            binaryExpression.astRight(),
-                                            errorNode != null ? errorNode : argument, flag,
-                                            allAnnotations);
-                                    return;
+                        } else if (prev instanceof PsiExpressionStatement) {
+                            PsiExpression expression = ((PsiExpressionStatement) prev)
+                                    .getExpression();
+                            if (expression instanceof PsiAssignmentExpression) {
+                                PsiAssignmentExpression assign
+                                        = (PsiAssignmentExpression) expression;
+                                PsiExpression lhs = assign.getLExpression();
+                                if (lhs instanceof PsiReferenceExpression) {
+                                    PsiReferenceExpression reference = (PsiReferenceExpression) lhs;
+                                    if (targetName.equals(reference.getReferenceName()) &&
+                                            reference.getQualifier() == null) {
+                                        checkTypeDefConstant(context, annotation,
+                                                assign.getRExpression(),
+                                                errorNode != null ? errorNode : argument, flag,
+                                                allAnnotations);
+                                        return;
+                                    }
                                 }
                             }
                         }
+                        prev = PsiTreeUtil.getPrevSiblingOfType(prev,
+                                PsiStatement.class);
+                    }
+                }
+            }
+        } else if (argument instanceof PsiNewExpression) {
+            PsiNewExpression newExpression = (PsiNewExpression) argument;
+            PsiArrayInitializerExpression initializer = newExpression.getArrayInitializer();
+            if (initializer != null) {
+                PsiType type = initializer.getType();
+                if (type != null) {
+                    type = type.getDeepComponentType();
+                }
+                if (PsiType.INT.equals(type) || PsiType.LONG.equals(type)) {
+                    for (PsiExpression expression : initializer.getInitializers()) {
+                        checkTypeDefConstant(context, annotation, expression, errorNode, flag,
+                                allAnnotations);
                     }
                 }
             }
@@ -1448,51 +1383,71 @@
     }
 
     private static void checkTypeDefConstant(@NonNull JavaContext context,
-            @NonNull ResolvedAnnotation annotation, @NonNull Node argument,
-            @Nullable Node errorNode, boolean flag, Object value,
-            @NonNull Iterable<ResolvedAnnotation> allAnnotations) {
-        Object allowed = annotation.getValue();
-        if (allowed instanceof Object[]) {
-            Object[] allowedValues = (Object[]) allowed;
-            for (Object o : allowedValues) {
-                if (o.equals(value)) {
-                    return;
-                }
+            @NonNull PsiAnnotation annotation, @NonNull PsiElement argument,
+            @Nullable PsiElement errorNode, boolean flag, Object value,
+            @NonNull PsiAnnotation[] allAnnotations) {
+        PsiAnnotation rangeAnnotation = findIntRange(allAnnotations);
+        if (rangeAnnotation != null) {
+            // Allow @IntRange on this number
+            if (getIntRangeError(context, rangeAnnotation, argument) == null) {
+                return;
             }
+        }
 
-            if (value instanceof ResolvedField) {
-                Node astNode = ((ResolvedField)value).findAstNode();
-                if (astNode instanceof VariableDeclaration) {
-                    VariableDefinition definition = ((VariableDeclaration) astNode).astDefinition();
-                    if (definition.astVariables().size() == 1) {
-                        Expression initializer = definition.astVariables().iterator().next()
-                                .astInitializer();
-                        if (initializer != null) {
-                            checkTypeDefConstant(context, annotation, initializer, errorNode,
-                                    flag, allAnnotations);
-                            return;
-                        }
+        PsiAnnotationMemberValue allowed = getAnnotationValue(annotation);
+        if (allowed == null) {
+            return;
+        }
+
+        if (allowed instanceof PsiArrayInitializerMemberValue) {
+            PsiArrayInitializerMemberValue initializerExpression =
+                    (PsiArrayInitializerMemberValue) allowed;
+            PsiAnnotationMemberValue[] initializers = initializerExpression.getInitializers();
+            for (PsiAnnotationMemberValue expression : initializers) {
+                if (expression instanceof PsiLiteral) {
+                    if (value.equals(((PsiLiteral)expression).getValue())) {
+                        return;
+                    }
+                } else if (expression instanceof PsiReference) {
+                    PsiElement resolved = ((PsiReference) expression).resolve();
+                    if (resolved != null && resolved.equals(value)) {
+                        return;
                     }
                 }
             }
 
-            reportTypeDef(context, argument, errorNode, flag, allowedValues, allAnnotations);
+            if (value instanceof PsiField) {
+                PsiField astNode = (PsiField)value;
+                PsiExpression initializer = astNode.getInitializer();
+                if (initializer != null) {
+                    checkTypeDefConstant(context, annotation, initializer, errorNode,
+                            flag, allAnnotations);
+                    return;
+                }
+            }
+
+            reportTypeDef(context, argument, errorNode, flag,
+                    initializers, allAnnotations);
         }
     }
 
     private static void reportTypeDef(@NonNull JavaContext context,
-            @NonNull ResolvedAnnotation annotation, @NonNull Node argument,
-            @Nullable Node errorNode, @NonNull Iterable<ResolvedAnnotation> allAnnotations) {
-        Object allowed = annotation.getValue();
-        if (allowed instanceof Object[]) {
-            Object[] allowedValues = (Object[]) allowed;
-            reportTypeDef(context, argument, errorNode, false, allowedValues, allAnnotations);
+            @NonNull PsiAnnotation annotation, @NonNull PsiElement argument,
+            @Nullable PsiElement errorNode, @NonNull PsiAnnotation[] allAnnotations) {
+        //    reportTypeDef(context, argument, errorNode, false, allowedValues, allAnnotations);
+        PsiAnnotationMemberValue allowed = getAnnotationValue(annotation);
+        if (allowed instanceof PsiArrayInitializerMemberValue) {
+            PsiArrayInitializerMemberValue initializerExpression =
+                    (PsiArrayInitializerMemberValue) allowed;
+            PsiAnnotationMemberValue[] initializers = initializerExpression.getInitializers();
+            reportTypeDef(context, argument, errorNode, false, initializers, allAnnotations);
         }
     }
 
-    private static void reportTypeDef(@NonNull JavaContext context, @NonNull Node node,
-            @Nullable Node errorNode, boolean flag, @NonNull Object[] allowedValues,
-            @NonNull Iterable<ResolvedAnnotation> allAnnotations) {
+    private static void reportTypeDef(@NonNull JavaContext context, @NonNull PsiElement node,
+            @Nullable PsiElement errorNode, boolean flag,
+            @NonNull PsiAnnotationMemberValue[] allowedValues,
+            @NonNull PsiAnnotation[] allAnnotations) {
         if (errorNode == null) {
             errorNode = node;
         }
@@ -1508,7 +1463,7 @@
             message = "Must be one of: " + values;
         }
 
-        ResolvedAnnotation rangeAnnotation = findIntRange(allAnnotations);
+        PsiAnnotation rangeAnnotation = findIntRange(allAnnotations);
         if (rangeAnnotation != null) {
             // Allow @IntRange on this number
             String rangeError = getIntRangeError(context, rangeAnnotation, node);
@@ -1521,27 +1476,35 @@
         context.report(TYPE_DEF, errorNode, context.getLocation(errorNode), message);
     }
 
-    private static String listAllowedValues(@NonNull Object[] allowedValues) {
+    @Nullable
+    private static PsiAnnotationMemberValue getAnnotationValue(@NonNull PsiAnnotation annotation) {
+        PsiNameValuePair[] attributes = annotation.getParameterList().getAttributes();
+        for (PsiNameValuePair pair : attributes) {
+            if (pair.getName() == null || pair.getName().equals(ATTR_VALUE)) {
+                return pair.getValue();
+            }
+        }
+        return null;
+    }
+
+    private static String listAllowedValues(@NonNull PsiAnnotationMemberValue[] allowedValues) {
         StringBuilder sb = new StringBuilder();
-        for (Object allowedValue : allowedValues) {
-            String s;
-            if (allowedValue instanceof Integer) {
-                s = allowedValue.toString();
-            } else if (allowedValue instanceof ResolvedNode) {
-                ResolvedNode node = (ResolvedNode) allowedValue;
-                if (node instanceof ResolvedField) {
-                    ResolvedField field = (ResolvedField) node;
-                    String containingClassName = field.getContainingClassName();
+        for (PsiAnnotationMemberValue allowedValue : allowedValues) {
+            String s = null;
+            if (allowedValue instanceof PsiReference) {
+                PsiElement resolved = ((PsiReference) allowedValue).resolve();
+                if (resolved instanceof PsiField) {
+                    PsiField field = (PsiField) resolved;
+                    String containingClassName = field.getContainingClass() != null
+                            ? field.getContainingClass().getName() : null;
                     if (containingClassName == null) {
                         continue;
                     }
-                    containingClassName = containingClassName.substring(containingClassName.lastIndexOf('.') + 1);
                     s = containingClassName + "." + field.getName();
-                } else {
-                    s = node.getSignature();
                 }
-            } else {
-                continue;
+            }
+            if (s == null) {
+                s = allowedValue.getText();
             }
             if (sb.length() > 0) {
                 sb.append(", ");
@@ -1551,48 +1514,47 @@
         return sb.toString();
     }
 
-    static double getDoubleAttribute(@NonNull ResolvedAnnotation annotation,
+    static double getDoubleAttribute(@NonNull PsiAnnotation annotation,
             @NonNull String name, double defaultValue) {
-        Object value = annotation.getValue(name);
-        if (value instanceof Number) {
-            return ((Number) value).doubleValue();
+        Double value = getAnnotationDoubleValue(annotation, name);
+        if (value != null) {
+            return value;
         }
 
         return defaultValue;
     }
 
-    static long getLongAttribute(@NonNull ResolvedAnnotation annotation,
+    static long getLongAttribute(@NonNull PsiAnnotation annotation,
             @NonNull String name, long defaultValue) {
-        Object value = annotation.getValue(name);
-        if (value instanceof Number) {
-            return ((Number) value).longValue();
+        Long value = getAnnotationLongValue(annotation, name);
+        if (value != null) {
+            return value;
         }
 
         return defaultValue;
     }
 
-    static boolean getBoolean(@NonNull ResolvedAnnotation annotation,
+    static boolean getBoolean(@NonNull PsiAnnotation annotation,
             @NonNull String name, boolean defaultValue) {
-        Object value = annotation.getValue(name);
-        if (value instanceof Boolean) {
-            return ((Boolean) value);
+        Boolean value = getAnnotationBooleanValue(annotation, name);
+        if (value != null) {
+            return value;
         }
 
         return defaultValue;
     }
 
     @NonNull
-    static Iterable<ResolvedAnnotation> filterRelevantAnnotations(
-            @NonNull Iterable<ResolvedAnnotation> annotations) {
-        List<ResolvedAnnotation> result = null;
-        Iterator<ResolvedAnnotation> iterator = annotations.iterator();
-        int index = 0;
-        while (iterator.hasNext()) {
-            ResolvedAnnotation annotation = iterator.next();
-            index++;
-
-            String signature = annotation.getSignature();
-            if (signature.startsWith("java.")) {
+    static PsiAnnotation[] filterRelevantAnnotations(
+            @NonNull PsiAnnotation[] annotations) {
+        List<PsiAnnotation> result = null;
+        int length = annotations.length;
+        if (length == 0) {
+            return annotations;
+        }
+        for (PsiAnnotation annotation : annotations) {
+            String signature = annotation.getQualifiedName();
+            if (signature == null || signature.startsWith("java.")) {
                 // @Override, @SuppressWarnings etc. Ignore
                 continue;
             }
@@ -1605,11 +1567,11 @@
                 }
 
                 // Common case: there's just one annotation; no need to create a list copy
-                if (!iterator.hasNext() && index == 1) {
+                if (length == 1) {
                     return annotations;
                 }
                 if (result == null) {
-                    result = new ArrayList<ResolvedAnnotation>(2);
+                    result = new ArrayList<PsiAnnotation>(2);
                 }
                 result.add(annotation);
             }
@@ -1619,21 +1581,33 @@
             // annotate it with @IntDef, and then use @foo.bar.Baz in your signatures.
             // Here we want to map from @foo.bar.Baz to the corresponding int def.
             // Don't need to compute this if performing @IntDef or @StringDef lookup
-            ResolvedClass type = annotation.getClassType();
-            if (type != null) {
-                Iterable<ResolvedAnnotation> innerAnnotations = type.getAnnotations();
-                Iterator<ResolvedAnnotation> iterator2 = innerAnnotations.iterator();
-                while (iterator2.hasNext()) {
-                    ResolvedAnnotation inner = iterator2.next();
-                    if (inner.matches(INT_DEF_ANNOTATION)
-                            || inner.matches(PERMISSION_ANNOTATION)
-                            || inner.matches(INT_RANGE_ANNOTATION)
-                            || inner.matches(STRING_DEF_ANNOTATION)) {
-                        if (!iterator.hasNext() && !iterator2.hasNext() && index == 1) {
+            PsiJavaCodeReferenceElement ref = annotation.getNameReferenceElement();
+            if (ref == null) {
+                continue;
+            }
+            PsiElement resolved = ref.resolve();
+            if (!(resolved instanceof PsiClass) || !((PsiClass)resolved).isAnnotationType()) {
+                continue;
+            }
+            PsiClass cls = (PsiClass)resolved;
+            PsiModifierList modifierList = cls.getModifierList();
+            if (modifierList != null) {
+                PsiAnnotation[] innerAnnotations = modifierList.getAnnotations();
+                for (int j = 0; j < innerAnnotations.length; j++) {
+                    PsiAnnotation inner = innerAnnotations[j];
+                    String a = inner.getQualifiedName();
+                    if (a == null) {
+                        continue;
+                    }
+                    if (a.equals(INT_DEF_ANNOTATION)
+                            || a.equals(PERMISSION_ANNOTATION)
+                            || a.equals(INT_RANGE_ANNOTATION)
+                            || a.equals(STRING_DEF_ANNOTATION)) {
+                        if (length == 1 && j == innerAnnotations.length - 1) {
                             return innerAnnotations;
                         }
                         if (result == null) {
-                            result = new ArrayList<ResolvedAnnotation>(2);
+                            result = new ArrayList<PsiAnnotation>(2);
                         }
                         result.add(inner);
                     }
@@ -1641,28 +1615,29 @@
             }
         }
 
-        return result != null ? result : Collections.<ResolvedAnnotation>emptyList();
+        return result != null
+                ? result.toArray(PsiAnnotation.EMPTY_ARRAY) : PsiAnnotation.EMPTY_ARRAY;
     }
 
     // ---- Implements JavaScanner ----
 
     @Override
-    public
-    List<Class<? extends Node>> getApplicableNodeTypes() {
-        //noinspection unchecked
-        return Arrays.<Class<? extends Node>>asList(
-          MethodInvocation.class,
-          ConstructorInvocation.class,
-          EnumConstant.class);
+    public List<Class<? extends PsiElement>> getApplicablePsiTypes() {
+        List<Class<? extends PsiElement>> types = new ArrayList<Class<? extends PsiElement>>(3);
+        types.add(PsiCallExpression.class);
+        //types.add(PsiMethodCallExpression.class);
+        //types.add(PsiNewExpression.class);
+        types.add(PsiEnumConstant.class);
+        return types;
     }
 
     @Nullable
     @Override
-    public AstVisitor createJavaVisitor(@NonNull JavaContext context) {
+    public JavaElementVisitor createPsiVisitor(@NonNull JavaContext context) {
         return new CallVisitor(context);
     }
 
-    private class CallVisitor extends ForwardingAstVisitor {
+    private class CallVisitor extends JavaElementVisitor {
         private final JavaContext mContext;
 
         public CallVisitor(JavaContext context) {
@@ -1670,67 +1645,59 @@
         }
 
         @Override
-        public boolean visitMethodInvocation(@NonNull MethodInvocation call) {
-            ResolvedNode resolved = mContext.resolve(call);
-            if (resolved instanceof ResolvedMethod) {
-                ResolvedMethod method = (ResolvedMethod) resolved;
-                checkCall(call, method);
+        public void visitCallExpression(PsiCallExpression call) {
+            PsiMethod method = call.resolveMethod();
+            if (method != null) {
+                checkCall(method, call);
             }
-
-            return false;
         }
 
         @Override
-        public boolean visitConstructorInvocation(@NonNull ConstructorInvocation call) {
-            ResolvedNode resolved = mContext.resolve(call);
-            if (resolved instanceof ResolvedMethod) {
-                ResolvedMethod method = (ResolvedMethod) resolved;
-                checkCall(call, method);
+        public void visitEnumConstant(PsiEnumConstant call) {
+            PsiMethod method = call.resolveMethod();
+            if (method != null) {
+                checkCall(method, call);
             }
-
-            return false;
         }
 
-        @Override
-        public boolean visitEnumConstant(EnumConstant node) {
-            ResolvedNode resolved = mContext.resolve(node);
-            if (resolved instanceof ResolvedMethod) {
-                ResolvedMethod method = (ResolvedMethod) resolved;
-                checkCall(node, method);
-            }
-
-            return false;
-        }
-
-        private void checkCall(@NonNull Node call, ResolvedMethod method) {
-            Iterable<ResolvedAnnotation> annotations = method.getAnnotations();
+        public void checkCall(PsiMethod method, PsiCall call) {
+            JavaEvaluator evaluator = mContext.getEvaluator();
+            PsiAnnotation[] annotations = evaluator.getAllAnnotations(method, true);
             annotations = filterRelevantAnnotations(annotations);
-            for (ResolvedAnnotation annotation : annotations) {
+            for (PsiAnnotation annotation : annotations) {
                 checkMethodAnnotation(mContext, method, call, annotation);
             }
 
             // Look for annotations on the class as well: these trickle
             // down to all the methods in the class
-            ResolvedClass containingClass = method.getContainingClass();
-            annotations = containingClass.getAnnotations();
-            annotations = filterRelevantAnnotations(annotations);
-            for (ResolvedAnnotation annotation : annotations) {
-                checkMethodAnnotation(mContext, method, call, annotation);
-            }
-
-            Iterator<Expression> arguments = JavaContext.getParameters(call);
-            for (int i = 0, n = method.getArgumentCount();
-                    i < n && arguments.hasNext();
-                    i++) {
-                Expression argument = arguments.next();
-
-                annotations = method.getParameterAnnotations(i);
+            PsiClass containingClass = method.getContainingClass();
+            if (containingClass != null) {
+                annotations = evaluator.getAllAnnotations(containingClass, true);
                 annotations = filterRelevantAnnotations(annotations);
-                checkParameterAnnotations(mContext, argument, call, method, annotations);
+                for (PsiAnnotation annotation : annotations) {
+                    checkMethodAnnotation(mContext, method, call, annotation);
+                }
             }
-            while (arguments.hasNext()) { // last parameter is varargs (same parameter annotations)
-                Expression argument = arguments.next();
-                checkParameterAnnotations(mContext, argument, call, method, annotations);
+
+            PsiExpressionList argumentList = call.getArgumentList();
+            if (argumentList != null) {
+                PsiExpression[] arguments = argumentList.getExpressions();
+                PsiParameterList parameterList = method.getParameterList();
+                PsiParameter[] parameters = parameterList.getParameters();
+                for (int i = 0, n = Math.min(parameters.length, arguments.length);
+                        i < n;
+                        i++) {
+                    PsiExpression argument = arguments[i];
+                    PsiParameter parameter = parameters[i];
+                    annotations = evaluator.getAllAnnotations(parameter, true);
+                    annotations = filterRelevantAnnotations(annotations);
+                    checkParameterAnnotations(mContext, argument, call, method, annotations);
+                }
+                // last parameter is varargs (same parameter annotations)
+                for (int i = parameters.length; i < arguments.length; i++) {
+                    PsiExpression argument = arguments[i];
+                    checkParameterAnnotations(mContext, argument, call, method, annotations);
+                }
             }
         }
     }
diff --git a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/TitleDetector.java b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/TitleDetector.java
index a8d6a5e..16952e8 100644
--- a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/TitleDetector.java
+++ b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/TitleDetector.java
@@ -26,13 +26,11 @@
 import com.android.annotations.Nullable;
 import com.android.resources.ResourceFolderType;
 import com.android.tools.lint.detector.api.Category;
-import com.android.tools.lint.detector.api.Detector.JavaScanner;
 import com.android.tools.lint.detector.api.Implementation;
 import com.android.tools.lint.detector.api.Issue;
 import com.android.tools.lint.detector.api.ResourceXmlDetector;
 import com.android.tools.lint.detector.api.Scope;
 import com.android.tools.lint.detector.api.Severity;
-import com.android.tools.lint.detector.api.Speed;
 import com.android.tools.lint.detector.api.XmlContext;
 
 import org.w3c.dom.Element;
@@ -43,7 +41,7 @@
 /**
  * Check which makes sure menu items specify a title
  */
-public class TitleDetector extends ResourceXmlDetector implements JavaScanner {
+public class TitleDetector extends ResourceXmlDetector {
     /** The main issue discovered by this detector */
     public static final Issue ISSUE = Issue.create(
             "MenuTitle", //$NON-NLS-1$
@@ -80,12 +78,6 @@
         return folderType == ResourceFolderType.MENU;
     }
 
-    @NonNull
-    @Override
-    public Speed getSpeed() {
-        return Speed.FAST;
-    }
-
     @Override
     @Nullable
     public Collection<String> getApplicableElements() {
diff --git a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/ToastDetector.java b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/ToastDetector.java
index 40c01c2..3d20be6 100644
--- a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/ToastDetector.java
+++ b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/ToastDetector.java
@@ -19,29 +19,28 @@
 import com.android.annotations.NonNull;
 import com.android.annotations.Nullable;
 import com.android.tools.lint.detector.api.Category;
-import com.android.tools.lint.detector.api.Context;
 import com.android.tools.lint.detector.api.Detector;
+import com.android.tools.lint.detector.api.Detector.JavaPsiScanner;
 import com.android.tools.lint.detector.api.Implementation;
 import com.android.tools.lint.detector.api.Issue;
 import com.android.tools.lint.detector.api.JavaContext;
 import com.android.tools.lint.detector.api.Scope;
 import com.android.tools.lint.detector.api.Severity;
+import com.intellij.psi.JavaElementVisitor;
+import com.intellij.psi.JavaRecursiveElementVisitor;
+import com.intellij.psi.PsiExpression;
+import com.intellij.psi.PsiLiteral;
+import com.intellij.psi.PsiMethod;
+import com.intellij.psi.PsiMethodCallExpression;
+import com.intellij.psi.PsiReferenceExpression;
+import com.intellij.psi.PsiReturnStatement;
+import com.intellij.psi.util.PsiTreeUtil;
 
-import java.io.File;
 import java.util.Collections;
 import java.util.List;
 
-import lombok.ast.AstVisitor;
-import lombok.ast.Expression;
-import lombok.ast.ForwardingAstVisitor;
-import lombok.ast.IntegralLiteral;
-import lombok.ast.MethodInvocation;
-import lombok.ast.Node;
-import lombok.ast.Return;
-import lombok.ast.StrictListAccessor;
-
 /** Detector looking for Toast.makeText() without a corresponding show() call */
-public class ToastDetector extends Detector implements Detector.JavaScanner {
+public class ToastDetector extends Detector implements JavaPsiScanner {
     /** The main issue discovered by this detector */
     public static final Issue ISSUE = Issue.create(
             "ShowToast", //$NON-NLS-1$
@@ -62,12 +61,6 @@
     public ToastDetector() {
     }
 
-    @Override
-    public boolean appliesTo(@NonNull Context context, @NonNull File file) {
-        return true;
-    }
-
-
     // ---- Implements JavaScanner ----
 
     @Override
@@ -76,78 +69,75 @@
     }
 
     @Override
-    public void visitMethod(@NonNull JavaContext context, @Nullable AstVisitor visitor,
-            @NonNull MethodInvocation node) {
-        assert node.astName().astValue().equals("makeText");
-        if (node.astOperand() == null) {
-            // "makeText()" in the code with no operand
-            return;
-        }
-
-        String operand = node.astOperand().toString();
-        if (!(operand.equals("Toast") || operand.endsWith(".Toast"))) {
+    public void visitMethod(@NonNull JavaContext context, @Nullable JavaElementVisitor visitor,
+            @NonNull PsiMethodCallExpression call, @NonNull PsiMethod method) {
+        if (!context.getEvaluator().isMemberInClass(method, "android.widget.Toast")) {
             return;
         }
 
         // Make sure you pass the right kind of duration: it's not a delay, it's
         //  LENGTH_SHORT or LENGTH_LONG
         // (see http://code.google.com/p/android/issues/detail?id=3655)
-        StrictListAccessor<Expression, MethodInvocation> args = node.astArguments();
-        if (args.size() == 3) {
-            Expression duration = args.last();
-            if (duration instanceof IntegralLiteral) {
+        PsiExpression[] args = call.getArgumentList().getExpressions();
+        if (args.length == 3) {
+            PsiExpression duration = args[2];
+            if (duration instanceof PsiLiteral) {
                 context.report(ISSUE, duration, context.getLocation(duration),
                         "Expected duration `Toast.LENGTH_SHORT` or `Toast.LENGTH_LONG`, a custom " +
                         "duration value is not supported");
             }
         }
 
-        Node method = JavaContext.findSurroundingMethod(node.getParent());
-        if (method == null) {
+        PsiMethod surroundingMethod = PsiTreeUtil.getParentOfType(call, PsiMethod.class, true);
+        if (surroundingMethod == null) {
             return;
         }
 
-        ShowFinder finder = new ShowFinder(node);
-        method.accept(finder);
+        ShowFinder finder = new ShowFinder(call);
+        surroundingMethod.accept(finder);
         if (!finder.isShowCalled()) {
-            context.report(ISSUE, node, context.getLocation(node),
+            context.report(ISSUE, call, context.getLocation(call.getMethodExpression()),
                     "Toast created but not shown: did you forget to call `show()` ?");
         }
     }
 
-    private static class ShowFinder extends ForwardingAstVisitor {
+    private static class ShowFinder extends JavaRecursiveElementVisitor {
         /** The target makeText call */
-        private final MethodInvocation mTarget;
+        private final PsiMethodCallExpression mTarget;
         /** Whether we've found the show method */
         private boolean mFound;
         /** Whether we've seen the target makeText node yet */
         private boolean mSeenTarget;
 
-        private ShowFinder(MethodInvocation target) {
+        private ShowFinder(PsiMethodCallExpression target) {
             mTarget = target;
         }
 
         @Override
-        public boolean visitMethodInvocation(MethodInvocation node) {
+        public void visitMethodCallExpression(PsiMethodCallExpression node) {
+            super.visitMethodCallExpression(node);
+
             if (node == mTarget) {
                 mSeenTarget = true;
-            } else if ((mSeenTarget || node.astOperand() == mTarget)
-                    && "show".equals(node.astName().astValue())) { //$NON-NLS-1$
-                // TODO: Do more flow analysis to see whether we're really calling show
-                // on the right type of object?
-                mFound = true;
+            } else {
+                PsiReferenceExpression methodExpression = node.getMethodExpression();
+                if ((mSeenTarget || methodExpression.getQualifier() == mTarget)
+                        && "show".equals(methodExpression.getReferenceName())) { //$NON-NLS-1$
+                    // TODO: Do more flow analysis to see whether we're really calling show
+                    // on the right type of object?
+                    mFound = true;
+                }
             }
-
-            return true;
         }
 
         @Override
-        public boolean visitReturn(Return node) {
-            if (node.astValue() == mTarget) {
+        public void visitReturnStatement(PsiReturnStatement node) {
+            super.visitReturnStatement(node);
+
+            if (node.getReturnValue() == mTarget) {
                 // If you just do "return Toast.makeText(...) don't warn
                 mFound = true;
             }
-            return super.visitReturn(node);
         }
 
         boolean isShowCalled() {
diff --git a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/TrustAllX509TrustManagerDetector.java b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/TrustAllX509TrustManagerDetector.java
index 7e92f89..9eb93f3 100644
--- a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/TrustAllX509TrustManagerDetector.java
+++ b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/TrustAllX509TrustManagerDetector.java
@@ -18,18 +18,23 @@
 
 import com.android.annotations.NonNull;
 import com.android.annotations.Nullable;
-import com.android.tools.lint.client.api.JavaParser.ResolvedClass;
+import com.android.tools.lint.client.api.JavaEvaluator;
 import com.android.tools.lint.detector.api.Category;
 import com.android.tools.lint.detector.api.ClassContext;
 import com.android.tools.lint.detector.api.Detector;
 import com.android.tools.lint.detector.api.Detector.ClassScanner;
-import com.android.tools.lint.detector.api.Detector.JavaScanner;
+import com.android.tools.lint.detector.api.Detector.JavaPsiScanner;
 import com.android.tools.lint.detector.api.Implementation;
 import com.android.tools.lint.detector.api.Issue;
 import com.android.tools.lint.detector.api.JavaContext;
 import com.android.tools.lint.detector.api.Location;
 import com.android.tools.lint.detector.api.Scope;
 import com.android.tools.lint.detector.api.Severity;
+import com.intellij.psi.PsiClass;
+import com.intellij.psi.PsiCodeBlock;
+import com.intellij.psi.PsiMethod;
+import com.intellij.psi.PsiReturnStatement;
+import com.intellij.psi.PsiStatement;
 
 import org.objectweb.asm.Opcodes;
 import org.objectweb.asm.tree.AbstractInsnNode;
@@ -41,14 +46,7 @@
 import java.util.EnumSet;
 import java.util.List;
 
-import lombok.ast.ClassDeclaration;
-import lombok.ast.MethodDeclaration;
-import lombok.ast.Node;
-import lombok.ast.NormalTypeBody;
-import lombok.ast.Return;
-import lombok.ast.Statement;
-
-public class TrustAllX509TrustManagerDetector extends Detector implements JavaScanner,
+public class TrustAllX509TrustManagerDetector extends Detector implements JavaPsiScanner,
         ClassScanner {
 
     @SuppressWarnings("unchecked")
@@ -80,48 +78,46 @@
     }
 
     @Override
-    public void checkClass(@NonNull JavaContext context, @Nullable ClassDeclaration node,
-            @NonNull Node declarationOrAnonymous, @NonNull ResolvedClass cls) {
-        NormalTypeBody body;
-        if (declarationOrAnonymous instanceof NormalTypeBody) {
-            body = (NormalTypeBody) declarationOrAnonymous;
-        } else if (node != null) {
-            body = node.astBody();
-        } else {
-            return;
-        }
+    public void checkClass(@NonNull JavaContext context, @NonNull PsiClass cls) {
+        checkMethod(context, cls, "checkServerTrusted");
+        checkMethod(context, cls, "checkClientTrusted");
+    }
 
-        for (Node member : body.astMembers()) {
-            if (member instanceof MethodDeclaration) {
-                MethodDeclaration declaration = (MethodDeclaration)member;
-                String methodName = declaration.astMethodName().astValue();
-                if ("checkServerTrusted".equals(methodName)
-                        || "checkClientTrusted".equals(methodName)) {
+    private static void checkMethod(@NonNull JavaContext context,
+            @NonNull PsiClass cls,
+            @NonNull String methodName) {
+        JavaEvaluator evaluator = context.getEvaluator();
+        for (PsiMethod method : cls.findMethodsByName(methodName, true)) {
+            if (evaluator.isAbstract(method)) {
+                continue;
+            }
 
-                    // For now very simple; only checks if nothing is done.
-                    // Future work: Improve this check to be less sensitive to irrelevant
-                    // instructions/statements/invocations (e.g. System.out.println) by
-                    // looking for calls that could lead to a CertificateException being
-                    // thrown, e.g. throw statement within the method itself or invocation
-                    // of another method that may throw a CertificateException, and only
-                    // reporting an issue if none of these calls are found. ControlFlowGraph
-                    // may be useful here.
+            // For now very simple; only checks if nothing is done.
+            // Future work: Improve this check to be less sensitive to irrelevant
+            // instructions/statements/invocations (e.g. System.out.println) by
+            // looking for calls that could lead to a CertificateException being
+            // thrown, e.g. throw statement within the method itself or invocation
+            // of another method that may throw a CertificateException, and only
+            // reporting an issue if none of these calls are found. ControlFlowGraph
+            // may be useful here.
 
-                    boolean complex = false;
-                    for (Statement statement : declaration.astBody().astContents()) {
-                        if (!(statement instanceof Return)) {
-                            complex = true;
-                            break;
-                        }
-                    }
-
-                    if (!complex) {
-                        Location location = context.getNameLocation(declaration);
-                        String message = getErrorMessage(methodName);
-                        context.report(ISSUE, declaration, location, message);
-                    }
+            boolean complex = false;
+            PsiCodeBlock body = method.getBody();
+            if (body == null) {
+                return;
+            }
+            for (PsiStatement statement : body.getStatements()) {
+                if (!(statement instanceof PsiReturnStatement)) {
+                    complex = true;
+                    break;
                 }
             }
+
+            if (!complex) {
+                Location location = context.getNameLocation(method);
+                String message = getErrorMessage(methodName);
+                context.report(ISSUE, method, location, message);
+            }
         }
     }
 
diff --git a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/TypoLookup.java b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/TypoLookup.java
index f9da908..8818bce 100644
--- a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/TypoLookup.java
+++ b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/TypoLookup.java
@@ -243,10 +243,10 @@
             // First skip the header
             byte[] expectedHeader = FILE_HEADER.getBytes(Charsets.US_ASCII);
             buffer.rewind();
-            for (int offset = 0; offset < expectedHeader.length; offset++) {
-                if (expectedHeader[offset] != buffer.get()) {
+            for (byte anExpectedHeader : expectedHeader) {
+                if (anExpectedHeader != buffer.get()) {
                     client.log(null, "Incorrect file header: not an typo database cache " +
-                            "file, or a corrupt cache file");
+                                     "file, or a corrupt cache file");
                     return;
                 }
             }
@@ -378,15 +378,14 @@
         // 7. Word entry table. Each word entry consists of the word, followed by the byte 0
         //      as a terminator, followed by a comma separated list of suggestions (which
         //      may be empty), or a final 0.
-        for (int i = 0; i < entryCount; i++) {
-            byte[] word = wordArrays[i];
+        for (byte[] word : wordArrays) {
             buffer.position(nextOffset);
             buffer.putInt(nextEntry);
             nextOffset = buffer.position();
             buffer.position(nextEntry);
 
             buffer.put(word); // already embeds 0 to separate typo from words
-            buffer.put((byte) 0);
+            buffer.put((byte)0);
 
             nextEntry = buffer.position();
         }
diff --git a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/UnsafeBroadcastReceiverDetector.java b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/UnsafeBroadcastReceiverDetector.java
index 1287465..027a059 100644
--- a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/UnsafeBroadcastReceiverDetector.java
+++ b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/UnsafeBroadcastReceiverDetector.java
@@ -28,13 +28,10 @@
 import com.android.annotations.NonNull;
 import com.android.annotations.Nullable;
 import com.android.annotations.VisibleForTesting;
-import com.android.tools.lint.client.api.JavaParser.ResolvedClass;
-import com.android.tools.lint.client.api.JavaParser.ResolvedMethod;
-import com.android.tools.lint.client.api.JavaParser.ResolvedNode;
-import com.android.tools.lint.client.api.JavaParser.ResolvedVariable;
+import com.android.tools.lint.client.api.JavaEvaluator;
 import com.android.tools.lint.detector.api.Category;
 import com.android.tools.lint.detector.api.Detector;
-import com.android.tools.lint.detector.api.Detector.JavaScanner;
+import com.android.tools.lint.detector.api.Detector.JavaPsiScanner;
 import com.android.tools.lint.detector.api.Detector.XmlScanner;
 import com.android.tools.lint.detector.api.Implementation;
 import com.android.tools.lint.detector.api.Issue;
@@ -45,6 +42,13 @@
 import com.android.tools.lint.detector.api.Severity;
 import com.android.tools.lint.detector.api.XmlContext;
 import com.google.common.collect.Sets;
+import com.intellij.psi.JavaRecursiveElementVisitor;
+import com.intellij.psi.PsiClass;
+import com.intellij.psi.PsiElement;
+import com.intellij.psi.PsiMethod;
+import com.intellij.psi.PsiMethodCallExpression;
+import com.intellij.psi.PsiParameter;
+import com.intellij.psi.PsiReferenceExpression;
 
 import org.w3c.dom.Element;
 
@@ -55,15 +59,8 @@
 import java.util.List;
 import java.util.Set;
 
-import lombok.ast.ClassDeclaration;
-import lombok.ast.ForwardingAstVisitor;
-import lombok.ast.MethodDeclaration;
-import lombok.ast.MethodInvocation;
-import lombok.ast.Node;
-import lombok.ast.VariableReference;
-
 public class UnsafeBroadcastReceiverDetector extends Detector
-        implements JavaScanner, XmlScanner {
+        implements JavaPsiScanner, XmlScanner {
 
     /* Description of check implementations:
      *
@@ -379,7 +376,7 @@
     private static final Set<String> PROTECTED_BROADCAST_SET =
             Sets.newHashSet(PROTECTED_BROADCASTS);
 
-    private Set<String> mReceiversWithProtectedBroadcastIntentFilter = new HashSet<String>();
+    private final Set<String> mReceiversWithProtectedBroadcastIntentFilter = new HashSet<String>();
 
     public UnsafeBroadcastReceiverDetector() {
     }
@@ -451,38 +448,29 @@
     }
 
     @Override
-    public void checkClass(@NonNull JavaContext context, @Nullable ClassDeclaration node,
-            @NonNull Node declarationOrAnonymous, @NonNull ResolvedClass cls) {
-        if (node == null) { // anonymous classes can't be the ones referenced in the manifest
+    public void checkClass(@NonNull JavaContext context, @NonNull PsiClass declaration) {
+        String name = declaration.getName();
+        if (name == null) {
+            // anonymous classes can't be the ones referenced in the manifest
             return;
         }
-        String name = cls.getName();
-        if (!mReceiversWithProtectedBroadcastIntentFilter.contains(name)) {
+        String qualifiedName = declaration.getQualifiedName();
+        if (qualifiedName == null) {
             return;
         }
-        for (Node member : node.astBody().astMembers()) {
-            if (member instanceof MethodDeclaration) {
-                MethodDeclaration declaration = (MethodDeclaration)member;
-                if ("onReceive".equals(declaration.astMethodName().astValue())
-                        && declaration.astParameters().size() == 2) {
-                    ResolvedNode resolved = context.resolve(declaration);
-                    if (resolved instanceof ResolvedMethod) {
-                        ResolvedMethod method = (ResolvedMethod) resolved;
-                        if (method.getArgumentCount() == 2
-                                && method.getArgumentType(0).matchesName(CLASS_CONTEXT)
-                                && method.getArgumentType(1).matchesName(CLASS_INTENT)) {
-                            checkOnReceive(context, declaration, method);
-                            break;
-                        }
-                    }
-                }
+        if (!mReceiversWithProtectedBroadcastIntentFilter.contains(qualifiedName)) {
+            return;
+        }
+        JavaEvaluator evaluator = context.getEvaluator();
+        for (PsiMethod method : declaration.findMethodsByName("onReceive", false)) {
+            if (evaluator.parametersMatch(method, CLASS_CONTEXT, CLASS_INTENT)) {
+                checkOnReceive(context, method);
             }
         }
     }
 
     private static void checkOnReceive(@NonNull JavaContext context,
-            @NonNull MethodDeclaration declaration,
-            @NonNull ResolvedMethod method) {
+            @NonNull PsiMethod method) {
         // Search for call to getAction but also search for references to aload_2,
         // which indicates that the method is making use of the received intent in
         // some way.
@@ -492,16 +480,9 @@
         // method that might be performing the getAction check, so we warn that the
         // finding may be a false positive. (An alternative option would be to not
         // report a finding at all in this case.)
-        assert method.getArgumentCount() == 2;
-        ResolvedVariable parameter = null;
-        if (declaration.astParameters().size() == 2) {
-            ResolvedNode resolved = context.resolve(declaration.astParameters().last());
-            if (resolved instanceof ResolvedVariable) {
-                parameter = (ResolvedVariable) resolved;
-            }
-        }
-        OnReceiveVisitor visitor = new OnReceiveVisitor(context, parameter);
-        declaration.accept(visitor);
+        PsiParameter parameter = method.getParameterList().getParameters()[1];
+        OnReceiveVisitor visitor = new OnReceiveVisitor(context.getEvaluator(), parameter);
+        method.accept(visitor);
         if (!visitor.getCallsGetAction()) {
             String report;
             if (!visitor.getUsesIntent()) {
@@ -529,19 +510,19 @@
                         "to another method that checked the action string. If so, this " +
                         "finding can safely be ignored.";
             }
-            Location location = context.getNameLocation(declaration);
-            context.report(ACTION_STRING, declaration, location, report);
+            Location location = context.getNameLocation(method);
+            context.report(ACTION_STRING, method, location, report);
         }
     }
 
-    private static class OnReceiveVisitor extends ForwardingAstVisitor {
-        @NonNull private final JavaContext mContext;
-        @Nullable private final ResolvedVariable mParameter;
+    private static class OnReceiveVisitor extends JavaRecursiveElementVisitor {
+        @NonNull private final JavaEvaluator mEvaluator;
+        @Nullable private final PsiParameter mParameter;
         private boolean mCallsGetAction;
         private boolean mUsesIntent;
 
-        public OnReceiveVisitor(@NonNull JavaContext context, @Nullable ResolvedVariable parameter) {
-            mContext = context;
+        public OnReceiveVisitor(@NonNull JavaEvaluator context, @Nullable PsiParameter parameter) {
+            mEvaluator = context;
             mParameter = parameter;
         }
 
@@ -554,30 +535,27 @@
         }
 
         @Override
-        public boolean visitMethodInvocation(@NonNull MethodInvocation node) {
+        public void visitMethodCallExpression(PsiMethodCallExpression node) {
             if (!mCallsGetAction) {
-                ResolvedNode resolved = mContext.resolve(node);
-                if (resolved instanceof ResolvedMethod) {
-                    ResolvedMethod method = (ResolvedMethod) resolved;
-                    if (method.getName().equals("getAction") &&
-                            method.getContainingClass().isSubclassOf(CLASS_INTENT, false)) {
-                        mCallsGetAction = true;
-                    }
+                PsiMethod method = node.resolveMethod();
+                if (method != null && "getAction".equals(method.getName()) &&
+                        mEvaluator.isMemberInSubClassOf(method, CLASS_INTENT, false)) {
+                    mCallsGetAction = true;
                 }
             }
-            return super.visitMethodInvocation(node);
+
+            super.visitMethodCallExpression(node);
         }
 
         @Override
-        public boolean visitVariableReference(@NonNull VariableReference node) {
+        public void visitReferenceExpression(PsiReferenceExpression expression) {
             if (!mUsesIntent && mParameter != null) {
-                ResolvedNode resolved = mContext.resolve(node);
+                PsiElement resolved = expression.resolve();
                 if (mParameter.equals(resolved)) {
                     mUsesIntent = true;
                 }
             }
-
-            return super.visitVariableReference(node);
+            super.visitReferenceExpression(expression);
         }
     }
 }
diff --git a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/UnsafeNativeCodeDetector.java b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/UnsafeNativeCodeDetector.java
index 6033693..e361b6e 100644
--- a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/UnsafeNativeCodeDetector.java
+++ b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/UnsafeNativeCodeDetector.java
@@ -21,12 +21,11 @@
 import com.android.SdkConstants;
 import com.android.annotations.NonNull;
 import com.android.annotations.Nullable;
-import com.android.tools.lint.client.api.JavaParser.ResolvedClass;
-import com.android.tools.lint.client.api.JavaParser.ResolvedMethod;
-import com.android.tools.lint.client.api.JavaParser.ResolvedNode;
+import com.android.tools.lint.client.api.JavaEvaluator;
 import com.android.tools.lint.detector.api.Category;
 import com.android.tools.lint.detector.api.Context;
 import com.android.tools.lint.detector.api.Detector;
+import com.android.tools.lint.detector.api.Detector.JavaPsiScanner;
 import com.android.tools.lint.detector.api.Implementation;
 import com.android.tools.lint.detector.api.Issue;
 import com.android.tools.lint.detector.api.JavaContext;
@@ -36,6 +35,9 @@
 import com.android.tools.lint.detector.api.Scope;
 import com.android.tools.lint.detector.api.Severity;
 import com.android.tools.lint.detector.api.Speed;
+import com.intellij.psi.JavaElementVisitor;
+import com.intellij.psi.PsiMethod;
+import com.intellij.psi.PsiMethodCallExpression;
 
 import java.io.File;
 import java.io.FileInputStream;
@@ -44,11 +46,7 @@
 import java.util.Collections;
 import java.util.List;
 
-import lombok.ast.AstVisitor;
-import lombok.ast.MethodInvocation;
-
-public class UnsafeNativeCodeDetector extends Detector
-        implements Detector.JavaScanner {
+public class UnsafeNativeCodeDetector extends Detector implements JavaPsiScanner {
 
     private static final Implementation IMPLEMENTATION = new Implementation(
             UnsafeNativeCodeDetector.class,
@@ -106,20 +104,16 @@
     }
 
     @Override
-    public void visitMethod(@NonNull JavaContext context, @Nullable AstVisitor visitor,
-            @NonNull MethodInvocation node) {
-        ResolvedNode resolved = context.resolve(node);
-        if (resolved instanceof ResolvedMethod) {
-            String methodName = node.astName().astValue();
-            ResolvedClass resolvedClass = ((ResolvedMethod) resolved).getContainingClass();
-            if ((resolvedClass.isSubclassOf(RUNTIME_CLASS, false)) ||
-                    (resolvedClass.matches(SYSTEM_CLASS))) {
-                // Report calls to Runtime.load() and System.load()
-                if ("load".equals(methodName)) {
-                    context.report(LOAD, node, context.getLocation(node),
-                            "Dynamically loading code using `load` is risky, please use " +
-                                    "`loadLibrary` instead when possible");
-                }
+    public void visitMethod(@NonNull JavaContext context, @Nullable JavaElementVisitor visitor,
+            @NonNull PsiMethodCallExpression call, @NonNull PsiMethod method) {
+        // Report calls to Runtime.load() and System.load()
+        if ("load".equals(method.getName())) {
+            JavaEvaluator evaluator = context.getEvaluator();
+            if (evaluator.isMemberInSubClassOf(method, RUNTIME_CLASS, false) ||
+                    evaluator.isMemberInSubClassOf(method, SYSTEM_CLASS, false)) {
+                context.report(LOAD, call, context.getLocation(call),
+                        "Dynamically loading code using `load` is risky, please use " +
+                                "`loadLibrary` instead when possible");
             }
         }
     }
diff --git a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/UnusedResourceDetector.java b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/UnusedResourceDetector.java
index e5b37a1..681b23e 100644
--- a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/UnusedResourceDetector.java
+++ b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/UnusedResourceDetector.java
@@ -19,7 +19,6 @@
 import static com.android.SdkConstants.ATTR_NAME;
 import static com.android.SdkConstants.DOT_JAVA;
 import static com.android.SdkConstants.DOT_XML;
-import static com.android.SdkConstants.R_CLASS;
 import static com.android.tools.lint.detector.api.LintUtils.findSubstring;
 import static com.android.utils.SdkUtils.endsWithIgnoreCase;
 import static com.google.common.base.Charsets.UTF_8;
@@ -33,16 +32,14 @@
 import com.android.builder.model.ProductFlavorContainer;
 import com.android.builder.model.SourceProvider;
 import com.android.builder.model.Variant;
+import com.android.ide.common.resources.ResourceUrl;
 import com.android.resources.ResourceFolderType;
 import com.android.resources.ResourceType;
 import com.android.tools.lint.checks.ResourceUsageModel.Resource;
-import com.android.tools.lint.client.api.JavaParser.ResolvedClass;
-import com.android.tools.lint.client.api.JavaParser.ResolvedField;
-import com.android.tools.lint.client.api.JavaParser.ResolvedNode;
 import com.android.tools.lint.detector.api.Category;
 import com.android.tools.lint.detector.api.Context;
 import com.android.tools.lint.detector.api.Detector.BinaryResourceScanner;
-import com.android.tools.lint.detector.api.Detector.JavaScanner;
+import com.android.tools.lint.detector.api.Detector.JavaPsiScanner;
 import com.android.tools.lint.detector.api.Detector.XmlScanner;
 import com.android.tools.lint.detector.api.Implementation;
 import com.android.tools.lint.detector.api.Issue;
@@ -51,6 +48,7 @@
 import com.android.tools.lint.detector.api.Location;
 import com.android.tools.lint.detector.api.Project;
 import com.android.tools.lint.detector.api.ResourceContext;
+import com.android.tools.lint.detector.api.ResourceEvaluator;
 import com.android.tools.lint.detector.api.ResourceXmlDetector;
 import com.android.tools.lint.detector.api.Scope;
 import com.android.tools.lint.detector.api.Severity;
@@ -60,6 +58,12 @@
 import com.google.common.collect.Lists;
 import com.google.common.collect.Sets;
 import com.google.common.io.Files;
+import com.intellij.psi.JavaElementVisitor;
+import com.intellij.psi.JavaRecursiveElementVisitor;
+import com.intellij.psi.PsiElement;
+import com.intellij.psi.PsiField;
+import com.intellij.psi.PsiImportStaticStatement;
+import com.intellij.psi.PsiReferenceExpression;
 
 import org.w3c.dom.Document;
 import org.w3c.dom.Element;
@@ -72,23 +76,14 @@
 import java.util.Collections;
 import java.util.Comparator;
 import java.util.EnumSet;
-import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
 
-import lombok.ast.AstVisitor;
-import lombok.ast.ClassDeclaration;
-import lombok.ast.CompilationUnit;
-import lombok.ast.ForwardingAstVisitor;
-import lombok.ast.Identifier;
-import lombok.ast.ImportDeclaration;
-import lombok.ast.VariableReference;
-
 /**
  * Finds unused resources.
  */
-public class UnusedResourceDetector extends ResourceXmlDetector implements JavaScanner,
+public class UnusedResourceDetector extends ResourceXmlDetector implements JavaPsiScanner,
         BinaryResourceScanner, XmlScanner {
 
     private static final Implementation IMPLEMENTATION = new Implementation(
@@ -123,8 +118,10 @@
     private final UnusedResourceDetectorUsageModel mModel =
             new UnusedResourceDetectorUsageModel();
 
-    /** Whether the resource detector will look for inactive resources (e.g. resource and code references
-     * in source sets that are not the primary/active variant) */
+    /**
+     * Whether the resource detector will look for inactive resources (e.g. resource and code
+     * references in source sets that are not the primary/active variant)
+     */
     public static boolean sIncludeInactiveReferences = true;
 
     /**
@@ -510,11 +507,19 @@
     // ---- Implements JavaScanner ----
 
     @Override
-    public List<Class<? extends lombok.ast.Node>> getApplicableNodeTypes() {
-        //noinspection unchecked
-        return Arrays.<Class<? extends lombok.ast.Node>>asList(
-                ClassDeclaration.class,
-                ImportDeclaration.class);
+    public List<Class<? extends PsiElement>> getApplicablePsiTypes() {
+        return Collections.<Class<? extends PsiElement>>singletonList(PsiImportStaticStatement.class);
+    }
+
+    @Nullable
+    @Override
+    public JavaElementVisitor createPsiVisitor(@NonNull JavaContext context) {
+        if (context.getDriver().getPhase() == 1) {
+            return new UnusedResourceVisitor();
+        } else {
+            // Second pass, computing resource declaration locations: No need to look at Java
+            return null;
+        }
     }
 
     @Override
@@ -523,138 +528,54 @@
     }
 
     @Override
-    public void visitResourceReference(@NonNull JavaContext context, @Nullable AstVisitor visitor,
-            @NonNull lombok.ast.Node node, @NonNull String type, @NonNull String name,
-            boolean isFramework) {
+    public void visitResourceReference(@NonNull JavaContext context,
+            @Nullable JavaElementVisitor visitor, @NonNull PsiElement node,
+            @NonNull ResourceType type, @NonNull String name, boolean isFramework) {
         if (!isFramework) {
-            ResourceType t = ResourceType.getEnum(type);
-            assert t != null : type;
-            ResourceUsageModel.markReachable(mModel.addResource(t, name, null));
-        }
-    }
-
-    @Override
-    public AstVisitor createJavaVisitor(@NonNull JavaContext context) {
-        if (context.getDriver().getPhase() == 1) {
-            return new UnusedResourceVisitor(context);
-        } else {
-            // Second pass, computing resource declaration locations: No need to look at Java
-            return null;
+            ResourceUsageModel.markReachable(mModel.addResource(type, name, null));
         }
     }
 
     // Look for references and declarations
-    private class UnusedResourceVisitor extends ForwardingAstVisitor {
-        private final JavaContext mContext;
-
-        public UnusedResourceVisitor(JavaContext context) {
-            mContext = context;
+    private class UnusedResourceVisitor extends JavaElementVisitor {
+        public UnusedResourceVisitor() {
         }
 
         @Override
-        public boolean visitClassDeclaration(ClassDeclaration node) {
-            // Look for declarations of R class fields and record them as declarations
-            String description = node.astName().astValue();
-            if (description.equals(R_CLASS)) {
-                // Don't visit R class declarations; we don't need to look at R declarations
-                // since we now look for all the same resource declarations that the R
-                // class itself was derived from.
-                return true;
-            }
-
-            return false;
-        }
-
-        @Override
-        public boolean visitImportDeclaration(ImportDeclaration node) {
-            if (node.astStaticImport()) {
-                // import static pkg.R.type.name;
-                //   or
-                // import static pkg.R.type.*;
-                Iterator<Identifier> iterator = node.astParts().iterator();
-                while (iterator.hasNext()) {
-                    String identifier = iterator.next().astValue();
-                    if (identifier.equals(R_CLASS)) {
-                        if (iterator.hasNext()) {
-                            String typeString = iterator.next().astValue();
-                            ResourceType type = ResourceType.getEnum(typeString);
-                            if (type != null) {
-                                if (iterator.hasNext()) {
-                                    // import static pkg.R.type.name;
-                                    String name = iterator.next().astValue();
-                                    Resource resource = mModel.addResource(type, name, null);
-                                    ResourceUsageModel.markReachable(resource);
-                                } else if (!mScannedForStaticImports) {
-                                    // wildcard import of whole type:
-                                    // import static pkg.R.type.*;
-                                    // We have to do a more expensive analysis here to
-                                    // for example recognize "x" as a reference to R.string.x
-                                    mScannedForStaticImports = true;
-                                    CompilationUnit unit = node.upToCompilationUnit();
-                                    scanForStaticImportReferences(unit);
-                                }
-                            }
-                        }
-                        break;
-                    }
-                }
-            } else if (!mScannedForStaticImports) {
-                String last = node.astParts().last().astValue();
-                if (Character.isLowerCase(last.charAt(0))) {
-                    Iterator<Identifier> iterator = node.astParts().iterator();
-                    while (iterator.hasNext()) {
-                        String identifier = iterator.next().astValue();
-                        if (identifier.equals(R_CLASS)) {
-                            if (iterator.hasNext()) {
-                                String typeString = iterator.next().astValue();
-                                ResourceType type = ResourceType.getEnum(typeString);
-                                if (type != null && !iterator.hasNext()) {
-                                    // Import a type as a class:
-                                    // import pkg.R.type;
-                                    // We have to do a more expensive analysis here to
-                                    // for example recognize "string.x" as a reference to R.string.x
-                                    mScannedForStaticImports = true;
-                                    CompilationUnit unit = node.upToCompilationUnit();
-                                    scanForStaticImportReferences(unit);
-                                }
-                            }
-                            break;
-                        }
-                    }
-                }
-            }
-
-            return false;
-        }
-
-        private void scanForStaticImportReferences(@Nullable CompilationUnit unit) {
-            if (unit == null) {
+        public void visitImportStaticStatement(PsiImportStaticStatement statement) {
+            if (mScannedForStaticImports) {
                 return;
             }
-            unit.accept(new ForwardingAstVisitor() {
-                @Override
-                public boolean visitVariableReference(
-                        VariableReference node) {
-                    ResolvedNode resolved = mContext.resolve(node);
-                    if (resolved instanceof ResolvedField) {
-                        ResolvedField field = (ResolvedField) resolved;
-                        ResolvedClass typeClass = field.getContainingClass();
-                        if (typeClass != null) {
-                            ResolvedClass rClass = typeClass.getContainingClass();
-                            if (rClass != null && R_CLASS.equals(rClass.getSimpleName())) {
-                                ResourceType type = ResourceType.getEnum(typeClass.getSimpleName());
-                                if (type != null) {
-                                    Resource resource = mModel.addResource(type, field.getName(),
-                                            null);
-                                    ResourceUsageModel.markReachable(resource);
-                                }
+            if (statement.isOnDemand()) {
+                // Wildcard import of whole type:
+                // import static pkg.R.type.*;
+                // We have to do a more expensive analysis here to
+                // for example recognize "x" as a reference to R.string.x
+                mScannedForStaticImports = true;
+                statement.getContainingFile().accept(new JavaRecursiveElementVisitor() {
+                    @Override
+                    public void visitReferenceExpression(PsiReferenceExpression expression) {
+                        PsiElement resolved = expression.resolve();
+                        if (resolved instanceof PsiField) {
+                            ResourceUrl url = ResourceEvaluator.getResourceConstant(resolved);
+                            if (url != null && !url.framework) {
+                                Resource resource = mModel.addResource(url.type, url.name, null);
+                                ResourceUsageModel.markReachable(resource);
                             }
                         }
-
+                        super.visitReferenceExpression(expression);
                     }
-                    return super.visitVariableReference(node);
+                });
+            } else {
+                PsiElement resolved = statement.resolve();
+                if (resolved instanceof PsiField) {
+                    ResourceUrl url = ResourceEvaluator.getResourceConstant(resolved);
+                    if (url != null && !url.framework) {
+                        Resource resource = mModel.addResource(url.type, url.name, null);
+                        ResourceUsageModel.markReachable(resource);
+                    }
                 }
-            });
+            }
         }
 
         private boolean mScannedForStaticImports;
diff --git a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/ViewConstructorDetector.java b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/ViewConstructorDetector.java
index cacb28d..1edf8f0 100644
--- a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/ViewConstructorDetector.java
+++ b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/ViewConstructorDetector.java
@@ -18,34 +18,34 @@
 
 import static com.android.SdkConstants.CLASS_ATTRIBUTE_SET;
 import static com.android.SdkConstants.CLASS_CONTEXT;
-import static com.android.tools.lint.client.api.JavaParser.ResolvedClass;
-import static com.android.tools.lint.client.api.JavaParser.ResolvedMethod;
 
 import com.android.SdkConstants;
 import com.android.annotations.NonNull;
 import com.android.annotations.Nullable;
+import com.android.tools.lint.client.api.JavaEvaluator;
 import com.android.tools.lint.detector.api.Category;
 import com.android.tools.lint.detector.api.Detector;
+import com.android.tools.lint.detector.api.Detector.JavaPsiScanner;
 import com.android.tools.lint.detector.api.Implementation;
 import com.android.tools.lint.detector.api.Issue;
 import com.android.tools.lint.detector.api.JavaContext;
 import com.android.tools.lint.detector.api.Location;
 import com.android.tools.lint.detector.api.Scope;
 import com.android.tools.lint.detector.api.Severity;
-import com.android.tools.lint.detector.api.Speed;
+import com.intellij.psi.PsiAnonymousClass;
+import com.intellij.psi.PsiClass;
+import com.intellij.psi.PsiMethod;
+import com.intellij.psi.PsiParameter;
+import com.intellij.psi.PsiParameterList;
+import com.intellij.psi.PsiType;
 
-import java.lang.reflect.Modifier;
 import java.util.Collections;
 import java.util.List;
 
-import lombok.ast.ClassDeclaration;
-import lombok.ast.Node;
-import lombok.ast.NormalTypeBody;
-
 /**
  * Looks for custom views that do not define the view constructors needed by UI builders
  */
-public class ViewConstructorDetector extends Detector implements Detector.JavaScanner {
+public class ViewConstructorDetector extends Detector implements JavaPsiScanner {
     /** The main issue discovered by this detector */
     public static final Issue ISSUE = Issue.create(
             "ViewConstructor", //$NON-NLS-1$
@@ -73,37 +73,35 @@
     public ViewConstructorDetector() {
     }
 
-    @NonNull
-    @Override
-    public Speed getSpeed() {
-        return Speed.FAST;
-    }
-
     // ---- Implements JavaScanner ----
 
-    private static boolean isXmlConstructor(ResolvedMethod method) {
+    private static boolean isXmlConstructor(
+            @NonNull JavaEvaluator evaluator,
+            @NonNull PsiMethod method) {
         // Accept
         //   android.content.Context
         //   android.content.Context,android.util.AttributeSet
         //   android.content.Context,android.util.AttributeSet,int
-        int argumentCount = method.getArgumentCount();
+        PsiParameterList parameterList = method.getParameterList();
+        int argumentCount = parameterList.getParametersCount();
         if (argumentCount == 0 || argumentCount > 3) {
             return false;
         }
-        if (!method.getArgumentType(0).matchesName(CLASS_CONTEXT)) {
+        PsiParameter[] parameters = parameterList.getParameters();
+        if (!evaluator.typeMatches(parameters[0].getType(), CLASS_CONTEXT)) {
             return false;
         }
         if (argumentCount == 1) {
             return true;
         }
-        if (!method.getArgumentType(1).matchesName(CLASS_ATTRIBUTE_SET)) {
+        if (!evaluator.typeMatches(parameters[1].getType(), CLASS_ATTRIBUTE_SET)) {
             return false;
         }
         //noinspection SimplifiableIfStatement
         if (argumentCount == 2) {
             return true;
         }
-        return method.getArgumentType(2).matchesName("int");
+        return PsiType.INT.equals(parameters[2].getType());
     }
 
     @Nullable
@@ -113,29 +111,24 @@
     }
 
     @Override
-    public void checkClass(@NonNull JavaContext context, @Nullable ClassDeclaration node,
-            @NonNull Node declarationOrAnonymous, @NonNull ResolvedClass resolvedClass) {
-        if (node == null) {
-            return;
-        }
-
+    public void checkClass(@NonNull JavaContext context, @NonNull PsiClass declaration) {
         // Only applies to concrete classes
-        int flags = node.astModifiers().getEffectiveModifierFlags();
-        // Ignore abstract classes
-        if ((flags & Modifier.ABSTRACT) != 0) {
+        JavaEvaluator evaluator = context.getEvaluator();
+        if (evaluator.isAbstract(declaration) || declaration instanceof PsiAnonymousClass) {
+            // Ignore abstract classes
             return;
         }
 
-        if (node.getParent() instanceof NormalTypeBody
-                && ((flags & Modifier.STATIC) == 0)) {
+        if (declaration.getContainingClass() != null &&
+                !evaluator.isStatic(declaration)) {
             // Ignore inner classes that aren't static: we can't create these
             // anyway since we'd need the outer instance
             return;
         }
 
         boolean found = false;
-        for (ResolvedMethod constructor : resolvedClass.getConstructors()) {
-            if (isXmlConstructor(constructor)) {
+        for (PsiMethod constructor : declaration.getConstructors()) {
+            if (isXmlConstructor(evaluator, constructor)) {
                 found = true;
                 break;
             }
@@ -146,9 +139,9 @@
                     "Custom view `%1$s` is missing constructor used by tools: "
                             + "`(Context)` or `(Context,AttributeSet)` "
                             + "or `(Context,AttributeSet,int)`",
-                    node.astName().astValue());
-            Location location = context.getLocation(node.astName());
-            context.report(ISSUE, node, location, message  /*data*/);
+                    declaration.getName());
+            Location location = context.getNameLocation(declaration);
+            context.report(ISSUE, declaration, location, message  /*data*/);
         }
     }
 }
diff --git a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/ViewHolderDetector.java b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/ViewHolderDetector.java
index 841f727..9500cbd 100644
--- a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/ViewHolderDetector.java
+++ b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/ViewHolderDetector.java
@@ -16,40 +16,39 @@
 
 package com.android.tools.lint.checks;
 
-import static com.android.SdkConstants.VIEW;
-import static com.android.SdkConstants.VIEW_GROUP;
+import static com.android.SdkConstants.CLASS_VIEW;
+import static com.android.SdkConstants.CLASS_VIEWGROUP;
 import static com.android.tools.lint.client.api.JavaParser.TYPE_INT;
 
 import com.android.annotations.NonNull;
+import com.android.tools.lint.client.api.JavaEvaluator;
 import com.android.tools.lint.detector.api.Category;
 import com.android.tools.lint.detector.api.Detector;
+import com.android.tools.lint.detector.api.Detector.JavaPsiScanner;
 import com.android.tools.lint.detector.api.Implementation;
 import com.android.tools.lint.detector.api.Issue;
 import com.android.tools.lint.detector.api.JavaContext;
 import com.android.tools.lint.detector.api.Scope;
 import com.android.tools.lint.detector.api.Severity;
-import com.android.tools.lint.detector.api.Speed;
 import com.google.common.collect.Lists;
+import com.intellij.psi.JavaElementVisitor;
+import com.intellij.psi.JavaRecursiveElementVisitor;
+import com.intellij.psi.PsiConditionalExpression;
+import com.intellij.psi.PsiElement;
+import com.intellij.psi.PsiIfStatement;
+import com.intellij.psi.PsiMethod;
+import com.intellij.psi.PsiMethodCallExpression;
+import com.intellij.psi.PsiReferenceExpression;
+import com.intellij.psi.PsiSwitchStatement;
+import com.intellij.psi.util.PsiTreeUtil;
 
 import java.util.Collections;
-import java.util.Iterator;
 import java.util.List;
 
-import lombok.ast.AstVisitor;
-import lombok.ast.ForwardingAstVisitor;
-import lombok.ast.If;
-import lombok.ast.InlineIfExpression;
-import lombok.ast.MethodDeclaration;
-import lombok.ast.MethodInvocation;
-import lombok.ast.Node;
-import lombok.ast.StrictListAccessor;
-import lombok.ast.Switch;
-import lombok.ast.VariableDefinition;
-
 /**
  * Looks for ListView scrolling performance: should use view holder pattern
  */
-public class ViewHolderDetector extends Detector implements Detector.JavaScanner {
+public class ViewHolderDetector extends Detector implements JavaPsiScanner {
 
     private static final Implementation IMPLEMENTATION = new Implementation(
             ViewHolderDetector.class,
@@ -79,25 +78,20 @@
     public ViewHolderDetector() {
     }
 
-    @NonNull
-    @Override
-    public Speed getSpeed() {
-        return Speed.NORMAL;
-    }
-
     // ---- Implements JavaScanner ----
 
     @Override
-    public List<Class<? extends Node>> getApplicableNodeTypes() {
-        return Collections.<Class<? extends Node>>singletonList(MethodDeclaration.class);
+    public List<Class<? extends PsiElement>> getApplicablePsiTypes() {
+        return Collections.<Class<? extends PsiElement>>singletonList(PsiMethod.class);
     }
 
     @Override
-    public AstVisitor createJavaVisitor(@NonNull JavaContext context) {
+    public JavaElementVisitor createPsiVisitor(@NonNull JavaContext context) {
         return new ViewAdapterVisitor(context);
     }
 
-    private static class ViewAdapterVisitor extends ForwardingAstVisitor {
+
+    private static class ViewAdapterVisitor extends JavaElementVisitor {
         private final JavaContext mContext;
 
         public ViewAdapterVisitor(JavaContext context) {
@@ -105,67 +99,28 @@
         }
 
         @Override
-        public boolean visitMethodDeclaration(MethodDeclaration node) {
-            if (isViewAdapterMethod(node)) {
+        public void visitMethod(PsiMethod method) {
+            if (isViewAdapterMethod(mContext, method)) {
                 InflationVisitor visitor = new InflationVisitor(mContext);
-                node.accept(visitor);
+                method.accept(visitor);
                 visitor.finish();
             }
-            return super.visitMethodDeclaration(node);
         }
 
         /**
          * Returns true if this method looks like it's overriding android.widget.Adapter's getView
          * method: getView(int position, View convertView, ViewGroup parent)
          */
-        private static boolean isViewAdapterMethod(MethodDeclaration node) {
-            if (GET_VIEW.equals(node.astMethodName().astValue())) {
-                StrictListAccessor<VariableDefinition, MethodDeclaration> parameters =
-                        node.astParameters();
-                if (parameters != null && parameters.size() == 3) {
-                    Iterator<VariableDefinition> iterator = parameters.iterator();
-                    if (!iterator.hasNext()) {
-                        return false;
-                    }
-
-                    VariableDefinition first = iterator.next();
-                    if (!first.astTypeReference().astParts().last().getTypeName().equals(
-                            TYPE_INT)) {
-                        return false;
-                    }
-
-                    if (!iterator.hasNext()) {
-                        return false;
-                    }
-
-                    VariableDefinition second = iterator.next();
-                    if (!second.astTypeReference().astParts().last().getTypeName().equals(
-                            VIEW)) {
-                        return false;
-                    }
-
-                    if (!iterator.hasNext()) {
-                        return false;
-                    }
-
-                    VariableDefinition third = iterator.next();
-                    //noinspection RedundantIfStatement
-                    if (!third.astTypeReference().astParts().last().getTypeName().equals(
-                            VIEW_GROUP)) {
-                        return false;
-                    }
-
-                    return true;
-                }
-            }
-
-            return false;
+        private static boolean isViewAdapterMethod(JavaContext context, PsiMethod node) {
+            JavaEvaluator evaluator = context.getEvaluator();
+            return GET_VIEW.equals(node.getName()) && evaluator.parametersMatch(node,
+                    TYPE_INT, CLASS_VIEW, CLASS_VIEWGROUP);
         }
     }
 
-    private static class InflationVisitor extends ForwardingAstVisitor {
+    private static class InflationVisitor extends JavaRecursiveElementVisitor {
         private final JavaContext mContext;
-        private List<Node> mNodes;
+        private List<PsiElement> mNodes;
         private boolean mHaveConditional;
 
         public InflationVisitor(JavaContext context) {
@@ -173,23 +128,21 @@
         }
 
         @Override
-        public boolean visitMethodInvocation(MethodInvocation node) {
-            if (node.astOperand() != null) {
-                String methodName = node.astName().astValue();
-                if (methodName.equals(INFLATE) && node.astArguments().size() >= 1) {
+        public void visitMethodCallExpression(PsiMethodCallExpression node) {
+            super.visitMethodCallExpression(node);
+
+            PsiReferenceExpression expression = node.getMethodExpression();
+            if (expression.getQualifierExpression() != null) {
+                String methodName = expression.getReferenceName();
+                if (INFLATE.equals(methodName)
+                        && node.getArgumentList().getExpressions().length >= 1) {
                     // See if we're inside a conditional
                     boolean insideIf = false;
-                    Node p = node.getParent();
-                    while (p != null) {
-                        if (p instanceof If || p instanceof InlineIfExpression
-                                || p instanceof Switch) {
-                            insideIf = true;
-                            mHaveConditional = true;
-                            break;
-                        } else if (p == node) {
-                            break;
-                        }
-                        p = p.getParent();
+                    //noinspection unchecked
+                    if (PsiTreeUtil.getParentOfType(node, PsiConditionalExpression.class,
+                            PsiIfStatement.class, PsiSwitchStatement.class) != null) {
+                        insideIf = true;
+                        mHaveConditional = true;
                     }
                     if (!insideIf) {
                         // Rather than reporting immediately, we only report if we didn't
@@ -208,13 +161,11 @@
                     }
                 }
             }
-
-            return super.visitMethodInvocation(node);
         }
 
         public void finish() {
             if (!mHaveConditional && mNodes != null) {
-                for (Node node : mNodes) {
+                for (PsiElement node : mNodes) {
                     String message = "Unconditional layout inflation from view adapter: "
                             + "Should use View Holder pattern (use recycled view passed "
                             + "into this method as the second parameter) for smoother "
diff --git a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/ViewTagDetector.java b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/ViewTagDetector.java
index f4a15c7..ff879b3 100644
--- a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/ViewTagDetector.java
+++ b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/ViewTagDetector.java
@@ -21,30 +21,31 @@
 
 import com.android.annotations.NonNull;
 import com.android.annotations.Nullable;
-import com.android.tools.lint.client.api.JavaParser.ResolvedClass;
-import com.android.tools.lint.client.api.JavaParser.ResolvedMethod;
-import com.android.tools.lint.client.api.JavaParser.ResolvedNode;
-import com.android.tools.lint.client.api.JavaParser.TypeDescriptor;
+import com.android.tools.lint.client.api.JavaEvaluator;
 import com.android.tools.lint.detector.api.Category;
 import com.android.tools.lint.detector.api.Detector;
+import com.android.tools.lint.detector.api.Detector.JavaPsiScanner;
 import com.android.tools.lint.detector.api.Implementation;
 import com.android.tools.lint.detector.api.Issue;
 import com.android.tools.lint.detector.api.JavaContext;
 import com.android.tools.lint.detector.api.Scope;
 import com.android.tools.lint.detector.api.Severity;
 import com.android.tools.lint.detector.api.TypeEvaluator;
+import com.intellij.psi.JavaElementVisitor;
+import com.intellij.psi.PsiClass;
+import com.intellij.psi.PsiClassType;
+import com.intellij.psi.PsiExpression;
+import com.intellij.psi.PsiMethod;
+import com.intellij.psi.PsiMethodCallExpression;
+import com.intellij.psi.PsiType;
 
 import java.util.Collections;
 import java.util.List;
 
-import lombok.ast.AstVisitor;
-import lombok.ast.Expression;
-import lombok.ast.MethodInvocation;
-
 /**
  * Checks for missing view tag detectors
  */
-public class ViewTagDetector extends Detector implements Detector.JavaScanner {
+public class ViewTagDetector extends Detector implements JavaPsiScanner {
     /** Using setTag and leaking memory */
     public static final Issue ISSUE = Issue.create(
             "ViewTag", //$NON-NLS-1$
@@ -78,46 +79,43 @@
     }
 
     @Override
-    public void visitMethod(@NonNull JavaContext context, @Nullable AstVisitor visitor,
-            @NonNull MethodInvocation call) {
+    public void visitMethod(@NonNull JavaContext context, @Nullable JavaElementVisitor visitor,
+            @NonNull PsiMethodCallExpression call, @NonNull PsiMethod method) {
         // The leak behavior is fixed in ICS:
         // http://code.google.com/p/android/issues/detail?id=18273
         if (context.getMainProject().getMinSdk() >= 14) {
             return;
         }
 
-        ResolvedNode resolved = context.resolve(call);
-        if (!(resolved instanceof ResolvedMethod)) {
-            return;
-        }
-        ResolvedMethod method = (ResolvedMethod) resolved;
-        if (method.getArgumentCount() != 2) {
-            return;
-        }
-        Expression tagArgument = call.astArguments().last();
-        if (tagArgument == null) {
-            return;
-        }
-        ResolvedClass containingClass = method.getContainingClass();
-        if (!containingClass.matches(CLASS_VIEW)) {
+        JavaEvaluator evaluator = context.getEvaluator();
+        if (!evaluator.isMemberInSubClassOf(method, CLASS_VIEW, false)) {
             return;
         }
 
-        TypeDescriptor type = TypeEvaluator.evaluate(context, tagArgument);
-        if (type == null) {
+        PsiExpression[] arguments = call.getArgumentList().getExpressions();
+        if (arguments.length != 2) {
             return;
         }
-        ResolvedClass typeClass = type.getTypeClass();
+        PsiExpression tagArgument = arguments[1];
+        if (tagArgument == null) {
+            return;
+        }
+
+        PsiType type = TypeEvaluator.evaluate(context, tagArgument);
+        if ((!(type instanceof PsiClassType))) {
+            return;
+        }
+        PsiClass typeClass = ((PsiClassType) type).resolve();
         if (typeClass == null) {
             return;
         }
 
         String objectType;
-        if (typeClass.isSubclassOf(CLASS_VIEW, false)) {
+        if (evaluator.extendsClass(typeClass, CLASS_VIEW, false)) {
             objectType = "views";
-        } else if (typeClass.isImplementing(CURSOR_CLS, false)) {
+        } else if (evaluator.implementsInterface(typeClass, CURSOR_CLS, false)) {
             objectType = "cursors";
-        } else if (typeClass.getSimpleName().endsWith("ViewHolder")) {
+        } else if (typeClass.getName() != null && typeClass.getName().endsWith("ViewHolder")) {
             objectType = "view holders";
         } else {
             return;
diff --git a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/ViewTypeDetector.java b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/ViewTypeDetector.java
index 6718c43..631a118 100644
--- a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/ViewTypeDetector.java
+++ b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/ViewTypeDetector.java
@@ -29,16 +29,18 @@
 import com.android.ide.common.res2.AbstractResourceRepository;
 import com.android.ide.common.res2.ResourceFile;
 import com.android.ide.common.res2.ResourceItem;
+import com.android.ide.common.resources.ResourceUrl;
 import com.android.resources.ResourceFolderType;
 import com.android.resources.ResourceType;
 import com.android.tools.lint.client.api.LintClient;
 import com.android.tools.lint.detector.api.Category;
 import com.android.tools.lint.detector.api.Context;
-import com.android.tools.lint.detector.api.Detector.JavaScanner;
+import com.android.tools.lint.detector.api.Detector.JavaPsiScanner;
 import com.android.tools.lint.detector.api.Implementation;
 import com.android.tools.lint.detector.api.Issue;
 import com.android.tools.lint.detector.api.JavaContext;
 import com.android.tools.lint.detector.api.LintUtils;
+import com.android.tools.lint.detector.api.ResourceEvaluator;
 import com.android.tools.lint.detector.api.ResourceXmlDetector;
 import com.android.tools.lint.detector.api.Scope;
 import com.android.tools.lint.detector.api.Severity;
@@ -51,6 +53,16 @@
 import com.google.common.collect.Maps;
 import com.google.common.collect.Multimap;
 import com.google.common.collect.Sets;
+import com.intellij.psi.JavaElementVisitor;
+import com.intellij.psi.PsiClassType;
+import com.intellij.psi.PsiElement;
+import com.intellij.psi.PsiExpression;
+import com.intellij.psi.PsiMethod;
+import com.intellij.psi.PsiMethodCallExpression;
+import com.intellij.psi.PsiParenthesizedExpression;
+import com.intellij.psi.PsiType;
+import com.intellij.psi.PsiTypeCastExpression;
+import com.intellij.psi.PsiTypeElement;
 
 import org.w3c.dom.Attr;
 import org.w3c.dom.Document;
@@ -68,13 +80,6 @@
 import java.util.Map;
 import java.util.Set;
 
-import lombok.ast.AstVisitor;
-import lombok.ast.Cast;
-import lombok.ast.Expression;
-import lombok.ast.MethodInvocation;
-import lombok.ast.Select;
-import lombok.ast.StrictListAccessor;
-
 /** Detector for finding inconsistent usage of views and casts
  * <p>
  * TODO: Check findFragmentById
@@ -87,7 +92,7 @@
  * check its name or class attributes to make sure the cast is compatible with
  * the named fragment class!
  */
-public class ViewTypeDetector extends ResourceXmlDetector implements JavaScanner {
+public class ViewTypeDetector extends ResourceXmlDetector implements JavaPsiScanner {
     /** Mismatched view types */
     @SuppressWarnings("unchecked")
     public static final Issue ISSUE = Issue.create(
@@ -171,8 +176,8 @@
     }
 
     @Override
-    public void visitMethod(@NonNull JavaContext context, @Nullable AstVisitor visitor,
-            @NonNull MethodInvocation node) {
+    public void visitMethod(@NonNull JavaContext context, @Nullable JavaElementVisitor visitor,
+            @NonNull PsiMethodCallExpression call, @NonNull PsiMethod method) {
         LintClient client = context.getClient();
         if (mIgnore == Boolean.TRUE) {
             return;
@@ -183,53 +188,67 @@
                 return;
             }
         }
-        assert node.astName().astValue().equals("findViewById");
-        if (node.getParent() instanceof Cast) {
-            Cast cast = (Cast) node.getParent();
-            String castType = cast.astTypeReference().getTypeName();
-            StrictListAccessor<Expression, MethodInvocation> args = node.astArguments();
-            if (args.size() == 1) {
-                Expression first = args.first();
-                // TODO: Do flow analysis as in the StringFormatDetector in order
-                // to handle variable references too
-                if (first instanceof Select) {
-                    String resource = first.toString();
-                    if (resource.startsWith("R.id.")) { //$NON-NLS-1$
-                        String id = ((Select) first).astIdentifier().astValue();
+        assert method.getName().equals("findViewById");
+        PsiElement node = call;
+        while (node != null && node.getParent() instanceof PsiParenthesizedExpression) {
+            node = node.getParent();
+        }
+        if (node.getParent() instanceof PsiTypeCastExpression) {
+            PsiTypeCastExpression cast = (PsiTypeCastExpression) node.getParent();
+            PsiTypeElement castTypeElement = cast.getCastType();
+            if (castTypeElement == null) {
+                return;
+            }
+            PsiType type = castTypeElement.getType();
+            String castType = null;
+            if (type instanceof PsiClassType) {
+                castType = type.getCanonicalText();
+            }
+            if (castType == null) {
+                return;
+            }
 
-                        if (client.supportsProjectResources()) {
-                            AbstractResourceRepository resources = client
-                                    .getProjectResources(context.getMainProject(), true);
-                            if (resources == null) {
-                                return;
-                            }
+            PsiExpression[] args = call.getArgumentList().getExpressions();
+            if (args.length == 1) {
+                PsiExpression first = args[0];
+                ResourceUrl resourceUrl = ResourceEvaluator.getResource(context.getEvaluator(),
+                        first);
+                if (resourceUrl != null && resourceUrl.type == ResourceType.ID &&
+                        !resourceUrl.framework) {
+                    String id = resourceUrl.name;
 
-                            List<ResourceItem> items = resources.getResourceItem(ResourceType.ID,
-                                    id);
-                            if (items != null && !items.isEmpty()) {
-                                Set<String> compatible = Sets.newHashSet();
-                                for (ResourceItem item : items) {
-                                    Collection<String> tags = getViewTags(context, item);
-                                    if (tags != null) {
-                                       compatible.addAll(tags);
-                                    }
-                                }
-                                if (!compatible.isEmpty()) {
-                                    ArrayList<String> layoutTypes = Lists.newArrayList(compatible);
-                                    checkCompatible(context, castType, null, layoutTypes, cast);
+                    if (client.supportsProjectResources()) {
+                        AbstractResourceRepository resources = client
+                                .getProjectResources(context.getMainProject(), true);
+                        if (resources == null) {
+                            return;
+                        }
+
+                        List<ResourceItem> items = resources.getResourceItem(ResourceType.ID,
+                                id);
+                        if (items != null && !items.isEmpty()) {
+                            Set<String> compatible = Sets.newHashSet();
+                            for (ResourceItem item : items) {
+                                Collection<String> tags = getViewTags(context, item);
+                                if (tags != null) {
+                                   compatible.addAll(tags);
                                 }
                             }
-                        } else {
-                            Object types = mIdToViewTag.get(id);
-                            if (types instanceof String) {
-                                String layoutType = (String) types;
-                                checkCompatible(context, castType, layoutType, null, cast);
-                            } else if (types instanceof List<?>) {
-                                @SuppressWarnings("unchecked")
-                                List<String> layoutTypes = (List<String>) types;
+                            if (!compatible.isEmpty()) {
+                                ArrayList<String> layoutTypes = Lists.newArrayList(compatible);
                                 checkCompatible(context, castType, null, layoutTypes, cast);
                             }
                         }
+                    } else {
+                        Object types = mIdToViewTag.get(id);
+                        if (types instanceof String) {
+                            String layoutType = (String) types;
+                            checkCompatible(context, castType, layoutType, null, cast);
+                        } else if (types instanceof List<?>) {
+                            @SuppressWarnings("unchecked")
+                            List<String> layoutTypes = (List<String>) types;
+                            checkCompatible(context, castType, null, layoutTypes, cast);
+                        }
                     }
                 }
             }
@@ -301,7 +320,7 @@
 
     /** Check if the view and cast type are compatible */
     private static void checkCompatible(JavaContext context, String castType, String layoutType,
-            List<String> layoutTypes, Cast node) {
+            List<String> layoutTypes, PsiTypeCastExpression node) {
         assert layoutType == null || layoutTypes == null; // Should only specify one or the other
         boolean compatible = true;
         if (layoutType != null) {
@@ -327,7 +346,7 @@
             }
             String message = String.format(
                     "Unexpected cast to `%1$s`: layout tag was `%2$s`",
-                    castType, layoutType);
+                    castType.substring(castType.lastIndexOf('.') + 1), layoutType);
             context.report(ISSUE, node, context.getLocation(node), message);
         }
     }
diff --git a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/WrongCallDetector.java b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/WrongCallDetector.java
index 77acff3..a292a6f 100644
--- a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/WrongCallDetector.java
+++ b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/WrongCallDetector.java
@@ -23,34 +23,30 @@
 
 import com.android.annotations.NonNull;
 import com.android.annotations.Nullable;
-import com.android.tools.lint.client.api.JavaParser.ResolvedClass;
-import com.android.tools.lint.client.api.JavaParser.ResolvedMethod;
-import com.android.tools.lint.client.api.JavaParser.ResolvedNode;
 import com.android.tools.lint.detector.api.Category;
 import com.android.tools.lint.detector.api.Detector;
+import com.android.tools.lint.detector.api.Detector.JavaPsiScanner;
 import com.android.tools.lint.detector.api.Implementation;
 import com.android.tools.lint.detector.api.Issue;
 import com.android.tools.lint.detector.api.JavaContext;
 import com.android.tools.lint.detector.api.LintUtils;
 import com.android.tools.lint.detector.api.Scope;
 import com.android.tools.lint.detector.api.Severity;
-import com.android.tools.lint.detector.api.Speed;
 import com.android.tools.lint.detector.api.TextFormat;
+import com.intellij.psi.JavaElementVisitor;
+import com.intellij.psi.PsiElement;
+import com.intellij.psi.PsiMethod;
+import com.intellij.psi.PsiMethodCallExpression;
+import com.intellij.psi.PsiSuperExpression;
+import com.intellij.psi.util.PsiTreeUtil;
 
 import java.util.Arrays;
 import java.util.List;
 
-import lombok.ast.AstVisitor;
-import lombok.ast.Expression;
-import lombok.ast.MethodDeclaration;
-import lombok.ast.MethodInvocation;
-import lombok.ast.Node;
-import lombok.ast.Super;
-
 /**
  * Checks for cases where the wrong call is being made
  */
-public class WrongCallDetector extends Detector implements Detector.JavaScanner {
+public class WrongCallDetector extends Detector implements JavaPsiScanner {
     /** Calling the wrong method */
     public static final Issue ISSUE = Issue.create(
             "WrongCall", //$NON-NLS-1$
@@ -70,12 +66,6 @@
     public WrongCallDetector() {
     }
 
-    @NonNull
-    @Override
-    public Speed getSpeed() {
-        return Speed.FAST;
-    }
-
     // ---- Implements JavaScanner ----
 
     @Override
@@ -89,44 +79,42 @@
     }
 
     @Override
-    public void visitMethod(@NonNull JavaContext context, @Nullable AstVisitor visitor,
-            @NonNull MethodInvocation node) {
-
+    public void visitMethod(@NonNull JavaContext context, @Nullable JavaElementVisitor visitor,
+            @NonNull PsiMethodCallExpression node, @NonNull PsiMethod calledMethod) {
         // Call is only allowed if it is both only called on the super class (invoke special)
         // as well as within the same overriding method (e.g. you can't call super.onLayout
         // from the onMeasure method)
-        Expression operand = node.astOperand();
-        if (!(operand instanceof Super)) {
-            report(context, node);
+        PsiElement operand = node.getMethodExpression().getQualifier();
+        if (!(operand instanceof PsiSuperExpression)) {
+            report(context, node, calledMethod);
             return;
         }
 
-        Node method = StringFormatDetector.getParentMethod(node);
-        if (!(method instanceof MethodDeclaration) ||
-                !((MethodDeclaration)method).astMethodName().astValue().equals(
-                        node.astName().astValue())) {
-            report(context, node);
+        PsiMethod method = PsiTreeUtil.getParentOfType(node, PsiMethod.class, true);
+        if (method != null) {
+            String callName = node.getMethodExpression().getReferenceName();
+            if (callName != null && !callName.equals(method.getName())) {
+                report(context, node, calledMethod);
+            }
         }
     }
 
-    private static void report(JavaContext context, MethodInvocation node) {
+    private static void report(
+            @NonNull JavaContext context,
+            @NonNull PsiMethodCallExpression node,
+            @NonNull PsiMethod method) {
         // Make sure the call is on a view
-        ResolvedNode resolved = context.resolve(node);
-        if (resolved instanceof ResolvedMethod) {
-            ResolvedMethod method = (ResolvedMethod) resolved;
-            ResolvedClass containingClass = method.getContainingClass();
-            if (!containingClass.isSubclassOf(CLASS_VIEW, false)) {
-                return;
-            }
+        if (!context.getEvaluator().isMemberInSubClassOf(method, CLASS_VIEW, false)) {
+            return;
         }
 
-        String name = node.astName().astValue();
+        String name = method.getName();
         String suggestion = Character.toLowerCase(name.charAt(2)) + name.substring(3);
         String message = String.format(
                 // Keep in sync with {@link #getOldValue} and {@link #getNewValue} below!
                 "Suspicious method call; should probably call \"`%1$s`\" rather than \"`%2$s`\"",
                 suggestion, name);
-        context.report(ISSUE, node, context.getLocation(node.astName()), message);
+        context.report(ISSUE, node, context.getNameLocation(node), message);
     }
 
     /**
diff --git a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/WrongImportDetector.java b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/WrongImportDetector.java
index 2c17555..c2659fc 100644
--- a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/WrongImportDetector.java
+++ b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/WrongImportDetector.java
@@ -19,22 +19,20 @@
 import com.android.annotations.NonNull;
 import com.android.tools.lint.detector.api.Category;
 import com.android.tools.lint.detector.api.Detector;
+import com.android.tools.lint.detector.api.Detector.JavaPsiScanner;
 import com.android.tools.lint.detector.api.Implementation;
 import com.android.tools.lint.detector.api.Issue;
 import com.android.tools.lint.detector.api.JavaContext;
 import com.android.tools.lint.detector.api.Location;
 import com.android.tools.lint.detector.api.Scope;
 import com.android.tools.lint.detector.api.Severity;
-import com.android.tools.lint.detector.api.Speed;
+import com.intellij.psi.JavaElementVisitor;
+import com.intellij.psi.PsiElement;
+import com.intellij.psi.PsiImportStatement;
 
 import java.util.Collections;
 import java.util.List;
 
-import lombok.ast.AstVisitor;
-import lombok.ast.ForwardingAstVisitor;
-import lombok.ast.ImportDeclaration;
-import lombok.ast.Node;
-
 /**
  * Checks for "import android.R", which seems to be a common source of confusion
  * (see for example http://stackoverflow.com/questions/885009/r-cannot-be-resolved-android-error
@@ -47,7 +45,7 @@
  * break. Look out for these erroneous import statements and delete them.
  * </blockquote>
  */
-public class WrongImportDetector extends Detector implements Detector.JavaScanner {
+public class WrongImportDetector extends Detector implements JavaPsiScanner {
     /** Is android.R being imported? */
     public static final Issue ISSUE = Issue.create(
             "SuspiciousImport", //$NON-NLS-1$
@@ -70,26 +68,20 @@
     public WrongImportDetector() {
     }
 
-    @NonNull
-    @Override
-    public Speed getSpeed() {
-        return Speed.FAST;
-    }
-
-    // ---- Implements Detector.JavaScanner ----
+    // ---- Implements JavaScanner ----
 
     @Override
-    public List<Class<? extends Node>> getApplicableNodeTypes() {
-        return Collections.<Class<? extends Node>> singletonList(
-                ImportDeclaration.class);
+    public List<Class<? extends PsiElement>> getApplicablePsiTypes() {
+        return Collections.<Class<? extends PsiElement>>singletonList(PsiImportStatement.class);
     }
 
     @Override
-    public AstVisitor createJavaVisitor(@NonNull JavaContext context) {
+    public JavaElementVisitor createPsiVisitor(@NonNull JavaContext context) {
         return new ImportVisitor(context);
     }
 
-    private static class ImportVisitor extends ForwardingAstVisitor {
+
+    private static class ImportVisitor extends JavaElementVisitor {
         private final JavaContext mContext;
 
         public ImportVisitor(JavaContext context) {
@@ -98,15 +90,14 @@
         }
 
         @Override
-        public boolean visitImportDeclaration(ImportDeclaration node) {
-            String fqn = node.asFullyQualifiedName();
-            if (fqn.equals("android.R")) { //$NON-NLS-1$
-                Location location = mContext.getLocation(node);
-                mContext.report(ISSUE, node, location,
-                    "Don't include `android.R` here; use a fully qualified name for "
-                            + "each usage instead");
+        public void visitImportStatement(PsiImportStatement statement) {
+            String qualifiedName = statement.getQualifiedName();
+            if ("android.R".equals(qualifiedName)) {
+                Location location = mContext.getLocation(statement);
+                mContext.report(ISSUE, statement, location,
+                        "Don't include `android.R` here; use a fully qualified name for "
+                                + "each usage instead");
             }
-            return false;
         }
     }
 }
diff --git a/lint/libs/lint-tests/src/main/java/com/android/tools/lint/checks/infrastructure/LintDetectorTest.java b/lint/libs/lint-tests/src/main/java/com/android/tools/lint/checks/infrastructure/LintDetectorTest.java
index 3d3bbc4..889331c 100644
--- a/lint/libs/lint-tests/src/main/java/com/android/tools/lint/checks/infrastructure/LintDetectorTest.java
+++ b/lint/libs/lint-tests/src/main/java/com/android/tools/lint/checks/infrastructure/LintDetectorTest.java
@@ -49,12 +49,12 @@
 import com.android.tools.lint.client.api.DefaultConfiguration;
 import com.android.tools.lint.client.api.IssueRegistry;
 import com.android.tools.lint.client.api.JavaParser;
+import com.android.tools.lint.client.api.JavaPsiVisitor;
 import com.android.tools.lint.client.api.JavaVisitor;
 import com.android.tools.lint.client.api.LintClient;
 import com.android.tools.lint.client.api.LintDriver;
 import com.android.tools.lint.client.api.LintRequest;
 import com.android.tools.lint.detector.api.Context;
-import com.android.tools.lint.detector.api.DefaultPosition;
 import com.android.tools.lint.detector.api.Detector;
 import com.android.tools.lint.detector.api.Issue;
 import com.android.tools.lint.detector.api.JavaContext;
@@ -64,6 +64,7 @@
 import com.android.tools.lint.detector.api.Scope;
 import com.android.tools.lint.detector.api.Severity;
 import com.android.tools.lint.detector.api.TextFormat;
+import com.android.tools.lint.psi.EcjPsiBuilder;
 import com.android.utils.FileUtils;
 import com.android.utils.ILogger;
 import com.android.utils.SdkUtils;
@@ -101,6 +102,7 @@
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.StringWriter;
+import java.lang.reflect.Field;
 import java.net.MalformedURLException;
 import java.net.URL;
 import java.security.CodeSource;
@@ -130,12 +132,12 @@
 @Beta
 @SuppressWarnings("javadoc")
 public abstract class LintDetectorTest extends SdkTestCase {
-
     @Override
     protected void setUp() throws Exception {
         super.setUp();
         BuiltinIssueRegistry.reset();
         JavaVisitor.clearCrashCount();
+        JavaPsiVisitor.clearCrashCount();
     }
 
     @Override
@@ -152,7 +154,9 @@
         }
         for (Issue issue : issues) {
             if (issue.getImplementation().getScope().contains(Scope.JAVA_FILE)) {
-                assertEquals(0, JavaVisitor.getCrashCount());
+                if (JavaVisitor.getCrashCount() > 0 || JavaPsiVisitor.getCrashCount() > 0) {
+                    fail("There was a crash during lint execution; consult log for details");
+                }
                 break;
             }
         }
@@ -174,6 +178,18 @@
         return false;
     }
 
+    /**
+     * If false (the default), lint will run your detectors <b>twice</b>, first on the
+     * plain source code, and then a second time where it has inserted whitespace
+     * and parentheses pretty much everywhere, to help catch bugs where your detector
+     * is only checking direct parents or siblings rather than properly allowing for
+     * whitespace and parenthesis nodes which can be present for example when using
+     * PSI inside the IDE.
+     */
+    protected boolean skipExtraTokenChecks() {
+        return false;
+    }
+
     protected abstract List<Issue> getIssues();
 
     public class CustomIssueRegistry extends IssueRegistry {
@@ -227,6 +243,37 @@
         mOutput = new StringBuilder();
         String result = lintClient.analyze(files);
 
+        if (getDetector() instanceof Detector.JavaPsiScanner && !skipExtraTokenChecks()) {
+            mOutput.setLength(0);
+            lintClient.reset();
+            try {
+                //lintClient.mWarnings.clear();
+                Field field = LintCliClient.class.getDeclaredField("mWarnings");
+                field.setAccessible(true);
+                List list = (List)field.get(lintClient);
+                list.clear();
+            } catch (Throwable t) {
+                fail(t.toString());
+            }
+
+            String secondResult;
+            try {
+                EcjPsiBuilder.setDebugOptions(true, true);
+                secondResult = lintClient.analyze(files);
+            } finally {
+                EcjPsiBuilder.setDebugOptions(false, false);
+            }
+
+            assertEquals("The lint check produced different results when run on the "
+                    + "normal test files and a version where parentheses and whitespace tokens "
+                    + "have been inserted everywhere. The lint check should be resilient towards "
+                    + "these kinds of differences (since in the IDE, PSI will include both "
+                    + "types of nodes. Your detector should call LintUtils.skipParenthes(parent) "
+                    + "to jump across parentheses nodes when checking parents, and there are "
+                    + "similar methods in LintUtils to skip across whitespace siblings.\n",
+                    result, secondResult);
+        }
+
         // The output typically contains a few directory/filenames.
         // On Windows we need to change the separators to the unix-style
         // forward slash to make the test as OS-agnostic as possible.
@@ -828,6 +875,12 @@
             return super.getSuperClass(project, name);
         }
 
+        @Override
+        public void reset() {
+            super.reset();
+            mWriter.getBuffer().setLength(0);
+        }
+
         public String analyze(List<File> files) throws Exception {
             mDriver = new LintDriver(new CustomIssueRegistry(), this);
             configureDriver(mDriver);
@@ -907,45 +960,32 @@
                 public void prepareJavaParse(@NonNull List<JavaContext> contexts) {
                     super.prepareJavaParse(contexts);
                     if (!allowCompilationErrors() && mEcjResult != null) {
-                        assertTrue(!contexts.isEmpty());
-                        JavaContext first = contexts.get(0);
-                        boolean foundErrors = false;
+                        StringBuilder sb = new StringBuilder();
                         for (CompilationUnitDeclaration unit : mEcjResult.getCompilationUnits()) {
                             // so maybe I don't need my map!!
                             CategorizedProblem[] problems = unit.compilationResult()
                                     .getAllProblems();
-                            if (problems != null && problems.length > 0) {
+                            if (problems != null) {
                                 for (IProblem problem : problems) {
                                     if (problem == null || !problem.isError()) {
                                         continue;
                                     }
-
-                                    foundErrors = true;
-                                    char[] path = problem.getOriginatingFileName();
-                                    File file = new File(new String(path));
-                                    JavaContext context = new JavaContext(first.getDriver(),
-                                                first.getProject(), first.getMainProject(),
-                                                file, first.getParser());
-                                    Location location = Location.create(context.file,
-                                            new DefaultPosition(problem.getSourceLineNumber() - 1,
-                                                    -1, problem.getSourceStart()),
-                                            new DefaultPosition(problem.getSourceLineNumber() - 1,
-                                                    -1, problem.getSourceEnd()));
-                                    String message = problem.getMessage();
-                                    context.report(IssueRegistry.PARSER_ERROR, location,
-                                            message);
+                                    String filename = new File(new String(
+                                            problem.getOriginatingFileName())).getName();
+                                    sb.append(filename)
+                                            .append(":")
+                                            .append(problem.isError() ? "Error" : "Warning")
+                                            .append(": ").append(problem.getSourceLineNumber())
+                                            .append(": ").append(problem.getMessage())
+                                            .append('\n');
                                 }
                             }
                         }
-                        if (foundErrors) {
-                            Context context = new Context(first.getDriver(),
-                                    first.getProject(), first.getMainProject(),
-                                    first.getProject().getDir());
-                            context.report(IssueRegistry.PARSER_ERROR,
-                                    Location.create(context.file),
-                                    "Found compilation problems in lint test not overriding "
-                                            + "allowCompilationErrors()");
+                        if (sb.length() > 0) {
+                            fail("Found compilation problems in lint test not overriding "
+                                    + "allowCompilationErrors():\n" + sb);
                         }
+
                     }
                 }
             };
@@ -962,7 +1002,6 @@
             if (ignoreSystemErrors() && (issue == IssueRegistry.LINT_ERROR)) {
                 return;
             }
-
             // Use plain ascii in the test golden files for now. (This also ensures
             // that the markup is well-formed, e.g. if we have a ` without a matching
             // closing `, the ` would show up in the plain text.)
@@ -1019,7 +1058,9 @@
             System.err.println(sb);
 
             if (exception != null) {
-                fail(exception.toString());
+                // Ensure that we get the full cause
+                //fail(exception.toString());
+                throw new RuntimeException(exception);
             }
         }
 
diff --git a/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/AddJavascriptInterfaceDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/AddJavascriptInterfaceDetectorTest.java
index 03bb482..32b7639 100644
--- a/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/AddJavascriptInterfaceDetectorTest.java
+++ b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/AddJavascriptInterfaceDetectorTest.java
@@ -37,20 +37,8 @@
             + "0 errors, 2 warnings\n",
 
             lintProject(
-                "bytecode/.classpath=>.classpath",
-                "bytecode/AndroidManifest.xml=>AndroidManifest.xml",
-                "bytecode/AddJavascriptInterfaceTest.java.txt=>src/test/pkg/AddJavascriptInterfaceTest.java",
-                "bytecode/AddJavascriptInterfaceTest.class.data=>bin/classes/test/pkg/AddJavascriptInterfaceTest.class",
-                "bytecode/AddJavascriptInterfaceTest$CallAddJavascriptInterfaceOnWebView.class.data=>"
-                        + "bin/classes/test/pkg/AddJavascriptInterfaceTest$CallAddJavascriptInterfaceOnWebView.class",
-                "bytecode/AddJavascriptInterfaceTest$WebViewChild.class.data=>"
-                        + "bin/classes/test/pkg/AddJavascriptInterfaceTest$WebViewChild.class",
-                "bytecode/AddJavascriptInterfaceTest$CallAddJavascriptInterfaceOnWebViewChild.class.data=>"
-                        + "bin/classes/test/pkg/AddJavascriptInterfaceTest$CallAddJavascriptInterfaceOnWebViewChild.class",
-                "bytecode/AddJavascriptInterfaceTest$NonWebView.class.data=>"
-                        + "bin/classes/test/pkg/AddJavascriptInterfaceTest$NonWebView.class",
-                "bytecode/AddJavascriptInterfaceTest$CallAddJavascriptInterfaceOnNonWebView.class.data=>"
-                        + "bin/classes/test/pkg/AddJavascriptInterfaceTest$CallAddJavascriptInterfaceOnNonWebView.class"
+                    manifest().minSdk(10),
+                    mTestFile
             ));
     }
 
@@ -58,12 +46,48 @@
         assertEquals(
             "No warnings.",
             lintProject(
-                "bytecode/.classpath=>.classpath",
-                "bytecode/AndroidManifestMinSdk17.xml=>AndroidManifest.xml",
-                "bytecode/AddJavascriptInterfaceTest.java.txt=>src/test/pkg/AddJavascriptInterfaceTest.java",
-                "bytecode/AddJavascriptInterfaceTest.class.data=>bin/classes/test/pkg/AddJavascriptInterfaceTest.class",
-                "bytecode/AddJavascriptInterfaceTest$CallAddJavascriptInterfaceOnWebView.class.data=>"
-                        + "bin/classes/test/pkg/AddJavascriptInterfaceTest$CallAddJavascriptInterfaceOnWebView.class"
+                    manifest().minSdk(17),
+                    mTestFile
             ));
     }
+
+    @SuppressWarnings("all")
+    private TestFile mTestFile = java("src/test/pkg/AddJavascriptInterfaceTest.java", ""
+            + "package test.pkg;\n"
+            + "\n"
+            + "import android.webkit.WebView;\n"
+            + "import android.content.Context;\n"
+            + "\n"
+            + "\n"
+            + "public class AddJavascriptInterfaceTest {\n"
+            + "    private static class WebViewChild extends WebView {\n"
+            + "        WebViewChild(Context context) {\n"
+            + "            super(context);\n"
+            + "        }\n"
+            + "    }\n"
+            + "\n"
+            + "    private static class CallAddJavascriptInterfaceOnWebView {\n"
+            + "        public void addJavascriptInterfaceToWebView(WebView webView, Object object, String string) {\n"
+            + "            webView.addJavascriptInterface(object, string);\n"
+            + "        }\n"
+            + "    }\n"
+            + "\n"
+            + "    private static class CallAddJavascriptInterfaceOnWebViewChild {\n"
+            + "        public void addJavascriptInterfaceToWebViewChild(\n"
+            + "            WebViewChild webView, Object object, String string) {\n"
+            + "            webView.addJavascriptInterface(object, string);\n"
+            + "        }\n"
+            + "    }\n"
+            + "\n"
+            + "    private static class NonWebView {\n"
+            + "        public void addJavascriptInterface(Object object, String string) { }\n"
+            + "    }\n"
+            + "\n"
+            + "    private static class CallAddJavascriptInterfaceOnNonWebView {\n"
+            + "        public void addJavascriptInterfaceToNonWebView(\n"
+            + "            NonWebView webView, Object object, String string) {\n"
+            + "            webView.addJavascriptInterface(object, string);\n"
+            + "        }\n"
+            + "    }\n"
+            + "}");
 }
diff --git a/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/AlarmDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/AlarmDetectorTest.java
index 2d69a36..66d9acf 100644
--- a/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/AlarmDetectorTest.java
+++ b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/AlarmDetectorTest.java
@@ -24,6 +24,7 @@
         return new AlarmDetector();
     }
 
+    @SuppressWarnings("ClassNameDiffersFromFileName")
     public void test() throws Exception {
         assertEquals(""
                 + "src/test/pkg/AlarmTest.java:9: Warning: Value will be forced up to 5000 as of Android 5.1; don't rely on this to be exact [ShortAlarm]\n"
@@ -40,6 +41,29 @@
                 + "                                                                       ~~~~~~~~~\n"
                 + "0 errors, 4 warnings\n",
 
-                lintProject("src/test/pkg/AlarmTest.java.txt=>src/test/pkg/AlarmTest.java"));
+                lintProject(
+                        java("src/test/pkg/AlarmTest.java", ""
+                                + "package test.pkg;\n"
+                                + "\n"
+                                + "import android.app.AlarmManager;\n"
+                                + "\n"
+                                + "public class AlarmTest {\n"
+                                + "    public void test(AlarmManager alarmManager) {\n"
+                                + "        alarmManager.setRepeating(AlarmManager.ELAPSED_REALTIME, 5000, 60000, null); // OK\n"
+                                + "        alarmManager.setRepeating(AlarmManager.ELAPSED_REALTIME, 6000, 70000, null); // OK\n"
+                                + "        alarmManager.setRepeating(AlarmManager.ELAPSED_REALTIME, 50, 10, null); // ERROR\n"
+                                + "        alarmManager.setRepeating(AlarmManager.ELAPSED_REALTIME, 5000,  // ERROR\n"
+                                + "                OtherClass.MY_INTERVAL, null);                          // ERROR\n"
+                                + "\n"
+                                + "        // Check value flow analysis\n"
+                                + "        int interval = 10;\n"
+                                + "        long interval2 = 2 * interval;\n"
+                                + "        alarmManager.setRepeating(AlarmManager.ELAPSED_REALTIME, 5000, interval2, null); // ERROR\n"
+                                + "    }\n"
+                                + "\n"
+                                + "    private static class OtherClass {\n"
+                                + "        public static final long MY_INTERVAL = 1000L;\n"
+                                + "    }\n"
+                                + "}\n")));
     }
 }
diff --git a/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/AnnotationDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/AnnotationDetectorTest.java
index 134ab73..75f2c78 100644
--- a/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/AnnotationDetectorTest.java
+++ b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/AnnotationDetectorTest.java
@@ -35,26 +35,29 @@
 
 @SuppressWarnings({"javadoc", "ClassNameDiffersFromFileName", "UnnecessaryLocalVariable",
         "ConstantConditionalExpression", "StatementWithEmptyBody", "RedundantCast",
-        "MethodMayBeStatic"})
+        "MethodMayBeStatic", "OnDemandImport"})
 public class AnnotationDetectorTest extends AbstractCheckTest {
     public void test() throws Exception {
-        assertEquals(
-            "src/test/pkg/WrongAnnotation.java:9: Error: The @SuppressLint annotation cannot be used on a local variable with the lint check 'NewApi': move out to the surrounding method [LocalSuppress]\n" +
-            "    public static void foobar(View view, @SuppressLint(\"NewApi\") int foo) { // Invalid: class-file check\n" +
-            "                                         ~~~~~~~~~~~~~~~~~~~~~~~\n" +
-            "src/test/pkg/WrongAnnotation.java:10: Error: The @SuppressLint annotation cannot be used on a local variable with the lint check 'NewApi': move out to the surrounding method [LocalSuppress]\n" +
-            "        @SuppressLint(\"NewApi\") // Invalid\n" +
-            "        ~~~~~~~~~~~~~~~~~~~~~~~\n" +
-            "src/test/pkg/WrongAnnotation.java:12: Error: The @SuppressLint annotation cannot be used on a local variable with the lint check 'NewApi': move out to the surrounding method [LocalSuppress]\n" +
-            "        @SuppressLint({\"SdCardPath\", \"NewApi\"}) // Invalid: class-file based check on local variable\n" +
-            "        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" +
-            "src/test/pkg/WrongAnnotation.java:14: Error: The @SuppressLint annotation cannot be used on a local variable with the lint check 'NewApi': move out to the surrounding method [LocalSuppress]\n" +
-            "        @android.annotation.SuppressLint({\"SdCardPath\", \"NewApi\"}) // Invalid (FQN)\n" +
-            "        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" +
-            "src/test/pkg/WrongAnnotation.java:28: Error: The @SuppressLint annotation cannot be used on a local variable with the lint check 'NewApi': move out to the surrounding method [LocalSuppress]\n" +
-            "        @SuppressLint(\"NewApi\")\n" +
-            "        ~~~~~~~~~~~~~~~~~~~~~~~\n" +
-            "5 errors, 0 warnings\n",
+        assertEquals(""
+                + "src/test/pkg/WrongAnnotation.java:9: Error: The @SuppressLint annotation cannot be used on a local variable with the lint check 'NewApi': move out to the surrounding method [LocalSuppress]\n"
+                + "    public static void foobar(View view, @SuppressLint(\"NewApi\") int foo) { // Invalid: class-file check\n"
+                + "                                         ~~~~~~~~~~~~~~~~~~~~~~~\n"
+                + "src/test/pkg/WrongAnnotation.java:10: Error: The @SuppressLint annotation cannot be used on a local variable with the lint check 'NewApi': move out to the surrounding method [LocalSuppress]\n"
+                + "        @SuppressLint(\"NewApi\") // Invalid\n"
+                + "        ~~~~~~~~~~~~~~~~~~~~~~~\n"
+                + "src/test/pkg/WrongAnnotation.java:12: Error: The @SuppressLint annotation cannot be used on a local variable with the lint check 'NewApi': move out to the surrounding method [LocalSuppress]\n"
+                + "        @SuppressLint({\"SdCardPath\", \"NewApi\"}) // Invalid: class-file based check on local variable\n"
+                + "        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
+                + "src/test/pkg/WrongAnnotation.java:14: Error: The @SuppressLint annotation cannot be used on a local variable with the lint check 'NewApi': move out to the surrounding method [LocalSuppress]\n"
+                + "        @android.annotation.SuppressLint({\"SdCardPath\", \"NewApi\"}) // Invalid (FQN)\n"
+                + "        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
+                + "src/test/pkg/WrongAnnotation.java:28: Error: The @SuppressLint annotation cannot be used on a local variable with the lint check 'NewApi': move out to the surrounding method [LocalSuppress]\n"
+                + "        @SuppressLint(\"NewApi\")\n"
+                + "        ~~~~~~~~~~~~~~~~~~~~~~~\n"
+                + "src/test/pkg/WrongAnnotation.java:33: Error: The @SuppressLint annotation cannot be used on a local variable with the lint check 'NewApi': move out to the surrounding method [LocalSuppress]\n"
+                + "        @SuppressLint(\"NewApi\") // Invalid\n"
+                + "        ~~~~~~~~~~~~~~~~~~~~~~~\n"
+                + "6 errors, 0 warnings\n",
 
             lintProject(
                 "src/test/pkg/WrongAnnotation.java.txt=>src/test/pkg/WrongAnnotation.java"
@@ -342,10 +345,9 @@
         ));
     }
 
-
     public void testMissingSwitchFailingIntDef() throws Exception {
         assertEquals(""
-                + "src/test/pkg/X.java:8: Warning: Switch statement on an int with known associated constant missing case EXACTLY, UNSPECIFIED [SwitchIntDef]\n"
+                + "src/test/pkg/X.java:8: Warning: Switch statement on an int with known associated constant missing case MeasureSpec.EXACTLY, MeasureSpec.UNSPECIFIED [SwitchIntDef]\n"
                 + "        switch (val) {\n"
                 + "        ~~~~~~\n"
                 + "0 errors, 1 warnings\n",
@@ -410,37 +412,46 @@
     @SuppressWarnings("ClassNameDiffersFromFileName")
     public void testWrongUsages() throws Exception {
         assertEquals(""
-                        + "src/test/pkg/WrongUsages.java:33: Error: This annotation does not apply for type String; expected int or long [SupportAnnotationUsage]\n"
-                        + "    @DialogStyle\n"
-                        + "    ~~~~~~~~~~~~\n"
-                        + "src/test/pkg/WrongUsages.java:38: Error: Invalid range: the from attribute must be less than the to attribute [SupportAnnotationUsage]\n"
-                        + "    @IntRange(from = 1, to = 0)\n"
-                        + "    ~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
-                        + "src/test/pkg/WrongUsages.java:38: Error: This annotation does not apply for type String; expected int or long [SupportAnnotationUsage]\n"
-                        + "    @IntRange(from = 1, to = 0)\n"
-                        + "    ~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
-                        + "src/test/pkg/WrongUsages.java:39: Error: Invalid size range: the min attribute must be less than the max attribute [SupportAnnotationUsage]\n"
-                        + "    @Size(min=10, max = 8)\n"
-                        + "    ~~~~~~~~~~~~~~~~~~~~~~\n"
-                        + "src/test/pkg/WrongUsages.java:44: Error: Invalid range: the from attribute must be less than the to attribute [SupportAnnotationUsage]\n"
-                        + "    @FloatRange(from = 1.0, to = 0.0)\n"
-                        + "    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
-                        + "src/test/pkg/WrongUsages.java:44: Error: This annotation does not apply for type String; expected float or double [SupportAnnotationUsage]\n"
-                        + "    @FloatRange(from = 1.0, to = 0.0)\n"
-                        + "    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
-                        + "src/test/pkg/WrongUsages.java:45: Error: This annotation does not apply for type String; expected int or long [SupportAnnotationUsage]\n"
-                        + "    @ColorInt\n"
-                        + "    ~~~~~~~~~\n"
-                        + "src/test/pkg/WrongUsages.java:46: Error: The size multiple must be at least 1 [SupportAnnotationUsage]\n"
-                        + "    @Size(multiple=0)\n"
-                        + "    ~~~~~~~~~~~~~~~~~\n"
-                        + "src/test/pkg/WrongUsages.java:47: Error: This annotation does not apply for type String; expected int or long [SupportAnnotationUsage]\n"
-                        + "    @DrawableRes\n"
-                        + "    ~~~~~~~~~~~~\n"
-                        + "src/test/pkg/WrongUsages.java:52: Error: The size can't be negative [SupportAnnotationUsage]\n"
-                        + "    @Size(-5)\n"
-                        + "    ~~~~~~~~~\n"
-                        + "10 errors, 0 warnings\n",
+                + "src/test/pkg/WrongUsages.java:33: Error: This annotation does not apply for type String; expected int or long [SupportAnnotationUsage]\n"
+                + "    @DialogStyle\n"
+                + "    ~~~~~~~~~~~~\n"
+                + "src/test/pkg/WrongUsages.java:38: Error: Invalid range: the from attribute must be less than the to attribute [SupportAnnotationUsage]\n"
+                + "    @IntRange(from = 1, to = 0)\n"
+                + "    ~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
+                + "src/test/pkg/WrongUsages.java:38: Error: This annotation does not apply for type String; expected int or long [SupportAnnotationUsage]\n"
+                + "    @IntRange(from = 1, to = 0)\n"
+                + "    ~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
+                + "src/test/pkg/WrongUsages.java:39: Error: Invalid size range: the min attribute must be less than the max attribute [SupportAnnotationUsage]\n"
+                + "    @Size(min=10, max = 8)\n"
+                + "    ~~~~~~~~~~~~~~~~~~~~~~\n"
+                + "src/test/pkg/WrongUsages.java:44: Error: Invalid range: the from attribute must be less than the to attribute [SupportAnnotationUsage]\n"
+                + "    @FloatRange(from = 1.0, to = 0.0)\n"
+                + "    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
+                + "src/test/pkg/WrongUsages.java:44: Error: This annotation does not apply for type String; expected float or double [SupportAnnotationUsage]\n"
+                + "    @FloatRange(from = 1.0, to = 0.0)\n"
+                + "    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
+                + "src/test/pkg/WrongUsages.java:45: Error: This annotation does not apply for type String; expected int or long [SupportAnnotationUsage]\n"
+                + "    @ColorInt\n"
+                + "    ~~~~~~~~~\n"
+                + "src/test/pkg/WrongUsages.java:46: Error: The size multiple must be at least 1 [SupportAnnotationUsage]\n"
+                + "    @Size(multiple=0)\n"
+                + "    ~~~~~~~~~~~~~~~~~\n"
+                + "src/test/pkg/WrongUsages.java:47: Error: This annotation does not apply for type String; expected int or long [SupportAnnotationUsage]\n"
+                + "    @DrawableRes\n"
+                + "    ~~~~~~~~~~~~\n"
+                + "src/test/pkg/WrongUsages.java:52: Error: The size can't be negative [SupportAnnotationUsage]\n"
+                + "    @Size(-5)\n"
+                + "    ~~~~~~~~~\n"
+                + "src/test/pkg/WrongUsages.java:57: Error: For methods, permission annotation should specify one of value, anyOf or allOf [SupportAnnotationUsage]\n"
+                + "    @RequiresPermission\n"
+                + "    ~~~~~~~~~~~~~~~~~~~\n"
+                + "src/test/pkg/WrongUsages.java:62: Error: Only specify one of value, anyOf or allOf [SupportAnnotationUsage]\n"
+                + "    @RequiresPermission(allOf = {\"my.permission.PERM1\",\"my.permission.PERM2\"},anyOf = {\"my.permission.PERM1\",\"my.permission.PERM2\"})\n"
+                + "    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
+                + "src/test/pkg/WrongUsages.java:63: Error: @CheckResult should not be specified on void methods [SupportAnnotationUsage]\n"
+                + "    @CheckResult // Error on void methods\n"
+                + "    ~~~~~~~~~~~~\n"
+                + "13 errors, 0 warnings\n",
 
                 lintProject(
                         java("src/test/pkg/WrongUsages.java", ""
@@ -448,13 +459,13 @@
                                 + "import android.support.annotation.IntDef;\n"
                                 + "import android.support.annotation.IntRange;\n"
                                 + "import android.support.annotation.FloatRange;\n"
+                                + "import android.support.annotation.CheckResult;\n"
                                 + "import android.support.annotation.ColorInt;\n"
                                 + "import android.support.annotation.DrawableRes;\n"
                                 + "import android.support.annotation.Size;\n"
+                                + "import android.support.annotation.RequiresPermission;\n"
                                 + "import android.annotation.SuppressLint;\n"
-                                + "import java.lang.annotation.Retention;\n"
-                                + "import java.lang.annotation.RetentionPolicy;\n"
-                                + "\n"
+                                + "import java.lang.annotation.*;\n"
                                 + "@SuppressLint(\"UnusedDeclaration\")\n"
                                 + "public class WrongUsages {\n"
                                 + "    @IntDef({STYLE_NORMAL, STYLE_NO_TITLE, STYLE_NO_FRAME, STYLE_NO_INPUT})\n"
@@ -500,8 +511,18 @@
                                 + "        return null;\n"
                                 + "    }\n"
                                 + "\n"
+                                + "    @RequiresPermission\n" // Must specify value
+                                + "    public void wrongPermission(\n"
+                                + "        @RequiresPermission int allowed) { // OK\n"
+                                + "    }\n"
+                                + "\n"
+                                // Too many values
+                                + "    @RequiresPermission(allOf = {\"my.permission.PERM1\",\"my.permission.PERM2\"},anyOf = {\"my.permission.PERM1\",\"my.permission.PERM2\"})\n"
+                                + "    @CheckResult // Error on void methods\n"
+                                + "    public void wrongPermission2() {\n"
+                                + "    }\n"
+                                + "\n"
                                 // TODO: Warn when using on collections that aren't supported
-                                // TODO: Resource types (@IntRes, @FloatRes, etc)
                                 // TODO: Warn about inapplicable nullness stuff (outside of IDE)
                                 + "}"),
                         copy("src/android/support/annotation/CheckResult.java.txt",
@@ -519,10 +540,37 @@
                         copy("src/android/support/annotation/IntRange.java.txt",
                                 "src/android/support/annotation/IntRange.java"),
                         copy("src/android/support/annotation/FloatRange.java.txt",
-                                "src/android/support/annotation/FloatRange.java")
+                                "src/android/support/annotation/FloatRange.java"),
+                        mRequirePermissionAnnotation
                 ));
     }
 
+    private final TestFile mRequirePermissionAnnotation = java("src/android/support/annotation/RequiresPermission.java", ""
+            + "package android.support.annotation;\n"
+            + "\n"
+            + "import java.lang.annotation.Retention;\n"
+            + "import java.lang.annotation.Target;\n"
+            + "\n"
+            + "import static java.lang.annotation.ElementType.*;\n"
+            + "import static java.lang.annotation.RetentionPolicy.CLASS;\n"
+            + "@Retention(CLASS)\n"
+            + "@Target({METHOD,CONSTRUCTOR,FIELD,PARAMETER,ANNOTATION_TYPE})\n"
+            + "public @interface RequiresPermission {\n"
+            + "    String value() default \"\";\n"
+            + "    String[] allOf() default {};\n"
+            + "    String[] anyOf() default {};\n"
+            + "    boolean conditional() default false;\n"
+            + "    String notes() default \"\";\n"
+            + "    @Target({FIELD,METHOD,PARAMETER})\n"
+            + "    @interface Read {\n"
+            + "        RequiresPermission value();\n"
+            + "    }\n"
+            + "    @Target({FIELD,METHOD,PARAMETER})\n"
+            + "    @interface Write {\n"
+            + "        RequiresPermission value();\n"
+            + "    }\n"
+            + "}");
+
     @Override
     protected void checkReportedError(@NonNull Context context, @NonNull Issue issue,
             @NonNull Severity severity, @Nullable Location location, @NonNull String message) {
diff --git a/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/ApiDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/ApiDetectorTest.java
index a2b20e9..5ba8aa6 100644
--- a/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/ApiDetectorTest.java
+++ b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/ApiDetectorTest.java
@@ -299,30 +299,32 @@
     }
 
     public void testApi1() throws Exception {
-        assertEquals(
-            "src/foo/bar/ApiCallTest.java:20: Error: Call requires API level 11 (current min is 1): android.app.Activity#getActionBar [NewApi]\n" +
-            "  getActionBar(); // API 11\n" +
-            "  ~~~~~~~~~~~~\n" +
-            "src/foo/bar/ApiCallTest.java:24: Error: Class requires API level 8 (current min is 1): org.w3c.dom.DOMErrorHandler [NewApi]\n" +
-            "  Class<?> clz = DOMErrorHandler.class; // API 8\n" +
-            "                 ~~~~~~~~~~~~~~~\n" +
-            "src/foo/bar/ApiCallTest.java:27: Error: Call requires API level 3 (current min is 1): android.widget.Chronometer#getOnChronometerTickListener [NewApi]\n" +
-            "  chronometer.getOnChronometerTickListener(); // API 3 \n" +
-            "              ~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" +
-            "src/foo/bar/ApiCallTest.java:30: Error: Call requires API level 11 (current min is 1): android.widget.Chronometer#setTextIsSelectable [NewApi]\n" +
-            "  chronometer.setTextIsSelectable(true); // API 11\n" +
-            "              ~~~~~~~~~~~~~~~~~~~\n" +
-            "src/foo/bar/ApiCallTest.java:33: Error: Field requires API level 11 (current min is 1): dalvik.bytecode.OpcodeInfo#MAXIMUM_VALUE [NewApi]\n" +
-            "  int field = OpcodeInfo.MAXIMUM_VALUE; // API 11\n" +
-            "                         ~~~~~~~~~~~~~\n" +
-            "src/foo/bar/ApiCallTest.java:38: Error: Field requires API level 14 (current min is 1): android.app.ApplicationErrorReport#batteryInfo [NewApi]\n" +
-            "  BatteryInfo batteryInfo = getReport().batteryInfo;\n" +
-            "              ~~~~~~~~~~~\n" +
-            //             Note: the above error range is wrong; should be pointing to the second
-            "src/foo/bar/ApiCallTest.java:41: Error: Field requires API level 11 (current min is 1): android.graphics.PorterDuff.Mode#OVERLAY [NewApi]\n" +
-            "  Mode mode = PorterDuff.Mode.OVERLAY; // API 11\n" +
-            "                              ~~~~~~~\n" +
-            "7 errors, 0 warnings\n",
+        assertEquals(""
+                + "src/foo/bar/ApiCallTest.java:33: Warning: Field requires API level 11 (current min is 1): dalvik.bytecode.OpcodeInfo#MAXIMUM_VALUE [InlinedApi]\n"
+                + "  int field = OpcodeInfo.MAXIMUM_VALUE; // API 11\n"
+                + "              ~~~~~~~~~~~~~~~~~~~~~~~~\n"
+                + "src/foo/bar/ApiCallTest.java:20: Error: Call requires API level 11 (current min is 1): android.app.Activity#getActionBar [NewApi]\n"
+                + "  getActionBar(); // API 11\n"
+                + "  ~~~~~~~~~~~~\n"
+                + "src/foo/bar/ApiCallTest.java:24: Error: Class requires API level 8 (current min is 1): org.w3c.dom.DOMErrorHandler [NewApi]\n"
+                + "  Class<?> clz = DOMErrorHandler.class; // API 8\n"
+                + "                 ~~~~~~~~~~~~~~~\n"
+                + "src/foo/bar/ApiCallTest.java:27: Error: Call requires API level 3 (current min is 1): android.widget.Chronometer#getOnChronometerTickListener [NewApi]\n"
+                + "  chronometer.getOnChronometerTickListener(); // API 3 \n"
+                + "              ~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
+                + "src/foo/bar/ApiCallTest.java:30: Error: Call requires API level 11 (current min is 1): android.widget.Chronometer#setTextIsSelectable [NewApi]\n"
+                + "  chronometer.setTextIsSelectable(true); // API 11\n"
+                + "              ~~~~~~~~~~~~~~~~~~~\n"
+                + "src/foo/bar/ApiCallTest.java:33: Error: Field requires API level 11 (current min is 1): dalvik.bytecode.OpcodeInfo#MAXIMUM_VALUE [NewApi]\n"
+                + "  int field = OpcodeInfo.MAXIMUM_VALUE; // API 11\n"
+                + "                         ~~~~~~~~~~~~~\n"
+                + "src/foo/bar/ApiCallTest.java:38: Error: Field requires API level 14 (current min is 1): android.app.ApplicationErrorReport#batteryInfo [NewApi]\n"
+                + "  BatteryInfo batteryInfo = getReport().batteryInfo;\n"
+                + "              ~~~~~~~~~~~\n"
+                + "src/foo/bar/ApiCallTest.java:41: Error: Field requires API level 11 (current min is 1): android.graphics.PorterDuff.Mode#OVERLAY [NewApi]\n"
+                + "  Mode mode = PorterDuff.Mode.OVERLAY; // API 11\n"
+                + "                              ~~~~~~~\n"
+                + "7 errors, 1 warnings\n",
 
             lintProject(
                 "apicheck/classpath=>.classpath",
@@ -333,29 +335,32 @@
     }
 
     public void testApi2() throws Exception {
-        assertEquals(
-            "src/foo/bar/ApiCallTest.java:20: Error: Call requires API level 11 (current min is 2): android.app.Activity#getActionBar [NewApi]\n" +
-            "  getActionBar(); // API 11\n" +
-            "  ~~~~~~~~~~~~\n" +
-            "src/foo/bar/ApiCallTest.java:24: Error: Class requires API level 8 (current min is 2): org.w3c.dom.DOMErrorHandler [NewApi]\n" +
-            "  Class<?> clz = DOMErrorHandler.class; // API 8\n" +
-            "                 ~~~~~~~~~~~~~~~\n" +
-            "src/foo/bar/ApiCallTest.java:27: Error: Call requires API level 3 (current min is 2): android.widget.Chronometer#getOnChronometerTickListener [NewApi]\n" +
-            "  chronometer.getOnChronometerTickListener(); // API 3 \n" +
-            "              ~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" +
-            "src/foo/bar/ApiCallTest.java:30: Error: Call requires API level 11 (current min is 2): android.widget.Chronometer#setTextIsSelectable [NewApi]\n" +
-            "  chronometer.setTextIsSelectable(true); // API 11\n" +
-            "              ~~~~~~~~~~~~~~~~~~~\n" +
-            "src/foo/bar/ApiCallTest.java:33: Error: Field requires API level 11 (current min is 2): dalvik.bytecode.OpcodeInfo#MAXIMUM_VALUE [NewApi]\n" +
-            "  int field = OpcodeInfo.MAXIMUM_VALUE; // API 11\n" +
-            "                         ~~~~~~~~~~~~~\n" +
-            "src/foo/bar/ApiCallTest.java:38: Error: Field requires API level 14 (current min is 2): android.app.ApplicationErrorReport#batteryInfo [NewApi]\n" +
-            "  BatteryInfo batteryInfo = getReport().batteryInfo;\n" +
-            "              ~~~~~~~~~~~\n" +
-            "src/foo/bar/ApiCallTest.java:41: Error: Field requires API level 11 (current min is 2): android.graphics.PorterDuff.Mode#OVERLAY [NewApi]\n" +
-            "  Mode mode = PorterDuff.Mode.OVERLAY; // API 11\n" +
-            "                              ~~~~~~~\n" +
-            "7 errors, 0 warnings\n",
+        assertEquals(""
+                + "src/foo/bar/ApiCallTest.java:33: Warning: Field requires API level 11 (current min is 2): dalvik.bytecode.OpcodeInfo#MAXIMUM_VALUE [InlinedApi]\n"
+                + "  int field = OpcodeInfo.MAXIMUM_VALUE; // API 11\n"
+                + "              ~~~~~~~~~~~~~~~~~~~~~~~~\n"
+                + "src/foo/bar/ApiCallTest.java:20: Error: Call requires API level 11 (current min is 2): android.app.Activity#getActionBar [NewApi]\n"
+                + "  getActionBar(); // API 11\n"
+                + "  ~~~~~~~~~~~~\n"
+                + "src/foo/bar/ApiCallTest.java:24: Error: Class requires API level 8 (current min is 2): org.w3c.dom.DOMErrorHandler [NewApi]\n"
+                + "  Class<?> clz = DOMErrorHandler.class; // API 8\n"
+                + "                 ~~~~~~~~~~~~~~~\n"
+                + "src/foo/bar/ApiCallTest.java:27: Error: Call requires API level 3 (current min is 2): android.widget.Chronometer#getOnChronometerTickListener [NewApi]\n"
+                + "  chronometer.getOnChronometerTickListener(); // API 3 \n"
+                + "              ~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
+                + "src/foo/bar/ApiCallTest.java:30: Error: Call requires API level 11 (current min is 2): android.widget.Chronometer#setTextIsSelectable [NewApi]\n"
+                + "  chronometer.setTextIsSelectable(true); // API 11\n"
+                + "              ~~~~~~~~~~~~~~~~~~~\n"
+                + "src/foo/bar/ApiCallTest.java:33: Error: Field requires API level 11 (current min is 2): dalvik.bytecode.OpcodeInfo#MAXIMUM_VALUE [NewApi]\n"
+                + "  int field = OpcodeInfo.MAXIMUM_VALUE; // API 11\n"
+                + "                         ~~~~~~~~~~~~~\n"
+                + "src/foo/bar/ApiCallTest.java:38: Error: Field requires API level 14 (current min is 2): android.app.ApplicationErrorReport#batteryInfo [NewApi]\n"
+                + "  BatteryInfo batteryInfo = getReport().batteryInfo;\n"
+                + "              ~~~~~~~~~~~\n"
+                + "src/foo/bar/ApiCallTest.java:41: Error: Field requires API level 11 (current min is 2): android.graphics.PorterDuff.Mode#OVERLAY [NewApi]\n"
+                + "  Mode mode = PorterDuff.Mode.OVERLAY; // API 11\n"
+                + "                              ~~~~~~~\n"
+                + "7 errors, 1 warnings\n",
 
             lintProject(
                 "apicheck/classpath=>.classpath",
@@ -366,26 +371,29 @@
     }
 
     public void testApi4() throws Exception {
-        assertEquals(
-            "src/foo/bar/ApiCallTest.java:20: Error: Call requires API level 11 (current min is 4): android.app.Activity#getActionBar [NewApi]\n" +
-            "  getActionBar(); // API 11\n" +
-            "  ~~~~~~~~~~~~\n" +
-            "src/foo/bar/ApiCallTest.java:24: Error: Class requires API level 8 (current min is 4): org.w3c.dom.DOMErrorHandler [NewApi]\n" +
-            "  Class<?> clz = DOMErrorHandler.class; // API 8\n" +
-            "                 ~~~~~~~~~~~~~~~\n" +
-            "src/foo/bar/ApiCallTest.java:30: Error: Call requires API level 11 (current min is 4): android.widget.Chronometer#setTextIsSelectable [NewApi]\n" +
-            "  chronometer.setTextIsSelectable(true); // API 11\n" +
-            "              ~~~~~~~~~~~~~~~~~~~\n" +
-            "src/foo/bar/ApiCallTest.java:33: Error: Field requires API level 11 (current min is 4): dalvik.bytecode.OpcodeInfo#MAXIMUM_VALUE [NewApi]\n" +
-            "  int field = OpcodeInfo.MAXIMUM_VALUE; // API 11\n" +
-            "                         ~~~~~~~~~~~~~\n" +
-            "src/foo/bar/ApiCallTest.java:38: Error: Field requires API level 14 (current min is 4): android.app.ApplicationErrorReport#batteryInfo [NewApi]\n" +
-            "  BatteryInfo batteryInfo = getReport().batteryInfo;\n" +
-            "              ~~~~~~~~~~~\n" +
-            "src/foo/bar/ApiCallTest.java:41: Error: Field requires API level 11 (current min is 4): android.graphics.PorterDuff.Mode#OVERLAY [NewApi]\n" +
-            "  Mode mode = PorterDuff.Mode.OVERLAY; // API 11\n" +
-            "                              ~~~~~~~\n" +
-            "6 errors, 0 warnings\n",
+        assertEquals(""
+                + "src/foo/bar/ApiCallTest.java:33: Warning: Field requires API level 11 (current min is 4): dalvik.bytecode.OpcodeInfo#MAXIMUM_VALUE [InlinedApi]\n"
+                + "  int field = OpcodeInfo.MAXIMUM_VALUE; // API 11\n"
+                + "              ~~~~~~~~~~~~~~~~~~~~~~~~\n"
+                + "src/foo/bar/ApiCallTest.java:20: Error: Call requires API level 11 (current min is 4): android.app.Activity#getActionBar [NewApi]\n"
+                + "  getActionBar(); // API 11\n"
+                + "  ~~~~~~~~~~~~\n"
+                + "src/foo/bar/ApiCallTest.java:24: Error: Class requires API level 8 (current min is 4): org.w3c.dom.DOMErrorHandler [NewApi]\n"
+                + "  Class<?> clz = DOMErrorHandler.class; // API 8\n"
+                + "                 ~~~~~~~~~~~~~~~\n"
+                + "src/foo/bar/ApiCallTest.java:30: Error: Call requires API level 11 (current min is 4): android.widget.Chronometer#setTextIsSelectable [NewApi]\n"
+                + "  chronometer.setTextIsSelectable(true); // API 11\n"
+                + "              ~~~~~~~~~~~~~~~~~~~\n"
+                + "src/foo/bar/ApiCallTest.java:33: Error: Field requires API level 11 (current min is 4): dalvik.bytecode.OpcodeInfo#MAXIMUM_VALUE [NewApi]\n"
+                + "  int field = OpcodeInfo.MAXIMUM_VALUE; // API 11\n"
+                + "                         ~~~~~~~~~~~~~\n"
+                + "src/foo/bar/ApiCallTest.java:38: Error: Field requires API level 14 (current min is 4): android.app.ApplicationErrorReport#batteryInfo [NewApi]\n"
+                + "  BatteryInfo batteryInfo = getReport().batteryInfo;\n"
+                + "              ~~~~~~~~~~~\n"
+                + "src/foo/bar/ApiCallTest.java:41: Error: Field requires API level 11 (current min is 4): android.graphics.PorterDuff.Mode#OVERLAY [NewApi]\n"
+                + "  Mode mode = PorterDuff.Mode.OVERLAY; // API 11\n"
+                + "                              ~~~~~~~\n"
+                + "6 errors, 1 warnings\n",
 
             lintProject(
                 "apicheck/classpath=>.classpath",
@@ -396,23 +404,26 @@
     }
 
     public void testApi10() throws Exception {
-        assertEquals(
-            "src/foo/bar/ApiCallTest.java:20: Error: Call requires API level 11 (current min is 10): android.app.Activity#getActionBar [NewApi]\n" +
-            "  getActionBar(); // API 11\n" +
-            "  ~~~~~~~~~~~~\n" +
-            "src/foo/bar/ApiCallTest.java:30: Error: Call requires API level 11 (current min is 10): android.widget.Chronometer#setTextIsSelectable [NewApi]\n" +
-            "  chronometer.setTextIsSelectable(true); // API 11\n" +
-            "              ~~~~~~~~~~~~~~~~~~~\n" +
-            "src/foo/bar/ApiCallTest.java:33: Error: Field requires API level 11 (current min is 10): dalvik.bytecode.OpcodeInfo#MAXIMUM_VALUE [NewApi]\n" +
-            "  int field = OpcodeInfo.MAXIMUM_VALUE; // API 11\n" +
-            "                         ~~~~~~~~~~~~~\n" +
-            "src/foo/bar/ApiCallTest.java:38: Error: Field requires API level 14 (current min is 10): android.app.ApplicationErrorReport#batteryInfo [NewApi]\n" +
-            "  BatteryInfo batteryInfo = getReport().batteryInfo;\n" +
-            "              ~~~~~~~~~~~\n" +
-            "src/foo/bar/ApiCallTest.java:41: Error: Field requires API level 11 (current min is 10): android.graphics.PorterDuff.Mode#OVERLAY [NewApi]\n" +
-            "  Mode mode = PorterDuff.Mode.OVERLAY; // API 11\n" +
-            "                              ~~~~~~~\n" +
-            "5 errors, 0 warnings\n",
+        assertEquals(""
+                + "src/foo/bar/ApiCallTest.java:33: Warning: Field requires API level 11 (current min is 10): dalvik.bytecode.OpcodeInfo#MAXIMUM_VALUE [InlinedApi]\n"
+                + "  int field = OpcodeInfo.MAXIMUM_VALUE; // API 11\n"
+                + "              ~~~~~~~~~~~~~~~~~~~~~~~~\n"
+                + "src/foo/bar/ApiCallTest.java:20: Error: Call requires API level 11 (current min is 10): android.app.Activity#getActionBar [NewApi]\n"
+                + "  getActionBar(); // API 11\n"
+                + "  ~~~~~~~~~~~~\n"
+                + "src/foo/bar/ApiCallTest.java:30: Error: Call requires API level 11 (current min is 10): android.widget.Chronometer#setTextIsSelectable [NewApi]\n"
+                + "  chronometer.setTextIsSelectable(true); // API 11\n"
+                + "              ~~~~~~~~~~~~~~~~~~~\n"
+                + "src/foo/bar/ApiCallTest.java:33: Error: Field requires API level 11 (current min is 10): dalvik.bytecode.OpcodeInfo#MAXIMUM_VALUE [NewApi]\n"
+                + "  int field = OpcodeInfo.MAXIMUM_VALUE; // API 11\n"
+                + "                         ~~~~~~~~~~~~~\n"
+                + "src/foo/bar/ApiCallTest.java:38: Error: Field requires API level 14 (current min is 10): android.app.ApplicationErrorReport#batteryInfo [NewApi]\n"
+                + "  BatteryInfo batteryInfo = getReport().batteryInfo;\n"
+                + "              ~~~~~~~~~~~\n"
+                + "src/foo/bar/ApiCallTest.java:41: Error: Field requires API level 11 (current min is 10): android.graphics.PorterDuff.Mode#OVERLAY [NewApi]\n"
+                + "  Mode mode = PorterDuff.Mode.OVERLAY; // API 11\n"
+                + "                              ~~~~~~~\n"
+                + "5 errors, 1 warnings\n",
 
             lintProject(
                 "apicheck/classpath=>.classpath",
@@ -522,41 +533,43 @@
     // Test suppressing errors -- on classes, methods etc.
 
     public void testSuppress() throws Exception {
-        assertEquals(
-            // These errors are correctly -not- suppressed because they
-            // appear in method3 (line 74-98) which is annotated with a
-            // @SuppressLint annotation specifying only an unrelated issue id
+        assertEquals(""
+                // These errors are correctly -not- suppressed because they
+                // appear in method3 (line 74-98) which is annotated with a
+                // @SuppressLint annotation specifying only an unrelated issue id
+                + "src/foo/bar/SuppressTest1.java:89: Warning: Field requires API level 11 (current min is 1): dalvik.bytecode.OpcodeInfo#MAXIMUM_VALUE [InlinedApi]\n"
+                + "  int field = OpcodeInfo.MAXIMUM_VALUE; // API 11\n"
+                + "              ~~~~~~~~~~~~~~~~~~~~~~~~\n"
+                + "src/foo/bar/SuppressTest1.java:76: Error: Call requires API level 11 (current min is 1): android.app.Activity#getActionBar [NewApi]\n"
+                + "  getActionBar(); // API 11\n"
+                + "  ~~~~~~~~~~~~\n"
+                + "src/foo/bar/SuppressTest1.java:80: Error: Class requires API level 8 (current min is 1): org.w3c.dom.DOMErrorHandler [NewApi]\n"
+                + "  Class<?> clz = DOMErrorHandler.class; // API 8\n"
+                + "                 ~~~~~~~~~~~~~~~\n"
+                + "src/foo/bar/SuppressTest1.java:83: Error: Call requires API level 3 (current min is 1): android.widget.Chronometer#getOnChronometerTickListener [NewApi]\n"
+                + "  chronometer.getOnChronometerTickListener(); // API 3\n"
+                + "              ~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
+                + "src/foo/bar/SuppressTest1.java:86: Error: Call requires API level 11 (current min is 1): android.widget.Chronometer#setTextIsSelectable [NewApi]\n"
+                + "  chronometer.setTextIsSelectable(true); // API 11\n"
+                + "              ~~~~~~~~~~~~~~~~~~~\n"
+                + "src/foo/bar/SuppressTest1.java:89: Error: Field requires API level 11 (current min is 1): dalvik.bytecode.OpcodeInfo#MAXIMUM_VALUE [NewApi]\n"
+                + "  int field = OpcodeInfo.MAXIMUM_VALUE; // API 11\n"
+                + "                         ~~~~~~~~~~~~~\n"
+                + "src/foo/bar/SuppressTest1.java:94: Error: Field requires API level 14 (current min is 1): android.app.ApplicationErrorReport#batteryInfo [NewApi]\n"
+                + "  BatteryInfo batteryInfo = getReport().batteryInfo;\n"
+                + "              ~~~~~~~~~~~\n"
+                + "src/foo/bar/SuppressTest1.java:97: Error: Field requires API level 11 (current min is 1): android.graphics.PorterDuff.Mode#OVERLAY [NewApi]\n"
+                + "  Mode mode = PorterDuff.Mode.OVERLAY; // API 11\n"
+                + "                              ~~~~~~~\n"
 
-            "src/foo/bar/SuppressTest1.java:76: Error: Call requires API level 11 (current min is 1): android.app.Activity#getActionBar [NewApi]\n" +
-            "  getActionBar(); // API 11\n" +
-            "  ~~~~~~~~~~~~\n" +
-            "src/foo/bar/SuppressTest1.java:80: Error: Class requires API level 8 (current min is 1): org.w3c.dom.DOMErrorHandler [NewApi]\n" +
-            "  Class<?> clz = DOMErrorHandler.class; // API 8\n" +
-            "                 ~~~~~~~~~~~~~~~\n" +
-            "src/foo/bar/SuppressTest1.java:83: Error: Call requires API level 3 (current min is 1): android.widget.Chronometer#getOnChronometerTickListener [NewApi]\n" +
-            "  chronometer.getOnChronometerTickListener(); // API 3\n" +
-            "              ~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" +
-            "src/foo/bar/SuppressTest1.java:86: Error: Call requires API level 11 (current min is 1): android.widget.Chronometer#setTextIsSelectable [NewApi]\n" +
-            "  chronometer.setTextIsSelectable(true); // API 11\n" +
-            "              ~~~~~~~~~~~~~~~~~~~\n" +
-            "src/foo/bar/SuppressTest1.java:89: Error: Field requires API level 11 (current min is 1): dalvik.bytecode.OpcodeInfo#MAXIMUM_VALUE [NewApi]\n" +
-            "  int field = OpcodeInfo.MAXIMUM_VALUE; // API 11\n" +
-            "                         ~~~~~~~~~~~~~\n" +
-            "src/foo/bar/SuppressTest1.java:94: Error: Field requires API level 14 (current min is 1): android.app.ApplicationErrorReport#batteryInfo [NewApi]\n" +
-            "  BatteryInfo batteryInfo = getReport().batteryInfo;\n" +
-            "              ~~~~~~~~~~~\n" +
-            "src/foo/bar/SuppressTest1.java:97: Error: Field requires API level 11 (current min is 1): android.graphics.PorterDuff.Mode#OVERLAY [NewApi]\n" +
-            "  Mode mode = PorterDuff.Mode.OVERLAY; // API 11\n" +
-            "                              ~~~~~~~\n" +
-
-            // Note: These annotations are within the methods, not ON the methods, so they have
-            // no effect (because they don't end up in the bytecode)
+                // Note: These annotations are within the methods, not ON the methods, so they have
+                // no effect (because they don't end up in the bytecode)
 
 
-            "src/foo/bar/SuppressTest4.java:19: Error: Field requires API level 14 (current min is 1): android.app.ApplicationErrorReport#batteryInfo [NewApi]\n" +
-            "  BatteryInfo batteryInfo = report.batteryInfo;\n" +
-            "              ~~~~~~~~~~~\n" +
-            "8 errors, 0 warnings\n",
+                + "src/foo/bar/SuppressTest4.java:19: Error: Field requires API level 14 (current min is 1): android.app.ApplicationErrorReport#batteryInfo [NewApi]\n"
+                + "  BatteryInfo batteryInfo = report.batteryInfo;\n"
+                + "              ~~~~~~~~~~~\n"
+                + "8 errors, 1 warnings\n",
 
             lintProject(
                 "apicheck/classpath=>.classpath",
@@ -891,7 +904,7 @@
                 + "src/test/pkg/ApiSourceCheck.java:5: Warning: Field requires API level 11 (current min is 1): android.view.View#MEASURED_STATE_MASK [InlinedApi]\n"
                 + "import static android.view.View.MEASURED_STATE_MASK;\n"
                 + "              ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
-                + "src/test/pkg/ApiSourceCheck.java:30: Warning: Field requires API level 11 (current min is 1): android.widget.ZoomControls#MEASURED_STATE_MASK [InlinedApi]\n"
+                + "src/test/pkg/ApiSourceCheck.java:30: Warning: Field requires API level 11 (current min is 1): android.view.View#MEASURED_STATE_MASK [InlinedApi]\n"
                 + "        int x = MEASURED_STATE_MASK;\n"
                 + "                ~~~~~~~~~~~~~~~~~~~\n"
                 + "src/test/pkg/ApiSourceCheck.java:33: Warning: Field requires API level 11 (current min is 1): android.view.View#MEASURED_STATE_MASK [InlinedApi]\n"
@@ -906,13 +919,13 @@
                 + "src/test/pkg/ApiSourceCheck.java:40: Warning: Field requires API level 12 (current min is 1): android.app.ActivityManager#MOVE_TASK_NO_USER_ACTION [InlinedApi]\n"
                 + "        int w = ActivityManager.MOVE_TASK_NO_USER_ACTION;\n"
                 + "                ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
-                + "src/test/pkg/ApiSourceCheck.java:41: Warning: Field requires API level 14 (current min is 1): android.widget.ZoomButton#FIND_VIEWS_WITH_CONTENT_DESCRIPTION [InlinedApi]\n"
+                + "src/test/pkg/ApiSourceCheck.java:41: Warning: Field requires API level 14 (current min is 1): android.view.View#FIND_VIEWS_WITH_CONTENT_DESCRIPTION [InlinedApi]\n"
                 + "        int find1 = ZoomButton.FIND_VIEWS_WITH_CONTENT_DESCRIPTION; // requires\n"
                 + "                    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
-                + "src/test/pkg/ApiSourceCheck.java:44: Warning: Field requires API level 9 (current min is 1): android.widget.ZoomControls#OVER_SCROLL_ALWAYS [InlinedApi]\n"
+                + "src/test/pkg/ApiSourceCheck.java:44: Warning: Field requires API level 9 (current min is 1): android.view.View#OVER_SCROLL_ALWAYS [InlinedApi]\n"
                 + "        int overScroll = OVER_SCROLL_ALWAYS; // requires API 9\n"
                 + "                         ~~~~~~~~~~~~~~~~~~\n"
-                + "src/test/pkg/ApiSourceCheck.java:47: Warning: Field requires API level 16 (current min is 1): android.widget.ZoomControls#IMPORTANT_FOR_ACCESSIBILITY_AUTO [InlinedApi]\n"
+                + "src/test/pkg/ApiSourceCheck.java:47: Warning: Field requires API level 16 (current min is 1): android.view.View#IMPORTANT_FOR_ACCESSIBILITY_AUTO [InlinedApi]\n"
                 + "        int auto = IMPORTANT_FOR_ACCESSIBILITY_AUTO; // requires API 16\n"
                 + "                   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
                 + "src/test/pkg/ApiSourceCheck.java:54: Warning: Field requires API level 11 (current min is 1): android.view.View#MEASURED_STATE_MASK [InlinedApi]\n"
@@ -921,9 +934,15 @@
                 + "src/test/pkg/ApiSourceCheck.java:55: Warning: Field requires API level 11 (current min is 1): android.view.View#MEASURED_HEIGHT_STATE_SHIFT [InlinedApi]\n"
                 + "                | ((child.getMeasuredHeight() >> View.MEASURED_HEIGHT_STATE_SHIFT) & (View.MEASURED_STATE_MASK >> View.MEASURED_HEIGHT_STATE_SHIFT));\n"
                 + "                                                 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
+                + "src/test/pkg/ApiSourceCheck.java:55: Warning: Field requires API level 11 (current min is 1): android.view.View#MEASURED_HEIGHT_STATE_SHIFT [InlinedApi]\n"
+                + "                | ((child.getMeasuredHeight() >> View.MEASURED_HEIGHT_STATE_SHIFT) & (View.MEASURED_STATE_MASK >> View.MEASURED_HEIGHT_STATE_SHIFT));\n"
+                + "                                                                                                                  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
                 + "src/test/pkg/ApiSourceCheck.java:55: Warning: Field requires API level 11 (current min is 1): android.view.View#MEASURED_STATE_MASK [InlinedApi]\n"
                 + "                | ((child.getMeasuredHeight() >> View.MEASURED_HEIGHT_STATE_SHIFT) & (View.MEASURED_STATE_MASK >> View.MEASURED_HEIGHT_STATE_SHIFT));\n"
                 + "                                                                                      ~~~~~~~~~~~~~~~~~~~~~~~~\n"
+                + "src/test/pkg/ApiSourceCheck.java:67: Warning: Field requires API level 12 (current min is 1): android.app.ActivityManager#MOVE_TASK_NO_USER_ACTION [InlinedApi]\n"
+                + "        int w, z = ActivityManager.MOVE_TASK_NO_USER_ACTION;\n"
+                + "                   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
                 + "src/test/pkg/ApiSourceCheck.java:90: Warning: Field requires API level 8 (current min is 1): android.R.id#custom [InlinedApi]\n"
                 + "        int custom = android.R.id.custom; // API 8\n"
                 + "                     ~~~~~~~~~~~~~~~~~~~\n"
@@ -942,7 +961,7 @@
                 + "src/test/pkg/ApiSourceCheck.java:51: Error: Field requires API level 14 (current min is 1): android.widget.ZoomButton#ROTATION_X [NewApi]\n"
                 + "        Object rotationX = ZoomButton.ROTATION_X; // Requires API 14\n"
                 + "                                      ~~~~~~~~~~\n"
-                + "1 errors, 17 warnings\n",
+                + "1 errors, 19 warnings\n",
 
                 lintProject(
                         "apicheck/classpath=>.classpath",
@@ -1014,7 +1033,7 @@
                 + "src/test/pkg/ApiSourceCheck2.java:10: Warning: Field requires API level 11 (current min is 1): android.widget.AbsListView#CHOICE_MODE_MULTIPLE_MODAL [InlinedApi]\n"
                 + "        int mode2 = AbsListView.CHOICE_MODE_MULTIPLE_MODAL;\n"
                 + "                    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
-                + "src/test/pkg/ApiSourceCheck2.java:14: Warning: Field requires API level 11 (current min is 1): android.widget.ListView#CHOICE_MODE_MULTIPLE_MODAL [InlinedApi]\n"
+                + "src/test/pkg/ApiSourceCheck2.java:14: Warning: Field requires API level 11 (current min is 1): android.widget.AbsListView#CHOICE_MODE_MULTIPLE_MODAL [InlinedApi]\n"
                 + "        int mode6 = ListView.CHOICE_MODE_MULTIPLE_MODAL;\n"
                 + "                    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
                 + "0 errors, 2 warnings\n",
@@ -1093,7 +1112,10 @@
                 + "src/test/pkg/TryWithResources.java:13: Error: Try-with-resources requires API level 19 (current min is 1) [NewApi]\n"
                 + "        try (BufferedReader br = new BufferedReader(new FileReader(path))) {\n"
                 + "        ^\n"
-                + "1 errors, 0 warnings\n",
+                + "src/test/pkg/TryWithResources.java:21: Error: Multi-catch with these reflection exceptions requires API level 19 (current min is 1) because they get compiled to the common but new super type ReflectiveOperationException. As a workaround either create individual catch statements, or catch Exception. [NewApi]\n"
+                + "        } catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {\n"
+                + "                 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
+                + "2 errors, 0 warnings\n",
                 lintProject(
                         "apicheck/minsdk1.xml=>AndroidManifest.xml",
                         "src/test/pkg/TryWithResources.java.txt=>src/test/pkg/TryWithResources.java"
@@ -1111,7 +1133,7 @@
 
     public void testReflectiveOperationException() throws Exception {
         assertEquals(""
-                + "src/test/pkg/Java7API.java:8: Error: Class requires API level 19 (current min is 1): java.lang.ReflectiveOperationException [NewApi]\n"
+                + "src/test/pkg/Java7API.java:8: Error: Multi-catch with these reflection exceptions requires API level 19 (current min is 1) because they get compiled to the common but new super type ReflectiveOperationException. As a workaround either create individual catch statements, or catch Exception. [NewApi]\n"
                 + "        } catch (ReflectiveOperationException e) {\n"
                 + "                 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
                 + "src/test/pkg/Java7API.java:9: Error: Call requires API level 19 (current min is 1): java.lang.ReflectiveOperationException#printStackTrace [NewApi]\n"
@@ -1909,7 +1931,7 @@
                 + "                              ~\n"
                 + "src/test/pkg/ImplicitCastTest.java:26: Error: Cast from Cursor to Closeable requires API level 16 (current min is 14) [NewApi]\n"
                 + "        closeable = c;\n"
-                + "        ~~~~~~~~~~~~~\n"
+                + "                    ~\n"
                 + "src/test/pkg/ImplicitCastTest.java:36: Error: Cast from ParcelFileDescriptor to Closeable requires API level 16 (current min is 14) [NewApi]\n"
                 + "        safeClose(pfd);\n"
                 + "                  ~~~\n"
@@ -2131,10 +2153,13 @@
                 + "src/test/pkg/MultiCatch.java:19: Error: Class requires API level 18 (current min is 1): android.media.UnsupportedSchemeException [NewApi]\n"
                 + "                  | UnsupportedSchemeException e) {\n"
                 + "                    ~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
+                + "src/test/pkg/MultiCatch.java:25: Error: Multi-catch with these reflection exceptions requires API level 19 (current min is 1) because they get compiled to the common but new super type ReflectiveOperationException. As a workaround either create individual catch statements, or catch Exception. [NewApi]\n"
+                + "        } catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {\n"
+                + "                 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
                 + "src/test/pkg/MultiCatch.java:26: Error: Multi-catch with these reflection exceptions requires API level 19 (current min is 1) because they get compiled to the common but new super type ReflectiveOperationException. As a workaround either create individual catch statements, or catch Exception. [NewApi]\n"
                 + "            e.printStackTrace();\n"
                 + "              ~~~~~~~~~~~~~~~\n"
-                + "5 errors, 0 warnings\n",
+                + "6 errors, 0 warnings\n",
 
                 lintProject(
                         java("src/test/pkg/MultiCatch.java", ""
diff --git a/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/AssertDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/AssertDetectorTest.java
index 9b64e00..6a2d486 100644
--- a/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/AssertDetectorTest.java
+++ b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/AssertDetectorTest.java
@@ -25,6 +25,7 @@
         return new AssertDetector();
     }
 
+    @SuppressWarnings("ClassNameDiffersFromFileName")
     public void test() throws Exception {
         assertEquals(""
                 + "src/test/pkg/Assert.java:7: Warning: Assertions are unreliable. Use BuildConfig.DEBUG conditional checks instead. [Assert]\n"
@@ -41,6 +42,33 @@
                 + "        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
                 + "0 errors, 4 warnings\n",
 
-            lintProject("src/test/pkg/Assert.java.txt=>src/test/pkg/Assert.java"));
+            lintProject(java("src/test/pkg/Assert.java", ""
+                    + "package test.pkg;\n"
+                    + "\n"
+                    + "import android.annotation.SuppressLint;\n"
+                    + "\n"
+                    + "public class Assert {\n"
+                    + "    public Assert(int param, Object param2, Object param3) {\n"
+                    + "        assert false;                              // ERROR\n"
+                    + "        assert param > 5 : \"My description\";       // ERROR\n"
+                    + "        assert param2 == param3;                   // ERROR\n"
+                    + "        assert param2 != null && param3 == param2; // ERROR\n"
+                    + "        assert true;                               // OK\n"
+                    + "        assert param2 == null;                     // OK\n"
+                    + "        assert param2 != null && param3 == null;   // OK\n"
+                    + "        assert param2 == null && param3 != null;   // OK\n"
+                    + "        assert param2 != null && param3 != null;   // OK\n"
+                    + "        assert null != param2;                     // OK\n"
+                    + "        assert param2 != null;                     // OK\n"
+                    + "        assert param2 != null : \"My description\";  // OK\n"
+                    + "        assert checkSuppressed(5) != null;         // OK\n"
+                    + "    }\n"
+                    + "\n"
+                    + "    @SuppressLint(\"Assert\")\n"
+                    + "    public static Object checkSuppressed(int param) {\n"
+                    + "        assert param > 5 : \"My description\";\n"
+                    + "        return null;\n"
+                    + "    }\n"
+                    + "}\n")));
     }
 }
diff --git a/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/CleanupDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/CleanupDetectorTest.java
index 5d56a76..13013b1 100644
--- a/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/CleanupDetectorTest.java
+++ b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/CleanupDetectorTest.java
@@ -18,7 +18,7 @@
 
 import com.android.tools.lint.detector.api.Detector;
 
-@SuppressWarnings("javadoc")
+@SuppressWarnings({"javadoc", "ClassNameDiffersFromFileName"})
 public class CleanupDetectorTest extends AbstractCheckTest {
     @Override
     protected Detector getDetector() {
@@ -273,4 +273,159 @@
                         + "    }\n"
                         + "}\n")));
     }
+
+    // Shared preference tests
+
+    public void test() throws Exception {
+        assertEquals(
+                "src/test/pkg/SharedPrefsTest.java:54: Warning: SharedPreferences.edit() without a corresponding commit() or apply() call [CommitPrefEdits]\n" +
+                        "        SharedPreferences.Editor editor = preferences.edit();\n" +
+                        "                                          ~~~~~~~~~~~~~~~~~~\n" +
+                        "src/test/pkg/SharedPrefsTest.java:62: Warning: SharedPreferences.edit() without a corresponding commit() or apply() call [CommitPrefEdits]\n" +
+                        "        SharedPreferences.Editor editor = preferences.edit();\n" +
+                        "                                          ~~~~~~~~~~~~~~~~~~\n" +
+                        "0 errors, 2 warnings\n" +
+                        "",
+
+                lintProject("src/test/pkg/SharedPrefsTest.java.txt=>" +
+                        "src/test/pkg/SharedPrefsTest.java"));
+    }
+
+    public void test2() throws Exception {
+        // Regression test 1 for http://code.google.com/p/android/issues/detail?id=34322
+        assertEquals(
+                "src/test/pkg/SharedPrefsTest2.java:13: Warning: SharedPreferences.edit() without a corresponding commit() or apply() call [CommitPrefEdits]\n" +
+                        "        SharedPreferences.Editor editor = preferences.edit();\n" +
+                        "                                          ~~~~~~~~~~~~~~~~~~\n" +
+                        "src/test/pkg/SharedPrefsTest2.java:17: Warning: SharedPreferences.edit() without a corresponding commit() or apply() call [CommitPrefEdits]\n" +
+                        "        Editor editor = preferences.edit();\n" +
+                        "                        ~~~~~~~~~~~~~~~~~~\n" +
+                        "0 errors, 2 warnings\n",
+
+                lintProject("src/test/pkg/SharedPrefsTest2.java.txt=>" +
+                        "src/test/pkg/SharedPrefsTest2.java"));
+    }
+
+    public void test3() throws Exception {
+        // Regression test 2 for http://code.google.com/p/android/issues/detail?id=34322
+        assertEquals(
+                "src/test/pkg/SharedPrefsTest3.java:13: Warning: SharedPreferences.edit() without a corresponding commit() or apply() call [CommitPrefEdits]\n" +
+                        "        Editor editor = preferences.edit();\n" +
+                        "                        ~~~~~~~~~~~~~~~~~~\n" +
+                        "0 errors, 1 warnings\n",
+
+                lintProject("src/test/pkg/SharedPrefsTest3.java.txt=>" +
+                        "src/test/pkg/SharedPrefsTest3.java"));
+    }
+
+    public void test4() throws Exception {
+        // Regression test 3 for http://code.google.com/p/android/issues/detail?id=34322
+        assertEquals(""
+                        + "src/test/pkg/SharedPrefsTest4.java:13: Warning: SharedPreferences.edit() without a corresponding commit() or apply() call [CommitPrefEdits]\n"
+                        + "        Editor editor = preferences.edit();\n"
+                        + "                        ~~~~~~~~~~~~~~~~~~\n"
+                        + "0 errors, 1 warnings\n",
+
+                lintProject("src/test/pkg/SharedPrefsTest4.java.txt=>" +
+                        "src/test/pkg/SharedPrefsTest4.java"));
+    }
+
+    public void test5() throws Exception {
+        // Check fields too: http://code.google.com/p/android/issues/detail?id=39134
+        assertEquals(
+                "src/test/pkg/SharedPrefsTest5.java:16: Warning: SharedPreferences.edit() without a corresponding commit() or apply() call [CommitPrefEdits]\n" +
+                        "        mPreferences.edit().putString(PREF_FOO, \"bar\");\n" +
+                        "        ~~~~~~~~~~~~~~~~~~~\n" +
+                        "src/test/pkg/SharedPrefsTest5.java:17: Warning: SharedPreferences.edit() without a corresponding commit() or apply() call [CommitPrefEdits]\n" +
+                        "        mPreferences.edit().remove(PREF_BAZ).remove(PREF_FOO);\n" +
+                        "        ~~~~~~~~~~~~~~~~~~~\n" +
+                        "src/test/pkg/SharedPrefsTest5.java:26: Warning: SharedPreferences.edit() without a corresponding commit() or apply() call [CommitPrefEdits]\n" +
+                        "        preferences.edit().putString(PREF_FOO, \"bar\");\n" +
+                        "        ~~~~~~~~~~~~~~~~~~\n" +
+                        "src/test/pkg/SharedPrefsTest5.java:27: Warning: SharedPreferences.edit() without a corresponding commit() or apply() call [CommitPrefEdits]\n" +
+                        "        preferences.edit().remove(PREF_BAZ).remove(PREF_FOO);\n" +
+                        "        ~~~~~~~~~~~~~~~~~~\n" +
+                        "src/test/pkg/SharedPrefsTest5.java:32: Warning: SharedPreferences.edit() without a corresponding commit() or apply() call [CommitPrefEdits]\n" +
+                        "        preferences.edit().putString(PREF_FOO, \"bar\");\n" +
+                        "        ~~~~~~~~~~~~~~~~~~\n" +
+                        "src/test/pkg/SharedPrefsTest5.java:33: Warning: SharedPreferences.edit() without a corresponding commit() or apply() call [CommitPrefEdits]\n" +
+                        "        preferences.edit().remove(PREF_BAZ).remove(PREF_FOO);\n" +
+                        "        ~~~~~~~~~~~~~~~~~~\n" +
+                        "src/test/pkg/SharedPrefsTest5.java:38: Warning: SharedPreferences.edit() without a corresponding commit() or apply() call [CommitPrefEdits]\n" +
+                        "        Editor editor = preferences.edit().putString(PREF_FOO, \"bar\");\n" +
+                        "                        ~~~~~~~~~~~~~~~~~~\n" +
+                        "0 errors, 7 warnings\n",
+
+                lintProject("src/test/pkg/SharedPrefsTest5.java.txt=>" +
+                        "src/test/pkg/SharedPrefsTest5.java"));
+    }
+
+    public void test6() throws Exception {
+        // Regression test for https://code.google.com/p/android/issues/detail?id=68692
+        assertEquals(""
+                        + "src/test/pkg/SharedPrefsTest7.java:13: Warning: SharedPreferences.edit() without a corresponding commit() or apply() call [CommitPrefEdits]\n"
+                        + "        settings.edit().putString(MY_PREF_KEY, myPrefValue);\n"
+                        + "        ~~~~~~~~~~~~~~~\n"
+                        + "0 errors, 1 warnings\n",
+
+                lintProject("src/test/pkg/SharedPrefsTest7.java.txt=>" +
+                        "src/test/pkg/SharedPrefsTest7.java"));
+    }
+
+    public void test7() throws Exception {
+        assertEquals("No warnings.", // minSdk < 9: no warnings
+
+                lintProject("src/test/pkg/SharedPrefsTest8.java.txt=>" +
+                        "src/test/pkg/SharedPrefsTest8.java"));
+    }
+
+    public void test8() throws Exception {
+        assertEquals(""
+                        + "src/test/pkg/SharedPrefsTest8.java:11: Warning: Consider using apply() instead; commit writes its data to persistent storage immediately, whereas apply will handle it in the background [CommitPrefEdits]\n"
+                        + "        editor.commit();\n"
+                        + "        ~~~~~~~~~~~~~~~\n"
+                        + "0 errors, 1 warnings\n",
+
+                lintProject(
+                        "apicheck/minsdk11.xml=>AndroidManifest.xml",
+                        "src/test/pkg/SharedPrefsTest8.java.txt=>src/test/pkg/SharedPrefsTest8.java"));
+    }
+
+    public void testChainedCalls() throws Exception {
+        assertEquals(""
+                        + "src/test/pkg/Chained.java:24: Warning: SharedPreferences.edit() without a corresponding commit() or apply() call [CommitPrefEdits]\n"
+                        + "        PreferenceManager\n"
+                        + "        ^\n"
+                        + "0 errors, 1 warnings\n",
+                lintProject(java("src/test/pkg/Chained.java", ""
+                        + "package test.pkg;\n"
+                        + "\n"
+                        + "import android.content.Context;\n"
+                        + "import android.preference.PreferenceManager;\n"
+                        + "\n"
+                        + "public class Chained {\n"
+                        + "    private static void falsePositive(Context context) {\n"
+                        + "        PreferenceManager\n"
+                        + "                .getDefaultSharedPreferences(context)\n"
+                        + "                .edit()\n"
+                        + "                .putString(\"wat\", \"wat\")\n"
+                        + "                .commit();\n"
+                        + "    }\n"
+                        + "\n"
+                        + "    private static void falsePositive2(Context context) {\n"
+                        + "        boolean var = PreferenceManager\n"
+                        + "                .getDefaultSharedPreferences(context)\n"
+                        + "                .edit()\n"
+                        + "                .putString(\"wat\", \"wat\")\n"
+                        + "                .commit();\n"
+                        + "    }\n"
+                        + "\n"
+                        + "    private static void truePositive(Context context) {\n"
+                        + "        PreferenceManager\n"
+                        + "                .getDefaultSharedPreferences(context)\n"
+                        + "                .edit()\n"
+                        + "                .putString(\"wat\", \"wat\");\n"
+                        + "    }\n"
+                        + "}\n")));
+    }
 }
diff --git a/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/CustomViewDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/CustomViewDetectorTest.java
index 130db9b..9ab0ca5 100644
--- a/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/CustomViewDetectorTest.java
+++ b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/CustomViewDetectorTest.java
@@ -18,12 +18,14 @@
 
 import com.android.tools.lint.detector.api.Detector;
 
+@SuppressWarnings("MethodMayBeStatic")
 public class CustomViewDetectorTest extends AbstractCheckTest {
     @Override
     protected Detector getDetector() {
         return new CustomViewDetector();
     }
 
+    @SuppressWarnings("ClassNameDiffersFromFileName")
     public void test() throws Exception {
         assertEquals(""
             + "src/test/pkg/CustomView1.java:18: Warning: By convention, the custom view (CustomView1) and the declare-styleable (MyDeclareStyleable) should have the same name (various editor features rely on this convention) [CustomViewStyleable]\n"
@@ -46,7 +48,107 @@
             + "                                               ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
             + "0 errors, 6 warnings\n",
 
-            lintProject("src/test/pkg/CustomView1.java.txt=>" +
-                    "src/test/pkg/CustomView1.java"));
+            lintProject(java("src/test/pkg/CustomView1.java", ""
+                    + "package test.pkg;\n"
+                    + "\n"
+                    + "import android.content.Context;\n"
+                    + "import android.util.AttributeSet;\n"
+                    + "import android.widget.Button;\n"
+                    + "import android.widget.LinearLayout;\n"
+                    + "\n"
+                    + "public class CustomView1 extends Button {\n"
+                    + "    public CustomView1(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {\n"
+                    + "        super(context, attrs, defStyleAttr);\n"
+                    + "        // OK\n"
+                    + "        context.obtainStyledAttributes(R.styleable.CustomView1);\n"
+                    + "        context.obtainStyledAttributes(defStyleRes, R.styleable.CustomView1);\n"
+                    + "        context.obtainStyledAttributes(attrs, R.styleable.CustomView1);\n"
+                    + "        context.obtainStyledAttributes(attrs, R.styleable.CustomView1, defStyleAttr, defStyleRes);\n"
+                    + "\n"
+                    + "        // Wrong:\n"
+                    + "        context.obtainStyledAttributes(R.styleable.MyDeclareStyleable);\n"
+                    + "        context.obtainStyledAttributes(defStyleRes, R.styleable.MyDeclareStyleable);\n"
+                    + "        context.obtainStyledAttributes(attrs, R.styleable.MyDeclareStyleable);\n"
+                    + "        context.obtainStyledAttributes(attrs, R.styleable.MyDeclareStyleable, defStyleAttr,\n"
+                    + "                defStyleRes);\n"
+                    + "\n"
+                    + "        // Unknown: Not flagged\n"
+                    + "        int[] dynamic = getStyleable();\n"
+                    + "        context.obtainStyledAttributes(dynamic);\n"
+                    + "        context.obtainStyledAttributes(defStyleRes, dynamic);\n"
+                    + "        context.obtainStyledAttributes(attrs, dynamic);\n"
+                    + "        context.obtainStyledAttributes(attrs, dynamic, defStyleAttr, defStyleRes);\n"
+                    + "    }\n"
+                    + "\n"
+                    + "    private int[] getStyleable() {\n"
+                    + "        return new int[0];\n"
+                    + "    }\n"
+                    + "\n"
+                    + "    public static class MyLayout extends LinearLayout {\n"
+                    + "        public MyLayout(Context context, AttributeSet attrs, int defStyle) {\n"
+                    + "            super(context, attrs, defStyle);\n"
+                    + "            context.obtainStyledAttributes(R.styleable.MyLayout);\n"
+                    + "        }\n"
+                    + "\n"
+                    + "        public static class MyLayoutParams extends LinearLayout.LayoutParams {\n"
+                    + "            public MyLayoutParams(Context context, AttributeSet attrs) {\n"
+                    + "                super(context, attrs);\n"
+                    + "                context.obtainStyledAttributes(R.styleable.MyLayout_Layout); // OK\n"
+                    + "                context.obtainStyledAttributes(R.styleable.MyLayout); // Wrong\n"
+                    + "                context.obtainStyledAttributes(R.styleable.MyDeclareStyleable); // Wrong\n"
+                    + "            }\n"
+                    + "        }\n"
+                    + "    }\n"
+                    + "\n"
+                    + "    public static final class R {\n"
+                    + "        public static final class attr {\n"
+                    + "            public static final int layout_myWeight=0x7f010001;\n"
+                    + "            public static final int myParam=0x7f010000;\n"
+                    + "        }\n"
+                    + "        public static final class dimen {\n"
+                    + "            public static final int activity_horizontal_margin=0x7f040000;\n"
+                    + "            public static final int activity_vertical_margin=0x7f040001;\n"
+                    + "        }\n"
+                    + "        public static final class drawable {\n"
+                    + "            public static final int ic_launcher=0x7f020000;\n"
+                    + "        }\n"
+                    + "        public static final class id {\n"
+                    + "            public static final int action_settings=0x7f080000;\n"
+                    + "        }\n"
+                    + "        public static final class layout {\n"
+                    + "            public static final int activity_my=0x7f030000;\n"
+                    + "        }\n"
+                    + "        public static final class menu {\n"
+                    + "            public static final int my=0x7f070000;\n"
+                    + "        }\n"
+                    + "        public static final class string {\n"
+                    + "            public static final int action_settings=0x7f050000;\n"
+                    + "            public static final int app_name=0x7f050001;\n"
+                    + "            public static final int hello_world=0x7f050002;\n"
+                    + "        }\n"
+                    + "        public static final class style {\n"
+                    + "            public static final int AppTheme=0x7f060000;\n"
+                    + "        }\n"
+                    + "        public static final class styleable {\n"
+                    + "            public static final int[] CustomView1 = {\n"
+                    + "\n"
+                    + "            };\n"
+                    + "            public static final int[] MyDeclareStyleable = {\n"
+                    + "\n"
+                    + "            };\n"
+                    + "            public static final int[] MyLayout = {\n"
+                    + "                    0x010100c4, 0x7f010000\n"
+                    + "            };\n"
+                    + "            public static final int MyLayout_android_orientation = 0;\n"
+                    + "            public static final int MyLayout_myParam = 1;\n"
+                    + "            public static final int[] MyLayout_Layout = {\n"
+                    + "                    0x010100f4, 0x010100f5, 0x7f010001\n"
+                    + "            };\n"
+                    + "            public static final int MyLayout_Layout_android_layout_height = 1;\n"
+                    + "            public static final int MyLayout_Layout_android_layout_width = 0;\n"
+                    + "            public static final int MyLayout_Layout_layout_myWeight = 2;\n"
+                    + "        }\n"
+                    + "    }\n"
+                    + "}\n")));
     }
 }
diff --git a/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/CutPasteDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/CutPasteDetectorTest.java
index 314f146..c3bf9cf 100644
--- a/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/CutPasteDetectorTest.java
+++ b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/CutPasteDetectorTest.java
@@ -18,7 +18,8 @@
 
 import com.android.tools.lint.detector.api.Detector;
 
-@SuppressWarnings("javadoc")
+@SuppressWarnings({"javadoc", "ConstantConditions", "UnusedAssignment", "UnnecessaryLocalVariable",
+        "ConstantIfStatement", "StatementWithEmptyBody"})
 public class CutPasteDetectorTest extends AbstractCheckTest {
     @Override
     protected Detector getDetector() {
@@ -32,6 +33,7 @@
         return true;
     }
 
+    @SuppressWarnings("ClassNameDiffersFromFileName")
     public void test() throws Exception {
         assertEquals(
             "src/test/pkg/PasteError.java:15: Warning: The id R.id.textView1 has already been looked up in this method; possible cut & paste error? [CutPasteId]\n" +
@@ -56,7 +58,115 @@
             "    src/test/pkg/PasteError.java:91: First usage here\n" +
             "0 errors, 5 warnings\n",
 
-            lintProject("src/test/pkg/PasteError.java.txt=>" +
-                    "src/test/pkg/PasteError.java"));
+            lintProject(java("src/test/pkg/PasteError.java", ""
+                    + "package test.pkg;\n"
+                    + "\n"
+                    + "import android.app.Activity;\n"
+                    + "import android.view.View;\n"
+                    + "\n"
+                    + "public class PasteError extends Activity {\n"
+                    + "    protected void ok() {\n"
+                    + "        Button button1 = (Button) findViewById(R.id.textView1);\n"
+                    + "        mView2 = findViewById(R.id.textView2);\n"
+                    + "        View view3 = findViewById(R.id.activity_main);\n"
+                    + "    }\n"
+                    + "\n"
+                    + "    protected void error() {\n"
+                    + "        View view1 = findViewById(R.id.textView1);\n"
+                    + "        View view2 = findViewById(R.id.textView1);\n"
+                    + "        View view3 = findViewById(R.id.textView2);\n"
+                    + "    }\n"
+                    + "\n"
+                    + "    protected void ok2() {\n"
+                    + "        View view1;\n"
+                    + "        if (true) {\n"
+                    + "            view1 = findViewById(R.id.textView1);\n"
+                    + "        } else {\n"
+                    + "            view1 = findViewById(R.id.textView1);\n"
+                    + "        }\n"
+                    + "    }\n"
+                    + "\n"
+                    + "    @SuppressLint(\"CutPasteId\")\n"
+                    + "    protected void suppressed() {\n"
+                    + "        View view1 = findViewById(R.id.textView1);\n"
+                    + "        View view2 = findViewById(R.id.textView1);\n"
+                    + "    }\n"
+                    + "\n"
+                    + "    private void ok3() {\n"
+                    + "        if (view == null || view.findViewById(R.id.city_name) == null) {\n"
+                    + "            view = mInflater.inflate(R.layout.city_list_item, parent, false);\n"
+                    + "        }\n"
+                    + "        TextView name = (TextView) view.findViewById(R.id.city_name);\n"
+                    + "    }\n"
+                    + "\n"
+                    + "    private void ok4() {\n"
+                    + "        mPrevAlbumWrapper = mPrevTrackLayout.findViewById(R.id.album_wrapper);\n"
+                    + "        mNextAlbumWrapper = mNextTrackLayout.findViewById(R.id.album_wrapper);\n"
+                    + "    }\n"
+                    + "\n"
+                    + "    @Override\n"
+                    + "    public View getView(int position, View convertView, ViewGroup parent) {\n"
+                    + "        View listItem = convertView;\n"
+                    + "        if (getItemViewType(position) == VIEW_TYPE_HEADER) {\n"
+                    + "            TextView header = (TextView) listItem.findViewById(R.id.name);\n"
+                    + "        } else if (getItemViewType(position) == VIEW_TYPE_BOOLEAN) {\n"
+                    + "            TextView filterName = (TextView) listItem.findViewById(R.id.name);\n"
+                    + "        } else {\n"
+                    + "            TextView filterName = (TextView) listItem.findViewById(R.id.name);\n"
+                    + "        }\n"
+                    + "    }\n"
+                    + "\n"
+                    + "    protected void ok_branch_1() {\n"
+                    + "        if (true) {\n"
+                    + "            view1 = findViewById(R.id.textView1);\n"
+                    + "        } else {\n"
+                    + "            view2 = findViewById(R.id.textView1);\n"
+                    + "        }\n"
+                    + "    }\n"
+                    + "\n"
+                    + "    protected void error_branch_1() {\n"
+                    + "        if (true) {\n"
+                    + "            view1 = findViewById(R.id.textView1);\n"
+                    + "        }\n"
+                    + "        if (true) {\n"
+                    + "            view2 = findViewById(R.id.textView1);\n"
+                    + "        }\n"
+                    + "    }\n"
+                    + "\n"
+                    + "    protected void error_branch_2() {\n"
+                    + "        view1 = findViewById(R.id.textView1);\n"
+                    + "        if (true) {\n"
+                    + "            view2 = findViewById(R.id.textView1);\n"
+                    + "        }\n"
+                    + "    }\n"
+                    + "\n"
+                    + "    protected void error_branch_3() {\n"
+                    + "        view1 = findViewById(R.id.textView1);\n"
+                    + "        if (true) {\n"
+                    + "        } else {\n"
+                    + "            view2 = findViewById(R.id.textView1);\n"
+                    + "        }\n"
+                    + "    }\n"
+                    + "\n"
+                    + "    protected void error_branch_4() {\n"
+                    + "        view1 = findViewById(R.id.textView1);\n"
+                    + "        if (true) {\n"
+                    + "        } else {\n"
+                    + "            if (true) {\n"
+                    + "                view2 = findViewById(R.id.textView1);\n"
+                    + "            }\n"
+                    + "        }\n"
+                    + "    }\n"
+                    + "\n"
+                    + "    protected void ok_branch_2() {\n"
+                    + "        if (true) {\n"
+                    + "            view1 = findViewById(R.id.textView1);\n"
+                    + "        } else {\n"
+                    + "            if (true) {\n"
+                    + "                view2 = findViewById(R.id.textView1);\n"
+                    + "            }\n"
+                    + "        }\n"
+                    + "    }\n"
+                    + "}\n")));
     }
 }
diff --git a/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/DateFormatDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/DateFormatDetectorTest.java
index 2d016b2..ff08122 100644
--- a/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/DateFormatDetectorTest.java
+++ b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/DateFormatDetectorTest.java
@@ -18,13 +18,16 @@
 
 import com.android.tools.lint.detector.api.Detector;
 
-@SuppressWarnings("javadoc")
+@SuppressWarnings({"javadoc", "OnDemandImport", "ResultOfMethodCallIgnored", "MethodMayBeStatic",
+        "SimpleDateFormatWithoutLocale", "ResultOfObjectAllocationIgnored",
+        "StringToUpperCaseOrToLowerCaseWithoutLocale"})
 public class DateFormatDetectorTest extends AbstractCheckTest {
     @Override
     protected Detector getDetector() {
         return new DateFormatDetector();
     }
 
+    @SuppressWarnings("ClassNameDiffersFromFileName")
     public void test() throws Exception {
         assertEquals(
             "src/test/pkg/LocaleTest.java:32: Warning: To get local formatting use getDateInstance(), getDateTimeInstance(), or getTimeInstance(), or use new SimpleDateFormat(String template, Locale locale) with for example Locale.US for ASCII dates. [SimpleDateFormat]\n" +
@@ -39,12 +42,45 @@
             "0 errors, 3 warnings\n",
 
             lintProject(
-                    "bytecode/.classpath=>.classpath",
-                    "project.properties19=>project.properties",
-                    "bytecode/AndroidManifest.xml=>AndroidManifest.xml",
-                    "res/layout/onclick.xml=>res/layout/onclick.xml",
-                    "bytecode/LocaleTest.java.txt=>src/test/pkg/LocaleTest.java",
-                    "bytecode/LocaleTest.class.data=>bin/classes/test/pkg/LocaleTest.class"
-                    ));
+                    manifest().minSdk(19),
+                    java("src/test/pkg/LocaleTest.java", ""
+                            + "package test.pkg;\n"
+                            + "\n"
+                            + "import java.text.*;\n"
+                            + "import java.util.*;\n"
+                            + "\n"
+                            + "public class LocaleTest {\n"
+                            + "    public void testStrings() {\n"
+                            + "        System.out.println(\"OK\".toUpperCase(Locale.getDefault()));\n"
+                            + "        System.out.println(\"OK\".toUpperCase(Locale.US));\n"
+                            + "        System.out.println(\"OK\".toUpperCase(Locale.CHINA));\n"
+                            + "        System.out.println(\"WRONG\".toUpperCase());\n"
+                            + "\n"
+                            + "        System.out.println(\"OK\".toLowerCase(Locale.getDefault()));\n"
+                            + "        System.out.println(\"OK\".toLowerCase(Locale.US));\n"
+                            + "        System.out.println(\"OK\".toLowerCase(Locale.CHINA));\n"
+                            + "        System.out.println(\"WRONG\".toLowerCase());\n"
+                            + "\n"
+                            + "        String.format(Locale.getDefault(), \"OK: %f\", 1.0f);\n"
+                            + "        String.format(\"OK: %x %A %c %b %B %h %n %%\", 1, 2, 'c', true, false, 5);\n"
+                            + "        String.format(\"WRONG: %f\", 1.0f); // Implies locale\n"
+                            + "        String.format(\"WRONG: %1$f\", 1.0f);\n"
+                            + "        String.format(\"WRONG: %e\", 1.0f);\n"
+                            + "        String.format(\"WRONG: %d\", 1.0f);\n"
+                            + "        String.format(\"WRONG: %g\", 1.0f);\n"
+                            + "        String.format(\"WRONG: %g\", 1.0f);\n"
+                            + "        String.format(\"WRONG: %1$tm %1$te,%1$tY\",\n"
+                            + "                new GregorianCalendar(2012, GregorianCalendar.AUGUST, 27));\n"
+                            + "    }\n"
+                            + "\n"
+                            + "    @android.annotation.SuppressLint(\"NewApi\") // DateFormatSymbols requires API 9\n"
+                            + "    public void testSimpleDateFormat() {\n"
+                            + "        new SimpleDateFormat(); // WRONG\n"
+                            + "        new SimpleDateFormat(\"yyyy-MM-dd\"); // WRONG\n"
+                            + "        new SimpleDateFormat(\"yyyy-MM-dd\", DateFormatSymbols.getInstance()); // WRONG\n"
+                            + "        new SimpleDateFormat(\"yyyy-MM-dd\", Locale.US); // OK\n"
+                            + "    }\n"
+                            + "}\n")
+            ));
     }
 }
diff --git a/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/FragmentDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/FragmentDetectorTest.java
index 42e8e7a..a9423ca 100644
--- a/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/FragmentDetectorTest.java
+++ b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/FragmentDetectorTest.java
@@ -28,34 +28,87 @@
     public void test() throws Exception {
         assertEquals(""
             + "src/test/pkg/FragmentTest.java:10: Error: This fragment class should be public (test.pkg.FragmentTest.Fragment1) [ValidFragment]\n"
-            + " private static class Fragment1 extends Fragment {\n"
-            + "                      ~~~~~~~~~\n"
+            + "    private static class Fragment1 extends Fragment {\n"
+            + "                         ~~~~~~~~~\n"
             + "src/test/pkg/FragmentTest.java:15: Error: This fragment inner class should be static (test.pkg.FragmentTest.Fragment2) [ValidFragment]\n"
-            + " public class Fragment2 extends Fragment {\n"
-            + "              ~~~~~~~~~\n"
+            + "    public class Fragment2 extends Fragment {\n"
+            + "                 ~~~~~~~~~\n"
             + "src/test/pkg/FragmentTest.java:21: Error: The default constructor must be public [ValidFragment]\n"
-            + "  private Fragment3() {\n"
-            + "          ~~~~~~~~~~~\n"
+            + "        private Fragment3() {\n"
+            + "                ~~~~~~~~~\n"
             + "src/test/pkg/FragmentTest.java:26: Error: This fragment should provide a default constructor (a public constructor with no arguments) (test.pkg.FragmentTest.Fragment4) [ValidFragment]\n"
-            + " public static class Fragment4 extends Fragment {\n"
-            + "                     ~~~~~~~~~\n"
+            + "    public static class Fragment4 extends Fragment {\n"
+            + "                        ~~~~~~~~~\n"
             + "src/test/pkg/FragmentTest.java:27: Error: Avoid non-default constructors in fragments: use a default constructor plus Fragment#setArguments(Bundle) instead [ValidFragment]\n"
-            + "  private Fragment4(int dummy) {\n"
-            + "          ~~~~~~~~~~~~~~~~~~~~\n"
+            + "        private Fragment4(int dummy) {\n"
+            + "                ~~~~~~~~~\n"
             + "src/test/pkg/FragmentTest.java:36: Error: Avoid non-default constructors in fragments: use a default constructor plus Fragment#setArguments(Bundle) instead [ValidFragment]\n"
-            + "  public Fragment5(int dummy) {\n"
-            + "         ~~~~~~~~~~~~~~~~~~~~\n"
+            + "        public Fragment5(int dummy) {\n"
+            + "               ~~~~~~~~~\n"
             + "6 errors, 0 warnings\n",
 
-            lintProject(
-                "bytecode/FragmentTest$Fragment1.class.data=>bin/classes/test/pkg/FragmentTest$Fragment1.class",
-                "bytecode/FragmentTest$Fragment2.class.data=>bin/classes/test/pkg/FragmentTest$Fragment2.class",
-                "bytecode/FragmentTest$Fragment3.class.data=>bin/classes/test/pkg/FragmentTest$Fragment3.class",
-                "bytecode/FragmentTest$Fragment4.class.data=>bin/classes/test/pkg/FragmentTest$Fragment4.class",
-                "bytecode/FragmentTest$Fragment5.class.data=>bin/classes/test/pkg/FragmentTest$Fragment5.class",
-                "bytecode/FragmentTest$Fragment6.class.data=>bin/classes/test/pkg/FragmentTest$Fragment6.class",
-                "bytecode/FragmentTest$NotAFragment.class.data=>bin/classes/test/pkg/FragmentTest$NotAFragment.class",
-                "bytecode/FragmentTest.java.txt=>src/test/pkg/FragmentTest.java"));
+            lintProject(java("src/test/pkg/FragmentTest.java", ""
+                    + "package test.pkg;\n"
+                    + "\n"
+                    + "import android.annotation.SuppressLint;\n"
+                    + "import android.app.Fragment;\n"
+                    + "\n"
+                    + "@SuppressWarnings(\"unused\")\n"
+                    + "public class FragmentTest {\n"
+                    + "\n"
+                    + "    // Should be public\n"
+                    + "    private static class Fragment1 extends Fragment {\n"
+                    + "\n"
+                    + "    }\n"
+                    + "\n"
+                    + "    // Should be static\n"
+                    + "    public class Fragment2 extends Fragment {\n"
+                    + "\n"
+                    + "    }\n"
+                    + "\n"
+                    + "    // Should have a public constructor\n"
+                    + "    public static class Fragment3 extends Fragment {\n"
+                    + "        private Fragment3() {\n"
+                    + "        }\n"
+                    + "    }\n"
+                    + "\n"
+                    + "    // Should have a public constructor with no arguments\n"
+                    + "    public static class Fragment4 extends Fragment {\n"
+                    + "        private Fragment4(int dummy) {\n"
+                    + "        }\n"
+                    + "    }\n"
+                    + "\n"
+                    + "    // Should *only* have the default constructor, not the\n"
+                    + "    // multi-argument one\n"
+                    + "    public static class Fragment5 extends Fragment {\n"
+                    + "        public Fragment5() {\n"
+                    + "        }\n"
+                    + "        public Fragment5(int dummy) {\n"
+                    + "        }\n"
+                    + "    }\n"
+                    + "\n"
+                    + "    // Suppressed\n"
+                    + "    @SuppressLint(\"ValidFragment\")\n"
+                    + "    public static class Fragment6 extends Fragment {\n"
+                    + "        private Fragment6() {\n"
+                    + "        }\n"
+                    + "    }\n"
+                    + "\n"
+                    + "    public static class ValidFragment1 extends Fragment {\n"
+                    + "        public ValidFragment1() {\n"
+                    + "        }\n"
+                    + "    }\n"
+                    + "\n"
+                    + "    // (Not a fragment)\n"
+                    + "    private class NotAFragment {\n"
+                    + "    }\n"
+                    + "\n"
+                    + "    // Ok: Has implicit constructor\n"
+                    + "    public static class Fragment7 extends Fragment {\n"
+                    + "    }\n"
+                    + "}\n"))
+
+        );
     }
 
     public void testAnonymousInnerClass() throws Exception {
diff --git a/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/GetSignaturesDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/GetSignaturesDetectorTest.java
index 8b3ffad..3427efc 100644
--- a/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/GetSignaturesDetectorTest.java
+++ b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/GetSignaturesDetectorTest.java
@@ -18,6 +18,7 @@
 
 import com.android.tools.lint.detector.api.Detector;
 
+@SuppressWarnings("ClassNameDiffersFromFileName")
 public class GetSignaturesDetectorTest extends AbstractCheckTest {
 
     @Override
@@ -25,13 +26,6 @@
         return new GetSignaturesDetector();
     }
 
-    @Override
-    protected boolean allowCompilationErrors() {
-        // Some of these unit tests are still relying on source code that references
-        // unresolved symbols etc.
-        return true;
-    }
-
     public void testLintWarningOnSingleGetSignaturesFlag() throws Exception {
         assertEquals(
                 "src/test/pkg/GetSignaturesSingleFlagTest.java:9: Information: Reading app signatures from getPackageInfo: The app signatures could be exploited if not validated properly; see issue explanation for details. [PackageManagerGetSignatures]\n"
@@ -39,8 +33,18 @@
                         + "                                        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
                         + "0 errors, 1 warnings\n",
                 lintProject(
-                        "src/test/pkg/GetSignaturesSingleFlagTest.java.txt" +
-                                "=>src/test/pkg/GetSignaturesSingleFlagTest.java"
+                        java("src/test/pkg/GetSignaturesSingleFlagTest.java", ""
+                                + "package test.pkg;\n"
+                                + "\n"
+                                + "import android.app.Activity;\n"
+                                + "import android.content.pm.PackageManager;\n"
+                                + "\n"
+                                + "public class GetSignaturesSingleFlagTest extends Activity {\n"
+                                + "    public void failLintCheck() throws Exception {\n"
+                                + "        getPackageManager()\n"
+                                + "            .getPackageInfo(\"some.pkg\", PackageManager.GET_SIGNATURES);\n"
+                                + "    }\n"
+                                + "}")
                 ));
     }
 
@@ -51,8 +55,20 @@
                     + "                                        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
                     + "0 errors, 1 warnings\n",
             lintProject(
-                "src/test/pkg/GetSignaturesBitwiseOrTest.java.txt" +
-                        "=>src/test/pkg/GetSignaturesBitwiseOrTest.java"
+                java("src/test/pkg/GetSignaturesBitwiseOrTest.java", ""
+                        + "package test.pkg;\n"
+                        + "\n"
+                        + "import static android.content.pm.PackageManager.*;\n"
+                        + "\n"
+                        + "import android.app.Activity;\n"
+                        + "import android.content.pm.PackageManager;\n"
+                        + "\n"
+                        + "public class GetSignaturesBitwiseOrTest extends Activity {\n"
+                        + "    public void failLintCheck() throws Exception {\n"
+                        + "        getPackageManager()\n"
+                        + "            .getPackageInfo(\"some.pkg\", GET_GIDS | GET_SIGNATURES | GET_PROVIDERS);\n"
+                        + "    }\n"
+                        + "}")
             ));
     }
 
@@ -63,8 +79,17 @@
                         + "                                                       ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
                         + "0 errors, 1 warnings\n",
                 lintProject(
-                        "src/test/pkg/GetSignaturesBitwiseXorTest.java.txt" +
-                                "=>src/test/pkg/GetSignaturesBitwiseXorTest.java"
+                        java("src/test/pkg/GetSignaturesBitwiseXorTest.java", ""
+                                + "package test.pkg;\n"
+                                + "\n"
+                                + "import android.app.Activity;\n"
+                                + "import android.content.pm.PackageManager;\n"
+                                + "\n"
+                                + "public class GetSignaturesBitwiseXorTest extends Activity {\n"
+                                + "    public void failLintCheck() throws Exception {\n"
+                                + "        getPackageManager().getPackageInfo(\"some.pkg\", PackageManager.GET_SIGNATURES ^ 0x0);\n"
+                                + "    }\n"
+                                + "}")
                 ));
     }
 
@@ -75,8 +100,18 @@
                         + "            ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
                         + "0 errors, 1 warnings\n",
                 lintProject(
-                        "src/test/pkg/GetSignaturesBitwiseAndTest.java.txt" +
-                                "=>src/test/pkg/GetSignaturesBitwiseAndTest.java"
+                        java("src/test/pkg/GetSignaturesBitwiseAndTest.java", ""
+                                + "package test.pkg;\n"
+                                + "\n"
+                                + "import android.app.Activity;\n"
+                                + "import android.content.pm.PackageManager;\n"
+                                + "\n"
+                                + "public class GetSignaturesBitwiseAndTest extends Activity {\n"
+                                + "    public void failLintCheck() throws Exception {\n"
+                                + "        getPackageManager().getPackageInfo(\"some.pkg\",\n"
+                                + "            Integer.MAX_VALUE & PackageManager.GET_SIGNATURES);\n"
+                                + "    }\n"
+                                + "}")
                 ));
     }
 
@@ -87,17 +122,40 @@
                         + "                                                       ~~~~~\n"
                         + "0 errors, 1 warnings\n",
                 lintProject(
-                        "src/test/pkg/GetSignaturesStaticFieldTest.java.txt" +
-                                "=>src/test/pkg/GetSignaturesStaticFieldTest.java"
+                        java("src/test/pkg/GetSignaturesStaticFieldTest.java", ""
+                                + "package test.pkg;\n"
+                                + "\n"
+                                + "import android.app.Activity;\n"
+                                + "import android.content.pm.PackageManager;\n"
+                                + "\n"
+                                + "public class GetSignaturesStaticFieldTest extends Activity {\n"
+                                + "    private static final int FLAGS = PackageManager.GET_SIGNATURES;\n"
+                                + "    public void failLintCheck() throws Exception {\n"
+                                + "        getPackageManager().getPackageInfo(\"some.pkg\", FLAGS);\n"
+                                + "    }\n"
+                                + "}")
                 ));
     }
 
     public void testNoLintWarningOnFlagsInLocalVariable() throws Exception {
-        assertEquals(
-                "No warnings.",
+        assertEquals(""
+                        + "src/test/pkg/GetSignaturesLocalVariableTest.java:9: Information: Reading app signatures from getPackageInfo: The app signatures could be exploited if not validated properly; see issue explanation for details. [PackageManagerGetSignatures]\n"
+                        + "        getPackageManager().getPackageInfo(\"some.pkg\", flags);\n"
+                        + "                                                       ~~~~~\n"
+                        + "0 errors, 1 warnings\n",
                 lintProject(
-                        "src/test/pkg/GetSignaturesLocalVariableTest.java.txt" +
-                                "=>src/test/pkg/GetSignaturesLocalVariableTest.java"
+                        java("src/test/pkg/GetSignaturesLocalVariableTest.java", ""
+                                + "package test.pkg;\n"
+                                + "\n"
+                                + "import android.app.Activity;\n"
+                                + "import android.content.pm.PackageManager;\n"
+                                + "\n"
+                                + "public class GetSignaturesLocalVariableTest extends Activity {\n"
+                                + "    public void passLintCheck() throws Exception {\n"
+                                + "        int flags = PackageManager.GET_SIGNATURES;\n"
+                                + "        getPackageManager().getPackageInfo(\"some.pkg\", flags);\n"
+                                + "    }\n"
+                                + "}")
                 ));
     }
 
@@ -105,8 +163,28 @@
         assertEquals(
                 "No warnings.",
                 lintProject(
-                        "src/test/pkg/GetSignaturesNoFlagTest.java.txt" +
-                                "=>src/test/pkg/GetSignaturesNoFlagTest.java"
+                        java("src/test/pkg/GetSignaturesNoFlagTest.java", ""
+                                + "package test.pkg;\n"
+                                + "\n"
+                                + "import static android.content.pm.PackageManager.*;\n"
+                                + "\n"
+                                + "import android.app.Activity;\n"
+                                + "\n"
+                                + "public class GetSignaturesNoFlagTest extends Activity {\n"
+                                + "    public void passLintCheck() throws Exception {\n"
+                                + "        getPackageManager()\n"
+                                + "            .getPackageInfo(\"some.pkg\",\n"
+                                + "                GET_ACTIVITIES |\n"
+                                + "                GET_GIDS |\n"
+                                + "                GET_CONFIGURATIONS |\n"
+                                + "                GET_INSTRUMENTATION |\n"
+                                + "                GET_PERMISSIONS |\n"
+                                + "                GET_PROVIDERS |\n"
+                                + "                GET_RECEIVERS |\n"
+                                + "                GET_SERVICES |\n"
+                                + "                GET_UNINSTALLED_PACKAGES);\n"
+                                + "    }\n"
+                                + "}")
                 ));
     }
 
@@ -114,8 +192,21 @@
         assertEquals(
                 "No warnings.",
                 lintProject(
-                        "src/test/pkg/GetSignaturesNotPackageManagerTest.java.txt" +
-                                "=>src/test/pkg/GetSignaturesNotPackageManagerTest.java"
+                        java("src/test/pkg/GetSignaturesNotPackageManagerTest.java", ""
+                                + "package test.pkg;\n"
+                                + "\n"
+                                + "import android.app.Activity;\n"
+                                + "import android.content.pm.PackageManager;\n"
+                                + "import android.content.pm.PackageInfo;\n"
+                                + "\n"
+                                + "public class GetSignaturesNotPackageManagerTest extends Activity {\n"
+                                + "    public void passLintCheck(Mock mock) throws Exception {\n"
+                                + "        mock.getPackageInfo(\"some.pkg\", PackageManager.GET_SIGNATURES);\n"
+                                + "    }\n"
+                                + "    public interface Mock {\n"
+                                + "        PackageInfo getPackageInfo(String pkg, int flags);\n"
+                                + "    }\n"
+                                + "}")
                 ));
     }
 }
diff --git a/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/HandlerDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/HandlerDetectorTest.java
index 3211e5b..f739208 100644
--- a/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/HandlerDetectorTest.java
+++ b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/HandlerDetectorTest.java
@@ -18,7 +18,7 @@
 
 import com.android.tools.lint.detector.api.Detector;
 
-@SuppressWarnings({"javadoc", "ClassNameDiffersFromFileName"})
+@SuppressWarnings({"javadoc", "ClassNameDiffersFromFileName", "ConstantConditions"})
 public class HandlerDetectorTest extends AbstractCheckTest {
     @Override
     protected Detector getDetector() {
@@ -32,7 +32,7 @@
                 + "                 ~~~~~\n"
                 + "src/test/pkg/HandlerTest.java:18: Warning: This Handler class should be static or leaks might occur (anonymous android.os.Handler) [HandlerLeak]\n"
                 + "        Handler anonymous = new Handler() { // ERROR\n"
-                + "                            ~~~~~~~~~~~\n"
+                + "                                ~~~~~~~\n"
                 + "0 errors, 2 warnings\n",
 
             lintProject(
@@ -46,25 +46,25 @@
                             + "    public static class StaticInner extends Handler { // OK\n"
                             + "        public void dispatchMessage(Message msg) {\n"
                             + "            super.dispatchMessage(msg);\n"
-                            + "        };\n"
+                            + "        }\n"
                             + "    }\n"
                             + "    public class Inner extends Handler { // ERROR\n"
                             + "        public void dispatchMessage(Message msg) {\n"
                             + "            super.dispatchMessage(msg);\n"
-                            + "        };\n"
+                            + "        }\n"
                             + "    }\n"
                             + "    void method() {\n"
                             + "        Handler anonymous = new Handler() { // ERROR\n"
                             + "            public void dispatchMessage(Message msg) {\n"
                             + "                super.dispatchMessage(msg);\n"
-                            + "            };\n"
+                            + "            }\n"
                             + "        };\n"
                             + "\n"
                             + "        Looper looper = null;\n"
                             + "        Handler anonymous2 = new Handler(looper) { // OK\n"
                             + "            public void dispatchMessage(Message msg) {\n"
                             + "                super.dispatchMessage(msg);\n"
-                            + "            };\n"
+                            + "            }\n"
                             + "        };\n"
                             + "    }\n"
                             + "\n"
@@ -75,7 +75,7 @@
                             + "\n"
                             + "        public void dispatchMessage(Message msg) {\n"
                             + "            super.dispatchMessage(msg);\n"
-                            + "        };\n"
+                            + "        }\n"
                             + "    }\n"
                             + "}\n")
             ));
diff --git a/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/JavaPerformanceDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/JavaPerformanceDetectorTest.java
index e7ba0ca..ea58583 100644
--- a/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/JavaPerformanceDetectorTest.java
+++ b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/JavaPerformanceDetectorTest.java
@@ -32,78 +32,302 @@
         return true;
     }
 
+    @SuppressWarnings("all")
     public void test() throws Exception {
-        assertEquals(
-            "src/test/pkg/JavaPerformanceTest.java:28: Warning: Avoid object allocations during draw/layout operations (preallocate and reuse instead) [DrawAllocation]\n" +
-            "        new String(\"foo\");\n" +
-            "        ~~~~~~~~~~~~~~~~~\n" +
-            "src/test/pkg/JavaPerformanceTest.java:29: Warning: Avoid object allocations during draw/layout operations (preallocate and reuse instead) [DrawAllocation]\n" +
-            "        String s = new String(\"bar\");\n" +
-            "                   ~~~~~~~~~~~~~~~~~\n" +
-            "src/test/pkg/JavaPerformanceTest.java:103: Warning: Avoid object allocations during draw/layout operations (preallocate and reuse instead) [DrawAllocation]\n" +
-            "        new String(\"flag me\");\n" +
-            "        ~~~~~~~~~~~~~~~~~~~~~\n" +
-            "src/test/pkg/JavaPerformanceTest.java:109: Warning: Avoid object allocations during draw/layout operations (preallocate and reuse instead) [DrawAllocation]\n" +
-            "        new String(\"flag me\");\n" +
-            "        ~~~~~~~~~~~~~~~~~~~~~\n" +
-            "src/test/pkg/JavaPerformanceTest.java:112: Warning: Avoid object allocations during draw/layout operations (preallocate and reuse instead) [DrawAllocation]\n" +
-            "        Bitmap.createBitmap(100, 100, null);\n" +
-            "        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" +
-            "src/test/pkg/JavaPerformanceTest.java:113: Warning: Avoid object allocations during draw/layout operations (preallocate and reuse instead) [DrawAllocation]\n" +
-            "        android.graphics.Bitmap.createScaledBitmap(null, 100, 100, false);\n" +
-            "        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" +
-            "src/test/pkg/JavaPerformanceTest.java:114: Warning: Avoid object allocations during draw/layout operations (preallocate and reuse instead) [DrawAllocation]\n" +
-            "        BitmapFactory.decodeFile(null);\n" +
-            "        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" +
-            "src/test/pkg/JavaPerformanceTest.java:116: Warning: Avoid object allocations during draw operations: Use Canvas.getClipBounds(Rect) instead of Canvas.getClipBounds() which allocates a temporary Rect [DrawAllocation]\n" +
-            "        canvas.getClipBounds(); // allocates on your behalf\n" +
-            "        ~~~~~~~~~~~~~~~~~~~~~~\n" +
-            "src/test/pkg/JavaPerformanceTest.java:140: Warning: Avoid object allocations during draw/layout operations (preallocate and reuse instead) [DrawAllocation]\n" +
-            "            new String(\"foo\");\n" +
-            "            ~~~~~~~~~~~~~~~~~\n" +
-            "src/test/pkg/JavaPerformanceTest.java:70: Warning: Use new SparseArray<String>(...) instead for better performance [UseSparseArrays]\n" +
-            "        Map<Integer, String> myMap = new HashMap<Integer, String>();\n" +
-            "                                     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" +
-            "src/test/pkg/JavaPerformanceTest.java:72: Warning: Use new SparseBooleanArray(...) instead for better performance [UseSparseArrays]\n" +
-            "        Map<Integer, Boolean> myBoolMap = new HashMap<Integer, Boolean>();\n" +
-            "                                          ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" +
-            "src/test/pkg/JavaPerformanceTest.java:74: Warning: Use new SparseIntArray(...) instead for better performance [UseSparseArrays]\n" +
-            "        Map<Integer, Integer> myIntMap = new java.util.HashMap<Integer, Integer>();\n" +
-            "                                         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" +
-            "src/test/pkg/JavaPerformanceTest.java:190: Warning: Use new SparseIntArray(...) instead for better performance [UseSparseArrays]\n" +
-            "        new SparseArray<Integer>(); // Use SparseIntArray instead\n" +
-            "        ~~~~~~~~~~~~~~~~~~~~~~~~~~\n" +
-            "src/test/pkg/JavaPerformanceTest.java:192: Warning: Use new SparseBooleanArray(...) instead for better performance [UseSparseArrays]\n" +
-            "        new SparseArray<Boolean>(); // Use SparseBooleanArray instead\n" +
-            "        ~~~~~~~~~~~~~~~~~~~~~~~~~~\n" +
-            "src/test/pkg/JavaPerformanceTest.java:201: Warning: Use new SparseArray<String>(...) instead for better performance [UseSparseArrays]\n" +
-            "        Map<Byte, String> myByteMap = new HashMap<Byte, String>();\n" +
-            "                                      ~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" +
-            "src/test/pkg/JavaPerformanceTest.java:33: Warning: Use Integer.valueOf(5) instead [UseValueOf]\n" +
-            "        Integer i = new Integer(5);\n" +
-            "                    ~~~~~~~~~~~~~~\n" +
-            "src/test/pkg/JavaPerformanceTest.java:145: Warning: Use Integer.valueOf(42) instead [UseValueOf]\n" +
-            "        Integer i1 = new Integer(42);\n" +
-            "                     ~~~~~~~~~~~~~~~\n" +
-            "src/test/pkg/JavaPerformanceTest.java:146: Warning: Use Long.valueOf(42L) instead [UseValueOf]\n" +
-            "        Long l1 = new Long(42L);\n" +
-            "                  ~~~~~~~~~~~~~\n" +
-            "src/test/pkg/JavaPerformanceTest.java:147: Warning: Use Boolean.valueOf(true) instead [UseValueOf]\n" +
-            "        Boolean b1 = new Boolean(true);\n" +
-            "                     ~~~~~~~~~~~~~~~~~\n" +
-            "src/test/pkg/JavaPerformanceTest.java:148: Warning: Use Character.valueOf('c') instead [UseValueOf]\n" +
-            "        Character c1 = new Character('c');\n" +
-            "                       ~~~~~~~~~~~~~~~~~~\n" +
-            "src/test/pkg/JavaPerformanceTest.java:149: Warning: Use Float.valueOf(1.0f) instead [UseValueOf]\n" +
-            "        Float f1 = new Float(1.0f);\n" +
-            "                   ~~~~~~~~~~~~~~~\n" +
-            "src/test/pkg/JavaPerformanceTest.java:150: Warning: Use Double.valueOf(1.0) instead [UseValueOf]\n" +
-            "        Double d1 = new Double(1.0);\n" +
-            "                    ~~~~~~~~~~~~~~~\n" +
-            "0 errors, 22 warnings\n",
+        assertEquals(""
+                + "src/test/pkg/JavaPerformanceTest.java:31: Warning: Avoid object allocations during draw/layout operations (preallocate and reuse instead) [DrawAllocation]\n"
+                + "        new String(\"foo\");\n"
+                + "        ~~~~~~~~~~~~~~~~~\n"
+                + "src/test/pkg/JavaPerformanceTest.java:32: Warning: Avoid object allocations during draw/layout operations (preallocate and reuse instead) [DrawAllocation]\n"
+                + "        String s = new String(\"bar\");\n"
+                + "                   ~~~~~~~~~~~~~~~~~\n"
+                + "src/test/pkg/JavaPerformanceTest.java:106: Warning: Avoid object allocations during draw/layout operations (preallocate and reuse instead) [DrawAllocation]\n"
+                + "        new String(\"flag me\");\n"
+                + "        ~~~~~~~~~~~~~~~~~~~~~\n"
+                + "src/test/pkg/JavaPerformanceTest.java:112: Warning: Avoid object allocations during draw/layout operations (preallocate and reuse instead) [DrawAllocation]\n"
+                + "        new String(\"flag me\");\n"
+                + "        ~~~~~~~~~~~~~~~~~~~~~\n"
+                + "src/test/pkg/JavaPerformanceTest.java:115: Warning: Avoid object allocations during draw/layout operations (preallocate and reuse instead) [DrawAllocation]\n"
+                + "        Bitmap.createBitmap(100, 100, null);\n"
+                + "        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
+                + "src/test/pkg/JavaPerformanceTest.java:116: Warning: Avoid object allocations during draw/layout operations (preallocate and reuse instead) [DrawAllocation]\n"
+                + "        android.graphics.Bitmap.createScaledBitmap(null, 100, 100, false);\n"
+                + "        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
+                + "src/test/pkg/JavaPerformanceTest.java:117: Warning: Avoid object allocations during draw/layout operations (preallocate and reuse instead) [DrawAllocation]\n"
+                + "        BitmapFactory.decodeFile(null);\n"
+                + "        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
+                + "src/test/pkg/JavaPerformanceTest.java:119: Warning: Avoid object allocations during draw operations: Use Canvas.getClipBounds(Rect) instead of Canvas.getClipBounds() which allocates a temporary Rect [DrawAllocation]\n"
+                + "        canvas.getClipBounds(); // allocates on your behalf\n"
+                + "        ~~~~~~~~~~~~~~~~~~~~~~\n"
+                + "src/test/pkg/JavaPerformanceTest.java:143: Warning: Avoid object allocations during draw/layout operations (preallocate and reuse instead) [DrawAllocation]\n"
+                + "            new String(\"foo\");\n"
+                + "            ~~~~~~~~~~~~~~~~~\n"
+                + "src/test/pkg/JavaPerformanceTest.java:73: Warning: Use new SparseArray<String>(...) instead for better performance [UseSparseArrays]\n"
+                + "        Map<Integer, String> myMap = new HashMap<Integer, String>();\n"
+                + "                                     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
+                + "src/test/pkg/JavaPerformanceTest.java:75: Warning: Use new SparseBooleanArray(...) instead for better performance [UseSparseArrays]\n"
+                + "        Map<Integer, Boolean> myBoolMap = new HashMap<Integer, Boolean>();\n"
+                + "                                          ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
+                + "src/test/pkg/JavaPerformanceTest.java:77: Warning: Use new SparseIntArray(...) instead for better performance [UseSparseArrays]\n"
+                + "        Map<Integer, Integer> myIntMap = new java.util.HashMap<Integer, Integer>();\n"
+                + "                                         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
+                + "src/test/pkg/JavaPerformanceTest.java:193: Warning: Use new SparseIntArray(...) instead for better performance [UseSparseArrays]\n"
+                + "        new SparseArray<Integer>(); // Use SparseIntArray instead\n"
+                + "        ~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
+                + "src/test/pkg/JavaPerformanceTest.java:195: Warning: Use new SparseBooleanArray(...) instead for better performance [UseSparseArrays]\n"
+                + "        new SparseArray<Boolean>(); // Use SparseBooleanArray instead\n"
+                + "        ~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
+                + "src/test/pkg/JavaPerformanceTest.java:204: Warning: Use new SparseArray<String>(...) instead for better performance [UseSparseArrays]\n"
+                + "        Map<Byte, String> myByteMap = new HashMap<Byte, String>();\n"
+                + "                                      ~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
+                + "src/test/pkg/JavaPerformanceTest.java:36: Warning: Use Integer.valueOf(5) instead [UseValueOf]\n"
+                + "        Integer i = new Integer(5);\n"
+                + "                    ~~~~~~~~~~~~~~\n"
+                + "src/test/pkg/JavaPerformanceTest.java:148: Warning: Use Integer.valueOf(42) instead [UseValueOf]\n"
+                + "        Integer i1 = new Integer(42);\n"
+                + "                     ~~~~~~~~~~~~~~~\n"
+                + "src/test/pkg/JavaPerformanceTest.java:149: Warning: Use Long.valueOf(42L) instead [UseValueOf]\n"
+                + "        Long l1 = new Long(42L);\n"
+                + "                  ~~~~~~~~~~~~~\n"
+                + "src/test/pkg/JavaPerformanceTest.java:150: Warning: Use Boolean.valueOf(true) instead [UseValueOf]\n"
+                + "        Boolean b1 = new Boolean(true);\n"
+                + "                     ~~~~~~~~~~~~~~~~~\n"
+                + "src/test/pkg/JavaPerformanceTest.java:151: Warning: Use Character.valueOf('c') instead [UseValueOf]\n"
+                + "        Character c1 = new Character('c');\n"
+                + "                       ~~~~~~~~~~~~~~~~~~\n"
+                + "src/test/pkg/JavaPerformanceTest.java:152: Warning: Use Float.valueOf(1.0f) instead [UseValueOf]\n"
+                + "        Float f1 = new Float(1.0f);\n"
+                + "                   ~~~~~~~~~~~~~~~\n"
+                + "src/test/pkg/JavaPerformanceTest.java:153: Warning: Use Double.valueOf(1.0) instead [UseValueOf]\n"
+                + "        Double d1 = new Double(1.0);\n"
+                + "                    ~~~~~~~~~~~~~~~\n"
+                + "0 errors, 22 warnings\n",
 
-            lintProject("src/test/pkg/JavaPerformanceTest.java.txt=>" +
-                    "src/test/pkg/JavaPerformanceTest.java"));
+            lintProject(
+                    java("src/test/pkg/JavaPerformanceTest.java", ""
+                            + "package test.pkg;\n"
+                            + "\n"
+                            + "import android.annotation.SuppressLint;\n"
+                            + "import android.content.Context;\n"
+                            + "import android.graphics.Bitmap;\n"
+                            + "import android.graphics.BitmapFactory;\n"
+                            + "import android.graphics.Canvas;\n"
+                            + "import android.graphics.LinearGradient;\n"
+                            + "import android.graphics.Rect;\n"
+                            + "import android.graphics.Shader.TileMode;\n"
+                            + "import android.util.AttributeSet;\n"
+                            + "import android.util.SparseArray;\n"
+                            + "import android.widget.Button;\n"
+                            + "import java.util.HashMap;\n"
+                            + "import java.util.Map;\n"
+                            + "\n"
+                            + "/** Some test data for the JavaPerformanceDetector */\n"
+                            + "@SuppressWarnings(\"unused\")\n"
+                            + "public class JavaPerformanceTest extends Button {\n"
+                            + "    public JavaPerformanceTest(Context context, AttributeSet attrs, int defStyle) {\n"
+                            + "        super(context, attrs, defStyle);\n"
+                            + "    }\n"
+                            + "\n"
+                            + "    private Rect cachedRect;\n"
+                            + "\n"
+                            + "    @Override\n"
+                            + "    protected void onDraw(android.graphics.Canvas canvas) {\n"
+                            + "        super.onDraw(canvas);\n"
+                            + "\n"
+                            + "        // Various allocations:\n"
+                            + "        new String(\"foo\");\n"
+                            + "        String s = new String(\"bar\");\n"
+                            + "\n"
+                            + "        // This one should not be reported:\n"
+                            + "        @SuppressLint(\"DrawAllocation\")\n"
+                            + "        Integer i = new Integer(5);\n"
+                            + "\n"
+                            + "        // Cached object initialized lazily: should not complain about these\n"
+                            + "        if (cachedRect == null) {\n"
+                            + "            cachedRect = new Rect(0, 0, 100, 100);\n"
+                            + "        }\n"
+                            + "        if (cachedRect == null || cachedRect.width() != 50) {\n"
+                            + "            cachedRect = new Rect(0, 0, 50, 100);\n"
+                            + "        }\n"
+                            + "\n"
+                            + "        boolean b = Boolean.valueOf(true); // auto-boxing\n"
+                            + "        dummy(1, 2);\n"
+                            + "\n"
+                            + "        // Non-allocations\n"
+                            + "        super.animate();\n"
+                            + "        dummy2(1, 2);\n"
+                            + "        int x = 4 + '5';\n"
+                            + "\n"
+                            + "        // This will involve allocations, but we don't track\n"
+                            + "        // inter-procedural stuff here\n"
+                            + "        someOtherMethod();\n"
+                            + "    }\n"
+                            + "\n"
+                            + "    void dummy(Integer foo, int bar) {\n"
+                            + "        dummy2(foo, bar);\n"
+                            + "    }\n"
+                            + "\n"
+                            + "    void dummy2(int foo, int bar) {\n"
+                            + "    }\n"
+                            + "\n"
+                            + "    void someOtherMethod() {\n"
+                            + "        // Allocations are okay here\n"
+                            + "        new String(\"foo\");\n"
+                            + "        String s = new String(\"bar\");\n"
+                            + "        boolean b = Boolean.valueOf(true); // auto-boxing\n"
+                            + "\n"
+                            + "        // Sparse array candidates\n"
+                            + "        Map<Integer, String> myMap = new HashMap<Integer, String>();\n"
+                            + "        // Should use SparseBooleanArray\n"
+                            + "        Map<Integer, Boolean> myBoolMap = new HashMap<Integer, Boolean>();\n"
+                            + "        // Should use SparseIntArray\n"
+                            + "        Map<Integer, Integer> myIntMap = new java.util.HashMap<Integer, Integer>();\n"
+                            + "\n"
+                            + "        // This one should not be reported:\n"
+                            + "        @SuppressLint(\"UseSparseArrays\")\n"
+                            + "        Map<Integer, Object> myOtherMap = new HashMap<Integer, Object>();\n"
+                            + "    }\n"
+                            + "\n"
+                            + "    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec,\n"
+                            + "                             boolean x) { // wrong signature\n"
+                            + "        new String(\"not an error\");\n"
+                            + "    }\n"
+                            + "\n"
+                            + "    protected void onMeasure(int widthMeasureSpec) { // wrong signature\n"
+                            + "        new String(\"not an error\");\n"
+                            + "    }\n"
+                            + "\n"
+                            + "    protected void onLayout(boolean changed, int left, int top, int right,\n"
+                            + "                            int bottom, int wrong) { // wrong signature\n"
+                            + "        new String(\"not an error\");\n"
+                            + "    }\n"
+                            + "\n"
+                            + "    protected void onLayout(boolean changed, int left, int top, int right) {\n"
+                            + "        // wrong signature\n"
+                            + "        new String(\"not an error\");\n"
+                            + "    }\n"
+                            + "\n"
+                            + "    @Override\n"
+                            + "    protected void onLayout(boolean changed, int left, int top, int right,\n"
+                            + "                            int bottom) {\n"
+                            + "        new String(\"flag me\");\n"
+                            + "    }\n"
+                            + "\n"
+                            + "    @SuppressWarnings(\"null\") // not real code\n"
+                            + "    @Override\n"
+                            + "    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {\n"
+                            + "        new String(\"flag me\");\n"
+                            + "\n"
+                            + "        // Forbidden factory methods:\n"
+                            + "        Bitmap.createBitmap(100, 100, null);\n"
+                            + "        android.graphics.Bitmap.createScaledBitmap(null, 100, 100, false);\n"
+                            + "        BitmapFactory.decodeFile(null);\n"
+                            + "        Canvas canvas = null;\n"
+                            + "        canvas.getClipBounds(); // allocates on your behalf\n"
+                            + "        canvas.getClipBounds(null); // NOT an error\n"
+                            + "\n"
+                            + "        final int layoutWidth = getWidth();\n"
+                            + "        final int layoutHeight = getHeight();\n"
+                            + "        if (mAllowCrop && (mOverlay == null || mOverlay.getWidth() != layoutWidth ||\n"
+                            + "                mOverlay.getHeight() != layoutHeight)) {\n"
+                            + "            mOverlay = Bitmap.createBitmap(layoutWidth, layoutHeight, Bitmap.Config.ARGB_8888);\n"
+                            + "            mOverlayCanvas = new Canvas(mOverlay);\n"
+                            + "        }\n"
+                            + "\n"
+                            + "        if (widthMeasureSpec == 42) {\n"
+                            + "            throw new IllegalStateException(\"Test\"); // NOT an allocation\n"
+                            + "        }\n"
+                            + "\n"
+                            + "        // More lazy init tests\n"
+                            + "        boolean initialized = false;\n"
+                            + "        if (!initialized) {\n"
+                            + "            new String(\"foo\");\n"
+                            + "            initialized = true;\n"
+                            + "        }\n"
+                            + "\n"
+                            + "        // NOT lazy initialization\n"
+                            + "        if (!initialized || mOverlay == null) {\n"
+                            + "            new String(\"foo\");\n"
+                            + "        }\n"
+                            + "    }\n"
+                            + "\n"
+                            + "    void factories() {\n"
+                            + "        Integer i1 = new Integer(42);\n"
+                            + "        Long l1 = new Long(42L);\n"
+                            + "        Boolean b1 = new Boolean(true);\n"
+                            + "        Character c1 = new Character('c');\n"
+                            + "        Float f1 = new Float(1.0f);\n"
+                            + "        Double d1 = new Double(1.0);\n"
+                            + "\n"
+                            + "        // The following should not generate errors:\n"
+                            + "        Object i2 = new foo.bar.Integer(42);\n"
+                            + "        Integer i3 = Integer.valueOf(42);\n"
+                            + "    }\n"
+                            + "\n"
+                            + "    private boolean mAllowCrop;\n"
+                            + "    private Canvas mOverlayCanvas;\n"
+                            + "    private Bitmap mOverlay;\n"
+                            + "private abstract class JavaPerformanceTest1 extends JavaPerformanceTest {\n"
+                            + "    @Override\n"
+                            + "    public void layout(int l, int t, int r, int b) {\n"
+                            + "        // Using \"this.\" to reference fields\n"
+                            + "        if (this.shader == null)\n"
+                            + "            this.shader = new LinearGradient(0, 0, getWidth(), 0, GRADIENT_COLORS, null,\n"
+                            + "                    TileMode.REPEAT);\n"
+                            + "    }\n"
+                            + "} private abstract class JavaPerformanceTest2 extends JavaPerformanceTest {\n"
+                            + "        @Override\n"
+                            + "    public void layout(int l, int t, int r, int b) {\n"
+                            + "        int width = getWidth();\n"
+                            + "        int height = getHeight();\n"
+                            + "\n"
+                            + "        if ((shader == null) || (lastWidth != width) || (lastHeight != height))\n"
+                            + "        {\n"
+                            + "            lastWidth = width;\n"
+                            + "            lastHeight = height;\n"
+                            + "\n"
+                            + "            shader = new LinearGradient(0, 0, width, 0, GRADIENT_COLORS, null, TileMode.REPEAT);\n"
+                            + "        }\n"
+                            + "    }\n"
+                            + "} private abstract class JavaPerformanceTest3 extends JavaPerformanceTest {\n"
+                            + "    @Override\n"
+                            + "    public void layout(int l, int t, int r, int b) {\n"
+                            + "        if ((shader == null) || (lastWidth != getWidth()) || (lastHeight != getHeight())) {\n"
+                            + "        }\n"
+                            + "    }\n"
+                            + "}\n"
+                            + "    public void inefficientSparseArray() {\n"
+                            + "        new SparseArray<Integer>(); // Use SparseIntArray instead\n"
+                            + "        new SparseArray<Long>();    // Use SparseLongArray instead\n"
+                            + "        new SparseArray<Boolean>(); // Use SparseBooleanArray instead\n"
+                            + "        new SparseArray<Object>();  // OK\n"
+                            + "    }\n"
+                            + "\n"
+                            + "    public void longSparseArray() { // but only minSdkVersion >= 17 or if has v4 support lib\n"
+                            + "        Map<Long, String> myStringMap = new HashMap<Long, String>();\n"
+                            + "    }\n"
+                            + "\n"
+                            + "    public void byteSparseArray() { // bytes easily apply to ints\n"
+                            + "        Map<Byte, String> myByteMap = new HashMap<Byte, String>();\n"
+                            + "    }\n"
+                            + "\n"
+                            + "    protected LinearGradient shader;\n"
+                            + "    protected int lastWidth;\n"
+                            + "    protected int lastHeight;\n"
+                            + "    protected int[] GRADIENT_COLORS;\n"
+                            + "\n"
+                            + "    private static class foo {\n"
+                            + "        private static class bar {\n"
+                            + "            private static class Integer {\n"
+                            + "                public Integer(int val) {\n"
+                            + "                }\n"
+                            + "            }\n"
+                            + "        }\n"
+                            + "    }\n"
+                            + "    public JavaPerformanceTest() {\n"
+                            + "        super(null);\n"
+                            + "    }\n"
+                            + "}\n")));
     }
 
     public void testLongSparseArray() throws Exception {
diff --git a/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/LayoutInflationDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/LayoutInflationDetectorTest.java
index 606c945..93a0a8f 100644
--- a/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/LayoutInflationDetectorTest.java
+++ b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/LayoutInflationDetectorTest.java
@@ -29,11 +29,56 @@
         return new LayoutInflationDetector();
     }
 
+    @SuppressWarnings("all")
+    private TestFile mLayoutInflationTest = java("src/test/pkg/LayoutInflationTest.java", ""
+            + "package test.pkg;\n"
+            + "\n"
+            + "import android.content.Context;\n"
+            + "import android.view.LayoutInflater;\n"
+            + "import android.view.View;\n"
+            + "import android.view.ViewGroup;\n"
+            + "import android.widget.BaseAdapter;\n"
+            + "import android.annotation.SuppressLint;\n"
+            + "import java.util.ArrayList;\n"
+            + "\n"
+            + "public abstract class LayoutInflationTest extends BaseAdapter {\n"
+            + "    public View getView(int position, View convertView, ViewGroup parent) {\n"
+            + "        convertView = mInflater.inflate(R.layout.your_layout, null);\n"
+            + "        convertView = mInflater.inflate(R.layout.your_layout, null, true);\n"
+            + "        //convertView = mInflater.inflate(R.layout.your_layout);\n"
+            + "        convertView = mInflater.inflate(R.layout.your_layout, parent);\n"
+            + "        convertView = WeirdInflater.inflate(convertView, null);\n"
+            + "\n"
+            + "        return convertView;\n"
+            + "    }\n"
+            + "\n"
+            // Suppressed checks
+            + "    @SuppressLint(\"InflateParams\")\n"
+            + "    public View getView2(int position, View convertView, ViewGroup parent) {\n"
+            + "        convertView = mInflater.inflate(R.layout.your_layout, null);\n"
+            + "        convertView = mInflater.inflate(R.layout.your_layout, null, true);\n"
+            + "        convertView = mInflater.inflate(R.layout.your_layout, parent);\n"
+            + "        convertView = WeirdInflater.inflate(convertView, null);\n"
+            + "\n"
+            + "        return convertView;\n"
+            + "    }\n"
+            // Test/Stub Setup
+            + "    private LayoutInflater mInflater;\n"
+            + "    private static class R {\n"
+            + "        private static class layout {\n"
+            + "            public static final int your_layout = 1;\n"
+            + "        }\n"
+            + "    }\n"
+            + "    private static class WeirdInflater {\n"
+            + "        public static View inflate(View view, Object params) { return null; }\n"
+            + "    }\n"
+            + "}\n");
+
     @Override
     protected boolean allowCompilationErrors() {
         // Some of these unit tests are still relying on source code that references
         // unresolved symbols etc.
-        return true;
+        return false;
     }
 
     public void test() throws Exception {
@@ -47,17 +92,17 @@
                 + "0 errors, 2 warnings\n",
 
             lintProject(
-                    "src/test/pkg/LayoutInflationTest.java.txt=>src/test/pkg/LayoutInflationTest.java",
-                    "res/layout/textsize.xml=>res/layout/your_layout.xml",
-                    "res/layout/listseparator.xml=>res/layout-port/your_layout.xml"));
+                    mLayoutInflationTest,
+                    copy("res/layout/textsize.xml", "res/layout/your_layout.xml"),
+                    copy("res/layout/listseparator.xml", "res/layout-port/your_layout.xml")));
     }
 
     public void testNoLayoutParams() throws Exception {
         assertEquals("No warnings.",
 
                 lintProject(
-                        "src/test/pkg/LayoutInflationTest.java.txt=>src/test/pkg/LayoutInflationTest.java",
-                        "res/layout/listseparator.xml=>res/layout/your_layout.xml"));
+                        mLayoutInflationTest,
+                        copy("res/layout/listseparator.xml", "res/layout/your_layout.xml")));
     }
 
     public void testHasLayoutParams() throws IOException, XmlPullParserException {
@@ -90,14 +135,5 @@
                 + "\n"
                 + "</LinearLayout>")));
     }
-
-    public void testSuppressed() throws Exception {
-        assertEquals("No warnings.",
-
-                lintProject(
-                        "src/test/pkg/LayoutInflationTest_ignored.java.txt=>src/test/pkg/LayoutInflationTest.java",
-                        "res/layout/textsize.xml=>res/layout/your_layout.xml",
-                        "res/layout/listseparator.xml=>res/layout-port/your_layout.xml"));
-    }
 }
 
diff --git a/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/LogDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/LogDetectorTest.java
index 48531fb..61937a2 100644
--- a/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/LogDetectorTest.java
+++ b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/LogDetectorTest.java
@@ -18,7 +18,7 @@
 
 import com.android.tools.lint.detector.api.Detector;
 
-@SuppressWarnings("javadoc")
+@SuppressWarnings({"javadoc", "ClassNameDiffersFromFileName"})
 public class LogDetectorTest extends AbstractCheckTest {
     @Override
     protected Detector getDetector() {
@@ -74,9 +74,114 @@
             "11 errors, 2 warnings\n",
 
             lintProject(
-                "src/test/pkg/LogTest.java.txt=>src/test/pkg/LogTest.java",
-                // stub for type resolution
-                "src/test/pkg/Log.java.txt=>src/android/util/Log.java"
+                java("src/test/pkg/LogTest.java", ""
+                        + "package test.pkg;\n"
+                        + "\n"
+                        + "import android.annotation.SuppressLint;\n"
+                        + "import android.util.Log;\n"
+                        + "import static android.util.Log.DEBUG;\n"
+                        + "\n"
+                        + "@SuppressWarnings(\"UnusedDeclaration\")\n"
+                        + "public class LogTest {\n"
+                        + "    private static final String TAG1 = \"MyTag1\";\n"
+                        + "    private static final String TAG2 = \"MyTag2\";\n"
+                        + "    private static final String TAG22 = \"1234567890123456789012\";\n"
+                        + "    private static final String TAG23 = \"12345678901234567890123\";\n"
+                        + "    private static final String TAG24 = \"123456789012345678901234\";\n"
+                        + "    private static final String LONG_TAG = \"MyReallyReallyReallyReallyReallyLongTag\";\n"
+                        + "\n"
+                        + "    public void checkConditional(String m) {\n"
+                        + "        Log.d(TAG1, \"message\"); // ok: unconditional, but not performing computation\n"
+                        + "        Log.d(TAG1, m); // ok: unconditional, but not performing computation\n"
+                        + "        Log.d(TAG1, \"a\" + \"b\"); // ok: unconditional, but not performing non-constant computation\n"
+                        + "        Log.d(TAG1, Constants.MY_MESSAGE); // ok: unconditional, but constant string\n"
+                        + "        Log.i(TAG1, \"message\" + m); // error: unconditional w/ computation\n"
+                        + "        Log.i(TAG1, toString()); // error: unconditional w/ computation\n"
+                        + "        Log.e(TAG1, toString()); // ok: only flagging debug/info messages\n"
+                        + "        Log.w(TAG1, toString()); // ok: only flagging debug/info messages\n"
+                        + "        Log.wtf(TAG1, toString()); // ok: only flagging debug/info messages\n"
+                        + "        if (Log.isLoggable(TAG1, 0)) {\n"
+                        + "            Log.d(TAG1, toString()); // ok: conditional\n"
+                        + "        }\n"
+                        + "    }\n"
+                        + "\n"
+                        + "    public void checkWrongTag(String tag) {\n"
+                        + "        if (Log.isLoggable(TAG1, Log.DEBUG)) {\n"
+                        + "            Log.d(TAG2, \"message\"); // warn: mismatched tags!\n"
+                        + "        }\n"
+                        + "        if (Log.isLoggable(\"my_tag\", Log.DEBUG)) {\n"
+                        + "            Log.d(\"other_tag\", \"message\"); // warn: mismatched tags!\n"
+                        + "        }\n"
+                        + "        if (Log.isLoggable(\"my_tag\", Log.DEBUG)) {\n"
+                        + "            Log.d(\"my_tag\", \"message\"); // ok: strings equal\n"
+                        + "        }\n"
+                        + "        if (Log.isLoggable(TAG1, Log.DEBUG)) {\n"
+                        + "            Log.d(LogTest.TAG1, \"message\"); // OK: same tag; different access syntax\n"
+                        + "        }\n"
+                        + "        if (Log.isLoggable(tag, Log.DEBUG)) {\n"
+                        + "            Log.d(tag, \"message\"); // ok: same variable\n"
+                        + "        }\n"
+                        + "    }\n"
+                        + "\n"
+                        + "    public void checkLongTag(boolean shouldLog) {\n"
+                        + "        if (shouldLog) {\n"
+                        + "            // String literal tags\n"
+                        + "            Log.d(\"short_tag\", \"message\"); // ok: short\n"
+                        + "            Log.d(\"really_really_really_really_really_long_tag\", \"message\"); // error: too long\n"
+                        + "\n"
+                        + "            // Resolved field tags\n"
+                        + "            Log.d(TAG1, \"message\"); // ok: short\n"
+                        + "            Log.d(TAG22, \"message\"); // ok: short\n"
+                        + "            Log.d(TAG23, \"message\"); // ok: threshold\n"
+                        + "            Log.d(TAG24, \"message\"); // error: too long\n"
+                        + "            Log.d(LONG_TAG, \"message\"); // error: way too long\n"
+                        + "\n"
+                        + "            // Locally defined variable tags\n"
+                        + "            final String LOCAL_TAG = \"MyReallyReallyReallyReallyReallyLongTag\";\n"
+                        + "            Log.d(LOCAL_TAG, \"message\"); // error: too long\n"
+                        + "\n"
+                        + "            // Concatenated tags\n"
+                        + "            Log.d(TAG22 + TAG1, \"message\"); // error: too long\n"
+                        + "            Log.d(TAG22 + \"MyTag\", \"message\"); // error: too long\n"
+                        + "        }\n"
+                        + "    }\n"
+                        + "\n"
+                        + "    public void checkWrongLevel(String tag) {\n"
+                        + "        if (Log.isLoggable(TAG1, Log.DEBUG)) {\n"
+                        + "            Log.d(TAG1, \"message\"); // ok: right level\n"
+                        + "        }\n"
+                        + "        if (Log.isLoggable(TAG1, Log.INFO)) {\n"
+                        + "            Log.i(TAG1, \"message\"); // ok: right level\n"
+                        + "        }\n"
+                        + "        if (Log.isLoggable(TAG1, Log.DEBUG)) {\n"
+                        + "            Log.v(TAG1, \"message\"); // warn: wrong level\n"
+                        + "        }\n"
+                        + "        if (Log.isLoggable(TAG1, DEBUG)) { // static import of level\n"
+                        + "            Log.v(TAG1, \"message\"); // warn: wrong level\n"
+                        + "        }\n"
+                        + "        if (Log.isLoggable(TAG1, Log.VERBOSE)) {\n"
+                        + "            Log.d(TAG1, \"message\"); // warn? verbose is a lower logging level, which includes debug\n"
+                        + "        }\n"
+                        + "        if (Log.isLoggable(TAG1, Constants.MY_LEVEL)) {\n"
+                        + "            Log.d(TAG1, \"message\"); // ok: unknown level alias\n"
+                        + "        }\n"
+                        + "    }\n"
+                        + "\n"
+                        + "    @SuppressLint(\"all\")\n"
+                        + "    public void suppressed1() {\n"
+                        + "        Log.d(TAG1, \"message\"); // ok: suppressed\n"
+                        + "    }\n"
+                        + "\n"
+                        + "    @SuppressLint(\"LogConditional\")\n"
+                        + "    public void suppressed2() {\n"
+                        + "        Log.d(TAG1, \"message\"); // ok: suppressed\n"
+                        + "    }\n"
+                        + "\n"
+                        + "    private static class Constants {\n"
+                        + "        public static final String MY_MESSAGE = \"My Message\";\n"
+                        + "        public static final int MY_LEVEL = 5;\n"
+                        + "    }\n"
+                        + "}")
             ));
     }
 }
diff --git a/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/MissingClassDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/MissingClassDetectorTest.java
index 2118db23c..d941a1a 100644
--- a/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/MissingClassDetectorTest.java
+++ b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/MissingClassDetectorTest.java
@@ -485,7 +485,6 @@
                 ));
     }
 
-
     public void testMissingClass() throws Exception {
         mScopes = null;
         mEnabled = Sets.newHashSet(MISSING, INSTANTIATABLE, INNERCLASS);
diff --git a/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/OverrideConcreteDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/OverrideConcreteDetectorTest.java
index 1da0e68..2073766 100644
--- a/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/OverrideConcreteDetectorTest.java
+++ b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/OverrideConcreteDetectorTest.java
@@ -24,6 +24,7 @@
         return new OverrideConcreteDetector();
     }
 
+    @SuppressWarnings("ClassNameDiffersFromFileName")
     public void test() throws Exception {
         assertEquals(""
                 + "src/test/pkg/OverrideConcreteTest.java:23: Error: Must override android.service.notification.NotificationListenerService.onNotificationPosted(android.service.notification.StatusBarNotification): Method was abstract until 21, and your minSdkVersion is 18 [OverrideAbstract]\n"
@@ -41,6 +42,75 @@
                 + "4 errors, 0 warnings\n",
 
                 lintProject(
-                        "src/test/pkg/OverrideConcreteTest.java.txt=>src/test/pkg/OverrideConcreteTest.java"));
+                        java("src/test/pkg/OverrideConcreteTest.java", ""
+                                + "package test.pkg;\n"
+                                + "\n"
+                                + "import android.annotation.SuppressLint;\n"
+                                + "import android.annotation.TargetApi;\n"
+                                + "import android.os.Build;\n"
+                                + "import android.service.notification.NotificationListenerService;\n"
+                                + "import android.service.notification.StatusBarNotification;\n"
+                                + "\n"
+                                + "@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR2)\n"
+                                + "public class OverrideConcreteTest {\n"
+                                + "    // OK: This one specifies both methods\n"
+                                + "    private static class MyNotificationListenerService1 extends NotificationListenerService {\n"
+                                + "        @Override\n"
+                                + "        public void onNotificationPosted(StatusBarNotification statusBarNotification) {\n"
+                                + "        }\n"
+                                + "\n"
+                                + "        @Override\n"
+                                + "        public void onNotificationRemoved(StatusBarNotification statusBarNotification) {\n"
+                                + "        }\n"
+                                + "    }\n"
+                                + "\n"
+                                + "    // Error: Misses onNotificationPosted\n"
+                                + "    private static class MyNotificationListenerService2 extends NotificationListenerService {\n"
+                                + "        @Override\n"
+                                + "        public void onNotificationRemoved(StatusBarNotification statusBarNotification) {\n"
+                                + "        }\n"
+                                + "    }\n"
+                                + "\n"
+                                + "    // Error: Misses onNotificationRemoved\n"
+                                + "    private static class MyNotificationListenerService3 extends NotificationListenerService {\n"
+                                + "        @Override\n"
+                                + "        public void onNotificationPosted(StatusBarNotification statusBarNotification) {\n"
+                                + "        }\n"
+                                + "    }\n"
+                                + "\n"
+                                + "    // Error: Missing both; wrong signatures (first has wrong arg count, second has wrong type)\n"
+                                + "    private static class MyNotificationListenerService4 extends NotificationListenerService {\n"
+                                + "        public void onNotificationPosted(StatusBarNotification statusBarNotification, int flags) {\n"
+                                + "        }\n"
+                                + "\n"
+                                + "        public void onNotificationRemoved(int statusBarNotification) {\n"
+                                + "        }\n"
+                                + "    }\n"
+                                + "\n"
+                                + "    // OK: Inherits from a class which define both\n"
+                                + "    private static class MyNotificationListenerService5 extends MyNotificationListenerService1 {\n"
+                                + "    }\n"
+                                + "\n"
+                                + "    // OK: Inherits from a class which defines only one, but the other one is defined here\n"
+                                + "    private static class MyNotificationListenerService6 extends MyNotificationListenerService3 {\n"
+                                + "        @Override\n"
+                                + "        public void onNotificationRemoved(StatusBarNotification statusBarNotification) {\n"
+                                + "        }\n"
+                                + "    }\n"
+                                + "\n"
+                                + "    // Error: Inheriting from a class which only defines one\n"
+                                + "    private static class MyNotificationListenerService7 extends MyNotificationListenerService3 {\n"
+                                + "    }\n"
+                                + "\n"
+                                + "    // OK: Has target api setting a local version that is high enough\n"
+                                + "    @TargetApi(21)\n"
+                                + "    private static class MyNotificationListenerService8 extends NotificationListenerService {\n"
+                                + "    }\n"
+                                + "\n"
+                                + "    // OK: Suppressed\n"
+                                + "    @SuppressLint(\"OverrideAbstract\")\n"
+                                + "    private static class MyNotificationListenerService9 extends MyNotificationListenerService1 {\n"
+                                + "    }\n"
+                                + "}")));
     }
 }
diff --git a/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/PermissionRequirementTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/PermissionRequirementTest.java
index 4ce0d07..20b245d 100644
--- a/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/PermissionRequirementTest.java
+++ b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/PermissionRequirementTest.java
@@ -37,6 +37,12 @@
 import com.google.common.collect.Lists;
 import com.google.common.collect.Sets;
 import com.google.common.io.Files;
+import com.intellij.psi.JavaTokenType;
+import com.intellij.psi.PsiAnnotation;
+import com.intellij.psi.PsiAnnotationParameterList;
+import com.intellij.psi.PsiLiteral;
+import com.intellij.psi.PsiNameValuePair;
+import com.intellij.psi.PsiParameterList;
 
 import junit.framework.TestCase;
 
@@ -53,18 +59,30 @@
 import java.util.Set;
 import java.util.regex.Pattern;
 
-import lombok.ast.BinaryOperator;
-
 public class PermissionRequirementTest extends TestCase {
-    private static ResolvedAnnotation createAnnotation(
+    private static PsiAnnotation createAnnotation(
             @NonNull String name,
+            // TODO: Put better mocks in here
             @NonNull ResolvedAnnotation.Value... values) {
-        ResolvedAnnotation annotation = mock(ResolvedAnnotation.class);
-        when(annotation.getName()).thenReturn(name);
-        when(annotation.getValues()).thenReturn(Arrays.asList(values));
+        PsiAnnotation annotation = mock(PsiAnnotation.class);
+        when(annotation.getQualifiedName()).thenReturn(name);
+
+        PsiAnnotationParameterList parameterList = mock(PsiAnnotationParameterList.class);
+        when(annotation.getParameterList()).thenReturn(parameterList);
+
+        List<PsiNameValuePair> pairs = Lists.newArrayListWithCapacity(10);
         for (ResolvedAnnotation.Value value : values) {
-            when(annotation.getValue(value.name)).thenReturn(value.value);
+            PsiNameValuePair pair = mock(PsiNameValuePair.class);
+            when (pair.getName()).thenReturn(value.name);
+            PsiLiteral literal = mock(PsiLiteral.class);
+            when(literal.getValue()).thenReturn(value.value);
+            when (pair.getValue()).thenReturn(literal);
+
+            when(annotation.findAttributeValue(value.name)).thenReturn(literal);
         }
+        PsiNameValuePair[] attributes = pairs.toArray(PsiNameValuePair.EMPTY_ARRAY);
+        when(parameterList.getAttributes()).thenReturn(attributes);
+
         return annotation;
     }
 
@@ -73,7 +91,7 @@
                 "android.permission.ACCESS_FINE_LOCATION");
         Set<String> emptySet = Collections.emptySet();
         Set<String> fineSet = Collections.singleton("android.permission.ACCESS_FINE_LOCATION");
-        ResolvedAnnotation annotation = createAnnotation(PERMISSION_ANNOTATION, values);
+        PsiAnnotation annotation = createAnnotation(PERMISSION_ANNOTATION, values);
         PermissionRequirement req = PermissionRequirement.create(null, annotation);
         assertTrue(req.isRevocable(new SetPermissionLookup(emptySet)));
 
@@ -100,7 +118,7 @@
                 "android.permission.ACCESS_FINE_LOCATION",
                 "android.permission.ACCESS_COARSE_LOCATION");
 
-        ResolvedAnnotation annotation = createAnnotation(PERMISSION_ANNOTATION, values);
+        PsiAnnotation annotation = createAnnotation(PERMISSION_ANNOTATION, values);
         PermissionRequirement req = PermissionRequirement.create(null, annotation);
         assertTrue(req.isRevocable(new SetPermissionLookup(emptySet)));
         assertFalse(req.isSatisfied(new SetPermissionLookup(emptySet)));
@@ -112,7 +130,7 @@
                 req.describeMissingPermissions(new SetPermissionLookup(emptySet)));
         assertEquals(bothSet, req.getMissingPermissions(new SetPermissionLookup(emptySet)));
         assertEquals(bothSet, req.getRevocablePermissions(new SetPermissionLookup(emptySet)));
-        assertSame(BinaryOperator.LOGICAL_OR, req.getOperator());
+        assertSame(JavaTokenType.OROR, req.getOperator());
     }
 
     public void testAll() {
@@ -126,7 +144,7 @@
                 "android.permission.ACCESS_FINE_LOCATION",
                 "android.permission.ACCESS_COARSE_LOCATION");
 
-        ResolvedAnnotation annotation = createAnnotation(PERMISSION_ANNOTATION, values);
+        PsiAnnotation annotation = createAnnotation(PERMISSION_ANNOTATION, values);
         PermissionRequirement req = PermissionRequirement.create(null, annotation);
         assertTrue(req.isRevocable(new SetPermissionLookup(emptySet)));
         assertFalse(req.isSatisfied(new SetPermissionLookup(emptySet)));
@@ -147,14 +165,14 @@
                 req.describeMissingPermissions(new SetPermissionLookup(coarseSet)));
         assertEquals(fineSet, req.getMissingPermissions(new SetPermissionLookup(coarseSet)));
         assertEquals(bothSet, req.getRevocablePermissions(new SetPermissionLookup(emptySet)));
-        assertSame(BinaryOperator.LOGICAL_AND, req.getOperator());
+        assertSame(JavaTokenType.ANDAND, req.getOperator());
     }
 
     public void testSingleAsArray() {
         // Annotations let you supply a single string to an array method
         ResolvedAnnotation.Value values = new ResolvedAnnotation.Value("allOf",
                 "android.permission.ACCESS_FINE_LOCATION");
-        ResolvedAnnotation annotation = createAnnotation(PERMISSION_ANNOTATION, values);
+        PsiAnnotation annotation = createAnnotation(PERMISSION_ANNOTATION, values);
         assertTrue(PermissionRequirement.create(null, annotation).isSingle());
     }
 
@@ -170,7 +188,7 @@
     }
 
     public void testAppliesTo() {
-        ResolvedAnnotation annotation;
+        PsiAnnotation annotation;
         PermissionRequirement req;
 
         // No date range applies to permission
diff --git a/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/RecyclerViewDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/RecyclerViewDetectorTest.java
index aead7dc..eea4183 100644
--- a/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/RecyclerViewDetectorTest.java
+++ b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/RecyclerViewDetectorTest.java
@@ -49,7 +49,7 @@
                             + "@SuppressWarnings({\"ClassNameDiffersFromFileName\", \"unused\"})\n"
                             + "public class RecyclerViewTest {\n"
                             + "    // From https://developer.android.com/training/material/lists-cards.html\n"
-                            + "    public static class Test1 extends RecyclerView.Adapter<Test1.ViewHolder> {\n"
+                            + "    public abstract static class Test1 extends RecyclerView.Adapter<Test1.ViewHolder> {\n"
                             + "        private String[] mDataset;\n"
                             + "        public static class ViewHolder extends RecyclerView.ViewHolder {\n"
                             + "            public TextView mTextView;\n"
@@ -69,7 +69,7 @@
                             + "        }\n"
                             + "    }\n"
                             + "\n"
-                            + "    public static class Test2 extends RecyclerView.Adapter<Test2.ViewHolder> {\n"
+                            + "    public abstract static class Test2 extends RecyclerView.Adapter<Test2.ViewHolder> {\n"
                             + "        public static class ViewHolder extends RecyclerView.ViewHolder {\n"
                             + "            public ViewHolder(View v) {\n"
                             + "                super(v);\n"
@@ -82,7 +82,7 @@
                             + "        }\n"
                             + "    }\n"
                             + "\n"
-                            + "    public static class Test3 extends RecyclerView.Adapter<Test3.ViewHolder> {\n"
+                            + "    public abstract static class Test3 extends RecyclerView.Adapter<Test3.ViewHolder> {\n"
                             + "        public static class ViewHolder extends RecyclerView.ViewHolder {\n"
                             + "            public ViewHolder(View v) {\n"
                             + "                super(v);\n"
@@ -91,11 +91,12 @@
                             + "\n"
                             + "        @Override\n"
                             + "        public void onBindViewHolder(ViewHolder holder, final int position) {\n"
-                            + "            // OK - final, but not referenced\n\n"
+                            + "            // OK - final, but not referenced\n"
+                            + "\n"
                             + "        }\n"
                             + "    }\n"
                             + "\n"
-                            + "    public static class Test4 extends RecyclerView.Adapter<Test4.ViewHolder> {\n"
+                            + "    public abstract static class Test4 extends RecyclerView.Adapter<Test4.ViewHolder> {\n"
                             + "        private int myCachedPosition;\n"
                             + "\n"
                             + "        public static class ViewHolder extends RecyclerView.ViewHolder {\n"
@@ -110,7 +111,7 @@
                             + "        }\n"
                             + "    }\n"
                             + "\n"
-                            + "    public static class Test5 extends RecyclerView.Adapter<Test5.ViewHolder> {\n"
+                            + "    public abstract static class Test5 extends RecyclerView.Adapter<Test5.ViewHolder> {\n"
                             + "        public static class ViewHolder extends RecyclerView.ViewHolder {\n"
                             + "            public ViewHolder(View v) {\n"
                             + "                super(v);\n"
@@ -128,7 +129,7 @@
                             + "    }\n"
                             + "\n"
                             + "    // https://code.google.com/p/android/issues/detail?id=172335\n"
-                            + "    public static class Test6 extends RecyclerView.Adapter<Test6.ViewHolder> {\n"
+                            + "    public abstract static class Test6 extends RecyclerView.Adapter<Test6.ViewHolder> {\n"
                             + "        List<String> myData;\n"
                             + "        public static class ViewHolder extends RecyclerView.ViewHolder {\n"
                             + "            private View itemView;\n"
diff --git a/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/SQLiteDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/SQLiteDetectorTest.java
index c500e1e..5fb970c 100644
--- a/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/SQLiteDetectorTest.java
+++ b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/SQLiteDetectorTest.java
@@ -26,19 +26,98 @@
 
     public void test() throws Exception {
         assertEquals(""
-                + "src/test/pkg/SQLiteTest.java:25: Warning: Using column type STRING; did you mean to use TEXT? (STRING is a numeric type and its value can be adjusted; for example,strings that look like integers can drop leading zeroes. See issue explanation for details.) [SQLiteString]\n"
+                + "src/test/pkg/SQLiteTest.java:25: Warning: Using column type STRING; did you mean to use TEXT? (STRING is a numeric type and its value can be adjusted; for example, strings that look like integers can drop leading zeroes. See issue explanation for details.) [SQLiteString]\n"
                 + "        db.execSQL(\"CREATE TABLE \" + name + \"(\" + Tables.AppKeys.SCHEMA + \");\"); // ERROR\n"
                 + "        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
-                + "src/test/pkg/SQLiteTest.java:30: Warning: Using column type STRING; did you mean to use TEXT? (STRING is a numeric type and its value can be adjusted; for example,strings that look like integers can drop leading zeroes. See issue explanation for details.) [SQLiteString]\n"
+                + "src/test/pkg/SQLiteTest.java:30: Warning: Using column type STRING; did you mean to use TEXT? (STRING is a numeric type and its value can be adjusted; for example, strings that look like integers can drop leading zeroes. See issue explanation for details.) [SQLiteString]\n"
                 + "        db.execSQL(TracksColumns.CREATE_TABLE); // ERROR\n"
                 + "        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
                 + "0 errors, 2 warnings\n",
 
                 lintProject(
-                        "src/test/pkg/MyTracksProvider.java.txt=>src/test/pkg/TracksColumns.java",
-                        "src/test/pkg/SQLiteTest.java.txt=>src/test/pkg/SQLiteTest.java",
+                        java("src/test/pkg/TracksColumns.java", ""
+                                + "package test.pkg;\n"
+                                + "\n"
+                                + "import android.provider.BaseColumns;\n"
+                                + "\n"
+                                + "public interface TracksColumns extends BaseColumns {\n"
+                                + "\n"
+                                + "    String TABLE_NAME = \"tracks\";\n"
+                                + "\n"
+                                + "    String NAME = \"name\";\n"
+                                + "    String CATEGORY = \"category\";\n"
+                                + "    String STARTTIME = \"starttime\";\n"
+                                + "    String MAXGRADE = \"maxgrade\";\n"
+                                + "    String MAPID = \"mapid\";\n"
+                                + "    String TABLEID = \"tableid\";\n"
+                                + "    String ICON = \"icon\";\n"
+                                + "\n"
+                                + "    String CREATE_TABLE = \"CREATE TABLE \" + TABLE_NAME + \" (\"\n"
+                                + "            + _ID + \" INTEGER PRIMARY KEY AUTOINCREMENT, \"\n"
+                                + "            + NAME + \" STRING, \"\n"
+                                + "            + CATEGORY + \" STRING, \"\n"
+                                + "            + STARTTIME + \" INTEGER, \"\n"
+                                + "            + MAXGRADE + \" FLOAT, \"\n"
+                                + "            + MAPID + \" STRING, \"\n"
+                                + "            + TABLEID + \" STRING, \"\n"
+                                + "            + ICON + \" STRING\"\n"
+                                + "            + \");\";\n"
+                                + "}\n"),
+
+                        java("src/test/pkg/SQLiteTest.java", ""
+                                + "package test.pkg;\n"
+                                + "\n"
+                                + "import android.database.sqlite.SQLiteDatabase;\n"
+                                + "\n"
+                                + "@SuppressWarnings({\"unused\", \"SpellCheckingInspection\"})\n"
+                                + "public class SQLiteTest {\n"
+                                + "    public interface Tables {\n"
+                                + "        interface AppKeys {\n"
+                                + "            String NAME = \"appkeys\";\n"
+                                + "\n"
+                                + "            interface Columns {\n"
+                                + "                String _ID = \"_id\";\n"
+                                + "                String PKG_NAME = \"packageName\";\n"
+                                + "                String PKG_SIG = \"signatureDigest\";\n"
+                                + "            }\n"
+                                + "\n"
+                                + "            String SCHEMA =\n"
+                                + "                    Columns._ID + \" INTEGER PRIMARY KEY AUTOINCREMENT,\" +\n"
+                                + "                            Columns.PKG_NAME + \" STRING NOT NULL,\" +\n"
+                                + "                            Columns.PKG_SIG + \" STRING NOT NULL\";\n"
+                                + "        }\n"
+                                + "    }\n"
+                                + "\n"
+                                + "    public void test(SQLiteDatabase db, String name) {\n"
+                                + "        db.execSQL(\"CREATE TABLE \" + name + \"(\" + Tables.AppKeys.SCHEMA + \");\"); // ERROR\n"
+                                + "\n"
+                                + "    }\n"
+                                + "\n"
+                                + "    public void onCreate(SQLiteDatabase db) {\n"
+                                + "        db.execSQL(TracksColumns.CREATE_TABLE); // ERROR\n"
+                                + "    }\n"
+                                + "\n"
+                                + "    private void doCreate(SQLiteDatabase db) {\n"
+                                + "        // Not yet handled; we need to flow string concatenation across procedure calls\n"
+                                + "        createTable(db, Tables.AppKeys.NAME, Tables.AppKeys.SCHEMA); // ERROR\n"
+                                + "    }\n"
+                                + "\n"
+                                + "    private void createTable(SQLiteDatabase db, String tableName, String schema) {\n"
+                                + "        db.execSQL(\"CREATE TABLE \" + tableName + \"(\" + schema + \");\");\n"
+                                + "    }\n"
+                                + "}"),
+
                         // stub for type resolution
-                        "src/test/pkg/SQLiteDatabase.java.txt=>src/android/database/sqlite/SQLiteDatabase.java"
+                        java("src/android/database/sqlite/SQLiteDatabase.java", ""
+                                + "package android.database.sqlite;\n"
+                                + "\n"
+                                + "import android.database.SQLException;\n"
+                                + "\n"
+                                + "// Lint unit testing stub\n"
+                                + "public class SQLiteDatabase {\n"
+                                + "    public void execSQL(String sql) throws SQLException {\n"
+                                + "    }\n"
+                                + "}")
                 ));
     }
 }
diff --git a/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/SharedPrefsDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/SharedPrefsDetectorTest.java
deleted file mode 100644
index ad778eb..0000000
--- a/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/SharedPrefsDetectorTest.java
+++ /dev/null
@@ -1,187 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * 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.android.tools.lint.checks;
-
-import com.android.tools.lint.detector.api.Detector;
-
-@SuppressWarnings({"javadoc", "ClassNameDiffersFromFileName"})
-public class SharedPrefsDetectorTest extends AbstractCheckTest {
-    @Override
-    protected Detector getDetector() {
-        return new SharedPrefsDetector();
-    }
-
-    @Override
-    protected boolean allowCompilationErrors() {
-        // Some of these unit tests are still relying on source code that references
-        // unresolved symbols etc.
-        return true;
-    }
-
-    public void test() throws Exception {
-        assertEquals(
-            "src/test/pkg/SharedPrefsTest.java:54: Warning: SharedPreferences.edit() without a corresponding commit() or apply() call [CommitPrefEdits]\n" +
-            "        SharedPreferences.Editor editor = preferences.edit();\n" +
-            "                                          ~~~~~~~~~~~~~~~~~~\n" +
-            "src/test/pkg/SharedPrefsTest.java:62: Warning: SharedPreferences.edit() without a corresponding commit() or apply() call [CommitPrefEdits]\n" +
-            "        SharedPreferences.Editor editor = preferences.edit();\n" +
-            "                                          ~~~~~~~~~~~~~~~~~~\n" +
-            "0 errors, 2 warnings\n" +
-            "",
-
-            lintProject("src/test/pkg/SharedPrefsTest.java.txt=>" +
-                    "src/test/pkg/SharedPrefsTest.java"));
-    }
-
-    public void test2() throws Exception {
-        // Regression test 1 for http://code.google.com/p/android/issues/detail?id=34322
-        assertEquals(
-            "src/test/pkg/SharedPrefsTest2.java:13: Warning: SharedPreferences.edit() without a corresponding commit() or apply() call [CommitPrefEdits]\n" +
-            "        SharedPreferences.Editor editor = preferences.edit();\n" +
-            "                                          ~~~~~~~~~~~~~~~~~~\n" +
-            "src/test/pkg/SharedPrefsTest2.java:17: Warning: SharedPreferences.edit() without a corresponding commit() or apply() call [CommitPrefEdits]\n" +
-            "        Editor editor = preferences.edit();\n" +
-            "                        ~~~~~~~~~~~~~~~~~~\n" +
-            "0 errors, 2 warnings\n",
-
-            lintProject("src/test/pkg/SharedPrefsTest2.java.txt=>" +
-                    "src/test/pkg/SharedPrefsTest2.java"));
-    }
-
-    public void test3() throws Exception {
-        // Regression test 2 for http://code.google.com/p/android/issues/detail?id=34322
-        assertEquals(
-            "src/test/pkg/SharedPrefsTest3.java:13: Warning: SharedPreferences.edit() without a corresponding commit() or apply() call [CommitPrefEdits]\n" +
-            "        Editor editor = preferences.edit();\n" +
-            "                        ~~~~~~~~~~~~~~~~~~\n" +
-            "0 errors, 1 warnings\n",
-
-            lintProject("src/test/pkg/SharedPrefsTest3.java.txt=>" +
-                    "src/test/pkg/SharedPrefsTest3.java"));
-    }
-
-    public void test4() throws Exception {
-        // Regression test 3 for http://code.google.com/p/android/issues/detail?id=34322
-        assertEquals(""
-            + "src/test/pkg/SharedPrefsTest4.java:13: Warning: SharedPreferences.edit() without a corresponding commit() or apply() call [CommitPrefEdits]\n"
-            + "        Editor editor = preferences.edit();\n"
-            + "                        ~~~~~~~~~~~~~~~~~~\n"
-            + "0 errors, 1 warnings\n",
-
-            lintProject("src/test/pkg/SharedPrefsTest4.java.txt=>" +
-                    "src/test/pkg/SharedPrefsTest4.java"));
-    }
-
-    public void test5() throws Exception {
-        // Check fields too: http://code.google.com/p/android/issues/detail?id=39134
-        assertEquals(
-            "src/test/pkg/SharedPrefsTest5.java:16: Warning: SharedPreferences.edit() without a corresponding commit() or apply() call [CommitPrefEdits]\n" +
-            "        mPreferences.edit().putString(PREF_FOO, \"bar\");\n" +
-            "        ~~~~~~~~~~~~~~~~~~~\n" +
-            "src/test/pkg/SharedPrefsTest5.java:17: Warning: SharedPreferences.edit() without a corresponding commit() or apply() call [CommitPrefEdits]\n" +
-            "        mPreferences.edit().remove(PREF_BAZ).remove(PREF_FOO);\n" +
-            "        ~~~~~~~~~~~~~~~~~~~\n" +
-            "src/test/pkg/SharedPrefsTest5.java:26: Warning: SharedPreferences.edit() without a corresponding commit() or apply() call [CommitPrefEdits]\n" +
-            "        preferences.edit().putString(PREF_FOO, \"bar\");\n" +
-            "        ~~~~~~~~~~~~~~~~~~\n" +
-            "src/test/pkg/SharedPrefsTest5.java:27: Warning: SharedPreferences.edit() without a corresponding commit() or apply() call [CommitPrefEdits]\n" +
-            "        preferences.edit().remove(PREF_BAZ).remove(PREF_FOO);\n" +
-            "        ~~~~~~~~~~~~~~~~~~\n" +
-            "src/test/pkg/SharedPrefsTest5.java:32: Warning: SharedPreferences.edit() without a corresponding commit() or apply() call [CommitPrefEdits]\n" +
-            "        preferences.edit().putString(PREF_FOO, \"bar\");\n" +
-            "        ~~~~~~~~~~~~~~~~~~\n" +
-            "src/test/pkg/SharedPrefsTest5.java:33: Warning: SharedPreferences.edit() without a corresponding commit() or apply() call [CommitPrefEdits]\n" +
-            "        preferences.edit().remove(PREF_BAZ).remove(PREF_FOO);\n" +
-            "        ~~~~~~~~~~~~~~~~~~\n" +
-            "src/test/pkg/SharedPrefsTest5.java:38: Warning: SharedPreferences.edit() without a corresponding commit() or apply() call [CommitPrefEdits]\n" +
-            "        Editor editor = preferences.edit().putString(PREF_FOO, \"bar\");\n" +
-            "                        ~~~~~~~~~~~~~~~~~~\n" +
-            "0 errors, 7 warnings\n",
-
-            lintProject("src/test/pkg/SharedPrefsTest5.java.txt=>" +
-                    "src/test/pkg/SharedPrefsTest5.java"));
-    }
-
-    public void test6() throws Exception {
-        // Regression test for https://code.google.com/p/android/issues/detail?id=68692
-        assertEquals(""
-            + "src/test/pkg/SharedPrefsTest7.java:13: Warning: SharedPreferences.edit() without a corresponding commit() or apply() call [CommitPrefEdits]\n"
-            + "        settings.edit().putString(MY_PREF_KEY, myPrefValue);\n"
-            + "        ~~~~~~~~~~~~~~~\n"
-            + "0 errors, 1 warnings\n",
-
-            lintProject("src/test/pkg/SharedPrefsTest7.java.txt=>" +
-                    "src/test/pkg/SharedPrefsTest7.java"));
-    }
-
-    public void test7() throws Exception {
-        assertEquals("No warnings.", // minSdk < 9: no warnings
-
-                lintProject("src/test/pkg/SharedPrefsTest8.java.txt=>" +
-                        "src/test/pkg/SharedPrefsTest8.java"));
-    }
-
-    public void test8() throws Exception {
-        assertEquals(""
-            + "src/test/pkg/SharedPrefsTest8.java:11: Warning: Consider using apply() instead; commit writes its data to persistent storage immediately, whereas apply will handle it in the background [CommitPrefEdits]\n"
-            + "        editor.commit();\n"
-            + "        ~~~~~~~~~~~~~~~\n"
-            + "0 errors, 1 warnings\n",
-
-            lintProject(
-                    "apicheck/minsdk11.xml=>AndroidManifest.xml",
-                    "src/test/pkg/SharedPrefsTest8.java.txt=>src/test/pkg/SharedPrefsTest8.java"));
-    }
-
-    public void testChainedCalls() throws Exception {
-        assertEquals(""
-                + "src/test/pkg/Chained.java:24: Warning: SharedPreferences.edit() without a corresponding commit() or apply() call [CommitPrefEdits]\n"
-                + "        PreferenceManager\n"
-                + "        ^\n"
-                + "0 errors, 1 warnings\n",
-                lintProject(java("src/test/pkg/Chained.java", ""
-                        + "package test.pkg;\n"
-                        + "\n"
-                        + "import android.content.Context;\n"
-                        + "import android.preference.PreferenceManager;\n"
-                        + "\n"
-                        + "public class Chained {\n"
-                        + "    private static void falsePositive(Context context) {\n"
-                        + "        PreferenceManager\n"
-                        + "                .getDefaultSharedPreferences(context)\n"
-                        + "                .edit()\n"
-                        + "                .putString(\"wat\", \"wat\")\n"
-                        + "                .commit();\n"
-                        + "    }\n"
-                        + "\n"
-                        + "    private static void falsePositive2(Context context) {\n"
-                        + "        boolean var = PreferenceManager\n"
-                        + "                .getDefaultSharedPreferences(context)\n"
-                        + "                .edit()\n"
-                        + "                .putString(\"wat\", \"wat\")\n"
-                        + "                .commit();\n"
-                        + "    }\n"
-                        + "\n"
-                        + "    private static void truePositive(Context context) {\n"
-                        + "        PreferenceManager\n"
-                        + "                .getDefaultSharedPreferences(context)\n"
-                        + "                .edit()\n"
-                        + "                .putString(\"wat\", \"wat\");\n"
-                        + "    }\n"
-                        + "}\n")));
-    }
-}
diff --git a/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/SslCertificateSocketFactoryDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/SslCertificateSocketFactoryDetectorTest.java
index 9d85889..c14c984 100644
--- a/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/SslCertificateSocketFactoryDetectorTest.java
+++ b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/SslCertificateSocketFactoryDetectorTest.java
@@ -32,6 +32,7 @@
         return true;
     }
 
+    @SuppressWarnings("ClassNameDiffersFromFileName")
     public void test() throws Exception {
         assertEquals(
                 "src/test/pkg/SSLCertificateSocketFactoryTest.java:21: Warning: Use of SSLCertificateSocketFactory.createSocket() with an InetAddress parameter can cause insecure network traffic due to trusting arbitrary hostnames in TLS/SSL certificates presented by peers [SSLCertificateSocketFactoryCreateSocket]\n" +
diff --git a/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/StringFormatDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/StringFormatDetectorTest.java
index 5ff4c9d..dc51ea1 100644
--- a/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/StringFormatDetectorTest.java
+++ b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/StringFormatDetectorTest.java
@@ -25,53 +25,58 @@
 
 @SuppressWarnings({"javadoc", "ClassNameDiffersFromFileName"})
 public class StringFormatDetectorTest extends AbstractCheckTest {
+
     @Override
     protected Detector getDetector() {
         return new StringFormatDetector();
     }
 
     public void testAll() throws Exception {
-        assertEquals(
-            "src/test/pkg/StringFormatActivity.java:13: Error: Wrong argument type for formatting argument '#1' in hello: conversion is 'd', received String (argument #2 in method call) [StringFormatMatches]\n" +
-            "        String output1 = String.format(hello, target);\n" +
-            "                                              ~~~~~~\n" +
-            "    res/values-es/formatstrings.xml:3: Conflicting argument declaration here\n" +
-            "src/test/pkg/StringFormatActivity.java:15: Error: Wrong argument count, format string hello2 requires 3 but format call supplies 2 [StringFormatMatches]\n" +
-            "        String output2 = String.format(hello2, target, \"How are you\");\n" +
-            "                         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" +
-            "    res/values-es/formatstrings.xml:4: This definition requires 3 arguments\n" +
-            "src/test/pkg/StringFormatActivity.java:21: Error: Wrong argument type for formatting argument '#1' in score: conversion is 'd', received boolean (argument #2 in method call) [StringFormatMatches]\n" +
-            "        String output4 = String.format(score, true);  // wrong\n" +
-            "                                              ~~~~\n" +
-            "    res/values/formatstrings.xml:6: Conflicting argument declaration here\n" +
-            "src/test/pkg/StringFormatActivity.java:22: Error: Wrong argument type for formatting argument '#1' in score: conversion is 'd', received boolean (argument #2 in method call) [StringFormatMatches]\n" +
-            "        String output  = String.format(score, won);   // wrong\n" +
-            "                                              ~~~\n" +
-            "    res/values/formatstrings.xml:6: Conflicting argument declaration here\n" +
-            "src/test/pkg/StringFormatActivity.java:24: Error: Wrong argument count, format string hello2 requires 3 but format call supplies 2 [StringFormatMatches]\n" +
-            "        String.format(getResources().getString(R.string.hello2), target, \"How are you\");\n" +
-            "        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" +
-            "    res/values-es/formatstrings.xml:4: This definition requires 3 arguments\n" +
-            "src/test/pkg/StringFormatActivity.java:26: Error: Wrong argument count, format string hello2 requires 3 but format call supplies 2 [StringFormatMatches]\n" +
-            "        getResources().getString(R.string.hello2, target, \"How are you\");\n" +
-            "        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" +
-            "    res/values-es/formatstrings.xml:4: This definition requires 3 arguments\n" +
-            "src/test/pkg/StringFormatActivity.java:33: Error: Wrong argument type for formatting argument '#1' in hello: conversion is 'd', received String (argument #2 in method call) [StringFormatMatches]\n" +
-            "        String output1 = String.format(hello, target);\n" +
-            "                                              ~~~~~~\n" +
-            "    res/values-es/formatstrings.xml:3: Conflicting argument declaration here\n" +
-            "res/values-es/formatstrings.xml:3: Error: Inconsistent formatting types for argument #1 in format string hello ('%1$d'): Found both 's' and 'd' (in values/formatstrings.xml) [StringFormatMatches]\n" +
-            "    <string name=\"hello\">%1$d</string>\n" +
-            "                         ~~~~\n" +
-            "    res/values/formatstrings.xml:3: Conflicting argument type here\n" +
-            "res/values-es/formatstrings.xml:4: Warning: Inconsistent number of arguments in formatting string hello2; found both 2 and 3 [StringFormatCount]\n" +
-            "    <string name=\"hello2\">%3$d: %1$s, %2$s?</string>\n" +
-            "    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" +
-            "    res/values/formatstrings.xml:4: Conflicting number of arguments here\n" +
-            "res/values/formatstrings.xml:5: Warning: Formatting string 'missing' is not referencing numbered arguments [1, 2] [StringFormatCount]\n" +
-            "    <string name=\"missing\">Hello %3$s World</string>\n" +
-            "    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" +
-            "8 errors, 2 warnings\n",
+        assertEquals(""
+                + "src/test/pkg/StringFormatActivity.java:13: Error: Wrong argument type for formatting argument '#1' in hello: conversion is 'd', received String (argument #2 in method call) [StringFormatMatches]\n"
+                + "        String output1 = String.format(hello, target);\n"
+                + "                                              ~~~~~~\n"
+                + "    res/values-es/formatstrings.xml:3: Conflicting argument declaration here\n"
+                + "src/test/pkg/StringFormatActivity.java:15: Error: Wrong argument count, format string hello2 requires 3 but format call supplies 2 [StringFormatMatches]\n"
+                + "        String output2 = String.format(hello2, target, \"How are you\");\n"
+                + "                         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
+                + "    res/values-es/formatstrings.xml:4: This definition requires 3 arguments\n"
+                + "src/test/pkg/StringFormatActivity.java:21: Error: Wrong argument type for formatting argument '#1' in score: conversion is 'd', received boolean (argument #2 in method call) [StringFormatMatches]\n"
+                + "        String output4 = String.format(score, true);  // wrong\n"
+                + "                                              ~~~~\n"
+                + "    res/values/formatstrings.xml:6: Conflicting argument declaration here\n"
+                + "src/test/pkg/StringFormatActivity.java:22: Error: Wrong argument type for formatting argument '#1' in score: conversion is 'd', received boolean (argument #2 in method call) [StringFormatMatches]\n"
+                + "        String output  = String.format(score, won);   // wrong\n"
+                + "                                              ~~~\n"
+                + "    res/values/formatstrings.xml:6: Conflicting argument declaration here\n"
+                + "src/test/pkg/StringFormatActivity.java:24: Error: Wrong argument count, format string hello2 requires 3 but format call supplies 2 [StringFormatMatches]\n"
+                + "        String.format(getResources().getString(R.string.hello2), target, \"How are you\");\n"
+                + "        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
+                + "    res/values-es/formatstrings.xml:4: This definition requires 3 arguments\n"
+                + "src/test/pkg/StringFormatActivity.java:26: Error: Wrong argument count, format string hello2 requires 3 but format call supplies 2 [StringFormatMatches]\n"
+                + "        getResources().getString(R.string.hello2, target, \"How are you\");\n"
+                + "        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
+                + "    res/values-es/formatstrings.xml:4: This definition requires 3 arguments\n"
+                + "src/test/pkg/StringFormatActivity.java:33: Error: Wrong argument type for formatting argument '#1' in hello: conversion is 'd', received String (argument #2 in method call) [StringFormatMatches]\n"
+                + "        String output1 = String.format(hello, target);\n"
+                + "                                              ~~~~~~\n"
+                + "    res/values-es/formatstrings.xml:3: Conflicting argument declaration here\n"
+                + "src/test/pkg/StringFormatActivity.java:41: Error: Wrong argument type for formatting argument '#1' in score: conversion is 'd', received Boolean (argument #2 in method call) [StringFormatMatches]\n"
+                + "        String output1  = String.format(score, won);   // wrong\n"
+                + "                                               ~~~\n"
+                + "    res/values/formatstrings.xml:6: Conflicting argument declaration here\n"
+                + "res/values-es/formatstrings.xml:3: Error: Inconsistent formatting types for argument #1 in format string hello ('%1$d'): Found both 's' and 'd' (in values/formatstrings.xml) [StringFormatMatches]\n"
+                + "    <string name=\"hello\">%1$d</string>\n"
+                + "                         ~~~~\n"
+                + "    res/values/formatstrings.xml:3: Conflicting argument type here\n"
+                + "res/values-es/formatstrings.xml:4: Warning: Inconsistent number of arguments in formatting string hello2; found both 2 and 3 [StringFormatCount]\n"
+                + "    <string name=\"hello2\">%3$d: %1$s, %2$s?</string>\n"
+                + "    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
+                + "    res/values/formatstrings.xml:4: Conflicting number of arguments here\n"
+                + "res/values/formatstrings.xml:5: Warning: Formatting string 'missing' is not referencing numbered arguments [1, 2] [StringFormatCount]\n"
+                + "    <string name=\"missing\">Hello %3$s World</string>\n"
+                + "    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
+                + "9 errors, 2 warnings\n",
 
             lintProject(
                     "res/values/formatstrings.xml",
@@ -301,40 +306,44 @@
     }
 
     public void testIncremental() throws Exception {
-        assertEquals(
-                "src/test/pkg/StringFormatActivity.java:13: Error: Wrong argument type for formatting argument '#1' in hello: conversion is 'd', received String (argument #2 in method call) [StringFormatMatches]\n" +
-                "        String output1 = String.format(hello, target);\n" +
-                "                                              ~~~~~~\n" +
-                "    res/values-es/formatstrings.xml: Conflicting argument declaration here\n" +
-                "src/test/pkg/StringFormatActivity.java:15: Error: Wrong argument count, format string hello2 requires 3 but format call supplies 2 [StringFormatMatches]\n" +
-                "        String output2 = String.format(hello2, target, \"How are you\");\n" +
-                "                         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" +
-                "    res/values-es/formatstrings.xml: This definition requires 3 arguments\n" +
-                "src/test/pkg/StringFormatActivity.java:21: Error: Wrong argument type for formatting argument '#1' in score: conversion is 'd', received boolean (argument #2 in method call) [StringFormatMatches]\n" +
-                "        String output4 = String.format(score, true);  // wrong\n" +
-                "                                              ~~~~\n" +
-                "    res/values/formatstrings.xml: Conflicting argument declaration here\n" +
-                "src/test/pkg/StringFormatActivity.java:22: Error: Wrong argument type for formatting argument '#1' in score: conversion is 'd', received boolean (argument #2 in method call) [StringFormatMatches]\n" +
-                "        String output  = String.format(score, won);   // wrong\n" +
-                "                                              ~~~\n" +
-                "    res/values/formatstrings.xml: Conflicting argument declaration here\n" +
-                "src/test/pkg/StringFormatActivity.java:24: Error: Wrong argument count, format string hello2 requires 3 but format call supplies 2 [StringFormatMatches]\n" +
-                "        String.format(getResources().getString(R.string.hello2), target, \"How are you\");\n" +
-                "        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" +
-                "    res/values-es/formatstrings.xml: This definition requires 3 arguments\n" +
-                "src/test/pkg/StringFormatActivity.java:26: Error: Wrong argument count, format string hello2 requires 3 but format call supplies 2 [StringFormatMatches]\n" +
-                "        getResources().getString(R.string.hello2, target, \"How are you\");\n" +
-                "        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" +
-                "    res/values-es/formatstrings.xml: This definition requires 3 arguments\n" +
-                "src/test/pkg/StringFormatActivity.java:33: Error: Wrong argument type for formatting argument '#1' in hello: conversion is 'd', received String (argument #2 in method call) [StringFormatMatches]\n" +
-                "        String output1 = String.format(hello, target);\n" +
-                "                                              ~~~~~~\n" +
-                "    res/values-es/formatstrings.xml: Conflicting argument declaration here\n" +
-                "res/values/formatstrings.xml: Error: Inconsistent formatting types for argument #1 in format string hello ('%1$s'): Found both 'd' and 's' (in values-es/formatstrings.xml) [StringFormatMatches]\n" +
-                "    res/values-es/formatstrings.xml: Conflicting argument type here\n" +
-                "res/values/formatstrings.xml: Warning: Inconsistent number of arguments in formatting string hello2; found both 3 and 2 [StringFormatCount]\n" +
-                "    res/values-es/formatstrings.xml: Conflicting number of arguments here\n" +
-                "8 errors, 1 warnings\n",
+        assertEquals(""
+                + "src/test/pkg/StringFormatActivity.java:13: Error: Wrong argument type for formatting argument '#1' in hello: conversion is 'd', received String (argument #2 in method call) [StringFormatMatches]\n"
+                + "        String output1 = String.format(hello, target);\n"
+                + "                                              ~~~~~~\n"
+                + "    res/values-es/formatstrings.xml: Conflicting argument declaration here\n"
+                + "src/test/pkg/StringFormatActivity.java:15: Error: Wrong argument count, format string hello2 requires 3 but format call supplies 2 [StringFormatMatches]\n"
+                + "        String output2 = String.format(hello2, target, \"How are you\");\n"
+                + "                         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
+                + "    res/values-es/formatstrings.xml: This definition requires 3 arguments\n"
+                + "src/test/pkg/StringFormatActivity.java:21: Error: Wrong argument type for formatting argument '#1' in score: conversion is 'd', received boolean (argument #2 in method call) [StringFormatMatches]\n"
+                + "        String output4 = String.format(score, true);  // wrong\n"
+                + "                                              ~~~~\n"
+                + "    res/values/formatstrings.xml: Conflicting argument declaration here\n"
+                + "src/test/pkg/StringFormatActivity.java:22: Error: Wrong argument type for formatting argument '#1' in score: conversion is 'd', received boolean (argument #2 in method call) [StringFormatMatches]\n"
+                + "        String output  = String.format(score, won);   // wrong\n"
+                + "                                              ~~~\n"
+                + "    res/values/formatstrings.xml: Conflicting argument declaration here\n"
+                + "src/test/pkg/StringFormatActivity.java:24: Error: Wrong argument count, format string hello2 requires 3 but format call supplies 2 [StringFormatMatches]\n"
+                + "        String.format(getResources().getString(R.string.hello2), target, \"How are you\");\n"
+                + "        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
+                + "    res/values-es/formatstrings.xml: This definition requires 3 arguments\n"
+                + "src/test/pkg/StringFormatActivity.java:26: Error: Wrong argument count, format string hello2 requires 3 but format call supplies 2 [StringFormatMatches]\n"
+                + "        getResources().getString(R.string.hello2, target, \"How are you\");\n"
+                + "        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
+                + "    res/values-es/formatstrings.xml: This definition requires 3 arguments\n"
+                + "src/test/pkg/StringFormatActivity.java:33: Error: Wrong argument type for formatting argument '#1' in hello: conversion is 'd', received String (argument #2 in method call) [StringFormatMatches]\n"
+                + "        String output1 = String.format(hello, target);\n"
+                + "                                              ~~~~~~\n"
+                + "    res/values-es/formatstrings.xml: Conflicting argument declaration here\n"
+                + "src/test/pkg/StringFormatActivity.java:41: Error: Wrong argument type for formatting argument '#1' in score: conversion is 'd', received Boolean (argument #2 in method call) [StringFormatMatches]\n"
+                + "        String output1  = String.format(score, won);   // wrong\n"
+                + "                                               ~~~\n"
+                + "    res/values/formatstrings.xml: Conflicting argument declaration here\n"
+                + "res/values/formatstrings.xml: Error: Inconsistent formatting types for argument #1 in format string hello ('%1$s'): Found both 'd' and 's' (in values-es/formatstrings.xml) [StringFormatMatches]\n"
+                + "    res/values-es/formatstrings.xml: Conflicting argument type here\n"
+                + "res/values/formatstrings.xml: Warning: Inconsistent number of arguments in formatting string hello2; found both 3 and 2 [StringFormatCount]\n"
+                + "    res/values-es/formatstrings.xml: Conflicting number of arguments here\n"
+                + "9 errors, 1 warnings\n",
 
         lintProjectIncrementally(
                 "src/test/pkg/StringFormatActivity.java",
@@ -496,11 +505,15 @@
                 + "        context.getString(R.string.two_args, new Object[] { \"first\", \"second\", \"third\" }); // ERROR\n"
                 + "        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
                 + "    res/values/strings.xml:6: This definition requires 2 arguments\n"
+                + "src/test/pkg/FormatCheck.java:30: Error: Wrong argument count, format string two_args requires 2 but format call supplies 3 [StringFormatMatches]\n"
+                + "        context.getString(R.string.two_args, args3); // ERROR\n"
+                + "        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
+                + "    res/values/strings.xml:6: This definition requires 2 arguments\n"
                 + "src/test/pkg/FormatCheck.java:36: Error: Wrong argument count, format string one_arg requires 1 but format call supplies 3 [StringFormatMatches]\n"
                 + "        fragment.getString(R.string.one_arg, \"too\", \"many\", \"args\"); // ERROR: not enough arguments\n"
                 + "        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
                 + "    res/values/strings.xml:5: This definition requires 1 arguments\n"
-                + "9 errors, 0 warnings\n",
+                + "10 errors, 0 warnings\n",
 
                 lintProject(
                         java("src/test/pkg/FormatCheck.java", ""
@@ -607,11 +620,15 @@
                 + "        context.getString(R.string.two_args, new Object[] { \"first\", \"second\", \"third\" }); // ERROR\n"
                 + "        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
                 + "    res/values/strings.xml: This definition requires 2 arguments\n"
+                + "src/test/pkg/FormatCheck.java:30: Error: Wrong argument count, format string two_args requires 2 but format call supplies 3 [StringFormatMatches]\n"
+                + "        context.getString(R.string.two_args, args3); // ERROR\n"
+                + "        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
+                + "    res/values/strings.xml: This definition requires 2 arguments\n"
                 + "src/test/pkg/FormatCheck.java:36: Error: Wrong argument count, format string one_arg requires 1 but format call supplies 3 [StringFormatMatches]\n"
                 + "        fragment.getString(R.string.one_arg, \"too\", \"many\", \"args\"); // ERROR: not enough arguments\n"
                 + "        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
                 + "    res/values/strings.xml: This definition requires 1 arguments\n"
-                + "9 errors, 0 warnings\n",
+                + "10 errors, 0 warnings\n",
 
                 lintProjectIncrementally(
                         "src/test/pkg/FormatCheck.java",
diff --git a/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/SupportAnnotationDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/SupportAnnotationDetectorTest.java
index fc85fd1..916eaf4 100644
--- a/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/SupportAnnotationDetectorTest.java
+++ b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/SupportAnnotationDetectorTest.java
@@ -357,6 +357,7 @@
                         mColorResAnnotation
                 ));
     }
+
     public void testResourceType() throws Exception {
         assertEquals((SDK_ANNOTATIONS_AVAILABLE ? ""
                 + "src/p1/p2/Flow.java:13: Error: Expected resource of type drawable [ResourceType]\n"
@@ -380,7 +381,10 @@
                 + "src/p1/p2/Flow.java:68: Error: Expected resource of type drawable [ResourceType]\n"
                 + "        myMethod(z, null); // ERROR\n"
                 + "                 ~\n"
-                + (SDK_ANNOTATIONS_AVAILABLE ? "7 errors, 0 warnings\n" : "4 errors, 0 warnings\n"),
+                + "src/p1/p2/Flow.java:71: Error: Expected resource of type drawable [ResourceType]\n"
+                + "        myMethod(w, null); // ERROR\n"
+                + "                 ~\n"
+                + (SDK_ANNOTATIONS_AVAILABLE ? "8 errors, 0 warnings\n" : "5 errors, 0 warnings\n"),
 
                 lintProject(
                         copy("src/p1/p2/Flow.java.txt", "src/p1/p2/Flow.java"),
@@ -449,7 +453,7 @@
                 + "src/test/pkg/ConstructorTest.java:14: Error: Value must be ≥ 5 (was 3) [Range]\n"
                 + "        new ConstructorTest(1, 3);\n"
                 + "                               ~\n"
-                + "src/test/pkg/ConstructorTest.java:19: Error: Method test.pkg.ConstructorTest must be called from the UI thread, currently inferred thread is worker thread [WrongThread]\n"
+                + "src/test/pkg/ConstructorTest.java:19: Error: Constructor ConstructorTest must be called from the UI thread, currently inferred thread is worker thread [WrongThread]\n"
                 + "        new ConstructorTest(res, range);\n"
                 + "        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
                 + "3 errors, 0 warnings\n",
@@ -540,24 +544,6 @@
                 + "    }\n"
                 + "}\n");
 
-        private final TestFile mComplexLocationManagerStub = java("src/android/location/LocationManager.java", ""
-                + "package android.location;\n"
-                + "\n"
-                + "import android.support.annotation.RequiresPermission;\n"
-                + "\n"
-                + "import static android.Manifest.permission.ACCESS_COARSE_LOCATION;\n"
-                + "import static android.Manifest.permission.ACCESS_FINE_LOCATION;\n"
-                + "import static android.Manifest.permission.BLUETOOTH;\n"
-                + "import static android.Manifest.permission.READ_SMS;\n"
-                + "\n"
-                + "@SuppressWarnings(\"UnusedDeclaration\")\n"
-                + "public abstract class LocationManager {\n"
-                + "    @RequiresPermission(\"(\" + ACCESS_FINE_LOCATION + \"|| \" + ACCESS_COARSE_LOCATION + \") && (\" + BLUETOOTH + \" ^ \" + READ_SMS + \")\")\n"
-                + "    public abstract Location myMethod(String provider);\n"
-                + "    public static class Location {\n"
-                + "    }\n"
-                + "}\n");
-
         private final TestFile mRequirePermissionAnnotation = java("src/android/support/annotation/RequiresPermission.java", ""
                 + "/*\n"
                 + " * Copyright (C) 2015 The Android Open Source Project\n"
@@ -913,47 +899,6 @@
                 ));
     }
 
-    public void testComplexPermission1() throws Exception {
-        assertEquals(""
-                + "src/test/pkg/PermissionTest.java:7: Error: Missing permissions required by LocationManager.myMethod: android.permission.BLUETOOTH xor android.permission.READ_SMS [MissingPermission]\n"
-                + "        LocationManager.Location location = locationManager.myMethod(provider);\n"
-                + "                                            ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
-                + "1 errors, 0 warnings\n",
-                lintProject(
-                        getManifestWithPermissions(14,
-                                "android.permission.ACCESS_FINE_LOCATION"),
-                        mPermissionTest,
-                        mComplexLocationManagerStub,
-                        mRequirePermissionAnnotation));
-    }
-
-    public void testComplexPermission2() throws Exception {
-        assertEquals("No warnings.",
-                lintProject(
-                        getManifestWithPermissions(14,
-                                "android.permission.ACCESS_FINE_LOCATION",
-                                "android.permission.BLUETOOTH"),
-                        mPermissionTest,
-                        mComplexLocationManagerStub,
-                        mRequirePermissionAnnotation));
-    }
-
-    public void testComplexPermission3() throws Exception {
-        assertEquals(""
-                + "src/test/pkg/PermissionTest.java:7: Error: Missing permissions required by LocationManager.myMethod: android.permission.BLUETOOTH xor android.permission.READ_SMS [MissingPermission]\n"
-                + "        LocationManager.Location location = locationManager.myMethod(provider);\n"
-                + "                                            ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
-                + "1 errors, 0 warnings\n",
-                lintProject(
-                        getManifestWithPermissions(14,
-                                "android.permission.ACCESS_FINE_LOCATION",
-                                "android.permission.BLUETOOTH",
-                                "android.permission.READ_SMS"),
-                        mPermissionTest,
-                        mComplexLocationManagerStub,
-                        mRequirePermissionAnnotation));
-    }
-
     public void testUsesPermissionSdk23() throws Exception {
         TestFile manifest = getManifestWithPermissions(14,
                 "android.permission.ACCESS_FINE_LOCATION",
@@ -966,7 +911,7 @@
                 lintProject(
                         manifest,
                         mPermissionTest,
-                        mComplexLocationManagerStub,
+                        mLocationManagerStub,
                         mRequirePermissionAnnotation));
     }
 
@@ -982,7 +927,7 @@
                 lintProject(
                         manifest,
                         mPermissionTest,
-                        mComplexLocationManagerStub,
+                        mLocationManagerStub,
                         mRequirePermissionAnnotation));
     }
 
diff --git a/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/ToastDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/ToastDetectorTest.java
index 821e64f..1c3f812 100644
--- a/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/ToastDetectorTest.java
+++ b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/ToastDetectorTest.java
@@ -25,24 +25,78 @@
         return new ToastDetector();
     }
 
-
     public void test() throws Exception {
-        assertEquals(
-            "src/test/pkg/ToastTest.java:31: Warning: Toast created but not shown: did you forget to call show() ? [ShowToast]\n" +
-            "        Toast.makeText(context, \"foo\", Toast.LENGTH_LONG);\n" +
-            "        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" +
-            "src/test/pkg/ToastTest.java:32: Warning: Expected duration Toast.LENGTH_SHORT or Toast.LENGTH_LONG, a custom duration value is not supported [ShowToast]\n" +
-            "        Toast toast = Toast.makeText(context, R.string.app_name, 5000);\n" +
-            "                                                                 ~~~~\n" +
-            "src/test/pkg/ToastTest.java:32: Warning: Toast created but not shown: did you forget to call show() ? [ShowToast]\n" +
-            "        Toast toast = Toast.makeText(context, R.string.app_name, 5000);\n" +
-            "                      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" +
-            "src/test/pkg/ToastTest.java:38: Warning: Toast created but not shown: did you forget to call show() ? [ShowToast]\n" +
-            "        Toast.makeText(context, \"foo\", Toast.LENGTH_LONG);\n" +
-            "        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" +
-            "0 errors, 4 warnings\n" +
-            "",
+        assertEquals(""
+                + "src/test/pkg/ToastTest.java:31: Warning: Toast created but not shown: did you forget to call show() ? [ShowToast]\n"
+                + "        Toast.makeText(context, \"foo\", Toast.LENGTH_LONG);\n"
+                + "        ~~~~~~~~~~~~~~\n"
+                + "src/test/pkg/ToastTest.java:32: Warning: Expected duration Toast.LENGTH_SHORT or Toast.LENGTH_LONG, a custom duration value is not supported [ShowToast]\n"
+                + "        Toast toast = Toast.makeText(context, R.string.app_name, 5000);\n"
+                + "                                                                 ~~~~\n"
+                + "src/test/pkg/ToastTest.java:32: Warning: Toast created but not shown: did you forget to call show() ? [ShowToast]\n"
+                + "        Toast toast = Toast.makeText(context, R.string.app_name, 5000);\n"
+                + "                      ~~~~~~~~~~~~~~\n"
+                + "src/test/pkg/ToastTest.java:38: Warning: Toast created but not shown: did you forget to call show() ? [ShowToast]\n"
+                + "        Toast.makeText(context, \"foo\", Toast.LENGTH_LONG);\n"
+                + "        ~~~~~~~~~~~~~~\n"
+                + "0 errors, 4 warnings\n",
 
-            lintProject("src/test/pkg/ToastTest.java.txt=>src/test/pkg/ToastTest.java"));
+            lintProject(java("src/test/pkg/ToastTest.java", ""
+                    + "package foo.bar;\n"
+                    + "\n"
+                    + "import android.app.Activity;\n"
+                    + "import android.content.Context;\n"
+                    + "import android.os.Bundle;\n"
+                    + "import android.widget.Toast;\n"
+                    + "\n"
+                    + "public abstract class ToastTest extends Context {\n"
+                    + "    private Toast createToast(Context context) {\n"
+                    + "        // Don't warn here\n"
+                    + "        return Toast.makeText(context, \"foo\", Toast.LENGTH_LONG);\n"
+                    + "    }\n"
+                    + "\n"
+                    + "    private void showToast(Context context) {\n"
+                    + "        // Don't warn here\n"
+                    + "        Toast toast = Toast.makeText(context, \"foo\", Toast.LENGTH_LONG);\n"
+                    + "        System.out.println(\"Other intermediate code here\");\n"
+                    + "        int temp = 5 + 2;\n"
+                    + "        toast.show();\n"
+                    + "    }\n"
+                    + "\n"
+                    + "    private void showToast2(Context context) {\n"
+                    + "        // Don't warn here\n"
+                    + "        int duration = Toast.LENGTH_LONG;\n"
+                    + "        Toast.makeText(context, \"foo\", Toast.LENGTH_LONG).show();\n"
+                    + "        Toast.makeText(context, R.string.app_name, duration).show();\n"
+                    + "    }\n"
+                    + "\n"
+                    + "    private void broken(Context context) {\n"
+                    + "        // Errors\n"
+                    + "        Toast.makeText(context, \"foo\", Toast.LENGTH_LONG);\n"
+                    + "        Toast toast = Toast.makeText(context, R.string.app_name, 5000);\n"
+                    + "        toast.getDuration();\n"
+                    + "    }\n"
+                    + "\n"
+                    + "    // Constructor test\n"
+                    + "    public ToastTest(Context context) {\n"
+                    + "        Toast.makeText(context, \"foo\", Toast.LENGTH_LONG);\n"
+                    + "    }\n"
+                    + "\n"
+                    + "    @android.annotation.SuppressLint(\"ShowToast\")\n"
+                    + "    private void checkSuppress1(Context context) {\n"
+                    + "        Toast toast = Toast.makeText(this, \"MyToast\", Toast.LENGTH_LONG);\n"
+                    + "    }\n"
+                    + "\n"
+                    + "    private void checkSuppress2(Context context) {\n"
+                    + "        @android.annotation.SuppressLint(\"ShowToast\")\n"
+                    + "        Toast toast = Toast.makeText(this, \"MyToast\", Toast.LENGTH_LONG);\n"
+                    + "    }\n"
+                    + "\n"
+                    + "    public static final class R {\n"
+                    + "        public static final class string {\n"
+                    + "            public static final int app_name = 0x7f0a0000;\n"
+                    + "        }\n"
+                    + "    }\n"
+                    + "}")));
     }
 }
diff --git a/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/UnsafeBroadcastReceiverDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/UnsafeBroadcastReceiverDetectorTest.java
index 8788497..80c4ba8 100644
--- a/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/UnsafeBroadcastReceiverDetectorTest.java
+++ b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/UnsafeBroadcastReceiverDetectorTest.java
@@ -307,9 +307,6 @@
     @Nullable
     private static List<String> getProtectedBroadcasts() throws IOException {
         String top = System.getenv("ANDROID_BUILD_TOP");   //$NON-NLS-1$
-        if (top == null) {
-            top = "/Users/tnorbye/dev/mnc-dev";
-        }
 
         // TODO: We should ship this file with the SDK!
         File file = new File(top, "frameworks/base/core/res/AndroidManifest.xml");
diff --git a/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/WrongImportDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/WrongImportDetectorTest.java
index c44f204..dc380e3 100644
--- a/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/WrongImportDetectorTest.java
+++ b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/WrongImportDetectorTest.java
@@ -26,12 +26,11 @@
     }
 
     public void test() throws Exception {
-        assertEquals(
-            "src/test/pkg/BadImport.java:5: Warning: Don't include android.R here; use a fully qualified name for each usage instead [SuspiciousImport]\n" +
-            "import android.R;\n" +
-            "~~~~~~~~~~~~~~~~~\n" +
-            "0 errors, 1 warnings\n" +
-            "",
+        assertEquals(""
+                + "src/test/pkg/BadImport.java:5: Warning: Don't include android.R here; use a fully qualified name for each usage instead [SuspiciousImport]\n"
+                + "import android.R;\n"
+                + "~~~~~~~~~~~~~~~~~\n"
+                + "0 errors, 1 warnings\n",
 
             lintProject(
                 // Java files must be renamed in source tree
diff --git a/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/AddJavascriptInterfaceTest$CallAddJavascriptInterfaceOnNonWebView.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/AddJavascriptInterfaceTest$CallAddJavascriptInterfaceOnNonWebView.class.data
deleted file mode 100644
index 33a5c28..0000000
--- a/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/AddJavascriptInterfaceTest$CallAddJavascriptInterfaceOnNonWebView.class.data
+++ /dev/null
Binary files differ
diff --git a/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/AddJavascriptInterfaceTest$CallAddJavascriptInterfaceOnWebView.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/AddJavascriptInterfaceTest$CallAddJavascriptInterfaceOnWebView.class.data
deleted file mode 100644
index 73cdecb..0000000
--- a/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/AddJavascriptInterfaceTest$CallAddJavascriptInterfaceOnWebView.class.data
+++ /dev/null
Binary files differ
diff --git a/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/AddJavascriptInterfaceTest$CallAddJavascriptInterfaceOnWebViewChild.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/AddJavascriptInterfaceTest$CallAddJavascriptInterfaceOnWebViewChild.class.data
deleted file mode 100644
index f7ca55c..0000000
--- a/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/AddJavascriptInterfaceTest$CallAddJavascriptInterfaceOnWebViewChild.class.data
+++ /dev/null
Binary files differ
diff --git a/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/AddJavascriptInterfaceTest$NonWebView.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/AddJavascriptInterfaceTest$NonWebView.class.data
deleted file mode 100644
index 00d1c52..0000000
--- a/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/AddJavascriptInterfaceTest$NonWebView.class.data
+++ /dev/null
Binary files differ
diff --git a/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/AddJavascriptInterfaceTest$WebViewChild.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/AddJavascriptInterfaceTest$WebViewChild.class.data
deleted file mode 100644
index 306f02c..0000000
--- a/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/AddJavascriptInterfaceTest$WebViewChild.class.data
+++ /dev/null
Binary files differ
diff --git a/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/AddJavascriptInterfaceTest.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/AddJavascriptInterfaceTest.class.data
deleted file mode 100644
index 4dd1548..0000000
--- a/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/AddJavascriptInterfaceTest.class.data
+++ /dev/null
Binary files differ
diff --git a/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/AddJavascriptInterfaceTest.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/AddJavascriptInterfaceTest.java.txt
deleted file mode 100644
index f8fb8b5..0000000
--- a/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/AddJavascriptInterfaceTest.java.txt
+++ /dev/null
@@ -1,37 +0,0 @@
-package test.pkg;
-
-import android.webkit.WebView;
-import android.content.Context;
-
-
-public class AddJavascriptInterfaceTest {
-    private static class WebViewChild extends WebView {
-        WebViewChild(Context context) {
-            super(context);
-        }
-    }
-
-    private static class CallAddJavascriptInterfaceOnWebView {
-        public void addJavascriptInterfaceToWebView(WebView webView, Object object, String string) {
-            webView.addJavascriptInterface(object, string);
-        }
-    }
-
-    private static class CallAddJavascriptInterfaceOnWebViewChild {
-        public void addJavascriptInterfaceToWebViewChild(
-            WebViewChild webView, Object object, String string) {
-            webView.addJavascriptInterface(object, string);
-        }
-    }
-
-    private static class NonWebView {
-        public void addJavascriptInterface(Object object, String string) { }
-    }
-
-    private static class CallAddJavascriptInterfaceOnNonWebView {
-        public void addJavascriptInterfaceToNonWebView(
-            NonWebView webView, Object object, String string) {
-            webView.addJavascriptInterface(object, string);
-        }
-    }
-}
\ No newline at end of file
diff --git a/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/LocaleTest.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/LocaleTest.class.data
deleted file mode 100644
index 2b10aa9..0000000
--- a/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/LocaleTest.class.data
+++ /dev/null
Binary files differ
diff --git a/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/LocaleTest.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/LocaleTest.java.txt
deleted file mode 100644
index 3c60c9d..0000000
--- a/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/LocaleTest.java.txt
+++ /dev/null
@@ -1,37 +0,0 @@
-package test.pkg;
-
-import java.text.*;
-import java.util.*;
-
-public class LocaleTest {
-    public void testStrings() {
-        System.out.println("OK".toUpperCase(Locale.getDefault()));
-        System.out.println("OK".toUpperCase(Locale.US));
-        System.out.println("OK".toUpperCase(Locale.CHINA));
-        System.out.println("WRONG".toUpperCase());
-
-        System.out.println("OK".toLowerCase(Locale.getDefault()));
-        System.out.println("OK".toLowerCase(Locale.US));
-        System.out.println("OK".toLowerCase(Locale.CHINA));
-        System.out.println("WRONG".toLowerCase());
-
-        String.format(Locale.getDefault(), "OK: %f", 1.0f);
-        String.format("OK: %x %A %c %b %B %h %n %%", 1, 2, 'c', true, false, 5);
-        String.format("WRONG: %f", 1.0f); // Implies locale
-        String.format("WRONG: %1$f", 1.0f);
-        String.format("WRONG: %e", 1.0f);
-        String.format("WRONG: %d", 1.0f);
-        String.format("WRONG: %g", 1.0f);
-        String.format("WRONG: %g", 1.0f);
-        String.format("WRONG: %1$tm %1$te,%1$tY",
-                new GregorianCalendar(2012, GregorianCalendar.AUGUST, 27));
-    }
-
-    @android.annotation.SuppressLint("NewApi") // DateFormatSymbols requires API 9
-    public void testSimpleDateFormat() {
-        new SimpleDateFormat(); // WRONG
-        new SimpleDateFormat("yyyy-MM-dd"); // WRONG
-        new SimpleDateFormat("yyyy-MM-dd", DateFormatSymbols.getInstance()); // WRONG
-        new SimpleDateFormat("yyyy-MM-dd", Locale.US); // OK
-    }
-}
diff --git a/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/formatstrings.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/formatstrings.xml
index 6b1985c..7c23316 100644
--- a/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/formatstrings.xml
+++ b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/formatstrings.xml
@@ -4,4 +4,5 @@
     <string name="hello2">Hello %1$s, %2$s?</string>
     <string name="missing">Hello %3$s World</string>
     <string name="score">Score: %1$d</string>
+    <string name="score2">Score: %1$b</string>
 </resources>
diff --git a/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/AlarmTest.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/AlarmTest.java.txt
deleted file mode 100644
index 523d93d..0000000
--- a/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/AlarmTest.java.txt
+++ /dev/null
@@ -1,22 +0,0 @@
-package test.pkg;
-
-import android.app.AlarmManager;
-
-public class AlarmTest {
-    public void test(AlarmManager alarmManager) {
-        alarmManager.setRepeating(AlarmManager.ELAPSED_REALTIME, 5000, 60000, null); // OK
-        alarmManager.setRepeating(AlarmManager.ELAPSED_REALTIME, 6000, 70000, null); // OK
-        alarmManager.setRepeating(AlarmManager.ELAPSED_REALTIME, 50, 10, null); // ERROR
-        alarmManager.setRepeating(AlarmManager.ELAPSED_REALTIME, 5000,  // ERROR
-                OtherClass.MY_INTERVAL, null);                          // ERROR
-
-        // Check value flow analysis
-        int interval = 10;
-        long interval2 = 2 * interval;
-        alarmManager.setRepeating(AlarmManager.ELAPSED_REALTIME, 5000, interval2, null); // ERROR
-    }
-
-    private static class OtherClass {
-        public static final long MY_INTERVAL = 1000L;
-    }
-}
diff --git a/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/Assert.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/Assert.java.txt
deleted file mode 100644
index 6b76230..0000000
--- a/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/Assert.java.txt
+++ /dev/null
@@ -1,27 +0,0 @@
-package test.pkg;
-
-import android.annotation.SuppressLint;
-
-public class Assert {
-    public Assert(int param, Object param2, Object param3) {
-        assert false;                              // ERROR
-        assert param > 5 : "My description";       // ERROR
-        assert param2 == param3;                   // ERROR
-        assert param2 != null && param3 == param2; // ERROR
-        assert true;                               // OK
-        assert param2 == null;                     // OK
-        assert param2 != null && param3 == null;   // OK
-        assert param2 == null && param3 != null;   // OK
-        assert param2 != null && param3 != null;   // OK
-        assert null != param2;                     // OK
-        assert param2 != null;                     // OK
-        assert param2 != null : "My description";  // OK
-        assert checkSuppressed(5) != null;         // OK
-    }
-
-    @SuppressLint("Assert")
-    public Object checkSuppressed(int param) {
-        assert param > 5 : "My description";
-        return null;
-    }
-}
diff --git a/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/GetSignaturesBitwiseAndTest.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/GetSignaturesBitwiseAndTest.java.txt
deleted file mode 100644
index 285832a..0000000
--- a/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/GetSignaturesBitwiseAndTest.java.txt
+++ /dev/null
@@ -1,11 +0,0 @@
-package test.pkg;
-
-import android.app.Activity;
-import android.content.pm.PackageManager;
-
-public class GetSignaturesBitwiseAndTest extends Activity {
-    public void failLintCheck() {
-        getPackageManager().getPackageInfo("some.pkg",
-            Integer.MAX_VALUE & PackageManager.GET_SIGNATURES);
-    }
-}
\ No newline at end of file
diff --git a/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/GetSignaturesBitwiseOrTest.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/GetSignaturesBitwiseOrTest.java.txt
deleted file mode 100644
index 480905b..0000000
--- a/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/GetSignaturesBitwiseOrTest.java.txt
+++ /dev/null
@@ -1,13 +0,0 @@
-package test.pkg;
-
-import static android.content.pm.PackageManager.*;
-
-import android.app.Activity;
-import android.content.pm.PackageManager;
-
-public class GetSignaturesFlagPresentTest extends Activity {
-    public void failLintCheck() {
-        getPackageManager()
-            .getPackageInfo("some.pkg", GET_GIDS | GET_SIGNATURES | GET_PROVIDERS);
-    }
-}
\ No newline at end of file
diff --git a/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/GetSignaturesBitwiseXorTest.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/GetSignaturesBitwiseXorTest.java.txt
deleted file mode 100644
index 88d25e2..0000000
--- a/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/GetSignaturesBitwiseXorTest.java.txt
+++ /dev/null
@@ -1,10 +0,0 @@
-package test.pkg;
-
-import android.app.Activity;
-import android.content.pm.PackageManager;
-
-public class GetSignaturesBitwiseXorTest extends Activity {
-    public void failLintCheck() {
-        getPackageManager().getPackageInfo("some.pkg", PackageManager.GET_SIGNATURES ^ 0x0);
-    }
-}
\ No newline at end of file
diff --git a/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/GetSignaturesLocalVariableTest.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/GetSignaturesLocalVariableTest.java.txt
deleted file mode 100644
index 8af861b..0000000
--- a/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/GetSignaturesLocalVariableTest.java.txt
+++ /dev/null
@@ -1,11 +0,0 @@
-package test.pkg;
-
-import android.app.Activity;
-import android.content.pm.PackageManager;
-
-public class GetSignaturesLocalVariableTest extends Activity {
-    public void passLintCheck() {
-        int flags = PackageManager.GET_SIGNATURES;
-        getPackageManager().getPackageInfo("some.pkg", flags);
-    }
-}
\ No newline at end of file
diff --git a/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/GetSignaturesNoFlagTest.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/GetSignaturesNoFlagTest.java.txt
deleted file mode 100644
index 6230901..0000000
--- a/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/GetSignaturesNoFlagTest.java.txt
+++ /dev/null
@@ -1,21 +0,0 @@
-package test.pkg;
-
-import static android.content.pm.PackageManager.*;
-
-import android.app.Activity;
-
-public class GetSignaturesNoFlagTest extends Activity {
-    public void passLintCheck() {
-        getPackageManager()
-            .getPackageInfo("some.pkg",
-                GET_ACTIVITIES |
-                GET_GIDS |
-                GET_CONFIGURATIONS |
-                GET_INSTRUMENTATION |
-                GET_PERMISSIONS |
-                GET_PROVIDERS |
-                GET_RECEIVERS |
-                GET_SERVICES |
-                GET_UNINSTALLED_PACKAGES);
-    }
-}
\ No newline at end of file
diff --git a/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/GetSignaturesNotPackageManagerTest.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/GetSignaturesNotPackageManagerTest.java.txt
deleted file mode 100644
index 5f71ac0..0000000
--- a/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/GetSignaturesNotPackageManagerTest.java.txt
+++ /dev/null
@@ -1,15 +0,0 @@
-package test.pkg;
-
-import android.app.Activity;
-import android.content.pm.PackageManager;
-import android.content.pm.PackageInfo;
-
-public interface Mock {
-    PackageInfo getPackageInfo(String package, int flags);
-}
-
-public class GetSignaturesNotPackageManagerTest extends Activity {
-    public void passLintCheck(Mock mock) {
-        mock.getPackageInfo("some.pkg", PackageManager.GET_SIGNATURES);
-    }
-}
\ No newline at end of file
diff --git a/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/GetSignaturesSingleFlagTest.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/GetSignaturesSingleFlagTest.java.txt
deleted file mode 100644
index a975b83..0000000
--- a/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/GetSignaturesSingleFlagTest.java.txt
+++ /dev/null
@@ -1,11 +0,0 @@
-package test.pkg;
-
-import android.app.Activity;
-import android.content.pm.PackageManager;
-
-public class GetSignaturesSingleFlagTest extends Activity {
-    public void failLintCheck() {
-        getPackageManager()
-            .getPackageInfo("some.pkg", PackageManager.GET_SIGNATURES);
-    }
-}
\ No newline at end of file
diff --git a/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/GetSignaturesStaticFieldTest.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/GetSignaturesStaticFieldTest.java.txt
deleted file mode 100644
index 31e6d8d..0000000
--- a/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/GetSignaturesStaticFieldTest.java.txt
+++ /dev/null
@@ -1,11 +0,0 @@
-package test.pkg;
-
-import android.app.Activity;
-import android.content.pm.PackageManager;
-
-public class GetSignaturesStaticFieldTest extends Activity {
-    private static final int FLAGS = PackageManager.GET_SIGNATURES;
-    public void failLintCheck() {
-        getPackageManager().getPackageInfo("some.pkg", FLAGS);
-    }
-}
\ No newline at end of file
diff --git a/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/JavaPerformanceTest.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/JavaPerformanceTest.java.txt
index adb806c..0e494ce 100644
--- a/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/JavaPerformanceTest.java.txt
+++ b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/JavaPerformanceTest.java.txt
@@ -1,16 +1,19 @@
 package test.pkg;
 
-import java.util.HashMap;
-import java.util.Map;
-
 import android.annotation.SuppressLint;
 import android.content.Context;
 import android.graphics.Bitmap;
 import android.graphics.BitmapFactory;
 import android.graphics.Canvas;
+import android.graphics.LinearGradient;
 import android.graphics.Rect;
+import android.graphics.Shader.TileMode;
 import android.util.AttributeSet;
+import android.util.SparseArray;
 import android.widget.Button;
+import java.util.HashMap;
+import java.util.Map;
+
 /** Some test data for the JavaPerformanceDetector */
 @SuppressWarnings("unused")
 public class JavaPerformanceTest extends Button {
@@ -79,7 +82,7 @@
     }
 
     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec,
-            boolean x) { // wrong signature
+                             boolean x) { // wrong signature
         new String("not an error");
     }
 
@@ -88,7 +91,7 @@
     }
 
     protected void onLayout(boolean changed, int left, int top, int right,
-            int bottom, int wrong) { // wrong signature
+                            int bottom, int wrong) { // wrong signature
         new String("not an error");
     }
 
@@ -99,7 +102,7 @@
 
     @Override
     protected void onLayout(boolean changed, int left, int top, int right,
-            int bottom) {
+                            int bottom) {
         new String("flag me");
     }
 
@@ -157,35 +160,35 @@
     private boolean mAllowCrop;
     private Canvas mOverlayCanvas;
     private Bitmap mOverlay;
-
+private abstract class JavaPerformanceTest1 extends JavaPerformanceTest {
     @Override
     public void layout(int l, int t, int r, int b) {
         // Using "this." to reference fields
         if (this.shader == null)
             this.shader = new LinearGradient(0, 0, getWidth(), 0, GRADIENT_COLORS, null,
-                TileMode.REPEAT);
+                    TileMode.REPEAT);
     }
-
-    @Override
+} private abstract class JavaPerformanceTest2 extends JavaPerformanceTest {
+        @Override
     public void layout(int l, int t, int r, int b) {
         int width = getWidth();
         int height = getHeight();
 
         if ((shader == null) || (lastWidth != width) || (lastHeight != height))
         {
-           lastWidth = width;
-           lastHeight = height;
+            lastWidth = width;
+            lastHeight = height;
 
-           shader = new LinearGradient(0, 0, width, 0, GRADIENT_COLORS, null, TileMode.REPEAT);
+            shader = new LinearGradient(0, 0, width, 0, GRADIENT_COLORS, null, TileMode.REPEAT);
         }
     }
-
+} private abstract class JavaPerformanceTest3 extends JavaPerformanceTest {
     @Override
     public void layout(int l, int t, int r, int b) {
         if ((shader == null) || (lastWidth != getWidth()) || (lastHeight != getHeight())) {
         }
     }
-
+}
     public void inefficientSparseArray() {
         new SparseArray<Integer>(); // Use SparseIntArray instead
         new SparseArray<Long>();    // Use SparseLongArray instead
@@ -200,4 +203,21 @@
     public void byteSparseArray() { // bytes easily apply to ints
         Map<Byte, String> myByteMap = new HashMap<Byte, String>();
     }
-}
+
+    protected LinearGradient shader;
+    protected int lastWidth;
+    protected int lastHeight;
+    protected int[] GRADIENT_COLORS;
+
+    private static class foo {
+        private static class bar {
+            private static class Integer {
+                public Integer(int val) {
+                }
+            }
+        }
+    }
+    public JavaPerformanceTest() {
+        super(null);
+    }
+}
\ No newline at end of file
diff --git a/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/LayoutInflationTest.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/LayoutInflationTest.java.txt
deleted file mode 100644
index 00fa008..0000000
--- a/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/LayoutInflationTest.java.txt
+++ /dev/null
@@ -1,21 +0,0 @@
-package test.pkg;
-
-import android.content.Context;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.BaseAdapter;
-
-import java.util.ArrayList;
-
-public abstract class LayoutInflationTest extends BaseAdapter {
-    public View getView(int position, View convertView, ViewGroup parent) {
-        convertView = mInflater.inflate(R.layout.your_layout, null);
-        convertView = mInflater.inflate(R.layout.your_layout, null, true);
-        convertView = mInflater.inflate(R.layout.your_layout);
-        convertView = mInflater.inflate(R.layout.your_layout, parent);
-        convertView = WeirdInflater.inflate(convertView, null);
-
-        return convertView;
-    }
-}
diff --git a/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/LayoutInflationTest_ignored.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/LayoutInflationTest_ignored.java.txt
deleted file mode 100644
index 14fa452..0000000
--- a/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/LayoutInflationTest_ignored.java.txt
+++ /dev/null
@@ -1,30 +0,0 @@
-package test.pkg;
-
-import android.annotation.SuppressLint;
-import android.content.Context;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.BaseAdapter;
-
-import java.util.ArrayList;
-
-public abstract class LayoutInflationTest extends BaseAdapter {
-    public View getView(int position, View convertView, ViewGroup parent) {
-        //noinspection InflateParams
-        convertView = mInflater.inflate(R.layout.your_layout, null);
-        //noinspection AndroidLintInflateParams
-        convertView = mInflater.inflate(R.layout.your_layout, null);
-        return convertView;
-    }
-    @SuppressLint("InflateParams")
-    public View getView(int position, View convertView, ViewGroup parent) {
-        convertView = mInflater.inflate(R.layout.your_layout, null);
-        convertView = mInflater.inflate(R.layout.your_layout, null, true);
-        convertView = mInflater.inflate(R.layout.your_layout);
-        convertView = mInflater.inflate(R.layout.your_layout, parent);
-        convertView = WeirdInflater.inflate(convertView, null);
-
-        return convertView;
-    }
-}
diff --git a/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/Log.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/Log.java.txt
deleted file mode 100644
index 702ca1e..0000000
--- a/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/Log.java.txt
+++ /dev/null
@@ -1,80 +0,0 @@
-package android.util;
-
-// Stub class used for unit test type/method resolution only
-@SuppressWarnings({"UnusedDeclaration", "SpellCheckingInspection"})
-public final class Log {
-    public static final int VERBOSE = 2;
-    public static final int DEBUG = 3;
-    public static final int INFO = 4;
-    public static final int WARN = 5;
-    public static final int ERROR = 6;
-    public static final int ASSERT = 7;
-
-    private Log() {
-    }
-    public static int v(String tag, String msg) {
-        return -1;
-    }
-
-    public static int v(String tag, String msg, Throwable tr) {
-        return -1;
-    }
-
-    public static int d(String tag, String msg) {
-        return -1;
-    }
-
-    public static int d(String tag, String msg, Throwable tr) {
-        return -1;
-    }
-
-    public static int i(String tag, String msg) {
-        return -1;
-    }
-
-    public static int i(String tag, String msg, Throwable tr) {
-        return -1;
-    }
-
-    public static int w(String tag, String msg) {
-        return -1;
-    }
-
-    public static int w(String tag, String msg, Throwable tr) {
-        return -1;
-    }
-
-    public static native boolean isLoggable(String tag, int level);
-
-    public static int w(String tag, Throwable tr) {
-        return -1;
-    }
-
-    public static int e(String tag, String msg) {
-        return -1;
-    }
-
-    public static int e(String tag, String msg, Throwable tr) {
-        return -1;
-    }
-
-    public static int wtf(String tag, String msg) {
-        return -1;
-    }
-
-    public static int wtfStack(String tag, String msg) {
-        return -1;
-    }
-
-    public static int wtf(String tag, Throwable tr) {
-        return -1;
-    }
-
-    public static int wtf(String tag, String msg, Throwable tr) {
-        return -1;
-    }
-
-    public static int println(int priority, String tag, String msg) {
-        return -1;
-    }
-}
diff --git a/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/LogTest.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/LogTest.java.txt
deleted file mode 100644
index 957b0ab..0000000
--- a/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/LogTest.java.txt
+++ /dev/null
@@ -1,107 +0,0 @@
-package test.pkg;
-
-import android.annotation.SuppressLint;
-import android.util.Log;
-import static android.util.Log.DEBUG;
-
-@SuppressWarnings("UnusedDeclaration")
-public class LogTest {
-    private static final String TAG1 = "MyTag1";
-    private static final String TAG2 = "MyTag2";
-    private static final String TAG22 = "1234567890123456789012";
-    private static final String TAG23 = "12345678901234567890123";
-    private static final String TAG24 = "123456789012345678901234";
-    private static final String LONG_TAG = "MyReallyReallyReallyReallyReallyLongTag";
-
-    public void checkConditional(String m) {
-        Log.d(TAG1, "message"); // ok: unconditional, but not performing computation
-        Log.d(TAG1, m); // ok: unconditional, but not performing computation
-        Log.d(TAG1, "a" + "b"); // ok: unconditional, but not performing non-constant computation
-        Log.d(TAG1, Constants.MY_MESSAGE); // ok: unconditional, but constant string
-        Log.i(TAG1, "message" + m); // error: unconditional w/ computation
-        Log.i(TAG1, toString()); // error: unconditional w/ computation
-        Log.e(TAG1, toString()); // ok: only flagging debug/info messages
-        Log.w(TAG1, toString()); // ok: only flagging debug/info messages
-        Log.wtf(TAG1, toString()); // ok: only flagging debug/info messages
-        if (Log.isLoggable(TAG1, 0)) {
-            Log.d(TAG1, toString()); // ok: conditional
-        }
-    }
-
-    public void checkWrongTag(String tag) {
-        if (Log.isLoggable(TAG1, Log.DEBUG)) {
-            Log.d(TAG2, "message"); // warn: mismatched tags!
-        }
-        if (Log.isLoggable("my_tag", Log.DEBUG)) {
-            Log.d("other_tag", "message"); // warn: mismatched tags!
-        }
-        if (Log.isLoggable("my_tag", Log.DEBUG)) {
-            Log.d("my_tag", "message"); // ok: strings equal
-        }
-        if (Log.isLoggable(TAG1, Log.DEBUG)) {
-            Log.d(LogTest.TAG1, "message"); // OK: same tag; different access syntax
-        }
-        if (Log.isLoggable(tag, Log.DEBUG)) {
-            Log.d(tag, "message"); // ok: same variable
-        }
-    }
-
-    public void checkLongTag(boolean shouldLog) {
-        if (shouldLog) {
-            // String literal tags
-            Log.d("short_tag", "message"); // ok: short
-            Log.d("really_really_really_really_really_long_tag", "message"); // error: too long
-
-            // Resolved field tags
-            Log.d(TAG1, "message"); // ok: short
-            Log.d(TAG22, "message"); // ok: short
-            Log.d(TAG23, "message"); // ok: threshold
-            Log.d(TAG24, "message"); // error: too long
-            Log.d(LONG_TAG, "message"); // error: way too long
-
-            // Locally defined variable tags
-            final String LOCAL_TAG = "MyReallyReallyReallyReallyReallyLongTag";
-            Log.d(LOCAL_TAG, "message"); // error: too long
-
-            // Concatenated tags
-            Log.d(TAG22 + TAG1, "message"); // error: too long
-            Log.d(TAG22 + "MyTag", "message"); // error: too long
-        }
-    }
-
-    public void checkWrongLevel(String tag) {
-        if (Log.isLoggable(TAG1, Log.DEBUG)) {
-            Log.d(TAG1, "message"); // ok: right level
-        }
-        if (Log.isLoggable(TAG1, Log.INFO)) {
-            Log.i(TAG1, "message"); // ok: right level
-        }
-        if (Log.isLoggable(TAG1, Log.DEBUG)) {
-            Log.v(TAG1, "message"); // warn: wrong level
-        }
-        if (Log.isLoggable(TAG1, DEBUG)) { // static import of level
-            Log.v(TAG1, "message"); // warn: wrong level
-        }
-        if (Log.isLoggable(TAG1, Log.VERBOSE)) {
-            Log.d(TAG1, "message"); // warn? verbose is a lower logging level, which includes debug
-        }
-        if (Log.isLoggable(TAG1, Constants.MY_LEVEL)) {
-            Log.d(TAG1, "message"); // ok: unknown level alias
-        }
-    }
-
-    @SuppressLint("all")
-    public void suppressed1() {
-        Log.d(TAG1, "message"); // ok: suppressed
-    }
-
-    @SuppressLint("LogConditional")
-    public void suppressed2() {
-        Log.d(TAG1, "message"); // ok: suppressed
-    }
-
-    private static class Constants {
-        public static final String MY_MESSAGE = "My Message";
-        public static final int MY_LEVEL = 5;
-    }
-}
\ No newline at end of file
diff --git a/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/MyTracksProvider.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/MyTracksProvider.java.txt
deleted file mode 100644
index d69a7f6..0000000
--- a/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/MyTracksProvider.java.txt
+++ /dev/null
@@ -1,27 +0,0 @@
-package test.pkg;
-
-import android.provider.BaseColumns;
-
-public interface TracksColumns extends BaseColumns {
-
-    String TABLE_NAME = "tracks";
-
-    String NAME = "name";
-    String CATEGORY = "category";
-    String STARTTIME = "starttime";
-    String MAXGRADE = "maxgrade";
-    String MAPID = "mapid";
-    String TABLEID = "tableid";
-    String ICON = "icon";
-
-    String CREATE_TABLE = "CREATE TABLE " + TABLE_NAME + " ("
-            + _ID + " INTEGER PRIMARY KEY AUTOINCREMENT, "
-            + NAME + " STRING, "
-            + CATEGORY + " STRING, "
-            + STARTTIME + " INTEGER, "
-            + MAXGRADE + " FLOAT, "
-            + MAPID + " STRING, "
-            + TABLEID + " STRING, "
-            + ICON + " STRING"
-            + ");";
-}
diff --git a/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/OverrideConcreteTest.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/OverrideConcreteTest.java.txt
deleted file mode 100644
index 97f2b45..0000000
--- a/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/OverrideConcreteTest.java.txt
+++ /dev/null
@@ -1,69 +0,0 @@
-package test.pkg;
-
-import android.annotation.SuppressLint;
-import android.annotation.TargetApi;
-import android.os.Build;
-import android.service.notification.NotificationListenerService;
-import android.service.notification.StatusBarNotification;
-
-@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR2)
-public class OverrideConcreteTest {
-    // OK: This one specifies both methods
-    private static class MyNotificationListenerService1 extends NotificationListenerService {
-        @Override
-        public void onNotificationPosted(StatusBarNotification statusBarNotification) {
-        }
-
-        @Override
-        public void onNotificationRemoved(StatusBarNotification statusBarNotification) {
-        }
-    }
-
-    // Error: Misses onNotificationPosted
-    private static class MyNotificationListenerService2 extends NotificationListenerService {
-        @Override
-        public void onNotificationRemoved(StatusBarNotification statusBarNotification) {
-        }
-    }
-
-    // Error: Misses onNotificationRemoved
-    private static class MyNotificationListenerService3 extends NotificationListenerService {
-        @Override
-        public void onNotificationPosted(StatusBarNotification statusBarNotification) {
-        }
-    }
-
-    // Error: Missing both; wrong signatures (first has wrong arg count, second has wrong type)
-    private static class MyNotificationListenerService4 extends NotificationListenerService {
-        public void onNotificationPosted(StatusBarNotification statusBarNotification, int flags) {
-        }
-
-        public void onNotificationRemoved(int statusBarNotification) {
-        }
-    }
-
-    // OK: Inherits from a class which define both
-    private static class MyNotificationListenerService5 extends MyNotificationListenerService1 {
-    }
-
-    // OK: Inherits from a class which defines only one, but the other one is defined here
-    private static class MyNotificationListenerService6 extends MyNotificationListenerService3 {
-        @Override
-        public void onNotificationRemoved(StatusBarNotification statusBarNotification) {
-        }
-    }
-
-    // Error: Inheriting from a class which only defines one
-    private static class MyNotificationListenerService7 extends MyNotificationListenerService3 {
-    }
-
-    // OK: Has target api setting a local version that is high enough
-    @TargetApi(21)
-    private static class MyNotificationListenerService8 extends NotificationListenerService {
-    }
-
-    // OK: Suppressed
-    @SuppressLint("OverrideAbstract")
-    private static class MyNotificationListenerService9 extends MyNotificationListenerService1 {
-    }
-}
\ No newline at end of file
diff --git a/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/SQLiteDatabase.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/SQLiteDatabase.java.txt
deleted file mode 100644
index 0f3e92c..0000000
--- a/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/SQLiteDatabase.java.txt
+++ /dev/null
@@ -1,10 +0,0 @@
-package android.database.sqlite;
-
-import android.database.SQLException;
-
-// Lint unit testing stub
-public class SQLiteDatabase {
-    public void execSQL(String sql) throws SQLException {
-    }
-}
-
diff --git a/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/SQLiteTest.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/SQLiteTest.java.txt
deleted file mode 100644
index 8ac42a5..0000000
--- a/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/SQLiteTest.java.txt
+++ /dev/null
@@ -1,41 +0,0 @@
-package test.pkg;
-
-import android.database.sqlite.SQLiteDatabase;
-
-@SuppressWarnings({"unused", "SpellCheckingInspection"})
-public class SQLiteTest {
-    public interface Tables {
-        interface AppKeys {
-            String NAME = "appkeys";
-
-            interface Columns {
-                String _ID = "_id";
-                String PKG_NAME = "packageName";
-                String PKG_SIG = "signatureDigest";
-            }
-
-            String SCHEMA =
-                    Columns._ID + " INTEGER PRIMARY KEY AUTOINCREMENT," +
-                            Columns.PKG_NAME + " STRING NOT NULL," +
-                            Columns.PKG_SIG + " STRING NOT NULL";
-        }
-    }
-
-    public void test(SQLiteDatabase db, String name) {
-        db.execSQL("CREATE TABLE " + name + "(" + Tables.AppKeys.SCHEMA + ");"); // ERROR
-
-    }
-
-    public void onCreate(SQLiteDatabase db) {
-        db.execSQL(TracksColumns.CREATE_TABLE); // ERROR
-    }
-
-    private void doCreate(SQLiteDatabase db) {
-        // Not yet handled; we need to flow string concatenation across procedure calls
-        createTable(db, Tables.AppKeys.NAME, Tables.AppKeys.SCHEMA); // ERROR
-    }
-
-    private void createTable(SQLiteDatabase db, String tableName, String schema) {
-        db.execSQL("CREATE TABLE " + tableName + "(" + schema + ");");
-    }
-}
\ No newline at end of file
diff --git a/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/SharedPrefsTest.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/SharedPrefsTest.java.txt
index 5261e34..b5cea7e 100644
--- a/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/SharedPrefsTest.java.txt
+++ b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/SharedPrefsTest.java.txt
@@ -1,11 +1,11 @@
 package foo.bar;
-
 import android.app.Activity;
 import android.content.Context;
 import android.os.Bundle;
 import android.widget.Toast;
-
-public class SharedPrefsText {
+import android.content.SharedPreferences; import android.content.SharedPreferences.Editor;
+import android.preference.PreferenceManager;
+public class SharedPrefsTest extends Activity {
     // OK 1
     public void onCreate1(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
@@ -17,7 +17,7 @@
     }
 
     // OK 2
-    public void onCreate2(Bundle savedInstanceState) {
+    public void onCreate2(Bundle savedInstanceState, boolean apply) {
         super.onCreate(savedInstanceState);
         SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this);
         SharedPreferences.Editor editor = preferences.edit();
@@ -35,7 +35,7 @@
         SharedPreferences.Editor editor = preferences.edit();
         editor.putString("foo", "bar");
         editor.putInt("bar", 42);
-        return editor.apply();
+        editor.apply(); return true;
     }
 
     // Not a bug
@@ -43,8 +43,8 @@
         Bar bar1 = foo.edit();
         Bar bar2 = Foo.edit();
         Bar bar3 = edit();
-        SharedPreferences.Editor editor = preferences.edit(42);
-        apply();
+
+
     }
 
     // Bug
@@ -57,10 +57,22 @@
     }
 
     // Constructor test
-    public SharedPrefsText(Context context) {
+    public SharedPrefsTest(Context context) {
         SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context);
         SharedPreferences.Editor editor = preferences.edit();
         editor.putString("foo", "bar");
     }
+
+    private Bar edit() {
+        return null;
+    }
+
+    private static class Foo {
+        static Bar edit() { return null; }
+    }
+
+    private static class Bar {
+
+    }
  }
 
diff --git a/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/SharedPrefsTest4.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/SharedPrefsTest4.java.txt
index 28de959..2f91300 100644
--- a/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/SharedPrefsTest4.java.txt
+++ b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/SharedPrefsTest4.java.txt
@@ -3,7 +3,7 @@
 import android.annotation.SuppressLint;
 import android.app.Activity;
 import android.content.SharedPreferences;
-import foo.bar.Editor;
+import android.content.SharedPreferences.Editor;
 import android.os.Bundle;
 import android.preference.PreferenceManager;
 
diff --git a/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/SharedPrefsTest5.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/SharedPrefsTest5.java.txt
index f005162..2e200b6 100644
--- a/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/SharedPrefsTest5.java.txt
+++ b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/SharedPrefsTest5.java.txt
@@ -43,7 +43,7 @@
         preferences.edit().putString(PREF_FOO, "bar").commit();
     }
 
-    private final SharedPreferences mPrefs;
+    private final SharedPreferences mPrefs = null;
 
     public void ok3() {
         final SharedPreferences.Editor editor = mPrefs.edit().putBoolean(
diff --git a/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/StringFormatActivity.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/StringFormatActivity.java.txt
index 1f387f2..79bf55d 100644
--- a/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/StringFormatActivity.java.txt
+++ b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/StringFormatActivity.java.txt
@@ -33,11 +33,22 @@
         String output1 = String.format(hello, target);
     }
 
+    public void testPrimitiveWrappers() {
+        Boolean won = true;
+        Integer value = 1;
+        String score = getResources().getString(R.string.score);
+        String score2 = getResources().getString(R.string.score2);
+        String output1  = String.format(score, won);   // wrong
+        String output2  = String.format(score, value);   // ok
+        String output3  = String.format(score2, won);   // ok
+    }
+
     private static class R {
         private static class string {
             public static final int hello = 1;
             public static final int hello2 = 2;
             public static final int score = 3;
+            public static final int score2 = 5;
         }
         private static class layout {
             public static final int main = 4;
diff --git a/lint/libs/lint-tests/src/test/java/com/android/tools/lint/detector/api/ConstantEvaluatorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/detector/api/ConstantEvaluatorTest.java
index 4ef36f8..a301818 100644
--- a/lint/libs/lint-tests/src/test/java/com/android/tools/lint/detector/api/ConstantEvaluatorTest.java
+++ b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/detector/api/ConstantEvaluatorTest.java
@@ -16,11 +16,17 @@
 
 package com.android.tools.lint.detector.api;
 
+import com.intellij.psi.JavaRecursiveElementVisitor;
+import com.intellij.psi.PsiExpression;
+import com.intellij.psi.PsiJavaFile;
+import com.intellij.psi.PsiLocalVariable;
+
 import junit.framework.TestCase;
 
 import org.intellij.lang.annotations.Language;
 
 import java.io.File;
+import java.util.Arrays;
 import java.util.concurrent.atomic.AtomicReference;
 
 import lombok.ast.CompilationUnit;
@@ -30,8 +36,66 @@
 
 @SuppressWarnings("ClassNameDiffersFromFileName")
 public class ConstantEvaluatorTest extends TestCase {
-    private static void check(Object expected, @Language("JAVA") String source,
+    private static void checkPsi(Object expected, @Language("JAVA") String source,
             final String targetVariable) {
+        JavaContext context = LintUtilsTest.parsePsi(source, new File("src/test/pkg/Test.java"));
+        assertNotNull(context);
+        PsiJavaFile javaFile = context.getJavaFile();
+        assertNotNull(javaFile);
+
+        // Find the expression
+        final AtomicReference<PsiExpression> reference = new AtomicReference<PsiExpression>();
+        javaFile.accept(new JavaRecursiveElementVisitor() {
+            @Override
+            public void visitLocalVariable(PsiLocalVariable variable) {
+                super.visitLocalVariable(variable);
+                String name = variable.getName();
+                if (name != null && name.equals(targetVariable)) {
+                    reference.set(variable.getInitializer());
+                }
+            }
+        });
+        PsiExpression expression = reference.get();
+        Object actual = ConstantEvaluator.evaluate(context, expression);
+        if (expected == null) {
+            assertNull(actual);
+        } else {
+            assertNotNull("Couldn't compute value for " + source + ", expected " + expected,
+                    actual);
+            assertEquals(expected.getClass(), actual.getClass());
+            if (expected instanceof Object[] && actual instanceof Object[]) {
+                assertEquals(Arrays.toString((Object[]) expected),
+                        Arrays.toString((Object[]) actual));
+                assertTrue(Arrays.equals((Object[]) expected, (Object[]) actual));
+            } else if (expected instanceof int[] && actual instanceof int[]) {
+                assertEquals(Arrays.toString((int[]) expected),
+                        Arrays.toString((int[]) actual));
+            } else if (expected instanceof boolean[] && actual instanceof boolean[]) {
+                assertEquals(Arrays.toString((boolean[]) expected),
+                        Arrays.toString((boolean[]) actual));
+            } else if (expected instanceof byte[] && actual instanceof byte[]) {
+                assertEquals(Arrays.toString((byte[]) expected),
+                        Arrays.toString((byte[]) actual));
+            } else {
+                assertEquals(expected.toString(), actual.toString());
+                assertEquals(expected, actual);
+            }
+        }
+        if (expected instanceof String) {
+            assertEquals(expected, ConstantEvaluator.evaluateString(context, expression,
+                    false));
+        }
+    }
+
+    private void check(Object expected, @Language("JAVA") String source,
+            final String targetVariable) {
+        checkPsi(expected, source, targetVariable);
+
+        if (getName().equals("testArrays")) {
+            // Not correctly implemented in old Lombok based lookup
+            return;
+        }
+
         JavaContext context = LintUtilsTest.parse(source, new File("src/test/pkg/Test.java"));
         assertNotNull(context);
         CompilationUnit unit = (CompilationUnit) context.getCompilationUnit();
@@ -65,7 +129,7 @@
         }
     }
 
-    private static void checkStatements(Object expected, String statementsSource,
+    private void checkStatements(Object expected, String statementsSource,
             final String targetVariable) {
         @Language("JAVA")
         String source = ""
@@ -82,7 +146,7 @@
         check(expected, source, targetVariable);
     }
 
-    private static void checkExpression(Object expected, String expressionSource) {
+    private void checkExpression(Object expected, String expressionSource) {
         @Language("JAVA")
         String source = ""
                 + "package test.pkg;\n"
@@ -104,6 +168,12 @@
         checkExpression("abcd", "\"ab\" + \"cd\"");
     }
 
+    public void testArrays() throws Exception {
+        checkExpression(new int[] {1, 2, 3}, "new int[] { 1,2,3] }");
+        checkExpression(new int[0], "new int[0]");
+        checkExpression(new byte[0], "new byte[0]");
+    }
+
     public void testBooleans() throws Exception {
         checkExpression(true, "true");
         checkExpression(false, "false");
diff --git a/lint/libs/lint-tests/src/test/java/com/android/tools/lint/detector/api/ResourceEvaluatorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/detector/api/ResourceEvaluatorTest.java
new file mode 100644
index 0000000..b676c3a
--- /dev/null
+++ b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/detector/api/ResourceEvaluatorTest.java
@@ -0,0 +1,197 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * 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.android.tools.lint.detector.api;
+
+import com.android.ide.common.resources.ResourceUrl;
+import com.android.resources.ResourceType;
+import com.intellij.psi.JavaRecursiveElementVisitor;
+import com.intellij.psi.PsiExpression;
+import com.intellij.psi.PsiJavaFile;
+import com.intellij.psi.PsiLocalVariable;
+
+import junit.framework.TestCase;
+
+import org.intellij.lang.annotations.Language;
+
+import java.io.File;
+import java.util.EnumSet;
+import java.util.concurrent.atomic.AtomicReference;
+
+@SuppressWarnings("ClassNameDiffersFromFileName")
+public class ResourceEvaluatorTest extends TestCase {
+    private static void check(String expected, String statementsSource,
+            final String targetVariable, boolean getSpecificType, boolean allowDereference) {
+        @Language("JAVA")
+        String source = ""
+                + "package test.pkg;\n"
+                + "public class Test {\n"
+                + "    public void test() {\n"
+                + "        " + statementsSource + "\n"
+                + "    }\n"
+                + "    public static final int RED = R.color.red;\n"
+                + "    public static final int MY_COLOR = RED;\n"
+                + "    public void someMethod(@android.support.annotation.DrawableRes @android.support.annotation.StringRes int param) { }\n"
+                + "\n"
+                + "    private static class R {\n"
+                + "        private static class color {\n"
+                + "            public static final int foo=0x7f050000;\n"
+                + "        }\n"
+                + "        private static class color {\n"
+                + "            public static final int red=0x7f060000;\n"
+                + "            public static final int green=0x7f060001;\n"
+                + "            public static final int blue=0x7f060002;\n"
+                + "        }\n"
+                + "    }"
+                + "}\n";
+
+        JavaContext context = LintUtilsTest.parsePsi(source, new File("src/test/pkg/Test.java"));
+        assertNotNull(context);
+        PsiJavaFile javaFile = context.getJavaFile();
+        assertNotNull(javaFile);
+
+        // Find the expression
+        final AtomicReference<PsiExpression> reference = new AtomicReference<PsiExpression>();
+        javaFile.accept(new JavaRecursiveElementVisitor() {
+            @Override
+            public void visitLocalVariable(PsiLocalVariable variable) {
+                super.visitLocalVariable(variable);
+                String name = variable.getName();
+                if (name != null && name.equals(targetVariable)) {
+                    reference.set(variable.getInitializer());
+                }
+            }
+        });
+        PsiExpression expression = reference.get();
+        ResourceEvaluator evaluator = new ResourceEvaluator(context.getEvaluator())
+                .allowDereference(allowDereference);
+
+        if (getSpecificType) {
+            ResourceUrl actual = evaluator.getResource(expression);
+            if (expected == null) {
+                assertNull(actual);
+            } else {
+                assertNotNull("Couldn't compute resource for " + source + ", expected " + expected,
+                        actual);
+
+                assertEquals(expected, actual.toString());
+            }
+        } else {
+            EnumSet<ResourceType> types = evaluator.getResourceTypes(expression);
+            if (expected == null) {
+                assertNull(types);
+            } else {
+                assertNotNull("Couldn't compute resource types for " + source
+                        + ", expected " + expected, types);
+
+                assertEquals(expected, types.toString());
+            }
+        }
+    }
+
+    private static void checkType(String expected, String statementsSource,
+            final String targetVariable) {
+        check(expected, statementsSource, targetVariable, true, true);
+    }
+
+    private static void checkTypes(String expected, String statementsSource,
+            final String targetVariable) {
+        check(expected, statementsSource, targetVariable, false, true);
+    }
+
+    private static void checkTypes(String expected, String statementsSource,
+            final String targetVariable, boolean allowDereference) {
+        check(expected, statementsSource, targetVariable, false, allowDereference);
+    }
+
+    public void test() throws Exception {
+        checkType("@string/foo", "int x = R.string.foo", "x");
+    }
+
+    public void testIndirectFieldReference() throws Exception {
+        checkType("@color/red", ""
+                + "int z = RED;\n"
+                + "int w = true ? z : 0",
+                "w");
+    }
+
+    public void testMethodCall() throws Exception {
+        checkType("@color/green", ""
+                        + "android.app.Activity context = null;"
+                        + "int w = context.getResources().getColor(R.color.green);",
+                "w");
+    }
+
+    public void testMethodCallNoDereference() throws Exception {
+        check(null, ""
+                        + "android.app.Activity context = null;"
+                        + "int w = context.getResources().getColor(R.color.green);",
+                "w", true, false);
+    }
+
+    public void testReassignment() throws Exception {
+        checkType("@string/foo", ""
+                        + "int x = R.string.foo;\n"
+                        + "int y = x;\n"
+                        + "int w;\n"
+                        + "w = -1;\n"
+                        + "int z = y;\n",
+                "z");
+    }
+
+    // Resource Types
+
+    public void testReassignmentType() throws Exception {
+        checkTypes("[string]", ""
+                        + "int x = R.string.foo;\n"
+                        + "int y = x;\n"
+                        + "int w;\n"
+                        + "w = -1;\n"
+                        + "int z = y;\n",
+                "z");
+    }
+
+    public void testMethodCallTypes() throws Exception {
+        // public=color int marker
+        checkTypes("[public]", ""
+                        + "android.app.Activity context = null;"
+                        + "int w = context.getResources().getColor(R.color.green);",
+                "w");
+    }
+
+    public void testMethodCallTypesNoDereference() throws Exception {
+        checkTypes(null, ""
+                        + "android.app.Activity context = null;"
+                        + "int w = context.getResources().getColor(R.color.green);",
+                "w", false);
+    }
+
+    public void testConditionalTypes() throws Exception {
+        // Constant expression: we know exactly which branch to take
+        checkTypes("[color]", ""
+                        + "int z = RED;\n"
+                        + "int w = true ? z : R.string.foo",
+                "w");
+    }
+
+    public void testConditionalTypesUnknownCondition() throws Exception {
+        // Constant expression: we know exactly which branch to take
+        checkTypes("[color, string]", ""
+                        + "int z = RED;\n"
+                        + "int w = toString().indexOf('x') > 2 ? z : R.string.foo",
+                "w");
+    }
+}
\ No newline at end of file
diff --git a/lint/libs/lint-tests/src/test/java/com/android/tools/lint/detector/api/TypeEvaluatorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/detector/api/TypeEvaluatorTest.java
index 0bdcb88..5dba233 100644
--- a/lint/libs/lint-tests/src/test/java/com/android/tools/lint/detector/api/TypeEvaluatorTest.java
+++ b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/detector/api/TypeEvaluatorTest.java
@@ -17,6 +17,11 @@
 package com.android.tools.lint.detector.api;
 
 import com.android.tools.lint.client.api.JavaParser.TypeDescriptor;
+import com.intellij.psi.JavaRecursiveElementVisitor;
+import com.intellij.psi.PsiExpression;
+import com.intellij.psi.PsiJavaFile;
+import com.intellij.psi.PsiLocalVariable;
+import com.intellij.psi.PsiType;
 
 import junit.framework.TestCase;
 
@@ -32,8 +37,49 @@
 
 @SuppressWarnings("ClassNameDiffersFromFileName")
 public class TypeEvaluatorTest extends TestCase {
+    private static void checkPsi(Object expected, @Language("JAVA") String source,
+            final String targetVariable) {
+        JavaContext context = LintUtilsTest.parsePsi(source, new File("src/test/pkg/Test.java"));
+        assertNotNull(context);
+        PsiJavaFile javaFile = context.getJavaFile();
+        assertNotNull(javaFile);
+
+        // Find the expression
+        final AtomicReference<PsiExpression> reference = new AtomicReference<PsiExpression>();
+        javaFile.accept(new JavaRecursiveElementVisitor() {
+            @Override
+            public void visitLocalVariable(PsiLocalVariable variable) {
+                super.visitLocalVariable(variable);
+                String name = variable.getName();
+                if (name != null && name.equals(targetVariable)) {
+                    reference.set(variable.getInitializer());
+                }
+            }
+        });
+        PsiExpression expression = reference.get();
+        PsiType actual = TypeEvaluator.evaluate(context, expression);
+        if (expected == null) {
+            assertNull(actual);
+        } else {
+            assertNotNull("Couldn't compute type for " + source + ", expected " + expected,
+                    actual);
+
+            if (expected instanceof PsiType) {
+                assertEquals(expected, actual);
+            } else {
+                String expectedString = expected.toString();
+                if (expectedString.startsWith("class ")) {
+                    expectedString = expectedString.substring("class ".length());
+                }
+                assertEquals(expectedString, actual.getCanonicalText());
+            }
+        }
+    }
+
     private static void check(Object expected, @Language("JAVA") String source,
             final String targetVariable) {
+        checkPsi(expected, source, targetVariable);
+
         JavaContext context = LintUtilsTest.parse(source, new File("src/test/pkg/Test.java"));
         assertNotNull(context);
         CompilationUnit unit = (CompilationUnit) context.getCompilationUnit();
@@ -52,6 +98,13 @@
         });
         Expression expression = reference.get();
         TypeDescriptor actual = TypeEvaluator.evaluate(context, expression);
+
+        if (expected == PsiType.NULL) {
+            // TypeDescriptor doesn't have a null type; this test is
+            // intended for the new PSI type evaluator
+            expected = null;
+        }
+
         if (expected == null) {
             assertNull(actual);
         } else {
@@ -101,7 +154,7 @@
     }
 
     public void testNull() throws Exception {
-        checkExpression(null, "null");
+        checkExpression(PsiType.NULL, "null");
     }
 
     public void testStrings() throws Exception {
@@ -142,11 +195,13 @@
         checkExpression(Integer.TYPE, "5 & 1");
         checkExpression(Integer.TYPE, "~5");
         checkExpression(Long.TYPE, "~(long)5");
-        checkExpression(Short.TYPE, "~(short)5");
-        checkExpression(Byte.TYPE, "~(byte)5");
+        // Psi and Lombok differ; PSI knows expression promotion
+        // and will treat bytes and shorts etc as promoted to int
+        //checkExpression(Integer.TYPE, "~(short)5");
+        //checkExpression(Byte.TYPE, "~(byte)5");
+        //checkExpression(Short.TYPE, "-(short)5");
+        //checkExpression(Byte.TYPE, "-(byte)5");
         checkExpression(Long.TYPE, "-(long)5");
-        checkExpression(Short.TYPE, "-(short)5");
-        checkExpression(Byte.TYPE, "-(byte)5");
         checkExpression(Double.TYPE, "-(double)5");
         checkExpression(Float.TYPE, "-(float)5");
         checkExpression(Integer.TYPE, "1 + -3");