diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/BuiltinIssueRegistry.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/BuiltinIssueRegistry.java
index 3d6e16e9cc0..518229c1867 100644
--- a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/BuiltinIssueRegistry.java
+++ b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/BuiltinIssueRegistry.java
@@ -42,7 +42,6 @@ public class BuiltinIssueRegistry extends IssueRegistry {
issues.add(ApiDetector.INLINED);
issues.add(ApiDetector.OVERRIDE);
issues.add(ApiDetector.UNSUPPORTED);
- issues.add(ApiDetector.UNUSED);
issues.add(AppCompatCallDetector.ISSUE);
issues.add(AppCompatResourceDetector.ISSUE);
issues.add(AssertDetector.ISSUE);
diff --git a/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/AndroidLintInspectionToolProvider.java b/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/AndroidLintInspectionToolProvider.java
index 069203c0135..6ec61151641 100644
--- a/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/AndroidLintInspectionToolProvider.java
+++ b/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/AndroidLintInspectionToolProvider.java
@@ -121,12 +121,6 @@ public class AndroidLintInspectionToolProvider {
}
}
- public static class AndroidKLintUnusedAttributeInspection extends AndroidLintInspectionBase {
- public AndroidKLintUnusedAttributeInspection() {
- super(AndroidBundle.message("android.lint.inspections.unused.attribute"), ApiDetector.UNUSED);
- }
- }
-
public static class AndroidKLintUnusedIdsInspection extends AndroidLintInspectionBase {
public AndroidKLintUnusedIdsInspection() {
super(AndroidBundle.message("android.lint.inspections.unused.ids"), UnusedResourceDetector.ISSUE_IDS);
diff --git a/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/IntellijApiDetector.java b/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/IntellijApiDetector.java
deleted file mode 100755
index 5dec5140022..00000000000
--- a/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/IntellijApiDetector.java
+++ /dev/null
@@ -1,817 +0,0 @@
-/*
- * Copyright (C) 2013 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 org.jetbrains.android.inspections.klint;
-
-import com.android.annotations.NonNull;
-import com.android.annotations.Nullable;
-import com.android.sdklib.SdkVersionInfo;
-import com.android.tools.klint.checks.ApiDetector;
-import com.android.tools.klint.checks.ApiLookup;
-import com.intellij.openapi.application.ApplicationManager;
-import com.intellij.psi.*;
-import com.intellij.psi.impl.source.PsiClassReferenceType;
-import com.intellij.psi.tree.IElementType;
-import com.intellij.psi.util.MethodSignatureUtil;
-import com.intellij.psi.util.PsiTreeUtil;
-import org.jetbrains.annotations.NonNls;
-import org.jetbrains.annotations.NotNull;
-import org.jetbrains.uast.UFile;
-import org.jetbrains.uast.check.UastAndroidContext;
-import org.jetbrains.uast.visitor.UastVisitor;
-
-import java.util.EnumSet;
-
-import static org.jetbrains.android.inspections.klint.IntellijLintUtils.SUPPRESS_LINT_FQCN;
-import static org.jetbrains.android.inspections.klint.IntellijLintUtils.SUPPRESS_WARNINGS_FQCN;
-
-/**
- * Intellij-specific version of the {@link ApiDetector} which uses the PSI structure
- * to check accesses
- *
- * TODO:
- *
- * - Compare to the bytecode based results
- *
- */
-public class IntellijApiDetector extends ApiDetector {
- @SuppressWarnings("unchecked")
- static final com.android.tools.klint.detector.api.Implementation IMPLEMENTATION = new com.android.tools.klint.detector.api.Implementation(
- IntellijApiDetector.class,
- EnumSet.of(com.android.tools.klint.detector.api.Scope.RESOURCE_FILE, com.android.tools.klint.detector.api.Scope.MANIFEST, com.android.tools.klint.detector.api.Scope.SOURCE_FILE),
- com.android.tools.klint.detector.api.Scope.MANIFEST_SCOPE,
- com.android.tools.klint.detector.api.Scope.RESOURCE_FILE_SCOPE,
- com.android.tools.klint.detector.api.Scope.JAVA_FILE_SCOPE
- );
-
- @NonNls
- private static final String TARGET_API_FQCN = "android.annotation.TargetApi";
- private static final String SDK_INT = "SDK_INT";
-
- @Override
- public UastVisitor createUastVisitor(final UastAndroidContext context) {
- return new UastVisitor() {
- @Override
- public boolean visitFile(@NotNull UFile node) {
- check(context);
- return true;
- }
- };
- }
-
- private void check(final UastAndroidContext uastContext) {
- if (mApiDatabase == null) {
- return;
- }
-
- final com.android.tools.klint.detector.api.JavaContext context = uastContext.getLintContext();
-
- ApplicationManager.getApplication().runReadAction(new Runnable() {
- @Override
- public void run() {
- final PsiFile psiFile = IntellijLintUtils.getPsiFile(context);
- if (!(psiFile instanceof PsiJavaFile)) {
- return;
- }
- PsiJavaFile javaFile = (PsiJavaFile)psiFile;
- for (PsiClass clz : javaFile.getClasses()) {
- PsiElementVisitor visitor = new ApiCheckVisitor(context, clz, psiFile);
-
- javaFile.accept(visitor);
- }
- }
- });
- }
-
- private static int getTargetApi(@NonNull PsiElement e, @NonNull PsiElement file) {
- PsiElement element = e;
- // Search upwards for target api annotations
- while (element != null && element != file) { // otherwise it will keep going into directories!
- if (element instanceof PsiModifierListOwner) {
- PsiModifierListOwner owner = (PsiModifierListOwner)element;
- PsiModifierList modifierList = owner.getModifierList();
- PsiAnnotation annotation = null;
- if (modifierList != null) {
- annotation = modifierList.findAnnotation(TARGET_API_FQCN);
- }
- if (annotation != null) {
- for (PsiNameValuePair pair : annotation.getParameterList().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);
- }
- } 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 fqcn = ((PsiReferenceExpression)v).getQualifiedName();
- return codeNameToApi(fqcn);
- } else {
- return codeNameToApi(v.getText());
- }
- }
- }
- }
- }
- element = element.getParent();
- }
-
- return -1;
- }
-
- private static int codeNameToApi(String text) {
- int dotIndex = text.lastIndexOf('.');
- if (dotIndex != -1) {
- text = text.substring(dotIndex + 1);
- }
-
- return SdkVersionInfo.getApiByBuildCode(text, true);
- }
-
- private class ApiCheckVisitor extends JavaRecursiveElementVisitor {
- private final com.android.tools.klint.detector.api.Context myContext;
- private boolean mySeenSuppress;
- private boolean mySeenTargetApi;
- private final PsiClass myClass;
- private final PsiFile myFile;
- private final boolean myCheckAccess;
- private boolean myCheckOverride;
- private String myFrameworkParent;
-
- public ApiCheckVisitor(com.android.tools.klint.detector.api.Context context, PsiClass clz, PsiFile file) {
- myContext = context;
- myClass = clz;
- myFile = file;
-
- myCheckAccess = context.isEnabled(UNSUPPORTED) || context.isEnabled(INLINED);
- myCheckOverride = context.isEnabled(OVERRIDE)
- && context.getMainProject().getBuildSdk() >= 1;
- if (myCheckOverride) {
- myFrameworkParent = null;
- PsiClass superClass = myClass.getSuperClass();
- while (superClass != null) {
- String fqcn = superClass.getQualifiedName();
- if (fqcn == null) {
- myCheckOverride = false;
- } else if (fqcn.startsWith("android.") //$NON-NLS-1$
- || fqcn.startsWith("java.") //$NON-NLS-1$
- || fqcn.startsWith("javax.")) { //$NON-NLS-1$
- if (!fqcn.equals(CommonClassNames.JAVA_LANG_OBJECT)) {
- myFrameworkParent = com.android.tools.klint.detector.api.ClassContext.getInternalName(fqcn);
- }
- break;
- }
- superClass = superClass.getSuperClass();
- }
- if (myFrameworkParent == null) {
- myCheckOverride = false;
- }
- }
- }
-
- @Override
- public void visitAnnotation(PsiAnnotation annotation) {
- super.visitAnnotation(annotation);
-
- String fqcn = annotation.getQualifiedName();
- if (TARGET_API_FQCN.equals(fqcn)) {
- mySeenTargetApi = true;
- }
- else if (SUPPRESS_LINT_FQCN.equals(fqcn) || SUPPRESS_WARNINGS_FQCN.equals(fqcn)) {
- mySeenSuppress = true;
- }
- }
-
- @Override
- public void visitMethod(PsiMethod method) {
- super.visitMethod(method);
-
- if (!myCheckOverride) {
- return;
- }
-
- int buildSdk = myContext.getMainProject().getBuildSdk();
- String name = method.getName();
- assert myFrameworkParent != null;
- String desc = IntellijLintUtils.getInternalDescription(method, false, false);
- if (desc == null) {
- // Couldn't compute description of method for some reason; probably
- // failure to resolve parameter types
- return;
- }
- int api = mApiDatabase.getCallVersion(myFrameworkParent, name, desc);
- if (api > buildSdk && buildSdk != -1) {
- if (mySeenSuppress &&
- IntellijLintUtils.isSuppressed(method, myFile, OVERRIDE)) {
- return;
- }
-
- // TODO: Don't complain if it's annotated with @Override; that means
- // somehow the build target isn't correct.
-
- String fqcn;
- PsiClass containingClass = method.getContainingClass();
- if (containingClass != null) {
- String className = containingClass.getName();
- String fullClassName = containingClass.getQualifiedName();
- if (fullClassName != null) {
- className = fullClassName;
- }
- fqcn = className + '#' + name;
- } else {
- fqcn = name;
- }
-
- String message = String.format(
- "This method is not overriding anything with the current build " +
- "target, but will in API level %1$d (current target is %2$d): %3$s",
- api, buildSdk, fqcn);
-
- PsiElement locationNode = method.getNameIdentifier();
- if (locationNode == null) {
- locationNode = method;
- }
- com.android.tools.klint.detector.api.Location location = IntellijLintUtils.getLocation(myContext.file, locationNode);
- myContext.report(OVERRIDE, location, message);
- }
- }
-
- @Override
- public void visitClass(PsiClass aClass) {
- super.visitClass(aClass);
-
- if (!myCheckAccess) {
- return;
- }
-
- for (PsiClassType type : aClass.getSuperTypes()) {
- String signature = IntellijLintUtils.getInternalName(type);
- if (signature == null) {
- continue;
- }
-
- int api = mApiDatabase.getClassVersion(signature);
- if (api == -1) {
- continue;
- }
- int minSdk = getMinSdk(myContext);
- if (api <= minSdk) {
- continue;
- }
- if (mySeenTargetApi) {
- int target = getTargetApi(aClass, myFile);
- if (target != -1) {
- if (api <= target) {
- continue;
- }
- }
- }
- if (mySeenSuppress && IntellijLintUtils.isSuppressed(aClass, myFile, UNSUPPORTED)) {
- continue;
- }
-
- com.android.tools.klint.detector.api.Location location;
- if (type instanceof PsiClassReferenceType) {
- PsiReference reference = ((PsiClassReferenceType)type).getReference();
- PsiElement element = reference.getElement();
- if (isWithinVersionCheckConditional(element, api)) {
- continue;
- }
- location = IntellijLintUtils.getLocation(myContext.file, element);
- } else {
- location = IntellijLintUtils.getLocation(myContext.file, aClass);
- }
- String fqcn = type.getClassName();
- String message = String.format("Class requires API level %1$d (current min is %2$d): %3$s", api, minSdk, fqcn);
- myContext.report(UNSUPPORTED, location, message);
- }
- }
-
- @Override
- public void visitReferenceExpression(PsiReferenceExpression expression) {
- super.visitReferenceExpression(expression);
-
- if (!myCheckAccess) {
- return;
- }
-
- PsiReference reference = expression.getReference();
- if (reference == null) {
- return;
- }
- PsiElement resolved = reference.resolve();
- if (resolved != null) {
- if (resolved instanceof PsiField) {
- PsiField field = (PsiField)resolved;
- PsiClass containingClass = field.getContainingClass();
- if (containingClass == null) {
- return;
- }
- String owner = IntellijLintUtils.getInternalName(containingClass);
- if (owner == null) {
- return; // Couldn't resolve type
- }
- String name = field.getName();
-
- int api = mApiDatabase.getFieldVersion(owner, name);
- if (api == -1) {
- return;
- }
- int minSdk = getMinSdk(myContext);
- if (isSuppressed(api, expression, minSdk)) {
- return;
- }
-
- com.android.tools.klint.detector.api.Location location = IntellijLintUtils.getLocation(myContext.file, expression);
- String fqcn = containingClass.getQualifiedName();
- String message = String.format(
- "Field requires API level %1$d (current min is %2$d): %3$s",
- api, minSdk, fqcn + '#' + name);
-
- com.android.tools.klint.detector.api.Issue issue = UNSUPPORTED;
- // When accessing primitive types or Strings, the values get copied into
- // the class files (e.g. get inlined) which has a separate issue type:
- // INLINED.
- PsiType type = field.getType();
- if (PsiType.INT.equals(type) || PsiType.CHAR.equals(type) || PsiType.BOOLEAN.equals(type)
- || PsiType.DOUBLE.equals(type) || PsiType.FLOAT.equals(type) || PsiType.BYTE.equals(type)
- || type.equalsToText(CommonClassNames.JAVA_LANG_STRING)) {
- issue = INLINED;
-
- // Some usages of inlined constants are okay:
- if (isBenignConstantUsage(expression, name, owner)) {
- return;
- }
- }
-
- myContext.report(issue, location, message);
- }
- }
- }
-
- @Override
- public void visitTryStatement(PsiTryStatement statement) {
- super.visitTryStatement(statement);
-
- PsiResourceList resourceList = statement.getResourceList();
- if (resourceList != null) {
- int api = 19; // minSdk for try with resources
- int minSdk = getMinSdk(myContext);
-
- if (isSuppressed(api, statement, minSdk)) {
- return;
- }
- com.android.tools.klint.detector.api.Location location = IntellijLintUtils.getLocation(myContext.file, resourceList);
- String message = String.format("Try-with-resources requires API level %1$d (current min is %2$d)", api, minSdk);
- myContext.report(UNSUPPORTED, location, message);
- }
-
- for (PsiParameter parameter : statement.getCatchBlockParameters()) {
- PsiTypeElement typeElement = parameter.getTypeElement();
- if (typeElement != null) {
- PsiType type = typeElement.getType();
- PsiClass resolved = null;
- PsiElement reference = parameter;
- if (type instanceof PsiDisjunctionType) {
- type = ((PsiDisjunctionType)type).getLeastUpperBound();
- if (type instanceof PsiClassType) {
- resolved = ((PsiClassType)type).resolve();
- }
- } else if (type instanceof PsiClassReferenceType) {
- PsiClassReferenceType referenceType = (PsiClassReferenceType)type;
- resolved = referenceType.resolve();
- reference = referenceType.getReference().getElement();
- } else if (type instanceof PsiClassType) {
- resolved = ((PsiClassType)type).resolve();
- }
- if (resolved != null) {
- String signature = IntellijLintUtils.getInternalName(resolved);
- if (signature == null) {
- continue;
- }
-
- int api = mApiDatabase.getClassVersion(signature);
- if (api == -1) {
- continue;
- }
- int minSdk = getMinSdk(myContext);
- if (api <= minSdk) {
- continue;
- }
- if (mySeenTargetApi) {
- int target = getTargetApi(statement, myFile);
- if (target != -1) {
- if (api <= target) {
- continue;
- }
- }
- }
- if (mySeenSuppress && IntellijLintUtils.isSuppressed(statement, myFile, UNSUPPORTED)) {
- continue;
- }
-
- com.android.tools.klint.detector.api.Location location;
- location = IntellijLintUtils.getLocation(myContext.file, reference);
- 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 && fqcn.equals("ReflectiveOperationException")) {
- 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);
- }
- myContext.report(UNSUPPORTED, location, message);
- }
- }
- }
- }
-
- private boolean isSuppressed(int api, PsiElement element, int minSdk) {
- if (api <= minSdk) {
- return true;
- }
- if (mySeenTargetApi) {
- int target = getTargetApi(element, myFile);
- if (target != -1) {
- if (api <= target) {
- return true;
- }
- }
- }
- if (mySeenSuppress &&
- (IntellijLintUtils.isSuppressed(element, myFile, UNSUPPORTED) || IntellijLintUtils.isSuppressed(element, myFile, INLINED))) {
- return true;
- }
-
- if (isWithinVersionCheckConditional(element, api)) {
- return true;
- }
-
- return false;
- }
-
- public boolean isBenignConstantUsage(
- @NonNull PsiElement node,
- @NonNull String name,
- @NonNull String owner) {
- if (ApiDetector.isBenignConstantUsage(null, name, owner)) {
- return true;
- }
-
- // 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
- // or as a case value
- PsiElement curr = node.getParent();
- while (curr != null) {
- if (curr instanceof PsiSwitchLabelStatement) {
- PsiSwitchLabelStatement caseStatement = (PsiSwitchLabelStatement)curr;
- PsiExpression condition = caseStatement.getCaseValue();
- return condition != null && PsiTreeUtil.isAncestor(condition, node, false);
- } else if (curr instanceof PsiIfStatement) {
- PsiIfStatement ifStatement = (PsiIfStatement)curr;
- PsiExpression condition = ifStatement.getCondition();
- return condition != null && PsiTreeUtil.isAncestor(condition, node, false);
- } else if (curr instanceof PsiConditionalExpression) {
- // ?:-statement
- PsiConditionalExpression ifStatement = (PsiConditionalExpression)curr;
- PsiExpression condition = ifStatement.getCondition();
- return PsiTreeUtil.isAncestor(condition, node, false);
- }
- curr = curr.getParent();
- }
-
- return false;
- }
-
- @Override
- public void visitCallExpression(PsiCallExpression expression) {
- super.visitCallExpression(expression);
-
- if (!myCheckAccess) {
- return;
- }
-
- // TODO: How does this differ from visitMethodCallExpression?
- // Inferred super perhaps? No, I think it refers to constructor invocations!
- PsiMethod method = expression.resolveMethod();
- if (method != null) {
- PsiClass containingClass = method.getContainingClass();
- if (containingClass == null) {
- return;
- }
- String fqcn = containingClass.getQualifiedName();
- String owner = IntellijLintUtils.getInternalName(containingClass);
- if (owner == null) {
- return; // Couldn't resolve type
- }
- String name = IntellijLintUtils.getInternalMethodName(method);
- String desc = IntellijLintUtils.getInternalDescription(method, false, false);
- if (desc == null) {
- // Couldn't compute description of method for some reason; probably
- // failure to resolve parameter types
- return;
- }
-
- int api = mApiDatabase.getCallVersion(owner, name, desc);
- if (api == -1) {
- return;
- }
- int minSdk = getMinSdk(myContext);
- if (api <= minSdk) {
- return;
- }
-
- // The lint API database contains two optimizations:
- // First, all members that were available in API 1 are omitted from the database, since that saves
- // about half of the size of the database, and for API check purposes, we don't need to distinguish
- // between "doesn't exist" and "available in all versions".
- // Second, all inherited members were inlined into each class, so that it doesn't have to do a
- // repeated search up the inheritance chain.
- //
- // Unfortunately, in this custom PSI detector, we look up the real resolved method, which can sometimes
- // have a different minimum API.
- //
- // For example, SQLiteDatabase had a close() method from API 1. Therefore, calling SQLiteDatabase is supported
- // in all versions. However, it extends SQLiteClosable, which in API 16 added "implements Closable". In
- // this detector, if we have the following code:
- // void test(SQLiteDatabase db) { db.close }
- // here the call expression will be the close method on type SQLiteClosable. And that will result in an API
- // requirement of API 16, since the close method it now resolves to is in API 16.
- //
- // To work around this, we can now look up the type of the call expression ("db" in the above, but it could
- // have been more complicated), and if that's a different type than the type of the method, we look up
- // *that* method from lint's database instead. Furthermore, it's possible for that method to return "-1"
- // and we can't tell if that means "doesn't exist" or "present in API 1", we then check the package prefix
- // to see whether we know it's an API method whose members should all have been inlined.
- if (expression instanceof PsiMethodCallExpression) {
- PsiExpression qualifier = ((PsiMethodCallExpression)expression).getMethodExpression().getQualifierExpression();
- if (qualifier != null && !(qualifier instanceof PsiThisExpression) && !(qualifier instanceof PsiSuperExpression)) {
- PsiType type = qualifier.getType();
- if (type != null && type instanceof PsiClassType) {
- String expressionOwner = IntellijLintUtils.getInternalName((PsiClassType)type);
- if (expressionOwner != null && !expressionOwner.equals(owner)) {
- int specificApi = mApiDatabase.getCallVersion(expressionOwner, name, desc);
- if (specificApi == -1) {
- if (ApiLookup.isRelevantOwner(expressionOwner)) {
- return;
- }
- } else if (specificApi <= minSdk) {
- return;
- }
- }
- }
- } else {
- // Unqualified call; need to search in our super hierarchy
- PsiClass cls = PsiTreeUtil.getParentOfType(expression, PsiClass.class);
-
- //noinspection ConstantConditions
- if (qualifier instanceof PsiThisExpression || qualifier instanceof PsiSuperExpression) {
- PsiQualifiedExpression pte = (PsiQualifiedExpression)qualifier;
- PsiJavaCodeReferenceElement operand = pte.getQualifier();
- if (operand != null) {
- PsiElement resolved = operand.resolve();
- if (resolved instanceof PsiClass) {
- cls = (PsiClass)resolved;
- }
- }
- }
-
- while (cls != null) {
- if (cls instanceof PsiAnonymousClass) {
- // If it's an unqualified call in an anonymous class, we need to rely on the
- // resolve method to find out whether the method is picked up from the anonymous
- // class chain or any outer classes
- break;
- }
- String expressionOwner = IntellijLintUtils.getInternalName(cls);
- if (expressionOwner == null) {
- break;
- }
- int specificApi = mApiDatabase.getCallVersion(expressionOwner, name, desc);
- if (specificApi == -1) {
- if (ApiLookup.isRelevantOwner(expressionOwner)) {
- return;
- }
- } else if (specificApi <= minSdk) {
- return;
- } else {
- break;
- }
- cls = cls.getSuperClass();
- }
- }
- }
-
- if (isSuppressed(api, expression, minSdk)) {
- return;
- }
-
- // If you're simply calling super.X from method X, even if method X is in a higher API level than the minSdk, we're
- // generally safe; that method should only be called by the framework on the right API levels. (There is a danger of
- // somebody calling that method locally in other contexts, but this is hopefully unlikely.)
- if (expression instanceof PsiMethodCallExpression) {
- PsiMethodCallExpression call = (PsiMethodCallExpression)expression;
- PsiReferenceExpression methodExpression = call.getMethodExpression();
- if (methodExpression.getQualifierExpression() instanceof PsiSuperExpression) {
- PsiMethod containingMethod = PsiTreeUtil.getParentOfType(expression, PsiMethod.class, true);
- if (containingMethod != null && name.equals(containingMethod.getName())
- && MethodSignatureUtil.areSignaturesEqual(method, containingMethod)
- // We specifically exclude constructors from this check, because we do want to flag constructors requiring the
- // new API level; it's highly likely that the constructor is called by local code so you should specifically
- // investigate this as a developer
- && !method.isConstructor()) {
- return;
- }
- }
- }
-
- PsiElement locationNode = IntellijLintUtils.getCallName(expression);
- if (locationNode == null) {
- locationNode = expression;
- }
- com.android.tools.klint.detector.api.Location location = IntellijLintUtils.getLocation(myContext.file, locationNode);
- String message = String.format("Call requires API level %1$d (current min is %2$d): %3$s", api, minSdk,
- fqcn + '#' + method.getName());
-
- myContext.report(UNSUPPORTED, location, message);
- }
- }
- }
-
- private 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;
- PsiExpression condition = ifStatement.getCondition();
- if (condition != prev && condition instanceof PsiBinaryExpression) {
- PsiBinaryExpression binary = (PsiBinaryExpression)condition;
- 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 = prev == ifStatement.getThenBranch();
- boolean fromElse = prev == ifStatement.getElseBranch();
- assert fromThen == !fromElse;
- if (tokenType == JavaTokenType.GE) {
- // if (SDK_INT >= ICE_CREAM_SANDWICH) { } else { ... }
- return level >= api && fromThen;
- }
- else if (tokenType == JavaTokenType.GT) {
- // if (SDK_INT > ICE_CREAM_SANDWICH) { } else { ... }
- return level >= api - 1 && fromThen;
- }
- else if (tokenType == JavaTokenType.LE) {
- // if (SDK_INT <= ICE_CREAM_SANDWICH) { ... } else { }
- return level >= api - 1 && fromElse;
- }
- else if (tokenType == JavaTokenType.LT) {
- // if (SDK_INT < ICE_CREAM_SANDWICH) { ... } else { }
- return level >= api && fromElse;
- }
- else if (tokenType == JavaTokenType.EQEQ) {
- // if (SDK_INT == ICE_CREAM_SANDWICH) { } else { }
- return level >= api && fromThen;
- } else {
- assert false : tokenType;
- }
- }
- }
- }
- } else if (tokenType == JavaTokenType.ANDAND && (prev == ifStatement.getThenBranch())) {
- if (isAndedWithConditional(ifStatement.getCondition(), api, prev)) {
- return true;
- }
- }
- } 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 (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;
- }
-
- 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 &&
- return level >= api;
- }
- else if (tokenType == JavaTokenType.GT) {
- // if (SDK_INT > ICE_CREAM_SANDWICH) &&
- return level >= api - 1;
- }
- else if (tokenType == JavaTokenType.EQEQ) {
- // if (SDK_INT == ICE_CREAM_SANDWICH) &&
- 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/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/IntellijLintIssueRegistry.java b/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/IntellijLintIssueRegistry.java
index 636a6928db9..943ce94b445 100644
--- a/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/IntellijLintIssueRegistry.java
+++ b/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/IntellijLintIssueRegistry.java
@@ -49,9 +49,7 @@ public class IntellijLintIssueRegistry extends BuiltinIssueRegistry {
Implementation implementation = issue.getImplementation();
EnumSet scope = implementation.getScope();
Class extends Detector> detectorClass = implementation.getDetectorClass();
- if (detectorClass == ApiDetector.class) {
- issue.setImplementation(IntellijApiDetector.IMPLEMENTATION);
- } else if (detectorClass == RegistrationDetector.class) {
+ if (detectorClass == RegistrationDetector.class) {
issue.setImplementation(IntellijRegistrationDetector.IMPLEMENTATION);
} else if (detectorClass == ViewTypeDetector.class) {
issue.setImplementation(IntellijViewTypeDetector.IMPLEMENTATION);