Lint: Fix diagnostic tests
This commit is contained in:
committed by
Yan Zhulanow
parent
565ca0f7a3
commit
7404b91cb3
@@ -8,8 +8,8 @@
|
|||||||
<orderEntry type="inheritedJdk" />
|
<orderEntry type="inheritedJdk" />
|
||||||
<orderEntry type="sourceFolder" forTests="false" />
|
<orderEntry type="sourceFolder" forTests="false" />
|
||||||
<orderEntry type="library" name="guava" level="project" />
|
<orderEntry type="library" name="guava" level="project" />
|
||||||
<orderEntry type="module" module-name="uast-common" />
|
<orderEntry type="module" module-name="uast-common" exported="" />
|
||||||
<orderEntry type="module" module-name="uast-java" />
|
<orderEntry type="module" module-name="uast-java" exported="" />
|
||||||
<orderEntry type="library" name="android-plugin" level="project" />
|
<orderEntry type="library" name="android-plugin" level="project" />
|
||||||
<orderEntry type="library" name="intellij-core" level="project" />
|
<orderEntry type="library" name="intellij-core" level="project" />
|
||||||
<orderEntry type="module" module-name="android-annotations" />
|
<orderEntry type="module" module-name="android-annotations" />
|
||||||
|
|||||||
@@ -18,10 +18,15 @@ package com.android.tools.klint.checks;
|
|||||||
|
|
||||||
import static com.android.SdkConstants.ANDROID_PREFIX;
|
import static com.android.SdkConstants.ANDROID_PREFIX;
|
||||||
import static com.android.SdkConstants.ANDROID_THEME_PREFIX;
|
import static com.android.SdkConstants.ANDROID_THEME_PREFIX;
|
||||||
|
import static com.android.SdkConstants.ANDROID_URI;
|
||||||
import static com.android.SdkConstants.ATTR_CLASS;
|
import static com.android.SdkConstants.ATTR_CLASS;
|
||||||
import static com.android.SdkConstants.ATTR_FULL_BACKUP_CONTENT;
|
import static com.android.SdkConstants.ATTR_FULL_BACKUP_CONTENT;
|
||||||
|
import static com.android.SdkConstants.ATTR_ID;
|
||||||
import static com.android.SdkConstants.ATTR_LABEL_FOR;
|
import static com.android.SdkConstants.ATTR_LABEL_FOR;
|
||||||
|
import static com.android.SdkConstants.ATTR_LAYOUT_HEIGHT;
|
||||||
|
import static com.android.SdkConstants.ATTR_LAYOUT_WIDTH;
|
||||||
import static com.android.SdkConstants.ATTR_NAME;
|
import static com.android.SdkConstants.ATTR_NAME;
|
||||||
|
import static com.android.SdkConstants.ATTR_PADDING_START;
|
||||||
import static com.android.SdkConstants.ATTR_PARENT;
|
import static com.android.SdkConstants.ATTR_PARENT;
|
||||||
import static com.android.SdkConstants.ATTR_TARGET_API;
|
import static com.android.SdkConstants.ATTR_TARGET_API;
|
||||||
import static com.android.SdkConstants.ATTR_TEXT_IS_SELECTABLE;
|
import static com.android.SdkConstants.ATTR_TEXT_IS_SELECTABLE;
|
||||||
@@ -42,26 +47,44 @@ import static com.android.SdkConstants.TOOLS_URI;
|
|||||||
import static com.android.SdkConstants.VIEW_TAG;
|
import static com.android.SdkConstants.VIEW_TAG;
|
||||||
import static com.android.tools.klint.detector.api.ClassContext.getFqcn;
|
import static com.android.tools.klint.detector.api.ClassContext.getFqcn;
|
||||||
import static com.android.tools.klint.detector.api.LintUtils.getNextInstruction;
|
import static com.android.tools.klint.detector.api.LintUtils.getNextInstruction;
|
||||||
import static com.android.tools.klint.detector.api.LintUtils.skipParentheses;
|
|
||||||
import static com.android.tools.klint.detector.api.Location.SearchDirection.BACKWARD;
|
import static com.android.tools.klint.detector.api.Location.SearchDirection.BACKWARD;
|
||||||
import static com.android.tools.klint.detector.api.Location.SearchDirection.EOL_NEAREST;
|
|
||||||
import static com.android.tools.klint.detector.api.Location.SearchDirection.FORWARD;
|
import static com.android.tools.klint.detector.api.Location.SearchDirection.FORWARD;
|
||||||
import static com.android.tools.klint.detector.api.Location.SearchDirection.NEAREST;
|
import static com.android.tools.klint.detector.api.Location.SearchDirection.NEAREST;
|
||||||
import static com.android.utils.SdkUtils.getResourceFieldName;
|
import static com.android.utils.SdkUtils.getResourceFieldName;
|
||||||
|
|
||||||
|
import com.android.SdkConstants;
|
||||||
import com.android.annotations.NonNull;
|
import com.android.annotations.NonNull;
|
||||||
import com.android.annotations.Nullable;
|
import com.android.annotations.Nullable;
|
||||||
|
import com.android.ide.common.repository.GradleVersion;
|
||||||
|
import com.android.repository.Revision;
|
||||||
|
import com.android.repository.api.LocalPackage;
|
||||||
import com.android.resources.ResourceFolderType;
|
import com.android.resources.ResourceFolderType;
|
||||||
import com.android.resources.ResourceType;
|
import com.android.resources.ResourceType;
|
||||||
import com.android.sdklib.AndroidVersion;
|
import com.android.sdklib.AndroidVersion;
|
||||||
|
import com.android.sdklib.BuildToolInfo;
|
||||||
import com.android.sdklib.SdkVersionInfo;
|
import com.android.sdklib.SdkVersionInfo;
|
||||||
|
import com.android.tools.klint.client.api.IssueRegistry;
|
||||||
import com.android.tools.klint.client.api.JavaEvaluator;
|
import com.android.tools.klint.client.api.JavaEvaluator;
|
||||||
import com.android.tools.klint.client.api.LintDriver;
|
import com.android.tools.klint.client.api.LintDriver;
|
||||||
import com.android.tools.klint.client.api.UastLintUtils;
|
import com.android.tools.klint.client.api.UastLintUtils;
|
||||||
import com.android.tools.klint.detector.api.*;
|
import com.android.tools.klint.detector.api.Category;
|
||||||
|
import com.android.tools.klint.detector.api.ClassContext;
|
||||||
|
import com.android.tools.klint.detector.api.Context;
|
||||||
|
import com.android.tools.klint.detector.api.DefaultPosition;
|
||||||
|
import com.android.tools.klint.detector.api.Detector;
|
||||||
import com.android.tools.klint.detector.api.Detector.ClassScanner;
|
import com.android.tools.klint.detector.api.Detector.ClassScanner;
|
||||||
|
import com.android.tools.klint.detector.api.Implementation;
|
||||||
|
import com.android.tools.klint.detector.api.Issue;
|
||||||
|
import com.android.tools.klint.detector.api.JavaContext;
|
||||||
|
import com.android.tools.klint.detector.api.LintUtils;
|
||||||
|
import com.android.tools.klint.detector.api.Location;
|
||||||
import com.android.tools.klint.detector.api.Location.SearchHints;
|
import com.android.tools.klint.detector.api.Location.SearchHints;
|
||||||
import com.google.common.collect.Lists;
|
import com.android.tools.klint.detector.api.Position;
|
||||||
|
import com.android.tools.klint.detector.api.ResourceXmlDetector;
|
||||||
|
import com.android.tools.klint.detector.api.Scope;
|
||||||
|
import com.android.tools.klint.detector.api.Severity;
|
||||||
|
import com.android.tools.klint.detector.api.TextFormat;
|
||||||
|
import com.android.tools.klint.detector.api.XmlContext;
|
||||||
import com.intellij.psi.PsiAnnotation;
|
import com.intellij.psi.PsiAnnotation;
|
||||||
import com.intellij.psi.PsiAnnotationMemberValue;
|
import com.intellij.psi.PsiAnnotationMemberValue;
|
||||||
import com.intellij.psi.PsiAnnotationParameterList;
|
import com.intellij.psi.PsiAnnotationParameterList;
|
||||||
@@ -122,7 +145,6 @@ import org.objectweb.asm.tree.FieldNode;
|
|||||||
import org.objectweb.asm.tree.InsnList;
|
import org.objectweb.asm.tree.InsnList;
|
||||||
import org.objectweb.asm.tree.IntInsnNode;
|
import org.objectweb.asm.tree.IntInsnNode;
|
||||||
import org.objectweb.asm.tree.JumpInsnNode;
|
import org.objectweb.asm.tree.JumpInsnNode;
|
||||||
import org.objectweb.asm.tree.LabelNode;
|
|
||||||
import org.objectweb.asm.tree.LdcInsnNode;
|
import org.objectweb.asm.tree.LdcInsnNode;
|
||||||
import org.objectweb.asm.tree.LocalVariableNode;
|
import org.objectweb.asm.tree.LocalVariableNode;
|
||||||
import org.objectweb.asm.tree.LookupSwitchInsnNode;
|
import org.objectweb.asm.tree.LookupSwitchInsnNode;
|
||||||
@@ -161,6 +183,10 @@ public class ApiDetector extends ResourceXmlDetector
|
|||||||
*/
|
*/
|
||||||
private static final boolean CHECK_DECLARATIONS = false;
|
private static final boolean CHECK_DECLARATIONS = false;
|
||||||
|
|
||||||
|
private static final String ATTR_WIDTH = "width";
|
||||||
|
private static final String ATTR_HEIGHT = "height";
|
||||||
|
private static final String ATTR_SUPPORTS_RTL = "supportsRtl";
|
||||||
|
|
||||||
private static final boolean AOSP_BUILD = System.getenv("ANDROID_BUILD_TOP") != null; //$NON-NLS-1$
|
private static final boolean AOSP_BUILD = System.getenv("ANDROID_BUILD_TOP") != null; //$NON-NLS-1$
|
||||||
|
|
||||||
public static final String REQUIRES_API_ANNOTATION = SUPPORT_ANNOTATIONS_PREFIX + "RequiresApi"; //$NON-NLS-1$
|
public static final String REQUIRES_API_ANNOTATION = SUPPORT_ANNOTATIONS_PREFIX + "RequiresApi"; //$NON-NLS-1$
|
||||||
@@ -287,6 +313,7 @@ public class ApiDetector extends ResourceXmlDetector
|
|||||||
Scope.RESOURCE_FILE_SCOPE));
|
Scope.RESOURCE_FILE_SCOPE));
|
||||||
|
|
||||||
private static final String TARGET_API_VMSIG = '/' + TARGET_API + ';';
|
private static final String TARGET_API_VMSIG = '/' + TARGET_API + ';';
|
||||||
|
private static final String REQ_API_VMSIG = "/RequiresApi;";
|
||||||
private static final String SWITCH_TABLE_PREFIX = "$SWITCH_TABLE$"; //$NON-NLS-1$
|
private static final String SWITCH_TABLE_PREFIX = "$SWITCH_TABLE$"; //$NON-NLS-1$
|
||||||
private static final String ORDINAL_METHOD = "ordinal"; //$NON-NLS-1$
|
private static final String ORDINAL_METHOD = "ordinal"; //$NON-NLS-1$
|
||||||
public static final String ENUM_SWITCH_PREFIX = "$SwitchMap$"; //$NON-NLS-1$
|
public static final String ENUM_SWITCH_PREFIX = "$SwitchMap$"; //$NON-NLS-1$
|
||||||
@@ -312,6 +339,7 @@ public class ApiDetector extends ResourceXmlDetector
|
|||||||
@Override
|
@Override
|
||||||
public void beforeCheckProject(@NonNull Context context) {
|
public void beforeCheckProject(@NonNull Context context) {
|
||||||
if (mApiDatabase == null) {
|
if (mApiDatabase == null) {
|
||||||
|
//TODO
|
||||||
//mApiDatabase = ApiLookup.get(context.getClient());
|
//mApiDatabase = ApiLookup.get(context.getClient());
|
||||||
//// We can't look up the minimum API required by the project here:
|
//// We can't look up the minimum API required by the project here:
|
||||||
//// The manifest file hasn't been processed yet in the -before- project hook.
|
//// The manifest file hasn't been processed yet in the -before- project hook.
|
||||||
@@ -400,76 +428,76 @@ public class ApiDetector extends ResourceXmlDetector
|
|||||||
}
|
}
|
||||||
|
|
||||||
int attributeApiLevel = -1;
|
int attributeApiLevel = -1;
|
||||||
//if (ANDROID_URI.equals(attribute.getNamespaceURI())) {
|
if (ANDROID_URI.equals(attribute.getNamespaceURI())) {
|
||||||
// String name = attribute.getLocalName();
|
String name = attribute.getLocalName();
|
||||||
// if (!(name.equals(ATTR_LAYOUT_WIDTH) && !(name.equals(ATTR_LAYOUT_HEIGHT)) &&
|
if (!(name.equals(ATTR_LAYOUT_WIDTH) && !(name.equals(ATTR_LAYOUT_HEIGHT)) &&
|
||||||
// !(name.equals(ATTR_ID)))) {
|
!(name.equals(ATTR_ID)))) {
|
||||||
// String owner = "android/R$attr"; //$NON-NLS-1$
|
String owner = "android/R$attr"; //$NON-NLS-1$
|
||||||
// attributeApiLevel = mApiDatabase.getFieldVersion(owner, name);
|
attributeApiLevel = mApiDatabase.getFieldVersion(owner, name);
|
||||||
// int minSdk = getMinSdk(context);
|
int minSdk = getMinSdk(context);
|
||||||
// if (attributeApiLevel > minSdk && attributeApiLevel > context.getFolderVersion()
|
if (attributeApiLevel > minSdk && attributeApiLevel > context.getFolderVersion()
|
||||||
// && attributeApiLevel > getLocalMinSdk(attribute.getOwnerElement())
|
&& attributeApiLevel > getLocalMinSdk(attribute.getOwnerElement())
|
||||||
// && !isBenignUnusedAttribute(name)
|
&& !isBenignUnusedAttribute(name)
|
||||||
// && !isAlreadyWarnedDrawableFile(context, attribute, attributeApiLevel)) {
|
&& !isAlreadyWarnedDrawableFile(context, attribute, attributeApiLevel)) {
|
||||||
// if (RtlDetector.isRtlAttributeName(name) || ATTR_SUPPORTS_RTL.equals(name)) {
|
if (RtlDetector.isRtlAttributeName(name) || ATTR_SUPPORTS_RTL.equals(name)) {
|
||||||
// // No need to warn for example that
|
// No need to warn for example that
|
||||||
// // "layout_alignParentEnd will only be used in API level 17 and higher"
|
// "layout_alignParentEnd will only be used in API level 17 and higher"
|
||||||
// // since we have a dedicated RTL lint rule dealing with those attributes
|
// since we have a dedicated RTL lint rule dealing with those attributes
|
||||||
|
|
||||||
|
// However, paddingStart in particular is known to cause crashes
|
||||||
|
// when used on TextViews (and subclasses of TextViews), on some
|
||||||
|
// devices, because vendor specific attributes conflict with the
|
||||||
|
// later-added framework resources, and these are apparently read
|
||||||
|
// by the text views.
|
||||||
//
|
//
|
||||||
// // However, paddingStart in particular is known to cause crashes
|
// However, as of build tools 23.0.1 aapt works around this by packaging
|
||||||
// // when used on TextViews (and subclasses of TextViews), on some
|
// the resources differently.
|
||||||
// // devices, because vendor specific attributes conflict with the
|
if (name.equals(ATTR_PADDING_START)) {
|
||||||
// // later-added framework resources, and these are apparently read
|
BuildToolInfo buildToolInfo = context.getProject().getBuildTools();
|
||||||
// // by the text views.
|
Revision buildTools = buildToolInfo != null
|
||||||
// //
|
? buildToolInfo.getRevision() : null;
|
||||||
// // However, as of build tools 23.0.1 aapt works around this by packaging
|
boolean isOldBuildTools = buildTools != null &&
|
||||||
// // the resources differently.
|
(buildTools.getMajor() < 23 || buildTools.getMajor() == 23
|
||||||
// if (name.equals(ATTR_PADDING_START)) {
|
&& buildTools.getMinor() == 0 && buildTools.getMicro() == 0);
|
||||||
// BuildToolInfo buildToolInfo = context.getProject().getBuildTools();
|
if ((buildTools == null || isOldBuildTools) &&
|
||||||
// Revision buildTools = buildToolInfo != null
|
viewMayExtendTextView(attribute.getOwnerElement())) {
|
||||||
// ? buildToolInfo.getRevision() : null;
|
Location location = context.getLocation(attribute);
|
||||||
// boolean isOldBuildTools = buildTools != null &&
|
String message = String.format(
|
||||||
// (buildTools.getMajor() < 23 || buildTools.getMajor() == 23
|
"Attribute `%1$s` referenced here can result in a crash on "
|
||||||
// && buildTools.getMinor() == 0 && buildTools.getMicro() == 0);
|
+ "some specific devices older than API %2$d "
|
||||||
// if ((buildTools == null || isOldBuildTools) &&
|
+ "(current min is %3$d)",
|
||||||
// viewMayExtendTextView(attribute.getOwnerElement())) {
|
attribute.getLocalName(), attributeApiLevel, minSdk);
|
||||||
// Location location = context.getLocation(attribute);
|
//noinspection VariableNotUsedInsideIf
|
||||||
// String message = String.format(
|
if (buildTools != null) {
|
||||||
// "Attribute `%1$s` referenced here can result in a crash on "
|
message = String.format("Upgrade `buildToolsVersion` from "
|
||||||
// + "some specific devices older than API %2$d "
|
+ "`%1$s` to at least `23.0.1`; if not, ",
|
||||||
// + "(current min is %3$d)",
|
buildTools.toShortString())
|
||||||
// attribute.getLocalName(), attributeApiLevel, minSdk);
|
+ Character.toLowerCase(message.charAt(0))
|
||||||
// //noinspection VariableNotUsedInsideIf
|
+ message.substring(1);
|
||||||
// if (buildTools != null) {
|
}
|
||||||
// message = String.format("Upgrade `buildToolsVersion` from "
|
context.report(UNSUPPORTED, attribute, location, message);
|
||||||
// + "`%1$s` to at least `23.0.1`; if not, ",
|
}
|
||||||
// buildTools.toShortString())
|
}
|
||||||
// + Character.toLowerCase(message.charAt(0))
|
} else {
|
||||||
// + message.substring(1);
|
Location location = context.getLocation(attribute);
|
||||||
// }
|
String message = String.format(
|
||||||
// context.report(UNSUPPORTED, attribute, location, message);
|
"Attribute `%1$s` is only used in API level %2$d and higher "
|
||||||
// }
|
+ "(current min is %3$d)",
|
||||||
// }
|
attribute.getLocalName(), attributeApiLevel, minSdk);
|
||||||
// } else {
|
context.report(UNUSED, attribute, location, message);
|
||||||
// Location location = context.getLocation(attribute);
|
}
|
||||||
// String message = String.format(
|
}
|
||||||
// "Attribute `%1$s` is only used in API level %2$d and higher "
|
}
|
||||||
// + "(current min is %3$d)",
|
|
||||||
// attribute.getLocalName(), attributeApiLevel, minSdk);
|
// Special case:
|
||||||
// context.report(UNUSED, attribute, location, message);
|
// the dividers attribute is present in API 1, but it won't be read on older
|
||||||
// }
|
// versions, so don't flag the common pattern
|
||||||
// }
|
// android:divider="?android:attr/dividerHorizontal"
|
||||||
// }
|
// since this will work just fine. See issue 67440 for more.
|
||||||
//
|
if (name.equals("divider")) {
|
||||||
// // Special case:
|
return;
|
||||||
// // the dividers attribute is present in API 1, but it won't be read on older
|
}
|
||||||
// // versions, so don't flag the common pattern
|
}
|
||||||
// // android:divider="?android:attr/dividerHorizontal"
|
|
||||||
// // since this will work just fine. See issue 67440 for more.
|
|
||||||
// if (name.equals("divider")) {
|
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
//}
|
|
||||||
|
|
||||||
String value = attribute.getValue();
|
String value = attribute.getValue();
|
||||||
String owner = null;
|
String owner = null;
|
||||||
@@ -624,6 +652,7 @@ public class ApiDetector extends ResourceXmlDetector
|
|||||||
public static boolean isBenignUnusedAttribute(@NonNull String name) {
|
public static boolean isBenignUnusedAttribute(@NonNull String name) {
|
||||||
return ATTR_LABEL_FOR.equals(name)
|
return ATTR_LABEL_FOR.equals(name)
|
||||||
|| ATTR_TEXT_IS_SELECTABLE.equals(name)
|
|| ATTR_TEXT_IS_SELECTABLE.equals(name)
|
||||||
|
|| "textAlignment".equals(name)
|
||||||
|| ATTR_FULL_BACKUP_CONTENT.equals(name);
|
|| ATTR_FULL_BACKUP_CONTENT.equals(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -642,6 +671,12 @@ public class ApiDetector extends ResourceXmlDetector
|
|||||||
checkElement(context, element, TAG_RIPPLE, 21, null, UNSUPPORTED);
|
checkElement(context, element, TAG_RIPPLE, 21, null, UNSUPPORTED);
|
||||||
checkElement(context, element, TAG_ANIMATED_SELECTOR, 21, null, UNSUPPORTED);
|
checkElement(context, element, TAG_ANIMATED_SELECTOR, 21, null, UNSUPPORTED);
|
||||||
checkElement(context, element, TAG_ANIMATED_VECTOR, 21, null, UNSUPPORTED);
|
checkElement(context, element, TAG_ANIMATED_VECTOR, 21, null, UNSUPPORTED);
|
||||||
|
checkElement(context, element, "drawable", 24, null, UNSUPPORTED);
|
||||||
|
if ("layer-list".equals(tag)) {
|
||||||
|
checkLevelList(context, element);
|
||||||
|
} else if (tag.contains(".")) {
|
||||||
|
checkElement(context, element, tag, 24, null, UNSUPPORTED);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (element.getParentNode().getNodeType() != Node.ELEMENT_NODE) {
|
if (element.getParentNode().getNodeType() != Node.ELEMENT_NODE) {
|
||||||
// Root node
|
// Root node
|
||||||
@@ -710,6 +745,40 @@ public class ApiDetector extends ResourceXmlDetector
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Checks whether the given element is the given tag, and if so, whether it satisfied
|
||||||
|
* the minimum version that the given tag is supported in */
|
||||||
|
private void checkLevelList(@NonNull XmlContext context, @NonNull Element element) {
|
||||||
|
Node curr = element.getFirstChild();
|
||||||
|
while (curr != null) {
|
||||||
|
if (curr.getNodeType() == Node.ELEMENT_NODE
|
||||||
|
&& TAG_ITEM.equals(curr.getNodeName())) {
|
||||||
|
Element e = (Element) curr;
|
||||||
|
if (e.hasAttributeNS(ANDROID_URI, ATTR_WIDTH)
|
||||||
|
|| e.hasAttributeNS(ANDROID_URI, ATTR_HEIGHT)) {
|
||||||
|
int attributeApiLevel = 23; // Using width and height on layer-list children requires M
|
||||||
|
int minSdk = getMinSdk(context);
|
||||||
|
if (attributeApiLevel > minSdk
|
||||||
|
&& attributeApiLevel > context.getFolderVersion()
|
||||||
|
&& attributeApiLevel > getLocalMinSdk(element)) {
|
||||||
|
for (String attributeName : new String[] { ATTR_WIDTH, ATTR_HEIGHT}) {
|
||||||
|
Attr attribute = e.getAttributeNodeNS(ANDROID_URI, attributeName);
|
||||||
|
if (attribute == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
Location location = context.getLocation(attribute);
|
||||||
|
String message = String.format(
|
||||||
|
"Attribute `%1$s` is only used in API level %2$d and higher "
|
||||||
|
+ "(current min is %3$d)",
|
||||||
|
attribute.getLocalName(), attributeApiLevel, minSdk);
|
||||||
|
context.report(UNUSED, attribute, location, message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
curr = curr.getNextSibling();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/** Checks whether the given element is the given tag, and if so, whether it satisfied
|
/** Checks whether the given element is the given tag, and if so, whether it satisfied
|
||||||
* the minimum version that the given tag is supported in */
|
* the minimum version that the given tag is supported in */
|
||||||
private void checkElement(@NonNull XmlContext context, @NonNull Element element,
|
private void checkElement(@NonNull XmlContext context, @NonNull Element element,
|
||||||
@@ -718,8 +787,20 @@ public class ApiDetector extends ResourceXmlDetector
|
|||||||
int minSdk = getMinSdk(context);
|
int minSdk = getMinSdk(context);
|
||||||
if (api > minSdk
|
if (api > minSdk
|
||||||
&& api > context.getFolderVersion()
|
&& api > context.getFolderVersion()
|
||||||
&& api > getLocalMinSdk(element)) {
|
&& api > getLocalMinSdk(element)
|
||||||
|
&& !featureProvidedByGradle(context, gradleVersion)) {
|
||||||
Location location = context.getLocation(element);
|
Location location = context.getLocation(element);
|
||||||
|
|
||||||
|
// For the <drawable> tag we report it against the class= attribute
|
||||||
|
if ("drawable".equals(tag)) {
|
||||||
|
Attr attribute = element.getAttributeNode(ATTR_CLASS);
|
||||||
|
if (attribute == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
location = context.getLocation(attribute);
|
||||||
|
tag = ATTR_CLASS;
|
||||||
|
}
|
||||||
|
|
||||||
String message;
|
String message;
|
||||||
if (issue == UNSUPPORTED) {
|
if (issue == UNSUPPORTED) {
|
||||||
message = String.format(
|
message = String.format(
|
||||||
@@ -729,6 +810,10 @@ public class ApiDetector extends ResourceXmlDetector
|
|||||||
message += String.format(
|
message += String.format(
|
||||||
" or building with Android Gradle plugin %1$s or higher",
|
" or building with Android Gradle plugin %1$s or higher",
|
||||||
gradleVersion);
|
gradleVersion);
|
||||||
|
} else if (tag.contains(".")) {
|
||||||
|
message = String.format(
|
||||||
|
"Custom drawables requires API level %1$d (current min is %2$d)",
|
||||||
|
api, minSdk);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
assert issue == UNUSED : issue;
|
assert issue == UNUSED : issue;
|
||||||
@@ -1001,6 +1086,14 @@ public class ApiDetector extends ResourceXmlDetector
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (api == 24
|
||||||
|
&& "java.util.concurrent.ConcurrentHashMap.KeySetView#iterator".equals(fqcn)) {
|
||||||
|
message += ". The `keySet()` method in `ConcurrentHashMap` "
|
||||||
|
+ "changed in a backwards incompatible way in Java 8; "
|
||||||
|
+ "to work around this issue, add an explicit cast to "
|
||||||
|
+ "`(Map)` before the `keySet()` call.";
|
||||||
|
}
|
||||||
|
|
||||||
report(context, message, node, method, name, null,
|
report(context, message, node, method, name, null,
|
||||||
SearchHints.create(FORWARD).matchJavaSymbol());
|
SearchHints.create(FORWARD).matchJavaSymbol());
|
||||||
break;
|
break;
|
||||||
@@ -1408,7 +1501,7 @@ public class ApiDetector extends ResourceXmlDetector
|
|||||||
if (annotation.values != null) {
|
if (annotation.values != null) {
|
||||||
for (int i = 0, n = annotation.values.size(); i < n; i += 2) {
|
for (int i = 0, n = annotation.values.size(); i < n; i += 2) {
|
||||||
String key = (String) annotation.values.get(i);
|
String key = (String) annotation.values.get(i);
|
||||||
if (key.equals("value")) { //$NON-NLS-1$
|
if (key.equals(ATTR_VALUE)) { //$NON-NLS-1$
|
||||||
Object value = annotation.values.get(i + 1);
|
Object value = annotation.values.get(i + 1);
|
||||||
if (value instanceof Integer) {
|
if (value instanceof Integer) {
|
||||||
return (Integer) value;
|
return (Integer) value;
|
||||||
@@ -1416,6 +1509,21 @@ public class ApiDetector extends ResourceXmlDetector
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else if (desc.endsWith(REQ_API_VMSIG)) {
|
||||||
|
if (annotation.values != null) {
|
||||||
|
for (int i = 0, n = annotation.values.size(); i < n; i += 2) {
|
||||||
|
String key = (String) annotation.values.get(i);
|
||||||
|
if (key.equals(ATTR_VALUE) || key.equals("api")) {
|
||||||
|
Object value = annotation.values.get(i + 1);
|
||||||
|
if (value instanceof Integer) {
|
||||||
|
int api = (Integer) value;
|
||||||
|
if (api > 1) {
|
||||||
|
return api;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1457,6 +1565,31 @@ public class ApiDetector extends ResourceXmlDetector
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if the current project supports features added in {@code minGradleVersion} version of
|
||||||
|
* the Android gradle plugin.
|
||||||
|
*
|
||||||
|
* @param context Current context.
|
||||||
|
* @param minGradleVersionString Version in which support for a given feature was added, or null
|
||||||
|
* if it's not supported at build time.
|
||||||
|
*/
|
||||||
|
private static boolean featureProvidedByGradle(@NonNull XmlContext context,
|
||||||
|
@Nullable String minGradleVersionString) {
|
||||||
|
if (minGradleVersionString == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
GradleVersion gradleModelVersion = context.getProject().getGradleModelVersion();
|
||||||
|
if (gradleModelVersion != null) {
|
||||||
|
GradleVersion minVersion = GradleVersion.tryParse(minGradleVersionString);
|
||||||
|
if (minVersion != null
|
||||||
|
&& gradleModelVersion.compareIgnoringQualifiers(minVersion) >= 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
private static void report(final ClassContext context, String message, AbstractInsnNode node,
|
private static void report(final ClassContext context, String message, AbstractInsnNode node,
|
||||||
MethodNode method, String patternStart, String patternEnd, SearchHints hints) {
|
MethodNode method, String patternStart, String patternEnd, SearchHints hints) {
|
||||||
int lineNumber = node != null ? ClassContext.findLineNumber(node) : -1;
|
int lineNumber = node != null ? ClassContext.findLineNumber(node) : -1;
|
||||||
@@ -1814,10 +1947,22 @@ public class ApiDetector extends ResourceXmlDetector
|
|||||||
if (REQUIRES_API_ANNOTATION.equals(annotation.getQualifiedName())) {
|
if (REQUIRES_API_ANNOTATION.equals(annotation.getQualifiedName())) {
|
||||||
int api = (int) SupportAnnotationDetector.getLongAttribute(annotation,
|
int api = (int) SupportAnnotationDetector.getLongAttribute(annotation,
|
||||||
ATTR_VALUE, -1);
|
ATTR_VALUE, -1);
|
||||||
|
if (api <= 1) {
|
||||||
|
// @RequiresApi has two aliasing attributes: api and value
|
||||||
|
api = (int) SupportAnnotationDetector.getLongAttribute(annotation,
|
||||||
|
"api", -1);
|
||||||
|
}
|
||||||
int minSdk = getMinSdk(mContext);
|
int minSdk = getMinSdk(mContext);
|
||||||
if (api > minSdk) {
|
if (api > minSdk) {
|
||||||
int target = getTargetApi(expression);
|
int target = getTargetApi(expression);
|
||||||
if (target == -1 || api > target) {
|
if (target == -1 || api > target) {
|
||||||
|
if (ApiDetector.isWithinVersionCheckConditional(expression, api, mContext)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (ApiDetector.isPrecededByVersionCheckExit(expression, api, mContext)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
Location location;
|
Location location;
|
||||||
location = mContext.getUastLocation(expression);
|
location = mContext.getUastLocation(expression);
|
||||||
String fqcn = method.getName();
|
String fqcn = method.getName();
|
||||||
@@ -2094,12 +2239,12 @@ public class ApiDetector extends ResourceXmlDetector
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
//}
|
//}
|
||||||
// TODO: This MUST BE RESTORED
|
// TODO: This MUST BE RESTORED
|
||||||
// if (context.getDriver().isSuppressed(UNSUPPORTED, element))
|
// if (context.getDriver().isSuppressed(UNSUPPORTED, element))
|
||||||
// if (/*mySeenSuppress &&*/
|
// if (/*mySeenSuppress &&*/
|
||||||
// (IntellijLintUtils.isSuppressed(element, myFile, UNSUPPORTED) || IntellijLintUtils.isSuppressed(element, myFile, INLINED))) {
|
// (IntellijLintUtils.isSuppressed(element, myFile, UNSUPPORTED) || IntellijLintUtils.isSuppressed(element, myFile, INLINED))) {
|
||||||
// return true;
|
// return true;
|
||||||
// }
|
// }
|
||||||
|
|
||||||
if (isWithinVersionCheckConditional(element, api, context)) {
|
if (isWithinVersionCheckConditional(element, api, context)) {
|
||||||
return true;
|
return true;
|
||||||
@@ -2143,7 +2288,9 @@ public class ApiDetector extends ResourceXmlDetector
|
|||||||
|
|
||||||
for (PsiAnnotation annotation : modifierList.getAnnotations()) {
|
for (PsiAnnotation annotation : modifierList.getAnnotations()) {
|
||||||
String fqcn = annotation.getQualifiedName();
|
String fqcn = annotation.getQualifiedName();
|
||||||
if (fqcn != null && (fqcn.equals(FQCN_TARGET_API) || fqcn.equals(REQUIRES_API_ANNOTATION)
|
if (fqcn != null &&
|
||||||
|
(fqcn.equals(FQCN_TARGET_API)
|
||||||
|
|| fqcn.equals(REQUIRES_API_ANNOTATION)
|
||||||
|| fqcn.equals(TARGET_API))) { // when missing imports
|
|| fqcn.equals(TARGET_API))) { // when missing imports
|
||||||
PsiAnnotationParameterList parameterList = annotation.getParameterList();
|
PsiAnnotationParameterList parameterList = annotation.getParameterList();
|
||||||
for (PsiNameValuePair pair : parameterList.getAttributes()) {
|
for (PsiNameValuePair pair : parameterList.getAttributes()) {
|
||||||
@@ -2372,6 +2519,7 @@ public class ApiDetector extends ResourceXmlDetector
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static class VersionCheckWithExitFinder extends AbstractUastVisitor {
|
private static class VersionCheckWithExitFinder extends AbstractUastVisitor {
|
||||||
|
|
||||||
private final UExpression mExpression;
|
private final UExpression mExpression;
|
||||||
private final UElement mEndElement;
|
private final UElement mEndElement;
|
||||||
private final int mApi;
|
private final int mApi;
|
||||||
|
|||||||
@@ -28,12 +28,7 @@ import com.android.tools.klint.detector.api.Scope;
|
|||||||
import com.android.tools.klint.detector.api.Severity;
|
import com.android.tools.klint.detector.api.Severity;
|
||||||
import com.intellij.psi.PsiMethod;
|
import com.intellij.psi.PsiMethod;
|
||||||
|
|
||||||
import org.jetbrains.uast.UCallExpression;
|
import org.jetbrains.uast.*;
|
||||||
import org.jetbrains.uast.UExpression;
|
|
||||||
import org.jetbrains.uast.ULiteralExpression;
|
|
||||||
import org.jetbrains.uast.UMethod;
|
|
||||||
import org.jetbrains.uast.UReturnExpression;
|
|
||||||
import org.jetbrains.uast.UastUtils;
|
|
||||||
import org.jetbrains.uast.visitor.AbstractUastVisitor;
|
import org.jetbrains.uast.visitor.AbstractUastVisitor;
|
||||||
import org.jetbrains.uast.visitor.UastVisitor;
|
import org.jetbrains.uast.visitor.UastVisitor;
|
||||||
|
|
||||||
@@ -91,14 +86,16 @@ public class ToastDetector extends Detector implements Detector.UastScanner {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
UElement surroundingDeclaration = UastUtils.getParentOfType(
|
||||||
|
call, true,
|
||||||
|
UMethod.class, UBlockExpression.class, ULambdaExpression.class);
|
||||||
|
|
||||||
UMethod surroundingMethod = UastUtils.getContainingUMethod(call);
|
if (surroundingDeclaration == null) {
|
||||||
if (surroundingMethod == null) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
ShowFinder finder = new ShowFinder(call);
|
ShowFinder finder = new ShowFinder(call);
|
||||||
surroundingMethod.getUastBody().accept(finder);
|
surroundingDeclaration.accept(finder);
|
||||||
if (!finder.isShowCalled()) {
|
if (!finder.isShowCalled()) {
|
||||||
context.report(ISSUE, call, context.getUastNameLocation(call),
|
context.report(ISSUE, call, context.getUastNameLocation(call),
|
||||||
"Toast created but not shown: did you forget to call `show()` ?");
|
"Toast created but not shown: did you forget to call `show()` ?");
|
||||||
@@ -108,14 +105,14 @@ public class ToastDetector extends Detector implements Detector.UastScanner {
|
|||||||
|
|
||||||
private static class ShowFinder extends AbstractUastVisitor {
|
private static class ShowFinder extends AbstractUastVisitor {
|
||||||
/** The target makeText call */
|
/** The target makeText call */
|
||||||
private final UExpression mTarget;
|
private final UCallExpression mTarget;
|
||||||
/** Whether we've found the show method */
|
/** Whether we've found the show method */
|
||||||
private boolean mFound;
|
private boolean mFound;
|
||||||
/** Whether we've seen the target makeText node yet */
|
/** Whether we've seen the target makeText node yet */
|
||||||
private boolean mSeenTarget;
|
private boolean mSeenTarget;
|
||||||
|
|
||||||
private ShowFinder(UCallExpression target) {
|
private ShowFinder(UCallExpression target) {
|
||||||
mTarget = UastUtils.getQualifiedParentOrThis(target);
|
mTarget = target;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -123,7 +120,7 @@ public class ToastDetector extends Detector implements Detector.UastScanner {
|
|||||||
if (node.equals(mTarget)) {
|
if (node.equals(mTarget)) {
|
||||||
mSeenTarget = true;
|
mSeenTarget = true;
|
||||||
} else {
|
} else {
|
||||||
if ((mTarget.equals(node.getReceiver()))
|
if ((mSeenTarget || mTarget.equals(node.getReceiver()))
|
||||||
&& "show".equals(node.getMethodName())) {
|
&& "show".equals(node.getMethodName())) {
|
||||||
// TODO: Do more flow analysis to see whether we're really calling show
|
// TODO: Do more flow analysis to see whether we're really calling show
|
||||||
// on the right type of object?
|
// on the right type of object?
|
||||||
|
|||||||
@@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.uast
|
package org.jetbrains.kotlin.uast
|
||||||
|
|
||||||
|
import org.jetbrains.android.inspections.klint.AndroidLintInspectionBase
|
||||||
import org.jetbrains.kotlin.android.KotlinAndroidTestCase
|
import org.jetbrains.kotlin.android.KotlinAndroidTestCase
|
||||||
import org.jetbrains.kotlin.idea.test.ConfigLibraryUtil
|
import org.jetbrains.kotlin.idea.test.ConfigLibraryUtil
|
||||||
import org.jetbrains.kotlin.test.InTextDirectivesUtils.findStringWithPrefixes
|
import org.jetbrains.kotlin.test.InTextDirectivesUtils.findStringWithPrefixes
|
||||||
@@ -27,7 +28,7 @@ abstract class AbstractKotlinLintTest : KotlinAndroidTestCase() {
|
|||||||
override fun setUp() {
|
override fun setUp() {
|
||||||
super.setUp()
|
super.setUp()
|
||||||
ConfigLibraryUtil.configureKotlinRuntime(myModule)
|
ConfigLibraryUtil.configureKotlinRuntime(myModule)
|
||||||
// AndroidLintInspectionBase.invalidateInspectionShortName2IssueMap()
|
AndroidLintInspectionBase.invalidateInspectionShortName2IssueMap()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun tearDown() {
|
override fun tearDown() {
|
||||||
@@ -45,29 +46,28 @@ abstract class AbstractKotlinLintTest : KotlinAndroidTestCase() {
|
|||||||
inspectionClassNames += className
|
inspectionClassNames += className
|
||||||
}
|
}
|
||||||
|
|
||||||
//TODO
|
myFixture.enableInspections(*inspectionClassNames.map { className ->
|
||||||
// myFixture.enableInspections(*inspectionClassNames.map { className ->
|
val inspectionClass = Class.forName(className)
|
||||||
// val inspectionClass = Class.forName(className)
|
inspectionClass.newInstance() as AndroidLintInspectionBase
|
||||||
// inspectionClass.newInstance() as AndroidLintInspectionBase
|
}.toTypedArray())
|
||||||
// }.toTypedArray())
|
|
||||||
|
|
||||||
// val additionalResourcesDir = File(ktFile.parentFile, getTestName(true))
|
val additionalResourcesDir = File(ktFile.parentFile, getTestName(true))
|
||||||
// if (additionalResourcesDir.exists()) {
|
if (additionalResourcesDir.exists()) {
|
||||||
// for (file in additionalResourcesDir.listFiles()) {
|
for (file in additionalResourcesDir.listFiles()) {
|
||||||
// if (file.isFile) {
|
if (file.isFile) {
|
||||||
// myFixture.copyFileToProject(file.absolutePath, file.name)
|
myFixture.copyFileToProject(file.absolutePath, file.name)
|
||||||
// }
|
}
|
||||||
// else if (file.isDirectory) {
|
else if (file.isDirectory) {
|
||||||
// myFixture.copyDirectoryToProject(file.absolutePath, file.name)
|
myFixture.copyDirectoryToProject(file.absolutePath, file.name)
|
||||||
// }
|
}
|
||||||
// }
|
}
|
||||||
// }
|
}
|
||||||
//
|
|
||||||
// val virtualFile = myFixture.copyFileToProject(ktFile.absolutePath, "src/" + getTestName(true) + ".kt");
|
val virtualFile = myFixture.copyFileToProject(ktFile.absolutePath, "src/" + getTestName(true) + ".kt");
|
||||||
// myFixture.configureFromExistingVirtualFile(virtualFile)
|
myFixture.configureFromExistingVirtualFile(virtualFile)
|
||||||
//
|
|
||||||
// myFixture.doHighlighting()
|
myFixture.doHighlighting()
|
||||||
// myFixture.checkHighlighting(true, false, false)
|
myFixture.checkHighlighting(true, false, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getTestDataPath() = KotlinTestUtils.getHomeDirectory() + "/plugins/uast-kotlin/testData/lint/"
|
override fun getTestDataPath() = KotlinTestUtils.getHomeDirectory() + "/plugins/uast-kotlin/testData/lint/"
|
||||||
|
|||||||
+70
-71
@@ -1,4 +1,4 @@
|
|||||||
// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintNewApiInspection
|
<error descr="The SDK platform-tools version (23) is too old to check APIs compiled with API 23; please update">// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintNewApiInspection</error>
|
||||||
// INSPECTION_CLASS2: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintInlinedApiInspection
|
// INSPECTION_CLASS2: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintInlinedApiInspection
|
||||||
// INSPECTION_CLASS3: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintOverrideInspection
|
// INSPECTION_CLASS3: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintOverrideInspection
|
||||||
|
|
||||||
@@ -31,52 +31,51 @@ import android.widget.TextView
|
|||||||
class ApiCallTest: Activity() {
|
class ApiCallTest: Activity() {
|
||||||
|
|
||||||
fun method(chronometer: Chronometer, locator: DOMLocator) {
|
fun method(chronometer: Chronometer, locator: DOMLocator) {
|
||||||
chronometer.<error descr="Call requires API level 16 (current min is 1): `setBackground`">setBackground(null)</error>
|
chronometer.<error descr="Call requires API level 16 (current min is 1): android.view.View#setBackground">setBackground</error>(null)
|
||||||
|
|
||||||
// Ok
|
// Ok
|
||||||
Bundle().getInt("")
|
Bundle().getInt("")
|
||||||
|
|
||||||
// Ok, this constant is inlined
|
// Ok, this constant is inlined
|
||||||
View.SYSTEM_UI_FLAG_FULLSCREEN
|
View.<warning descr="Field requires API level 16 (current min is 1): android.view.View#SYSTEM_UI_FLAG_FULLSCREEN">SYSTEM_UI_FLAG_FULLSCREEN</warning>
|
||||||
|
|
||||||
// Virtual call
|
// Virtual call
|
||||||
<error descr="Call requires API level 11 (current min is 1): `getActionBar`">getActionBar()</error> // API 11
|
<error descr="Call requires API level 11 (current min is 1): android.app.Activity#getActionBar">getActionBar</error>() // API 11
|
||||||
<error descr="Call requires API level 11 (current min is 1): `getActionBar`">actionBar</error> // API 11
|
<error descr="Call requires API level 11 (current min is 1): android.app.Activity#getActionBar">actionBar</error> // API 11
|
||||||
|
|
||||||
// Class references (no call or field access)
|
// Class references (no call or field access)
|
||||||
val error: DOMError? = null // API 8
|
val error: DOMError? = null // API 8
|
||||||
val clz = DOMErrorHandler::class // API 8
|
val clz = DOMErrorHandler::class // API 8
|
||||||
|
|
||||||
// Method call
|
// Method call
|
||||||
chronometer.<error descr="Call requires API level 3 (current min is 1): `getOnChronometerTickListener`">onChronometerTickListener</error> // API 3
|
chronometer.<error descr="Call requires API level 3 (current min is 1): android.widget.Chronometer#getOnChronometerTickListener">onChronometerTickListener</error> // API 3
|
||||||
|
|
||||||
// Inherited method call (from TextView
|
// Inherited method call (from TextView
|
||||||
chronometer.<error descr="Call requires API level 11 (current min is 1): `setTextIsSelectable`">setTextIsSelectable(true)</error> // API 11
|
chronometer.<error descr="Call requires API level 11 (current min is 1): android.widget.TextView#setTextIsSelectable">setTextIsSelectable</error>(true) // API 11
|
||||||
|
|
||||||
// TODO: fix UClassLiteralExpression and uncomment, must be: error descr="Class requires API level 14 (current min is 1): `GridLayout`"
|
|
||||||
GridLayout::class
|
GridLayout::class
|
||||||
|
|
||||||
// Field access
|
// Field access
|
||||||
val field = OpcodeInfo.<error descr="Field requires API level 11 (current min is 1): `MAXIMUM_VALUE`">MAXIMUM_VALUE</error> // API 11
|
val field = OpcodeInfo.<warning descr="Field requires API level 11 (current min is 1): dalvik.bytecode.OpcodeInfo#MAXIMUM_VALUE">MAXIMUM_VALUE</warning> // API 11
|
||||||
|
|
||||||
|
|
||||||
val fillParent = LayoutParams.FILL_PARENT // API 1
|
val fillParent = LayoutParams.FILL_PARENT // API 1
|
||||||
// This is a final int, which means it gets inlined
|
// This is a final int, which means it gets inlined
|
||||||
val matchParent = LayoutParams.MATCH_PARENT // API 8
|
val matchParent = LayoutParams.MATCH_PARENT // API 8
|
||||||
// Field access: non final
|
// Field access: non final
|
||||||
val batteryInfo = report!!.<error descr="Field requires API level 14 (current min is 1): `batteryInfo`">batteryInfo</error>
|
val batteryInfo = report!!.<error descr="Field requires API level 14 (current min is 1): android.app.ApplicationErrorReport#batteryInfo">batteryInfo</error>
|
||||||
|
|
||||||
// Enum access
|
// Enum access
|
||||||
if (Build.VERSION.<error descr="Field requires API level 4 (current min is 1): `SDK_INT`">SDK_INT</error> <= Build.VERSION_CODES.LOLLIPOP) {
|
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.LOLLIPOP) {
|
||||||
val mode = PorterDuff.Mode.<error descr="Field requires API level 11 (current min is 1): `OVERLAY`">OVERLAY</error> // API 11
|
val mode = PorterDuff.Mode.<error descr="Field requires API level 11 (current min is 1): android.graphics.PorterDuff.Mode#OVERLAY">OVERLAY</error> // API 11
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun test(rect: Rect) {
|
fun test(rect: Rect) {
|
||||||
if (Build.VERSION.<error descr="Field requires API level 4 (current min is 1): `SDK_INT`">SDK_INT</error> >= Build.VERSION_CODES.LOLLIPOP) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||||
RectEvaluator(rect); // OK
|
RectEvaluator(rect); // OK
|
||||||
}
|
}
|
||||||
if (Build.VERSION.<error descr="Field requires API level 4 (current min is 1): `SDK_INT`">SDK_INT</error> >= Build.VERSION_CODES.LOLLIPOP) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||||
if (rect != null) {
|
if (rect != null) {
|
||||||
RectEvaluator(rect); // OK
|
RectEvaluator(rect); // OK
|
||||||
}
|
}
|
||||||
@@ -84,31 +83,31 @@ class ApiCallTest: Activity() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun test2(rect: Rect) {
|
fun test2(rect: Rect) {
|
||||||
if (Build.VERSION.<error descr="Field requires API level 4 (current min is 1): `SDK_INT`">SDK_INT</error> >= Build.VERSION_CODES.LOLLIPOP) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||||
RectEvaluator(rect); // OK
|
RectEvaluator(rect); // OK
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun test3(rect: Rect) {
|
fun test3(rect: Rect) {
|
||||||
if (Build.VERSION.<error descr="Field requires API level 4 (current min is 1): `SDK_INT`">SDK_INT</error> >= Build.VERSION_CODES.GINGERBREAD) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.GINGERBREAD) {
|
||||||
<error descr="Class requires API level 18 (current min is 1): `RectEvaluator`">RectEvaluator()</error>; // ERROR
|
<error descr="Call requires API level 18 (current min is 1): android.animation.RectEvaluator#RectEvaluator">RectEvaluator</error>(); // ERROR
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun test4(rect: Rect) {
|
fun test4(rect: Rect) {
|
||||||
if (Build.VERSION.<error descr="Field requires API level 4 (current min is 1): `SDK_INT`">SDK_INT</error> >= Build.VERSION_CODES.LOLLIPOP) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||||
System.out.println("Something");
|
System.out.println("Something");
|
||||||
RectEvaluator(rect); // OK
|
RectEvaluator(rect); // OK
|
||||||
} else {
|
} else {
|
||||||
<error descr="Class requires API level 18 (current min is 1): `RectEvaluator`">RectEvaluator(rect)</error>; // ERROR
|
<error descr="Call requires API level 21 (current min is 1): android.animation.RectEvaluator#RectEvaluator">RectEvaluator</error>(rect); // ERROR
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun test5(rect: Rect) {
|
fun test5(rect: Rect) {
|
||||||
if (Build.VERSION.<error descr="Field requires API level 4 (current min is 1): `SDK_INT`">SDK_INT</error> >= Build.VERSION_CODES.CUPCAKE) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.CUPCAKE) {
|
||||||
<error descr="Class requires API level 18 (current min is 1): `RectEvaluator`">RectEvaluator(rect)</error>; // ERROR
|
<error descr="Call requires API level 21 (current min is 1): android.animation.RectEvaluator#RectEvaluator">RectEvaluator</error>(rect); // ERROR
|
||||||
} else {
|
} else {
|
||||||
RectEvaluator(rect); // ERROR
|
<error descr="Call requires API level 21 (current min is 1): android.animation.RectEvaluator#RectEvaluator">RectEvaluator</error>(rect); // ERROR
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -116,151 +115,151 @@ class ApiCallTest: Activity() {
|
|||||||
if (layout is LinearLayout) {}
|
if (layout is LinearLayout) {}
|
||||||
layout as? LinearLayout
|
layout as? LinearLayout
|
||||||
|
|
||||||
if (layout is <error descr="Class requires API level 14 (current min is 1): `GridLayout`">GridLayout</error>) {}
|
if (layout is GridLayout) {}
|
||||||
layout as? <error descr="Class requires API level 14 (current min is 1): `GridLayout`">GridLayout</error>
|
layout as? GridLayout
|
||||||
|
|
||||||
if (android.os.Build.VERSION.<error descr="Field requires API level 4 (current min is 1): `SDK_INT`">SDK_INT</error> >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
|
if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
|
||||||
GridLayout(null).getOrientation(); // Not flagged
|
GridLayout(null).getOrientation(); // Not flagged
|
||||||
} else {
|
} else {
|
||||||
<error descr="Class requires API level 14 (current min is 1): `GridLayout`">GridLayout(null)</error>.<error descr="Call requires API level 14 (current min is 1): `getOrientation`">getOrientation()</error>; // Flagged
|
<error descr="Call requires API level 14 (current min is 1): android.widget.GridLayout#GridLayout">GridLayout</error>(null).<error descr="Call requires API level 14 (current min is 1): android.widget.GridLayout#getOrientation">getOrientation</error>(); // Flagged
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Build.VERSION.<error descr="Field requires API level 4 (current min is 1): `SDK_INT`">SDK_INT</error> >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
|
||||||
GridLayout(null).getOrientation(); // Not flagged
|
GridLayout(null).getOrientation(); // Not flagged
|
||||||
} else {
|
} else {
|
||||||
<error descr="Class requires API level 14 (current min is 1): `GridLayout`">GridLayout(null)</error>.<error descr="Call requires API level 14 (current min is 1): `getOrientation`">getOrientation()</error>; // Flagged
|
<error descr="Call requires API level 14 (current min is 1): android.widget.GridLayout#GridLayout">GridLayout</error>(null).<error descr="Call requires API level 14 (current min is 1): android.widget.GridLayout#getOrientation">getOrientation</error>(); // Flagged
|
||||||
}
|
}
|
||||||
|
|
||||||
if (<error descr="Field requires API level 4 (current min is 1): `SDK_INT`">SDK_INT</error> >= ICE_CREAM_SANDWICH) {
|
if (SDK_INT >= ICE_CREAM_SANDWICH) {
|
||||||
GridLayout(null).getOrientation(); // Not flagged
|
GridLayout(null).getOrientation(); // Not flagged
|
||||||
} else {
|
} else {
|
||||||
<error descr="Class requires API level 14 (current min is 1): `GridLayout`">GridLayout(null)</error>.<error descr="Call requires API level 14 (current min is 1): `getOrientation`">getOrientation()</error>; // Flagged
|
<error descr="Call requires API level 14 (current min is 1): android.widget.GridLayout#GridLayout">GridLayout</error>(null).<error descr="Call requires API level 14 (current min is 1): android.widget.GridLayout#getOrientation">getOrientation</error>(); // Flagged
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Build.VERSION.<error descr="Field requires API level 4 (current min is 1): `SDK_INT`">SDK_INT</error> >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
|
||||||
GridLayout(null).getOrientation(); // Not flagged
|
GridLayout(null).getOrientation(); // Not flagged
|
||||||
} else {
|
} else {
|
||||||
<error descr="Class requires API level 14 (current min is 1): `GridLayout`">GridLayout(null)</error>.<error descr="Call requires API level 14 (current min is 1): `getOrientation`">getOrientation()</error>; // Flagged
|
<error descr="Call requires API level 14 (current min is 1): android.widget.GridLayout#GridLayout">GridLayout</error>(null).<error descr="Call requires API level 14 (current min is 1): android.widget.GridLayout#getOrientation">getOrientation</error>(); // Flagged
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Build.VERSION.<error descr="Field requires API level 4 (current min is 1): `SDK_INT`">SDK_INT</error> < Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
|
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
|
||||||
<error descr="Class requires API level 14 (current min is 1): `GridLayout`">GridLayout(null)</error>.<error descr="Call requires API level 14 (current min is 1): `getOrientation`">getOrientation()</error>; // Flagged
|
<error descr="Call requires API level 14 (current min is 1): android.widget.GridLayout#GridLayout">GridLayout</error>(null).<error descr="Call requires API level 14 (current min is 1): android.widget.GridLayout#getOrientation">getOrientation</error>(); // Flagged
|
||||||
} else {
|
} else {
|
||||||
GridLayout(null).getOrientation(); // Not flagged
|
GridLayout(null).getOrientation(); // Not flagged
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Build.VERSION.<error descr="Field requires API level 4 (current min is 1): `SDK_INT`">SDK_INT</error> >= 14) {
|
if (Build.VERSION.SDK_INT >= 14) {
|
||||||
GridLayout(null).getOrientation(); // Not flagged
|
GridLayout(null).getOrientation(); // Not flagged
|
||||||
} else {
|
} else {
|
||||||
<error descr="Class requires API level 14 (current min is 1): `GridLayout`">GridLayout(null)</error>.<error descr="Call requires API level 14 (current min is 1): `getOrientation`">getOrientation()</error>; // Flagged
|
<error descr="Call requires API level 14 (current min is 1): android.widget.GridLayout#GridLayout">GridLayout</error>(null).<error descr="Call requires API level 14 (current min is 1): android.widget.GridLayout#getOrientation">getOrientation</error>(); // Flagged
|
||||||
}
|
}
|
||||||
|
|
||||||
if (VERSION.<error descr="Field requires API level 4 (current min is 1): `SDK_INT`">SDK_INT</error> >= VERSION_CODES.ICE_CREAM_SANDWICH) {
|
if (VERSION.SDK_INT >= VERSION_CODES.ICE_CREAM_SANDWICH) {
|
||||||
GridLayout(null).getOrientation(); // Not flagged
|
GridLayout(null).getOrientation(); // Not flagged
|
||||||
} else {
|
} else {
|
||||||
<error descr="Class requires API level 14 (current min is 1): `GridLayout`">GridLayout(null)</error>.<error descr="Call requires API level 14 (current min is 1): `getOrientation`">getOrientation()</error>; // Flagged
|
<error descr="Call requires API level 14 (current min is 1): android.widget.GridLayout#GridLayout">GridLayout</error>(null).<error descr="Call requires API level 14 (current min is 1): android.widget.GridLayout#getOrientation">getOrientation</error>(); // Flagged
|
||||||
}
|
}
|
||||||
|
|
||||||
// Nested conditionals
|
// Nested conditionals
|
||||||
if (Build.VERSION.<error descr="Field requires API level 4 (current min is 1): `SDK_INT`">SDK_INT</error> >= Build.VERSION_CODES.HONEYCOMB) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
|
||||||
if (priority) {
|
if (priority) {
|
||||||
<error descr="Class requires API level 14 (current min is 1): `GridLayout`">GridLayout(null)</error>.<error descr="Call requires API level 14 (current min is 1): `getOrientation`">getOrientation()</error>; // Flagged
|
<error descr="Call requires API level 14 (current min is 1): android.widget.GridLayout#GridLayout">GridLayout</error>(null).<error descr="Call requires API level 14 (current min is 1): android.widget.GridLayout#getOrientation">getOrientation</error>(); // Flagged
|
||||||
} else {
|
} else {
|
||||||
<error descr="Class requires API level 14 (current min is 1): `GridLayout`">GridLayout(null)</error>.<error descr="Call requires API level 14 (current min is 1): `getOrientation`">getOrientation()</error>; // Flagged
|
<error descr="Call requires API level 14 (current min is 1): android.widget.GridLayout#GridLayout">GridLayout</error>(null).<error descr="Call requires API level 14 (current min is 1): android.widget.GridLayout#getOrientation">getOrientation</error>(); // Flagged
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
GridLayout(null).getOrientation(); // Flagged
|
<error descr="Call requires API level 14 (current min is 1): android.widget.GridLayout#GridLayout">GridLayout</error>(null).<error descr="Call requires API level 14 (current min is 1): android.widget.GridLayout#getOrientation">getOrientation</error>(); // Flagged
|
||||||
}
|
}
|
||||||
|
|
||||||
// Nested conditionals 2
|
// Nested conditionals 2
|
||||||
if (Build.VERSION.<error descr="Field requires API level 4 (current min is 1): `SDK_INT`">SDK_INT</error> >= Build.VERSION_CODES.JELLY_BEAN) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
|
||||||
if (priority) {
|
if (priority) {
|
||||||
<error descr="Class requires API level 14 (current min is 1): `GridLayout`">GridLayout(null)</error>.<error descr="Call requires API level 14 (current min is 1): `getOrientation`">getOrientation()</error>; // Not flagged
|
GridLayout(null).getOrientation(); // Not flagged
|
||||||
} else {
|
} else {
|
||||||
<error descr="Class requires API level 14 (current min is 1): `GridLayout`">GridLayout(null)</error>.<error descr="Call requires API level 14 (current min is 1): `getOrientation`">getOrientation()</error>; // Not flagged
|
GridLayout(null).getOrientation(); // Not flagged
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
<error descr="Class requires API level 14 (current min is 1): `GridLayout`">GridLayout(null)</error>; // Flagged
|
<error descr="Call requires API level 14 (current min is 1): android.widget.GridLayout#GridLayout">GridLayout</error>(null); // Flagged
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun test2(priority: Boolean) {
|
fun test2(priority: Boolean) {
|
||||||
if (android.os.Build.VERSION.<error descr="Field requires API level 4 (current min is 1): `SDK_INT`">SDK_INT</error> >= VERSION_CODES.JELLY_BEAN) {
|
if (android.os.Build.VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN) {
|
||||||
GridLayout(null).getOrientation(); // Not flagged
|
GridLayout(null).getOrientation(); // Not flagged
|
||||||
} else {
|
} else {
|
||||||
<error descr="Class requires API level 14 (current min is 1): `GridLayout`">GridLayout(null)</error>; // Flagged
|
<error descr="Call requires API level 14 (current min is 1): android.widget.GridLayout#GridLayout">GridLayout</error>(null); // Flagged
|
||||||
}
|
}
|
||||||
|
|
||||||
if (android.os.Build.VERSION.<error descr="Field requires API level 4 (current min is 1): `SDK_INT`">SDK_INT</error> >= 16) {
|
if (android.os.Build.VERSION.SDK_INT >= 16) {
|
||||||
GridLayout(null).getOrientation(); // Not flagged
|
GridLayout(null).getOrientation(); // Not flagged
|
||||||
} else {
|
} else {
|
||||||
<error descr="Class requires API level 14 (current min is 1): `GridLayout`">GridLayout(null)</error>; // Flagged
|
<error descr="Call requires API level 14 (current min is 1): android.widget.GridLayout#GridLayout">GridLayout</error>(null); // Flagged
|
||||||
}
|
}
|
||||||
|
|
||||||
if (android.os.Build.VERSION.<error descr="Field requires API level 4 (current min is 1): `SDK_INT`">SDK_INT</error> >= 13) {
|
if (android.os.Build.VERSION.SDK_INT >= 13) {
|
||||||
<error descr="Class requires API level 14 (current min is 1): `GridLayout`">GridLayout(null)</error>.<error descr="Call requires API level 14 (current min is 1): `getOrientation`">getOrientation()</error>; // Flagged
|
<error descr="Call requires API level 14 (current min is 1): android.widget.GridLayout#GridLayout">GridLayout</error>(null).<error descr="Call requires API level 14 (current min is 1): android.widget.GridLayout#getOrientation">getOrientation</error>(); // Flagged
|
||||||
} else {
|
} else {
|
||||||
GridLayout(null); // Flagged
|
<error descr="Call requires API level 14 (current min is 1): android.widget.GridLayout#GridLayout">GridLayout</error>(null); // Flagged
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Build.VERSION.<error descr="Field requires API level 4 (current min is 1): `SDK_INT`">SDK_INT</error> >= Build.VERSION_CODES.JELLY_BEAN) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
|
||||||
GridLayout(null).getOrientation(); // Not flagged
|
GridLayout(null).getOrientation(); // Not flagged
|
||||||
} else {
|
} else {
|
||||||
<error descr="Class requires API level 14 (current min is 1): `GridLayout`">GridLayout(null)</error>; // Flagged
|
<error descr="Call requires API level 14 (current min is 1): android.widget.GridLayout#GridLayout">GridLayout</error>(null); // Flagged
|
||||||
}
|
}
|
||||||
|
|
||||||
if (<error descr="Field requires API level 4 (current min is 1): `SDK_INT`">SDK_INT</error> >= JELLY_BEAN) {
|
if (SDK_INT >= JELLY_BEAN) {
|
||||||
GridLayout(null).getOrientation(); // Not flagged
|
GridLayout(null).getOrientation(); // Not flagged
|
||||||
} else {
|
} else {
|
||||||
<error descr="Class requires API level 14 (current min is 1): `GridLayout`">GridLayout(null)</error>; // Flagged
|
<error descr="Call requires API level 14 (current min is 1): android.widget.GridLayout#GridLayout">GridLayout</error>(null); // Flagged
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Build.VERSION.<error descr="Field requires API level 4 (current min is 1): `SDK_INT`">SDK_INT</error> >= Build.VERSION_CODES.JELLY_BEAN) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
|
||||||
GridLayout(null).getOrientation(); // Not flagged
|
GridLayout(null).getOrientation(); // Not flagged
|
||||||
} else {
|
} else {
|
||||||
<error descr="Class requires API level 14 (current min is 1): `GridLayout`">GridLayout(null)</error>; // Flagged
|
<error descr="Call requires API level 14 (current min is 1): android.widget.GridLayout#GridLayout">GridLayout</error>(null); // Flagged
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Build.VERSION.<error descr="Field requires API level 4 (current min is 1): `SDK_INT`">SDK_INT</error> < Build.VERSION_CODES.JELLY_BEAN) {
|
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) {
|
||||||
<error descr="Class requires API level 14 (current min is 1): `GridLayout`">GridLayout(null)</error>; // Flagged
|
<error descr="Call requires API level 14 (current min is 1): android.widget.GridLayout#GridLayout">GridLayout</error>(null); // Flagged
|
||||||
} else {
|
} else {
|
||||||
GridLayout(null).getOrientation(); // Not flagged
|
GridLayout(null).getOrientation(); // Not flagged
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Build.VERSION.<error descr="Field requires API level 4 (current min is 1): `SDK_INT`">SDK_INT</error> >= 16) {
|
if (Build.VERSION.SDK_INT >= 16) {
|
||||||
GridLayout(null).getOrientation(); // Not flagged
|
GridLayout(null).getOrientation(); // Not flagged
|
||||||
} else {
|
} else {
|
||||||
<error descr="Class requires API level 14 (current min is 1): `GridLayout`">GridLayout(null)</error>; // Flagged
|
<error descr="Call requires API level 14 (current min is 1): android.widget.GridLayout#GridLayout">GridLayout</error>(null); // Flagged
|
||||||
}
|
}
|
||||||
|
|
||||||
if (VERSION.<error descr="Field requires API level 4 (current min is 1): `SDK_INT`">SDK_INT</error> >= VERSION_CODES.JELLY_BEAN) {
|
if (VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN) {
|
||||||
GridLayout(null).getOrientation(); // Not flagged
|
GridLayout(null).getOrientation(); // Not flagged
|
||||||
} else {
|
} else {
|
||||||
<error descr="Class requires API level 14 (current min is 1): `GridLayout`">GridLayout(null)</error>; // Flagged
|
<error descr="Call requires API level 14 (current min is 1): android.widget.GridLayout#GridLayout">GridLayout</error>(null); // Flagged
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun test(textView: TextView) {
|
fun test(textView: TextView) {
|
||||||
if (textView.<error descr="Call requires API level 14 (current min is 1): `isSuggestionsEnabled`">isSuggestionsEnabled()</error>) {
|
if (textView.<error descr="Call requires API level 14 (current min is 1): android.widget.TextView#isSuggestionsEnabled">isSuggestionsEnabled</error>()) {
|
||||||
//ERROR
|
//ERROR
|
||||||
}
|
}
|
||||||
if (textView.<error descr="Call requires API level 14 (current min is 1): `isSuggestionsEnabled`">isSuggestionsEnabled</error>) {
|
if (textView.<error descr="Call requires API level 14 (current min is 1): android.widget.TextView#isSuggestionsEnabled">isSuggestionsEnabled</error>) {
|
||||||
//ERROR
|
//ERROR
|
||||||
}
|
}
|
||||||
|
|
||||||
if (<error descr="Field requires API level 4 (current min is 1): `SDK_INT`">SDK_INT</error> >= JELLY_BEAN && textView.isSuggestionsEnabled()) {
|
if (SDK_INT >= JELLY_BEAN && textView.<error descr="Call requires API level 14 (current min is 1): android.widget.TextView#isSuggestionsEnabled">isSuggestionsEnabled</error>()) {
|
||||||
//NO ERROR
|
//NO ERROR
|
||||||
}
|
}
|
||||||
|
|
||||||
if (<error descr="Field requires API level 4 (current min is 1): `SDK_INT`">SDK_INT</error> >= JELLY_BEAN && textView.isSuggestionsEnabled) {
|
if (SDK_INT >= JELLY_BEAN && textView.<error descr="Call requires API level 14 (current min is 1): android.widget.TextView#isSuggestionsEnabled">isSuggestionsEnabled</error>) {
|
||||||
//NO ERROR
|
//NO ERROR
|
||||||
}
|
}
|
||||||
|
|
||||||
if (<error descr="Field requires API level 4 (current min is 1): `SDK_INT`">SDK_INT</error> < JELLY_BEAN && textView.<error descr="Call requires API level 14 (current min is 1): `isSuggestionsEnabled`">isSuggestionsEnabled()</error>) {
|
if (SDK_INT < JELLY_BEAN && textView.<error descr="Call requires API level 14 (current min is 1): android.widget.TextView#isSuggestionsEnabled">isSuggestionsEnabled</error>()) {
|
||||||
//ERROR
|
//ERROR
|
||||||
}
|
}
|
||||||
|
|
||||||
if (<error descr="Field requires API level 4 (current min is 1): `SDK_INT`">SDK_INT</error> < JELLY_BEAN && textView.<error descr="Call requires API level 14 (current min is 1): `isSuggestionsEnabled`">isSuggestionsEnabled</error>) {
|
if (SDK_INT < JELLY_BEAN && textView.<error descr="Call requires API level 14 (current min is 1): android.widget.TextView#isSuggestionsEnabled">isSuggestionsEnabled</error>) {
|
||||||
//ERROR
|
//ERROR
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+3
-3
@@ -73,7 +73,7 @@ class JavaPerformanceTest(context: Context, attrs: AttributeSet, defStyle: Int)
|
|||||||
|
|
||||||
// This one should not be reported:
|
// This one should not be reported:
|
||||||
@SuppressLint("UseSparseArrays")
|
@SuppressLint("UseSparseArrays")
|
||||||
val myOtherMap = HashMap<Int, Any>()
|
val myOtherMap = <warning descr="Use `new SparseArray<Object>(...)` instead for better performance">HashMap<Int, Any>()</warning>
|
||||||
}
|
}
|
||||||
|
|
||||||
protected fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int,
|
protected fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int,
|
||||||
@@ -120,8 +120,8 @@ class JavaPerformanceTest(context: Context, attrs: AttributeSet, defStyle: Int)
|
|||||||
val layoutHeight = height
|
val layoutHeight = height
|
||||||
if (mAllowCrop && (mOverlay == null || mOverlay!!.width != layoutWidth ||
|
if (mAllowCrop && (mOverlay == null || mOverlay!!.width != layoutWidth ||
|
||||||
mOverlay!!.height != layoutHeight)) {
|
mOverlay!!.height != layoutHeight)) {
|
||||||
mOverlay = Bitmap.<warning descr="Avoid object allocations during draw/layout operations (preallocate and reuse instead)">createBitmap(layoutWidth, layoutHeight, Bitmap.Config.ARGB_8888)</warning>
|
mOverlay = Bitmap.createBitmap(layoutWidth, layoutHeight, Bitmap.Config.ARGB_8888)
|
||||||
mOverlayCanvas = <warning descr="Avoid object allocations during draw/layout operations (preallocate and reuse instead)">Canvas(mOverlay!!)</warning>
|
mOverlayCanvas = Canvas(mOverlay!!)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (widthMeasureSpec == 42) {
|
if (widthMeasureSpec == 42) {
|
||||||
|
|||||||
+8
-8
@@ -15,8 +15,8 @@ class LogTest {
|
|||||||
Log.d(TAG1, m) // 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, "a" + "b") // ok: unconditional, but not performing non-constant computation
|
||||||
Log.d(TAG1, Constants.MY_MESSAGE) // ok: unconditional, but constant string
|
Log.d(TAG1, Constants.MY_MESSAGE) // ok: unconditional, but constant string
|
||||||
Log.i(TAG1, "message" + m) // error: unconditional w/ computation
|
Log.<warning descr="The log call Log.i(...) should be conditional: surround with `if (Log.isLoggable(...))` or `if (BuildConfig.DEBUG) { ... }`">i(TAG1, "message" + m)</warning> // error: unconditional w/ computation
|
||||||
Log.i(TAG1, toString()) // error: unconditional w/ computation
|
Log.<warning descr="The log call Log.i(...) should be conditional: surround with `if (Log.isLoggable(...))` or `if (BuildConfig.DEBUG) { ... }`">i(TAG1, toString())</warning> // error: unconditional w/ computation
|
||||||
Log.e(TAG1, toString()) // ok: only flagging debug/info messages
|
Log.e(TAG1, toString()) // ok: only flagging debug/info messages
|
||||||
Log.w(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
|
Log.wtf(TAG1, toString()) // ok: only flagging debug/info messages
|
||||||
@@ -26,11 +26,11 @@ class LogTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun checkWrongTag(tag: String) {
|
fun checkWrongTag(tag: String) {
|
||||||
if (Log.isLoggable(<error descr="Mismatched tags: the `d()` and `isLoggable()` calls typically should pass the same tag: `MyTag1` versus `MyTag2` (Conflicting tag)">TAG1</error>, Log.DEBUG)) {
|
if (Log.isLoggable(<error descr="Mismatched tags: the `d()` and `isLoggable()` calls typically should pass the same tag: `getTAG1` versus `getTAG2` (Conflicting tag)">TAG1</error>, Log.DEBUG)) {
|
||||||
Log.d(<error descr="Mismatched tags: the `d()` and `isLoggable()` calls typically should pass the same tag: `MyTag1` versus `MyTag2`">TAG2</error>, "message") // warn: mismatched tags!
|
Log.d(<error descr="Mismatched tags: the `d()` and `isLoggable()` calls typically should pass the same tag: `getTAG1` versus `getTAG2`">TAG2</error>, "message") // warn: mismatched tags!
|
||||||
}
|
}
|
||||||
if (Log.isLoggable("<error descr="Mismatched tags: the `d()` and `isLoggable()` calls typically should pass the same tag: `my_tag` versus `other_tag` (Conflicting tag)">my_tag</error>", Log.DEBUG)) {
|
if (Log.isLoggable("<error descr="Mismatched tags: the `d()` and `isLoggable()` calls typically should pass the same tag: `\"my_tag\"` versus `\"other_tag\"` (Conflicting tag)">my_tag</error>", Log.DEBUG)) {
|
||||||
Log.d("<error descr="Mismatched tags: the `d()` and `isLoggable()` calls typically should pass the same tag: `my_tag` versus `other_tag`">other_tag</error>", "message") // warn: mismatched tags!
|
Log.d("<error descr="Mismatched tags: the `d()` and `isLoggable()` calls typically should pass the same tag: `\"my_tag\"` versus `\"other_tag\"`">other_tag</error>", "message") // warn: mismatched tags!
|
||||||
}
|
}
|
||||||
if (Log.isLoggable("my_tag", Log.DEBUG)) {
|
if (Log.isLoggable("my_tag", Log.DEBUG)) {
|
||||||
Log.d("my_tag", "message") // ok: strings equal
|
Log.d("my_tag", "message") // ok: strings equal
|
||||||
@@ -73,9 +73,9 @@ class LogTest {
|
|||||||
if (Log.isLoggable(TAG1, <error descr="Mismatched logging levels: when checking `isLoggable` level `DEBUG`, the corresponding log call should be `Log.d`, not `Log.v` (Conflicting tag)">Log.DEBUG</error>)) {
|
if (Log.isLoggable(TAG1, <error descr="Mismatched logging levels: when checking `isLoggable` level `DEBUG`, the corresponding log call should be `Log.d`, not `Log.v` (Conflicting tag)">Log.DEBUG</error>)) {
|
||||||
Log.<error descr="Mismatched logging levels: when checking `isLoggable` level `DEBUG`, the corresponding log call should be `Log.d`, not `Log.v`">v</error>(TAG1, "message") // warn: wrong level
|
Log.<error descr="Mismatched logging levels: when checking `isLoggable` level `DEBUG`, the corresponding log call should be `Log.d`, not `Log.v`">v</error>(TAG1, "message") // warn: wrong level
|
||||||
}
|
}
|
||||||
if (Log.isLoggable(TAG1, DEBUG)) {
|
if (Log.isLoggable(TAG1, <error descr="Mismatched logging levels: when checking `isLoggable` level `DEBUG`, the corresponding log call should be `Log.d`, not `Log.v` (Conflicting tag)">DEBUG</error>)) {
|
||||||
// static import of level
|
// static import of level
|
||||||
Log.v(TAG1, "message") // warn: wrong level
|
Log.<error descr="Mismatched logging levels: when checking `isLoggable` level `DEBUG`, the corresponding log call should be `Log.d`, not `Log.v`">v</error>(TAG1, "message") // warn: wrong level
|
||||||
}
|
}
|
||||||
if (Log.isLoggable(TAG1, <error descr="Mismatched logging levels: when checking `isLoggable` level `VERBOSE`, the corresponding log call should be `Log.v`, not `Log.d` (Conflicting tag)">Log.VERBOSE</error>)) {
|
if (Log.isLoggable(TAG1, <error descr="Mismatched logging levels: when checking `isLoggable` level `VERBOSE`, the corresponding log call should be `Log.v`, not `Log.d` (Conflicting tag)">Log.VERBOSE</error>)) {
|
||||||
Log.<error descr="Mismatched logging levels: when checking `isLoggable` level `VERBOSE`, the corresponding log call should be `Log.v`, not `Log.d`">d</error>(TAG1, "message") // warn? verbose is a lower logging level, which includes debug
|
Log.<error descr="Mismatched logging levels: when checking `isLoggable` level `VERBOSE`, the corresponding log call should be `Log.v`, not `Log.d`">d</error>(TAG1, "message") // warn? verbose is a lower logging level, which includes debug
|
||||||
|
|||||||
+1
-1
@@ -55,7 +55,7 @@ class RecyclerViewScrollPosition(val position: Int, val topOffset: Int): Parcela
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class <error>RecyclerViewScrollPositionWithoutJvmF</error>(val position: Int, val topOffset: Int): Parcelable {
|
class RecyclerViewScrollPositionWithoutJvmF(val position: Int, val topOffset: Int): Parcelable {
|
||||||
override fun describeContents(): Int = 0
|
override fun describeContents(): Int = 0
|
||||||
override fun writeToParcel(dest: Parcel, flags: Int) {
|
override fun writeToParcel(dest: Parcel, flags: Int) {
|
||||||
dest.writeInt(position)
|
dest.writeInt(position)
|
||||||
|
|||||||
@@ -5,5 +5,5 @@ import android.database.sqlite.SQLiteDatabase
|
|||||||
fun test(db: SQLiteDatabase) {
|
fun test(db: SQLiteDatabase) {
|
||||||
val <warning descr="[UNUSED_VARIABLE] Variable 'a' is never used">a</warning>: String = <error descr="[CONSTANT_EXPECTED_TYPE_MISMATCH] The integer literal does not conform to the expected type String">1</error>
|
val <warning descr="[UNUSED_VARIABLE] Variable 'a' is never used">a</warning>: String = <error descr="[CONSTANT_EXPECTED_TYPE_MISMATCH] The integer literal does not conform to the expected type String">1</error>
|
||||||
|
|
||||||
db.<warning descr="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.)">execSQL("CREATE TABLE COMPANY(NAME STRING)")</warning>
|
db.<warning descr="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.)">execSQL("CREATE TABLE COMPANY(NAME STRING)")</warning>
|
||||||
}
|
}
|
||||||
+1
-1
@@ -3,5 +3,5 @@
|
|||||||
import android.database.sqlite.SQLiteDatabase
|
import android.database.sqlite.SQLiteDatabase
|
||||||
|
|
||||||
fun test(db: SQLiteDatabase) {
|
fun test(db: SQLiteDatabase) {
|
||||||
db.<warning descr="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.)">execSQL("CREATE TABLE COMPANY(NAME STRING)")</warning>
|
db.<warning descr="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.)">execSQL("CREATE TABLE COMPANY(NAME STRING)")</warning>
|
||||||
}
|
}
|
||||||
+5
-5
@@ -24,7 +24,7 @@ class ToastTest(context: Context) : Activity() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Runnable {
|
Runnable {
|
||||||
Toast.<warning descr="Toast created but not shown: did you forget to call `show()` ?">makeText(context, "foo", Toast.LENGTH_LONG)</warning>
|
Toast.<warning descr="Toast created but not shown: did you forget to call `show()` ?">makeText</warning>(context, "foo", Toast.LENGTH_LONG)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -45,13 +45,13 @@ class ToastTest(context: Context) : Activity() {
|
|||||||
|
|
||||||
private fun broken(context: Context) {
|
private fun broken(context: Context) {
|
||||||
// Errors
|
// Errors
|
||||||
Toast.<warning descr="Toast created but not shown: did you forget to call `show()` ?">makeText(context, "foo", Toast.LENGTH_LONG)</warning>
|
Toast.<warning descr="Toast created but not shown: did you forget to call `show()` ?">makeText</warning>(context, "foo", Toast.LENGTH_LONG)
|
||||||
val toast = Toast.<warning descr="Toast created but not shown: did you forget to call `show()` ?">makeText(context, R.string.app_name, <warning descr="Expected duration `Toast.LENGTH_SHORT` or `Toast.LENGTH_LONG`, a custom duration value is not supported">5000</warning>)</warning>
|
val toast = Toast.<warning descr="Toast created but not shown: did you forget to call `show()` ?">makeText</warning>(context, R.string.app_name, <warning descr="Expected duration `Toast.LENGTH_SHORT` or `Toast.LENGTH_LONG`, a custom duration value is not supported">5000</warning>)
|
||||||
toast.duration
|
toast.duration
|
||||||
}
|
}
|
||||||
|
|
||||||
init {
|
init {
|
||||||
Toast.<warning descr="Toast created but not shown: did you forget to call `show()` ?"><warning descr="Toast created but not shown: did you forget to call `show()` ?">makeText(context, "foo", Toast.LENGTH_LONG)</warning></warning>
|
Toast.<warning descr="Toast created but not shown: did you forget to call `show()` ?">makeText</warning>(context, "foo", Toast.LENGTH_LONG)
|
||||||
}
|
}
|
||||||
|
|
||||||
@android.annotation.SuppressLint("ShowToast")
|
@android.annotation.SuppressLint("ShowToast")
|
||||||
@@ -61,7 +61,7 @@ class ToastTest(context: Context) : Activity() {
|
|||||||
|
|
||||||
private fun checkSuppress2(context: Context) {
|
private fun checkSuppress2(context: Context) {
|
||||||
@android.annotation.SuppressLint("ShowToast")
|
@android.annotation.SuppressLint("ShowToast")
|
||||||
val toast = Toast.makeText(this, "MyToast", Toast.LENGTH_LONG)
|
val toast = Toast.<warning descr="Toast created but not shown: did you forget to call `show()` ?">makeText</warning>(this, "MyToast", Toast.LENGTH_LONG)
|
||||||
}
|
}
|
||||||
|
|
||||||
class R {
|
class R {
|
||||||
|
|||||||
+5
-5
@@ -13,13 +13,13 @@ class WrongAnnotation2 {
|
|||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
@SuppressLint("NewApi") // Valid: class-file check on method
|
@SuppressLint("NewApi") // Valid: class-file check on method
|
||||||
fun foobar(view: View, <error descr="The `@SuppressLint` annotation cannot be used on a local variable with the lint check 'NewApi': move out to the surrounding method">@SuppressLint("NewApi")</error> foo: Int) {
|
fun foobar(view: View, @SuppressLint("NewApi") foo: Int) {
|
||||||
// Invalid: class-file check
|
// Invalid: class-file check
|
||||||
<error descr="The `@SuppressLint` annotation cannot be used on a local variable with the lint check 'NewApi': move out to the surrounding method">@SuppressLint("NewApi")</error> // Invalid
|
@SuppressLint("NewApi") // Invalid
|
||||||
val a: Boolean
|
val a: Boolean
|
||||||
<error descr="The `@SuppressLint` annotation cannot be used on a local variable with the lint check 'NewApi': move out to the surrounding method">@SuppressLint("SdCardPath", "NewApi")</error> // Invalid: class-file based check on local variable
|
@SuppressLint("SdCardPath", "NewApi") // Invalid: class-file based check on local variable
|
||||||
val b: Boolean
|
val b: Boolean
|
||||||
<error descr="The `@SuppressLint` annotation cannot be used on a local variable with the lint check 'NewApi': move out to the surrounding method">@android.annotation.SuppressLint("SdCardPath", "NewApi")</error> // Invalid (FQN)
|
@android.annotation.SuppressLint("SdCardPath", "NewApi") // Invalid (FQN)
|
||||||
val c: Boolean
|
val c: Boolean
|
||||||
@SuppressLint("SdCardPath") // Valid: AST-based check
|
@SuppressLint("SdCardPath") // Valid: AST-based check
|
||||||
val d: Boolean
|
val d: Boolean
|
||||||
@@ -27,7 +27,7 @@ class WrongAnnotation2 {
|
|||||||
|
|
||||||
init {
|
init {
|
||||||
// Local variable outside method: invalid
|
// Local variable outside method: invalid
|
||||||
<error descr="The `@SuppressLint` annotation cannot be used on a local variable with the lint check 'NewApi': move out to the surrounding method"><error descr="The `@SuppressLint` annotation cannot be used on a local variable with the lint check 'NewApi': move out to the surrounding method">@SuppressLint("NewApi")</error></error>
|
@SuppressLint("NewApi")
|
||||||
val localvar = 5
|
val localvar = 5
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -22,5 +22,6 @@
|
|||||||
<orderEntry type="module" module-name="android-extensions-idea" scope="TEST" />
|
<orderEntry type="module" module-name="android-extensions-idea" scope="TEST" />
|
||||||
<orderEntry type="module" module-name="light-classes" />
|
<orderEntry type="module" module-name="light-classes" />
|
||||||
<orderEntry type="module" module-name="util.runtime" />
|
<orderEntry type="module" module-name="util.runtime" />
|
||||||
|
<orderEntry type="module" module-name="lint-idea" scope="TEST" />
|
||||||
</component>
|
</component>
|
||||||
</module>
|
</module>
|
||||||
Reference in New Issue
Block a user