Fixed KotlinLintTestGenerated.testJavaPerformance + refactoring
Moved android lint tests from uast-kotlin to idea-android
This commit is contained in:
Vendored
+23
@@ -0,0 +1,23 @@
|
||||
// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintShortAlarmInspection
|
||||
|
||||
import android.app.AlarmManager
|
||||
|
||||
@Suppress("UsePropertyAccessSyntax", "UNUSED_VARIABLE", "unused", "UNUSED_PARAMETER", "DEPRECATION")
|
||||
class TestAlarm {
|
||||
fun test(alarmManager: AlarmManager) {
|
||||
alarmManager.setRepeating(AlarmManager.ELAPSED_REALTIME, 5000, 60000, null); // OK
|
||||
alarmManager.setRepeating(AlarmManager.ELAPSED_REALTIME, 6000, 70000, null); // OK
|
||||
alarmManager.setRepeating(AlarmManager.ELAPSED_REALTIME, <warning descr="Value will be forced up to 5000 as of Android 5.1; don't rely on this to be exact">50</warning>, <warning descr="Value will be forced up to 60000 as of Android 5.1; don't rely on this to be exact">10</warning>, null); // ERROR
|
||||
|
||||
alarmManager.setRepeating(AlarmManager.ELAPSED_REALTIME, 5000, // ERROR
|
||||
<warning descr="Value will be forced up to 60000 as of Android 5.1; don't rely on this to be exact">OtherClass.MY_INTERVAL</warning>, null); // ERROR
|
||||
|
||||
val interval = 10;
|
||||
val interval2 = 2L * interval;
|
||||
alarmManager.setRepeating(AlarmManager.ELAPSED_REALTIME, 5000, <warning descr="Value will be forced up to 60000 as of Android 5.1; don't rely on this to be exact">interval2</warning>, null); // ERROR
|
||||
}
|
||||
|
||||
private object OtherClass {
|
||||
val MY_INTERVAL = 1000L;
|
||||
}
|
||||
}
|
||||
+301
@@ -0,0 +1,301 @@
|
||||
|
||||
// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintNewApiInspection
|
||||
// INSPECTION_CLASS2: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintInlinedApiInspection
|
||||
// INSPECTION_CLASS3: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintOverrideInspection
|
||||
|
||||
import android.animation.RectEvaluator
|
||||
import android.annotation.TargetApi
|
||||
import org.w3c.dom.DOMError
|
||||
import org.w3c.dom.DOMErrorHandler
|
||||
import org.w3c.dom.DOMLocator
|
||||
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.view.ViewGroup.LayoutParams
|
||||
import android.app.Activity
|
||||
import android.app.ApplicationErrorReport
|
||||
import android.graphics.Path
|
||||
import android.graphics.PorterDuff
|
||||
import android.graphics.Rect
|
||||
import android.os.Build
|
||||
import android.widget.*
|
||||
import dalvik.bytecode.OpcodeInfo
|
||||
|
||||
import android.os.Build.VERSION
|
||||
import android.os.Build.VERSION.SDK_INT
|
||||
import android.os.Build.VERSION_CODES
|
||||
import android.os.Build.VERSION_CODES.ICE_CREAM_SANDWICH
|
||||
import android.os.Build.VERSION_CODES.JELLY_BEAN
|
||||
import android.os.Bundle
|
||||
import android.system.ErrnoException
|
||||
import android.widget.TextView
|
||||
|
||||
@Suppress("SENSELESS_COMPARISON", "UNUSED_EXPRESSION", "UsePropertyAccessSyntax", "UNUSED_VARIABLE", "unused", "UNUSED_PARAMETER", "DEPRECATION")
|
||||
class ApiCallTest: Activity() {
|
||||
|
||||
fun method(chronometer: Chronometer, locator: DOMLocator) {
|
||||
chronometer.<error descr="Call requires API level 16 (current min is 1): android.view.View#setBackground">setBackground</error>(null)
|
||||
|
||||
// Ok
|
||||
Bundle().getInt("")
|
||||
|
||||
// Ok, this constant is inlined
|
||||
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
|
||||
<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): android.app.Activity#getActionBar">actionBar</error> // API 11
|
||||
|
||||
// Class references (no call or field access)
|
||||
val error: DOMError? = null // API 8
|
||||
val clz = DOMErrorHandler::class // API 8
|
||||
|
||||
// Method call
|
||||
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
|
||||
chronometer.<error descr="Call requires API level 11 (current min is 1): android.widget.TextView#setTextIsSelectable">setTextIsSelectable</error>(true) // API 11
|
||||
|
||||
GridLayout::class
|
||||
|
||||
// Field access
|
||||
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
|
||||
// This is a final int, which means it gets inlined
|
||||
val matchParent = LayoutParams.MATCH_PARENT // API 8
|
||||
// Field access: non final
|
||||
val batteryInfo = report!!.<error descr="Field requires API level 14 (current min is 1): android.app.ApplicationErrorReport#batteryInfo">batteryInfo</error>
|
||||
|
||||
// Enum access
|
||||
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.LOLLIPOP) {
|
||||
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) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
RectEvaluator(rect); // OK
|
||||
}
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
if (rect != null) {
|
||||
RectEvaluator(rect); // OK
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun test2(rect: Rect) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
RectEvaluator(rect); // OK
|
||||
}
|
||||
}
|
||||
|
||||
fun test3(rect: Rect) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.GINGERBREAD) {
|
||||
<error descr="Call requires API level 18 (current min is 1): android.animation.RectEvaluator#RectEvaluator">RectEvaluator</error>(); // ERROR
|
||||
}
|
||||
}
|
||||
|
||||
fun test4(rect: Rect) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
System.out.println("Something");
|
||||
RectEvaluator(rect); // OK
|
||||
} else {
|
||||
<error descr="Call requires API level 21 (current min is 1): android.animation.RectEvaluator#RectEvaluator">RectEvaluator</error>(rect); // ERROR
|
||||
}
|
||||
}
|
||||
|
||||
fun test5(rect: Rect) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.CUPCAKE) {
|
||||
<error descr="Call requires API level 21 (current min is 1): android.animation.RectEvaluator#RectEvaluator">RectEvaluator</error>(rect); // ERROR
|
||||
} else {
|
||||
<error descr="Call requires API level 21 (current min is 1): android.animation.RectEvaluator#RectEvaluator">RectEvaluator</error>(rect); // ERROR
|
||||
}
|
||||
}
|
||||
|
||||
fun test(priority: Boolean, layout: ViewGroup) {
|
||||
if (layout is LinearLayout) {}
|
||||
layout as? LinearLayout
|
||||
|
||||
if (layout is GridLayout) {}
|
||||
layout as? GridLayout
|
||||
|
||||
if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
|
||||
GridLayout(null).getOrientation(); // Not flagged
|
||||
} else {
|
||||
<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.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
|
||||
GridLayout(null).getOrientation(); // Not flagged
|
||||
} else {
|
||||
<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 (SDK_INT >= ICE_CREAM_SANDWICH) {
|
||||
GridLayout(null).getOrientation(); // Not flagged
|
||||
} else {
|
||||
<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.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
|
||||
GridLayout(null).getOrientation(); // Not flagged
|
||||
} else {
|
||||
<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.SDK_INT < Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
|
||||
<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 {
|
||||
GridLayout(null).getOrientation(); // Not flagged
|
||||
}
|
||||
|
||||
if (Build.VERSION.SDK_INT >= 14) {
|
||||
GridLayout(null).getOrientation(); // Not flagged
|
||||
} else {
|
||||
<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.SDK_INT >= VERSION_CODES.ICE_CREAM_SANDWICH) {
|
||||
GridLayout(null).getOrientation(); // Not flagged
|
||||
} else {
|
||||
<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
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
|
||||
if (priority) {
|
||||
<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 {
|
||||
<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 {
|
||||
<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
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
|
||||
if (priority) {
|
||||
GridLayout(null).getOrientation(); // Not flagged
|
||||
} else {
|
||||
GridLayout(null).getOrientation(); // Not flagged
|
||||
}
|
||||
} else {
|
||||
<error descr="Call requires API level 14 (current min is 1): android.widget.GridLayout#GridLayout">GridLayout</error>(null); // Flagged
|
||||
}
|
||||
}
|
||||
|
||||
fun test2(priority: Boolean) {
|
||||
if (android.os.Build.VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN) {
|
||||
GridLayout(null).getOrientation(); // Not flagged
|
||||
} else {
|
||||
<error descr="Call requires API level 14 (current min is 1): android.widget.GridLayout#GridLayout">GridLayout</error>(null); // Flagged
|
||||
}
|
||||
|
||||
if (android.os.Build.VERSION.SDK_INT >= 16) {
|
||||
GridLayout(null).getOrientation(); // Not flagged
|
||||
} else {
|
||||
<error descr="Call requires API level 14 (current min is 1): android.widget.GridLayout#GridLayout">GridLayout</error>(null); // Flagged
|
||||
}
|
||||
|
||||
if (android.os.Build.VERSION.SDK_INT >= 13) {
|
||||
<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 {
|
||||
<error descr="Call requires API level 14 (current min is 1): android.widget.GridLayout#GridLayout">GridLayout</error>(null); // Flagged
|
||||
}
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
|
||||
GridLayout(null).getOrientation(); // Not flagged
|
||||
} else {
|
||||
<error descr="Call requires API level 14 (current min is 1): android.widget.GridLayout#GridLayout">GridLayout</error>(null); // Flagged
|
||||
}
|
||||
|
||||
if (SDK_INT >= JELLY_BEAN) {
|
||||
GridLayout(null).getOrientation(); // Not flagged
|
||||
} else {
|
||||
<error descr="Call requires API level 14 (current min is 1): android.widget.GridLayout#GridLayout">GridLayout</error>(null); // Flagged
|
||||
}
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
|
||||
GridLayout(null).getOrientation(); // Not flagged
|
||||
} else {
|
||||
<error descr="Call requires API level 14 (current min is 1): android.widget.GridLayout#GridLayout">GridLayout</error>(null); // Flagged
|
||||
}
|
||||
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) {
|
||||
<error descr="Call requires API level 14 (current min is 1): android.widget.GridLayout#GridLayout">GridLayout</error>(null); // Flagged
|
||||
} else {
|
||||
GridLayout(null).getOrientation(); // Not flagged
|
||||
}
|
||||
|
||||
if (Build.VERSION.SDK_INT >= 16) {
|
||||
GridLayout(null).getOrientation(); // Not flagged
|
||||
} else {
|
||||
<error descr="Call requires API level 14 (current min is 1): android.widget.GridLayout#GridLayout">GridLayout</error>(null); // Flagged
|
||||
}
|
||||
|
||||
if (VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN) {
|
||||
GridLayout(null).getOrientation(); // Not flagged
|
||||
} else {
|
||||
<error descr="Call requires API level 14 (current min is 1): android.widget.GridLayout#GridLayout">GridLayout</error>(null); // Flagged
|
||||
}
|
||||
}
|
||||
|
||||
fun test(textView: TextView) {
|
||||
if (textView.<error descr="Call requires API level 14 (current min is 1): android.widget.TextView#isSuggestionsEnabled">isSuggestionsEnabled</error>()) {
|
||||
//ERROR
|
||||
}
|
||||
if (textView.<error descr="Call requires API level 14 (current min is 1): android.widget.TextView#isSuggestionsEnabled">isSuggestionsEnabled</error>) {
|
||||
//ERROR
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
if (SDK_INT < JELLY_BEAN && textView.<error descr="Call requires API level 14 (current min is 1): android.widget.TextView#isSuggestionsEnabled">isSuggestionsEnabled</error>()) {
|
||||
//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
|
||||
}
|
||||
}
|
||||
|
||||
@TargetApi(JELLY_BEAN)
|
||||
fun testWithAnnotation(textView: TextView) {
|
||||
if (textView.isSuggestionsEnabled()) {
|
||||
//NO ERROR, annotation
|
||||
}
|
||||
|
||||
if (textView.isSuggestionsEnabled) {
|
||||
//NO ERROR, annotation
|
||||
}
|
||||
}
|
||||
|
||||
fun testCatch() {
|
||||
try {
|
||||
|
||||
} catch (e: <error descr="Class requires API level 21 (current min is 1): android.system.ErrnoException">ErrnoException</error>) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
fun testOverload() {
|
||||
// this overloaded addOval available only on API Level 21
|
||||
Path().<error descr="Call requires API level 21 (current min is 1): android.graphics.Path#addOval">addOval</error>(0f, 0f, 0f, 0f, Path.Direction.CW)
|
||||
}
|
||||
|
||||
// Return type
|
||||
internal // API 14
|
||||
val gridLayout: GridLayout?
|
||||
get() = null
|
||||
|
||||
private val report: ApplicationErrorReport?
|
||||
get() = null
|
||||
}
|
||||
+97
@@ -0,0 +1,97 @@
|
||||
// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintMissingSuperCallInspection
|
||||
|
||||
package android.support.annotation
|
||||
|
||||
@Retention(AnnotationRetention.BINARY)
|
||||
@Target(AnnotationTarget.FUNCTION)
|
||||
annotation class CallSuper
|
||||
|
||||
@Suppress("UsePropertyAccessSyntax", "UNUSED_VARIABLE", "unused", "UNUSED_PARAMETER", "DEPRECATION")
|
||||
class CallSuperTest {
|
||||
private class Child : Parent() {
|
||||
override fun <error descr="Overriding method should call `super.test1`">test1</error>() {
|
||||
// ERROR
|
||||
}
|
||||
|
||||
override fun <error descr="Overriding method should call `super.test2`">test2</error>() {
|
||||
// ERROR
|
||||
}
|
||||
|
||||
override fun <error descr="Overriding method should call `super.test3`">test3</error>() {
|
||||
// ERROR
|
||||
}
|
||||
|
||||
override fun <error descr="Overriding method should call `super.test4`">test4</error>(arg: Int) {
|
||||
// ERROR
|
||||
}
|
||||
|
||||
override fun test4(arg: String) {
|
||||
// OK
|
||||
}
|
||||
|
||||
|
||||
override fun <error descr="Overriding method should call `super.test5`">test5</error>(arg1: Int, arg2: Boolean, arg3: Map<List<String>, *>, // ERROR
|
||||
arg4: Array<IntArray>, vararg arg5: Int) {
|
||||
}
|
||||
|
||||
override fun <error descr="Overriding method should call `super.test5`">test5</error>() {
|
||||
// ERROR
|
||||
super.test6() // (wrong super)
|
||||
}
|
||||
|
||||
override fun test6() {
|
||||
// OK
|
||||
val x = 5
|
||||
super.test6()
|
||||
System.out.println(x)
|
||||
}
|
||||
}
|
||||
|
||||
private open class Parent : ParentParent() {
|
||||
@CallSuper
|
||||
protected open fun test1() {
|
||||
}
|
||||
|
||||
override fun test3() {
|
||||
super.test3()
|
||||
}
|
||||
|
||||
@CallSuper
|
||||
protected open fun test4(arg: Int) {
|
||||
}
|
||||
|
||||
protected open fun test4(arg: String) {
|
||||
}
|
||||
|
||||
@CallSuper
|
||||
protected open fun test5() {
|
||||
}
|
||||
|
||||
@CallSuper
|
||||
protected open fun test5(arg1: Int, arg2: Boolean, arg3: Map<List<String>, *>,
|
||||
arg4: Array<IntArray>, vararg arg5: Int) {
|
||||
}
|
||||
}
|
||||
|
||||
private open class ParentParent : ParentParentParent() {
|
||||
@CallSuper
|
||||
protected open fun test2() {
|
||||
}
|
||||
|
||||
@CallSuper
|
||||
protected open fun test3() {
|
||||
}
|
||||
|
||||
@CallSuper
|
||||
protected open fun test6() {
|
||||
}
|
||||
|
||||
@CallSuper
|
||||
protected fun test7() {
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
private open class ParentParentParent
|
||||
}
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintCommitTransactionInspection
|
||||
|
||||
import android.app.Activity
|
||||
import android.app.FragmentTransaction
|
||||
import android.os.Bundle
|
||||
|
||||
class MainActivity : Activity() {
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
//OK
|
||||
val transaction = fragmentManager.beginTransaction()
|
||||
val transaction2: FragmentTransaction
|
||||
transaction2 = fragmentManager.beginTransaction()
|
||||
transaction.commit()
|
||||
transaction2.commit()
|
||||
|
||||
//WARNING
|
||||
@Suppress("UNUSED_VARIABLE")
|
||||
val transaction3 = fragmentManager.<warning>beginTransaction</warning>()
|
||||
|
||||
//OK
|
||||
fragmentManager.beginTransaction().commit()
|
||||
fragmentManager.beginTransaction().add(null, "A").commit()
|
||||
|
||||
//OK KT-14470
|
||||
Runnable {
|
||||
val a = fragmentManager.beginTransaction()
|
||||
a.commit()
|
||||
}
|
||||
}
|
||||
}
|
||||
+193
@@ -0,0 +1,193 @@
|
||||
// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintDrawAllocationInspection
|
||||
// INSPECTION_CLASS2: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintUseSparseArraysInspection
|
||||
// INSPECTION_CLASS3: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintUseValueOfInspection
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import java.util.HashMap
|
||||
import android.content.Context
|
||||
import android.graphics.*
|
||||
import android.util.AttributeSet
|
||||
import android.util.SparseArray
|
||||
import android.widget.Button
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@Suppress("UsePropertyAccessSyntax", "UNUSED_VARIABLE", "unused", "UNUSED_PARAMETER", "DEPRECATION")
|
||||
class JavaPerformanceTest(context: Context, attrs: AttributeSet, defStyle: Int) : Button(context, attrs, defStyle) {
|
||||
|
||||
private var cachedRect: Rect? = null
|
||||
private var shader: Any? = null
|
||||
private var lastHeight: Int = 0
|
||||
private var lastWidth: Int = 0
|
||||
|
||||
override fun onDraw(canvas: android.graphics.Canvas) {
|
||||
super.onDraw(canvas)
|
||||
|
||||
// Various allocations:
|
||||
java.lang.<warning descr="Avoid object allocations during draw/layout operations (preallocate and reuse instead)">String("foo")</warning>
|
||||
val s = java.lang.<warning descr="Avoid object allocations during draw/layout operations (preallocate and reuse instead)">String("bar")</warning>
|
||||
|
||||
// This one should not be reported:
|
||||
@SuppressLint("DrawAllocation")
|
||||
val i = 5
|
||||
|
||||
// Cached object initialized lazily: should not complain about these
|
||||
if (cachedRect == null) {
|
||||
cachedRect = Rect(0, 0, 100, 100)
|
||||
}
|
||||
if (cachedRect == null || cachedRect!!.width() != 50) {
|
||||
cachedRect = Rect(0, 0, 50, 100)
|
||||
}
|
||||
|
||||
val b = java.lang.Boolean.valueOf(true)!! // auto-boxing
|
||||
dummy(1, 2)
|
||||
|
||||
// Non-allocations
|
||||
super.animate()
|
||||
dummy2(1, 2)
|
||||
|
||||
// This will involve allocations, but we don't track
|
||||
// inter-procedural stuff here
|
||||
someOtherMethod()
|
||||
}
|
||||
|
||||
internal fun dummy(foo: Int?, bar: Int) {
|
||||
dummy2(foo!!, bar)
|
||||
}
|
||||
|
||||
internal fun dummy2(foo: Int, bar: Int) {
|
||||
}
|
||||
|
||||
internal fun someOtherMethod() {
|
||||
// Allocations are okay here
|
||||
java.lang.String("foo")
|
||||
val s = java.lang.String("bar")
|
||||
val b = java.lang.Boolean.valueOf(true)!! // auto-boxing
|
||||
|
||||
|
||||
// Sparse array candidates
|
||||
val myMap = <warning descr="Use `new SparseArray<String>(...)` instead for better performance">HashMap<Int, String>()</warning>
|
||||
// Should use SparseBooleanArray
|
||||
val myBoolMap = <warning descr="Use `new SparseBooleanArray(...)` instead for better performance">HashMap<Int, Boolean>()</warning>
|
||||
// Should use SparseIntArray
|
||||
val myIntMap = java.util.<warning descr="Use new `SparseIntArray(...)` instead for better performance">HashMap<Int, Int>()</warning>
|
||||
|
||||
// This one should not be reported:
|
||||
@SuppressLint("UseSparseArrays")
|
||||
val myOtherMap = <warning descr="Use `new SparseArray<Object>(...)` instead for better performance">HashMap<Int, Any>()</warning>
|
||||
}
|
||||
|
||||
protected fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int,
|
||||
x: Boolean) {
|
||||
// wrong signature
|
||||
java.lang.String("not an error")
|
||||
}
|
||||
|
||||
protected fun onMeasure(widthMeasureSpec: Int) {
|
||||
// wrong signature
|
||||
java.lang.String("not an error")
|
||||
}
|
||||
|
||||
protected fun onLayout(changed: Boolean, left: Int, top: Int, right: Int,
|
||||
bottom: Int, wrong: Int) {
|
||||
// wrong signature
|
||||
java.lang.String("not an error")
|
||||
}
|
||||
|
||||
protected fun onLayout(changed: Boolean, left: Int, top: Int, right: Int) {
|
||||
// wrong signature
|
||||
java.lang.String("not an error")
|
||||
}
|
||||
|
||||
override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int,
|
||||
bottom: Int) {
|
||||
java.lang.<warning descr="Avoid object allocations during draw/layout operations (preallocate and reuse instead)">String("flag me")</warning>
|
||||
}
|
||||
|
||||
@SuppressWarnings("null") // not real code
|
||||
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
|
||||
java.lang.<warning descr="Avoid object allocations during draw/layout operations (preallocate and reuse instead)">String("flag me")</warning>
|
||||
|
||||
// Forbidden factory methods:
|
||||
Bitmap.<warning descr="Avoid object allocations during draw/layout operations (preallocate and reuse instead)">createBitmap(100, 100, null)</warning>
|
||||
android.graphics.Bitmap.<warning descr="Avoid object allocations during draw/layout operations (preallocate and reuse instead)">createScaledBitmap(null, 100, 100, false)</warning>
|
||||
BitmapFactory.<warning descr="Avoid object allocations during draw/layout operations (preallocate and reuse instead)">decodeFile(null)</warning>
|
||||
val canvas: Canvas? = null
|
||||
canvas!!.<warning descr="Avoid object allocations during draw operations: Use `Canvas.getClipBounds(Rect)` instead of `Canvas.getClipBounds()` which allocates a temporary `Rect`">getClipBounds()</warning> // allocates on your behalf
|
||||
canvas.<warning descr="Avoid object allocations during draw operations: Use `Canvas.getClipBounds(Rect)` instead of `Canvas.getClipBounds()` which allocates a temporary `Rect`">clipBounds</warning> // allocates on your behalf
|
||||
canvas.getClipBounds(null) // NOT an error
|
||||
|
||||
val layoutWidth = width
|
||||
val layoutHeight = height
|
||||
if (mAllowCrop && (mOverlay == null || mOverlay!!.width != layoutWidth ||
|
||||
mOverlay!!.height != layoutHeight)) {
|
||||
mOverlay = Bitmap.createBitmap(layoutWidth, layoutHeight, Bitmap.Config.ARGB_8888)
|
||||
mOverlayCanvas = Canvas(mOverlay!!)
|
||||
}
|
||||
|
||||
if (widthMeasureSpec == 42) {
|
||||
throw IllegalStateException("Test") // NOT an allocation
|
||||
}
|
||||
|
||||
// More lazy init tests
|
||||
var initialized = false
|
||||
if (!initialized) {
|
||||
java.lang.String("foo")
|
||||
initialized = true
|
||||
}
|
||||
|
||||
// NOT lazy initialization
|
||||
if (!initialized || mOverlay == null) {
|
||||
java.lang.<warning descr="Avoid object allocations during draw/layout operations (preallocate and reuse instead)">String("foo")</warning>
|
||||
}
|
||||
}
|
||||
|
||||
internal fun factories() {
|
||||
val i1 = 42
|
||||
val l1 = 42L
|
||||
val b1 = true
|
||||
val c1 = 'c'
|
||||
val f1 = 1.0f
|
||||
val d1 = 1.0
|
||||
|
||||
// The following should not generate errors:
|
||||
val i3 = Integer.valueOf(42)
|
||||
}
|
||||
|
||||
private val mAllowCrop: Boolean = false
|
||||
private var mOverlayCanvas: Canvas? = null
|
||||
private var mOverlay: Bitmap? = null
|
||||
|
||||
override fun layout(l: Int, t: Int, r: Int, b: Int) {
|
||||
// Using "this." to reference fields
|
||||
if (this.shader == null)
|
||||
this.shader = LinearGradient(0f, 0f, width.toFloat(), 0f, intArrayOf(0), null,
|
||||
Shader.TileMode.REPEAT)
|
||||
|
||||
val width = width
|
||||
val height = height
|
||||
|
||||
if (shader == null || lastWidth != width || lastHeight != height) {
|
||||
lastWidth = width
|
||||
lastHeight = height
|
||||
|
||||
shader = LinearGradient(0f, 0f, width.toFloat(), 0f, intArrayOf(0), null, Shader.TileMode.REPEAT)
|
||||
}
|
||||
}
|
||||
|
||||
fun inefficientSparseArray() {
|
||||
<warning descr="Use `new SparseIntArray(...)` instead for better performance">SparseArray<Int>()</warning> // Use SparseIntArray instead
|
||||
SparseArray<Long>() // Use SparseLongArray instead
|
||||
<warning descr="Use `new SparseBooleanArray(...)` instead for better performance">SparseArray<Boolean>()</warning> // Use SparseBooleanArray instead
|
||||
SparseArray<Any>() // OK
|
||||
}
|
||||
|
||||
fun longSparseArray() {
|
||||
// but only minSdkVersion >= 17 or if has v4 support lib
|
||||
val myStringMap = HashMap<Long, String>()
|
||||
}
|
||||
|
||||
fun byteSparseArray() {
|
||||
// bytes easily apply to ints
|
||||
val myByteMap = <warning descr="Use `new SparseArray<String>(...)` instead for better performance">HashMap<Byte, String>()</warning>
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintJavascriptInterfaceInspection
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.webkit.JavascriptInterface
|
||||
import android.webkit.WebView
|
||||
|
||||
@Suppress("UsePropertyAccessSyntax", "UNUSED_VARIABLE", "UNUSED_VALUE", "unused", "UNUSED_PARAMETER", "DEPRECATION")
|
||||
class JavaScriptTestK {
|
||||
fun test(webview: WebView) {
|
||||
webview.addJavascriptInterface(AnnotatedObject(), "myobj")
|
||||
|
||||
webview.addJavascriptInterface(InheritsFromAnnotated(), "myobj")
|
||||
webview.addJavascriptInterface(NonAnnotatedObject(), "myobj")
|
||||
|
||||
webview.addJavascriptInterface(null, "nothing")
|
||||
webview.addJavascriptInterface(object : Any() { @JavascriptInterface fun method() {} }, "nothing")
|
||||
webview.addJavascriptInterface(JavascriptFace(), "nothing")
|
||||
|
||||
var o: Any = NonAnnotatedObject()
|
||||
webview.addJavascriptInterface(o, "myobj")
|
||||
o = InheritsFromAnnotated()
|
||||
webview.addJavascriptInterface(o, "myobj")
|
||||
}
|
||||
|
||||
fun test(webview: WebView, object1: AnnotatedObject, object2: NonAnnotatedObject) {
|
||||
webview.addJavascriptInterface(object1, "myobj")
|
||||
webview.addJavascriptInterface(object2, "myobj")
|
||||
}
|
||||
|
||||
@SuppressLint("JavascriptInterface")
|
||||
fun testSuppressed(webview: WebView) {
|
||||
webview.addJavascriptInterface(NonAnnotatedObject(), "myobj")
|
||||
}
|
||||
|
||||
fun testLaterReassignment(webview: WebView) {
|
||||
var o: Any = NonAnnotatedObject()
|
||||
val t = o
|
||||
webview.addJavascriptInterface(t, "myobj")
|
||||
o = AnnotatedObject()
|
||||
}
|
||||
|
||||
class NonAnnotatedObject() {
|
||||
fun test1() {}
|
||||
fun test2() {}
|
||||
}
|
||||
|
||||
open class AnnotatedObject {
|
||||
@JavascriptInterface
|
||||
open fun test1() {}
|
||||
|
||||
open fun test2() {}
|
||||
|
||||
@JavascriptInterface
|
||||
fun test3() {}
|
||||
}
|
||||
|
||||
class InheritsFromAnnotated : AnnotatedObject() {
|
||||
override fun test1() {}
|
||||
override fun test2() {}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class JavascriptFace {
|
||||
fun method() {}
|
||||
}
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintInflateParamsInspection
|
||||
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.BaseAdapter
|
||||
|
||||
@Suppress("UsePropertyAccessSyntax", "UNUSED_VARIABLE", "unused", "UNUSED_PARAMETER", "DEPRECATION")
|
||||
abstract class LayoutInflationTest : BaseAdapter() {
|
||||
lateinit var mInflater: LayoutInflater
|
||||
|
||||
override fun getView(position: Int, convertView: View, parent: ViewGroup): View {
|
||||
var view = <warning descr="[VARIABLE_WITH_REDUNDANT_INITIALIZER] Variable 'view' initializer is redundant">convertView</warning>
|
||||
<warning descr="[UNUSED_VALUE] The value 'mInflater.inflate(R.layout.your_layout, null)' assigned to 'var view: View defined in LayoutInflationTest.getView' is never used">view =</warning> mInflater.inflate(R.layout.your_layout, null)
|
||||
<warning descr="[UNUSED_VALUE] The value 'mInflater.inflate(R.layout.your_layout, null, true)' assigned to 'var view: View defined in LayoutInflationTest.getView' is never used">view =</warning> mInflater.inflate(R.layout.your_layout, null, true)
|
||||
view = mInflater.inflate(R.layout.your_layout, parent)
|
||||
view = WeirdInflater.inflate(view, null)
|
||||
|
||||
return view
|
||||
}
|
||||
|
||||
object WeirdInflater {
|
||||
fun inflate(view: View, parent: View?) = view
|
||||
}
|
||||
|
||||
object R {
|
||||
object layout {
|
||||
val your_layout = 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("UsePropertyAccessSyntax", "UNUSED_VARIABLE", "unused", "UNUSED_PARAMETER", "DEPRECATION")
|
||||
abstract class LayoutInflationTest2 : BaseAdapter() {
|
||||
lateinit var mInflater: LayoutInflater
|
||||
|
||||
override fun getView(position: Int, convertView: View, parent: ViewGroup): View? {
|
||||
return if (true) {
|
||||
mInflater.inflate(R.layout.your_layout, parent)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
object R {
|
||||
object layout {
|
||||
val your_layout = 1
|
||||
}
|
||||
}
|
||||
}
|
||||
Vendored
+111
@@ -0,0 +1,111 @@
|
||||
// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintLogConditionalInspection
|
||||
// INSPECTION_CLASS2: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintLogTagMismatchInspection
|
||||
// INSPECTION_CLASS3: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintLongLogTagInspection
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.util.Log
|
||||
import android.util.Log.DEBUG
|
||||
|
||||
@SuppressWarnings("UnusedDeclaration")
|
||||
@Suppress("UsePropertyAccessSyntax", "UNUSED_VARIABLE", "unused", "UNUSED_PARAMETER", "DEPRECATION")
|
||||
class LogTest {
|
||||
|
||||
fun checkConditional(m: String) {
|
||||
Log.d(TAG1, "message") // ok: unconditional, but not performing computation
|
||||
Log.d(TAG1, m) // ok: unconditional, but not performing computation
|
||||
Log.d(TAG1, "a" + "b") // ok: unconditional, but not performing non-constant computation
|
||||
Log.d(TAG1, Constants.MY_MESSAGE) // ok: unconditional, but constant string
|
||||
Log.<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.<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.w(TAG1, toString()) // ok: only flagging debug/info messages
|
||||
Log.wtf(TAG1, toString()) // ok: only flagging debug/info messages
|
||||
if (Log.isLoggable(TAG1, 0)) {
|
||||
Log.d(TAG1, toString()) // ok: conditional
|
||||
}
|
||||
}
|
||||
|
||||
fun checkWrongTag(tag: String) {
|
||||
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: `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)) {
|
||||
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)) {
|
||||
Log.d("my_tag", "message") // ok: strings equal
|
||||
}
|
||||
if (Log.isLoggable(tag, Log.DEBUG)) {
|
||||
Log.d(tag, "message") // ok: same variable
|
||||
}
|
||||
}
|
||||
|
||||
fun checkLongTag(shouldLog: Boolean) {
|
||||
if (shouldLog) {
|
||||
// String literal tags
|
||||
Log.d("short_tag", "message") // ok: short
|
||||
Log.<error descr="The logging tag can be at most 23 characters, was 43 (really_really_really_really_really_long_tag)">d("really_really_really_really_really_long_tag", "message")</error> // error: too long
|
||||
|
||||
// Resolved field tags
|
||||
Log.d(TAG1, "message") // ok: short
|
||||
Log.d(TAG22, "message") // ok: short
|
||||
Log.d(TAG23, "message") // ok: threshold
|
||||
Log.<error descr="The logging tag can be at most 23 characters, was 24 (123456789012345678901234)">d(TAG24, "message")</error> // error: too long
|
||||
Log.<error descr="The logging tag can be at most 23 characters, was 39 (MyReallyReallyReallyReallyReallyLongTag)">d(LONG_TAG, "message")</error> // error: way too long
|
||||
|
||||
// Locally defined variable tags
|
||||
val LOCAL_TAG = "MyReallyReallyReallyReallyReallyLongTag"
|
||||
Log.<error descr="The logging tag can be at most 23 characters, was 39 (MyReallyReallyReallyReallyReallyLongTag)">d(LOCAL_TAG, "message")</error> // error: too long
|
||||
|
||||
// Concatenated tags
|
||||
Log.<error descr="The logging tag can be at most 23 characters, was 28 (1234567890123456789012MyTag1)">d(TAG22 + TAG1, "message")</error> // error: too long
|
||||
Log.<error descr="The logging tag can be at most 23 characters, was 27 (1234567890123456789012MyTag)">d(TAG22 + "MyTag", "message")</error> // error: too long
|
||||
}
|
||||
}
|
||||
|
||||
fun checkWrongLevel(tag: String) {
|
||||
if (Log.isLoggable(TAG1, Log.DEBUG)) {
|
||||
Log.d(TAG1, "message") // ok: right level
|
||||
}
|
||||
if (Log.isLoggable(TAG1, Log.INFO)) {
|
||||
Log.i(TAG1, "message") // ok: right level
|
||||
}
|
||||
if (Log.isLoggable(TAG1, <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
|
||||
}
|
||||
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
|
||||
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>)) {
|
||||
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
|
||||
}
|
||||
if (Log.isLoggable(TAG1, Constants.MY_LEVEL)) {
|
||||
Log.d(TAG1, "message") // ok: unknown level alias
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("all")
|
||||
fun suppressed1() {
|
||||
Log.d(TAG1, "message") // ok: suppressed
|
||||
}
|
||||
|
||||
@SuppressLint("LogConditional")
|
||||
fun suppressed2() {
|
||||
Log.d(TAG1, "message") // ok: suppressed
|
||||
}
|
||||
|
||||
private object Constants {
|
||||
val MY_MESSAGE = "My Message"
|
||||
val MY_LEVEL = 5
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val TAG1 = "MyTag1"
|
||||
private val TAG2 = "MyTag2"
|
||||
private val TAG22 = "1234567890123456789012"
|
||||
private val TAG23 = "12345678901234567890123"
|
||||
private val TAG24 = "123456789012345678901234"
|
||||
private val LONG_TAG = "MyReallyReallyReallyReallyReallyLongTag"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintUnlocalizedSmsInspection
|
||||
|
||||
import android.content.Context
|
||||
import android.telephony.SmsManager
|
||||
|
||||
@Suppress("UsePropertyAccessSyntax", "UNUSED_VARIABLE", "unused", "UNUSED_PARAMETER", "DEPRECATION")
|
||||
class NonInternationalizedSmsDetectorTest {
|
||||
private fun sendLocalizedMessage(context: Context) {
|
||||
// Don't warn here
|
||||
val sms = SmsManager.getDefault()
|
||||
sms.sendTextMessage("+1234567890", null, null, null, null)
|
||||
}
|
||||
|
||||
private fun sendAlternativeCountryPrefix(context: Context) {
|
||||
// Do warn here
|
||||
val sms = SmsManager.getDefault()
|
||||
sms.sendMultipartTextMessage("<warning descr="To make sure the SMS can be sent by all users, please start the SMS number with a + and a country code or restrict the code invocation to people in the country you are targeting.">001234567890</warning>", null, null, null, null)
|
||||
}
|
||||
}
|
||||
+61
@@ -0,0 +1,61 @@
|
||||
// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintOverrideAbstractInspection
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.annotation.TargetApi
|
||||
import android.os.Build
|
||||
import android.service.notification.NotificationListenerService
|
||||
import android.service.notification.StatusBarNotification
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR2)
|
||||
@Suppress("UsePropertyAccessSyntax", "UNUSED_VARIABLE", "unused", "UNUSED_PARAMETER", "DEPRECATION")
|
||||
class OverrideConcreteTest2 {
|
||||
// OK: This one specifies both methods
|
||||
private open class MyNotificationListenerService1 : NotificationListenerService() {
|
||||
override fun onNotificationPosted(statusBarNotification: StatusBarNotification) {
|
||||
}
|
||||
|
||||
override fun onNotificationRemoved(statusBarNotification: StatusBarNotification) {
|
||||
}
|
||||
}
|
||||
|
||||
// Error: Misses onNotificationPosted
|
||||
private class <error descr="Must override `android.service.notification.NotificationListenerService.onNotificationPosted(android.service.notification.StatusBarNotification)`: Method was abstract until 21, and your `minSdkVersion` is 18">MyNotificationListenerService2</error> : NotificationListenerService() {
|
||||
override fun onNotificationRemoved(statusBarNotification: StatusBarNotification) {
|
||||
}
|
||||
}
|
||||
|
||||
// Error: Misses onNotificationRemoved
|
||||
private open class <error descr="Must override `android.service.notification.NotificationListenerService.onNotificationRemoved(android.service.notification.StatusBarNotification)`: Method was abstract until 21, and your `minSdkVersion` is 18">MyNotificationListenerService3</error> : NotificationListenerService() {
|
||||
override fun onNotificationPosted(statusBarNotification: StatusBarNotification) {
|
||||
}
|
||||
}
|
||||
|
||||
// Error: Missing both; wrong signatures (first has wrong arg count, second has wrong type)
|
||||
private class <error descr="Must override `android.service.notification.NotificationListenerService.onNotificationPosted(android.service.notification.StatusBarNotification)`: Method was abstract until 21, and your `minSdkVersion` is 18">MyNotificationListenerService4</error> : NotificationListenerService() {
|
||||
fun onNotificationPosted(statusBarNotification: StatusBarNotification, flags: Int) {
|
||||
}
|
||||
|
||||
fun onNotificationRemoved(statusBarNotification: Int) {
|
||||
}
|
||||
}
|
||||
|
||||
// OK: Inherits from a class which define both
|
||||
private class MyNotificationListenerService5 : MyNotificationListenerService1()
|
||||
|
||||
// OK: Inherits from a class which defines only one, but the other one is defined here
|
||||
private class MyNotificationListenerService6 : MyNotificationListenerService3() {
|
||||
override fun onNotificationRemoved(statusBarNotification: StatusBarNotification) {
|
||||
}
|
||||
}
|
||||
|
||||
// Error: Inheriting from a class which only defines one
|
||||
private class <error descr="Must override `android.service.notification.NotificationListenerService.onNotificationRemoved(android.service.notification.StatusBarNotification)`: Method was abstract until 21, and your `minSdkVersion` is 18">MyNotificationListenerService7</error> : MyNotificationListenerService3()
|
||||
|
||||
// OK: Has target api setting a local version that is high enough
|
||||
@TargetApi(21)
|
||||
private class MyNotificationListenerService8 : NotificationListenerService()
|
||||
|
||||
// OK: Suppressed
|
||||
@SuppressLint("OverrideAbstract")
|
||||
private class MyNotificationListenerService9 : MyNotificationListenerService1()
|
||||
}
|
||||
Vendored
+100
@@ -0,0 +1,100 @@
|
||||
// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintParcelCreatorInspection
|
||||
|
||||
@file:Suppress("UsePropertyAccessSyntax", "UNUSED_VARIABLE", "unused", "UNUSED_PARAMETER", "DEPRECATION")
|
||||
import android.os.Parcel
|
||||
import android.os.Parcelable
|
||||
|
||||
class <error descr="This class implements `Parcelable` but does not provide a `CREATOR` field">MyParcelable1</error> : Parcelable {
|
||||
override fun describeContents() = 0
|
||||
override fun writeToParcel(arg0: Parcel, arg1: Int) {}
|
||||
}
|
||||
|
||||
internal class MyParcelable2 : Parcelable {
|
||||
override fun describeContents() = 0
|
||||
|
||||
override fun writeToParcel(arg0: Parcel, arg1: Int) {}
|
||||
|
||||
companion object {
|
||||
@JvmField
|
||||
val CREATOR: Parcelable.Creator<String> = object : Parcelable.Creator<String> {
|
||||
override fun newArray(size: Int) = null!!
|
||||
override fun createFromParcel(source: Parcel?) = null!!
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal class MyParcelable3 : Parcelable {
|
||||
override fun describeContents() = 0
|
||||
override fun writeToParcel(arg0: Parcel, arg1: Int) {}
|
||||
|
||||
companion object {
|
||||
@JvmField
|
||||
val CREATOR = 0 // Wrong type
|
||||
}
|
||||
}
|
||||
|
||||
class RecyclerViewScrollPosition(val position: Int, val topOffset: Int): Parcelable {
|
||||
override fun describeContents(): Int = 0
|
||||
override fun writeToParcel(dest: Parcel, flags: Int) {
|
||||
dest.writeInt(position)
|
||||
dest.writeInt(topOffset)
|
||||
}
|
||||
|
||||
companion object {
|
||||
@JvmField
|
||||
val CREATOR = object : Parcelable.Creator<RecyclerViewScrollPosition> {
|
||||
override fun createFromParcel(parcel: Parcel): RecyclerViewScrollPosition {
|
||||
val position = parcel.readInt()
|
||||
val topOffset = parcel.readInt()
|
||||
return RecyclerViewScrollPosition(position, topOffset)
|
||||
}
|
||||
|
||||
override fun newArray(size: Int): Array<RecyclerViewScrollPosition?> = arrayOfNulls(size)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
class RecyclerViewScrollPositionWithoutJvmF(val position: Int, val topOffset: Int): Parcelable {
|
||||
override fun describeContents(): Int = 0
|
||||
override fun writeToParcel(dest: Parcel, flags: Int) {
|
||||
dest.writeInt(position)
|
||||
dest.writeInt(topOffset)
|
||||
}
|
||||
|
||||
companion object {
|
||||
val CREATOR = object : Parcelable.Creator<RecyclerViewScrollPosition> {
|
||||
override fun createFromParcel(parcel: Parcel): RecyclerViewScrollPosition {
|
||||
val position = parcel.readInt()
|
||||
val topOffset = parcel.readInt()
|
||||
return RecyclerViewScrollPosition(position, topOffset)
|
||||
}
|
||||
|
||||
override fun newArray(size: Int): Array<RecyclerViewScrollPosition?> = arrayOfNulls(size)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
class RecyclerViewScrollPosition2(val position: Int, val topOffset: Int): Parcelable {
|
||||
override fun describeContents(): Int = 0
|
||||
override fun writeToParcel(dest: Parcel, flags: Int) {
|
||||
dest.writeInt(position)
|
||||
dest.writeInt(topOffset)
|
||||
}
|
||||
|
||||
companion object CREATOR: Parcelable.Creator<RecyclerViewScrollPosition> {
|
||||
override fun createFromParcel(parcel: Parcel): RecyclerViewScrollPosition {
|
||||
val position = parcel.readInt()
|
||||
val topOffset = parcel.readInt()
|
||||
return RecyclerViewScrollPosition(position, topOffset)
|
||||
}
|
||||
|
||||
override fun newArray(size: Int): Array<RecyclerViewScrollPosition?> = arrayOfNulls(size)
|
||||
}
|
||||
}
|
||||
|
||||
internal abstract class MyParcelable4 : Parcelable {
|
||||
override fun describeContents() = 0
|
||||
override fun writeToParcel(arg0: Parcel, arg1: Int) {}
|
||||
}
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintSdCardPathInspection
|
||||
|
||||
import java.io.File
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
|
||||
/**
|
||||
* Ignore comments - create("/sdcard/foo")
|
||||
*/
|
||||
@Suppress("UsePropertyAccessSyntax", "UNUSED_VARIABLE", "unused", "UNUSED_PARAMETER", "DEPRECATION")
|
||||
class SdCardTest {
|
||||
internal var deviceDir = File("<warning descr="Do not hardcode \"/sdcard/\"; use `Environment.getExternalStorageDirectory().getPath()` instead">/sdcard/vr</warning>")
|
||||
|
||||
init {
|
||||
if (PROFILE_STARTUP) {
|
||||
android.os.Debug.startMethodTracing("<warning descr="Do not hardcode \"/sdcard/\"; use `Environment.getExternalStorageDirectory().getPath()` instead"><warning descr="Do not hardcode \"/sdcard/\"; use `Environment.getExternalStorageDirectory().getPath()` instead">/sdcard/launcher</warning></warning>")
|
||||
}
|
||||
|
||||
if (File("<warning descr="Do not hardcode \"/sdcard/\"; use `Environment.getExternalStorageDirectory().getPath()` instead"><warning descr="Do not hardcode \"/sdcard/\"; use `Environment.getExternalStorageDirectory().getPath()` instead">/sdcard</warning></warning>").exists()) {
|
||||
}
|
||||
val FilePath = "<warning descr="Do not hardcode \"/sdcard/\"; use `Environment.getExternalStorageDirectory().getPath()` instead"><warning descr="Do not hardcode \"/sdcard/\"; use `Environment.getExternalStorageDirectory().getPath()` instead">/sdcard/</warning></warning>" + File("test")
|
||||
System.setProperty("foo.bar", "file://sdcard")
|
||||
|
||||
|
||||
val intent = Intent(Intent.ACTION_PICK)
|
||||
intent.setDataAndType(Uri.parse("<warning descr="Do not hardcode \"/sdcard/\"; use `Environment.getExternalStorageDirectory().getPath()` instead"><warning descr="Do not hardcode \"/sdcard/\"; use `Environment.getExternalStorageDirectory().getPath()` instead">file://sdcard/foo.json</warning></warning>"), "application/bar-json")
|
||||
intent.putExtra("path-filter", "<warning descr="Do not hardcode \"/sdcard/\"; use `Environment.getExternalStorageDirectory().getPath()` instead"><warning descr="Do not hardcode \"/sdcard/\"; use `Environment.getExternalStorageDirectory().getPath()` instead">/sdcard(/.+)*</warning></warning>")
|
||||
intent.putExtra("start-dir", "<warning descr="Do not hardcode \"/sdcard/\"; use `Environment.getExternalStorageDirectory().getPath()` instead"><warning descr="Do not hardcode \"/sdcard/\"; use `Environment.getExternalStorageDirectory().getPath()` instead">/sdcard</warning></warning>")
|
||||
val mypath = "<warning descr="Do not hardcode \"`/data/`\"; use `Context.getFilesDir().getPath()` instead"><warning descr="Do not hardcode \"`/data/`\"; use `Context.getFilesDir().getPath()` instead">/data/data/foo</warning></warning>"
|
||||
val base = "<warning descr="Do not hardcode \"`/data/`\"; use `Context.getFilesDir().getPath()` instead"><warning descr="Do not hardcode \"`/data/`\"; use `Context.getFilesDir().getPath()` instead">/data/data/foo.bar/test-profiling</warning></warning>"
|
||||
val s = "<warning descr="Do not hardcode \"/sdcard/\"; use `Environment.getExternalStorageDirectory().getPath()` instead"><warning descr="Do not hardcode \"/sdcard/\"; use `Environment.getExternalStorageDirectory().getPath()` instead">file://sdcard/foo</warning></warning>"
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val PROFILE_STARTUP = true
|
||||
private val SDCARD_TEST_HTML = "<warning descr="Do not hardcode \"/sdcard/\"; use `Environment.getExternalStorageDirectory().getPath()` instead">/sdcard/test.html</warning>"
|
||||
val SDCARD_ROOT = "<warning descr="Do not hardcode \"/sdcard/\"; use `Environment.getExternalStorageDirectory().getPath()` instead">/sdcard</warning>"
|
||||
val PACKAGES_PATH = "<warning descr="Do not hardcode \"/sdcard/\"; use `Environment.getExternalStorageDirectory().getPath()` instead">/sdcard/o/packages/</warning>"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintSetJavaScriptEnabledInspection
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.Activity
|
||||
import android.webkit.WebView
|
||||
|
||||
@Suppress("UsePropertyAccessSyntax", "UNUSED_VARIABLE", "unused", "UNUSED_PARAMETER", "DEPRECATION")
|
||||
public class HelloWebApp : Activity() {
|
||||
|
||||
fun test(webView: WebView) {
|
||||
webView.settings.<warning descr="Using `setJavaScriptEnabled` can introduce XSS vulnerabilities into you application, review carefully.">javaScriptEnabled</warning> = true // bad
|
||||
webView.getSettings().<warning descr="Using `setJavaScriptEnabled` can introduce XSS vulnerabilities into you application, review carefully.">setJavaScriptEnabled(true)</warning> // bad
|
||||
webView.getSettings().setJavaScriptEnabled(false) // good
|
||||
webView.loadUrl("file:///android_asset/www/index.html")
|
||||
}
|
||||
|
||||
@SuppressLint("SetJavaScriptEnabled")
|
||||
fun suppressed(webView: WebView) {
|
||||
webView.getSettings().javaScriptEnabled = true; // bad
|
||||
webView.getSettings().setJavaScriptEnabled(true) // bad
|
||||
webView.getSettings().setJavaScriptEnabled(false); // good
|
||||
webView.loadUrl("file:///android_asset/www/index.html");
|
||||
}
|
||||
}
|
||||
+69
@@ -0,0 +1,69 @@
|
||||
// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintCommitPrefEditsInspection
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.Context
|
||||
import android.os.Bundle
|
||||
import android.preference.PreferenceManager
|
||||
|
||||
@Suppress("UsePropertyAccessSyntax", "UNUSED_VARIABLE", "unused", "UNUSED_PARAMETER", "DEPRECATION")
|
||||
class SharedPrefsText(context: Context) : Activity() {
|
||||
// OK 1
|
||||
fun onCreate1(savedInstanceState: Bundle) {
|
||||
super.onCreate(savedInstanceState)
|
||||
val preferences = PreferenceManager.getDefaultSharedPreferences(this)
|
||||
val editor = preferences.edit()
|
||||
editor.putString("foo", "bar")
|
||||
editor.putInt("bar", 42)
|
||||
editor.commit()
|
||||
}
|
||||
|
||||
// OK 2
|
||||
fun onCreate2(savedInstanceState: Bundle, apply: Boolean) {
|
||||
super.onCreate(savedInstanceState)
|
||||
val preferences = PreferenceManager.getDefaultSharedPreferences(this)
|
||||
val editor = preferences.edit()
|
||||
editor.putString("foo", "bar")
|
||||
editor.putInt("bar", 42)
|
||||
if (apply) {
|
||||
editor.apply()
|
||||
}
|
||||
}
|
||||
|
||||
// Not a bug
|
||||
fun test(foo: Foo) {
|
||||
val bar1 = foo.edit()
|
||||
val bar3 = edit()
|
||||
apply()
|
||||
}
|
||||
|
||||
internal fun apply() {
|
||||
|
||||
}
|
||||
|
||||
fun edit(): Bar {
|
||||
return Bar()
|
||||
}
|
||||
|
||||
class Foo {
|
||||
internal fun edit(): Bar {
|
||||
return Bar()
|
||||
}
|
||||
}
|
||||
|
||||
class Bar
|
||||
|
||||
// Bug
|
||||
fun bug1(savedInstanceState: Bundle) {
|
||||
super.onCreate(savedInstanceState)
|
||||
val preferences = PreferenceManager.getDefaultSharedPreferences(this)
|
||||
val editor = preferences.<warning descr="`SharedPreferences.edit()` without a corresponding `commit()` or `apply()` call">edit()</warning>
|
||||
editor.putString("foo", "bar")
|
||||
editor.putInt("bar", 42)
|
||||
}
|
||||
|
||||
init {
|
||||
val preferences = PreferenceManager.getDefaultSharedPreferences(context)
|
||||
val editor = preferences.<warning descr="`SharedPreferences.edit()` without a corresponding `commit()` or `apply()` call"><warning descr="`SharedPreferences.edit()` without a corresponding `commit()` or `apply()` call">edit()</warning></warning>
|
||||
editor.putString("foo", "bar")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintSQLiteStringInspection
|
||||
|
||||
import android.database.sqlite.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>
|
||||
|
||||
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>
|
||||
}
|
||||
Vendored
+7
@@ -0,0 +1,7 @@
|
||||
// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintSQLiteStringInspection
|
||||
|
||||
import android.database.sqlite.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>
|
||||
}
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintServiceCastInspection
|
||||
|
||||
import android.content.ClipboardManager
|
||||
import android.app.Activity
|
||||
import android.app.WallpaperManager
|
||||
import android.content.Context
|
||||
import android.hardware.display.DisplayManager
|
||||
import android.service.wallpaper.WallpaperService
|
||||
|
||||
@Suppress("UsePropertyAccessSyntax", "UNUSED_VARIABLE", "unused", "UNUSED_PARAMETER", "DEPRECATION")
|
||||
class SystemServiceTest : Activity() {
|
||||
|
||||
fun test1() {
|
||||
val displayServiceOk = getSystemService(DISPLAY_SERVICE) as DisplayManager
|
||||
val displayServiceWrong = <error descr="Suspicious cast to `DisplayManager` for a `DEVICE_POLICY_SERVICE`: expected `DevicePolicyManager`">getSystemService(DEVICE_POLICY_SERVICE) as DisplayManager</error>
|
||||
val wallPaperOk = getSystemService(WALLPAPER_SERVICE) as WallpaperService
|
||||
val wallPaperWrong = <error descr="Suspicious cast to `WallpaperManager` for a `WALLPAPER_SERVICE`: expected `WallpaperService`">getSystemService(WALLPAPER_SERVICE) as WallpaperManager</error>
|
||||
}
|
||||
|
||||
fun test2(context: Context) {
|
||||
val displayServiceOk = context.getSystemService(DISPLAY_SERVICE) as DisplayManager
|
||||
val displayServiceWrong = <error descr="Suspicious cast to `DisplayManager` for a `DEVICE_POLICY_SERVICE`: expected `DevicePolicyManager`">context.getSystemService(DEVICE_POLICY_SERVICE) as DisplayManager</error>
|
||||
}
|
||||
|
||||
fun clipboard(context: Context) {
|
||||
val clipboard = context.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
|
||||
val clipboard2 = context.getSystemService(Context.CLIPBOARD_SERVICE) as android.content.ClipboardManager
|
||||
}
|
||||
}
|
||||
Vendored
+72
@@ -0,0 +1,72 @@
|
||||
// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintShowToastInspection
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.Context
|
||||
import android.widget.Toast
|
||||
|
||||
@Suppress("UsePropertyAccessSyntax", "UNUSED_VARIABLE", "unused", "UNUSED_PARAMETER", "DEPRECATION")
|
||||
class ToastTest(context: Context) : Activity() {
|
||||
private fun createToast(context: Context): Toast {
|
||||
// Don't warn here
|
||||
return Toast.makeText(context, "foo", Toast.LENGTH_LONG)
|
||||
}
|
||||
|
||||
private fun insideRunnable(context: Context) {
|
||||
Runnable {
|
||||
Toast.makeText(context, "foo", Toast.LENGTH_LONG).show()
|
||||
}
|
||||
|
||||
Runnable {
|
||||
val toast = Toast.makeText(context, "foo", Toast.LENGTH_LONG)
|
||||
if (5 > 3) {
|
||||
toast.show()
|
||||
}
|
||||
}
|
||||
|
||||
Runnable {
|
||||
Toast.<warning descr="Toast created but not shown: did you forget to call `show()` ?">makeText</warning>(context, "foo", Toast.LENGTH_LONG)
|
||||
}
|
||||
}
|
||||
|
||||
private fun showToast(context: Context) {
|
||||
// Don't warn here
|
||||
val toast = Toast.makeText(context, "foo", Toast.LENGTH_LONG)
|
||||
System.out.println("Other intermediate code here")
|
||||
val temp = 5 + 2
|
||||
toast.show()
|
||||
}
|
||||
|
||||
private fun showToast2(context: Context) {
|
||||
// Don't warn here
|
||||
val duration = Toast.LENGTH_LONG
|
||||
Toast.makeText(context, "foo", Toast.LENGTH_LONG).show()
|
||||
Toast.makeText(context, R.string.app_name, duration).show()
|
||||
}
|
||||
|
||||
private fun broken(context: Context) {
|
||||
// Errors
|
||||
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</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
|
||||
}
|
||||
|
||||
init {
|
||||
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")
|
||||
private fun checkSuppress1(context: Context) {
|
||||
val toast = Toast.makeText(this, "MyToast", Toast.LENGTH_LONG)
|
||||
}
|
||||
|
||||
private fun checkSuppress2(context: Context) {
|
||||
@android.annotation.SuppressLint("ShowToast")
|
||||
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 {
|
||||
object string {
|
||||
val app_name = 1
|
||||
}
|
||||
}
|
||||
}
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintUseValueOfInspection
|
||||
|
||||
@Suppress("UsePropertyAccessSyntax", "UNUSED_VARIABLE", "unused", "UNUSED_PARAMETER", "DEPRECATION")
|
||||
class Simple {
|
||||
fun test() {
|
||||
<warning descr="Use `Integer.valueOf(5)` instead">Integer(5)</warning>
|
||||
}
|
||||
}
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintViewConstructorInspection
|
||||
|
||||
import android.content.Context
|
||||
import android.util.AttributeSet
|
||||
import android.view.View
|
||||
import android.widget.TextView
|
||||
|
||||
class View1(context: Context?) : View(context)
|
||||
class View2(context: Context?, attrs: AttributeSet?) : View(context, attrs)
|
||||
class View3(context: Context?, attrs: AttributeSet?, defStyleAttr: Int) : TextView(context, attrs, defStyleAttr)
|
||||
|
||||
// Error
|
||||
class <warning descr="Custom view `View4` is missing constructor used by tools: `(Context)` or `(Context,AttributeSet)` or `(Context,AttributeSet,int)`">View4</warning>(<warning descr="[UNUSED_PARAMETER] Parameter 'int' is never used">int</warning>: Int, context: Context?) : View(context)
|
||||
|
||||
// Error
|
||||
class <warning descr="Custom view `View5` is missing constructor used by tools: `(Context)` or `(Context,AttributeSet)` or `(Context,AttributeSet,int)`">View5</warning>(context: Context?, attrs: AttributeSet?, val name: String) : View(context, attrs)
|
||||
|
||||
class View6 : View {
|
||||
constructor(context: Context) : super(context) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
class View7 : View {
|
||||
constructor(context: Context) : super(context)
|
||||
constructor(context: Context, attrs: AttributeSet?) : super(context, attrs)
|
||||
}
|
||||
|
||||
// Error
|
||||
class <warning descr="Custom view `View8` is missing constructor used by tools: `(Context)` or `(Context,AttributeSet)` or `(Context,AttributeSet,int)`">View8</warning> : View {
|
||||
constructor(context: Context, <warning descr="[UNUSED_PARAMETER] Parameter 'a' is never used">a</warning>: Int) : super(context)
|
||||
}
|
||||
+154
@@ -0,0 +1,154 @@
|
||||
// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintViewHolderInspection
|
||||
|
||||
@file:Suppress("NAME_SHADOWING", "unused", "UNUSED_VALUE", "VARIABLE_WITH_REDUNDANT_INITIALIZER", "UNUSED_VARIABLE")
|
||||
|
||||
import android.content.Context
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.BaseAdapter
|
||||
import android.widget.LinearLayout
|
||||
import android.widget.TextView
|
||||
import java.util.ArrayList
|
||||
|
||||
@SuppressWarnings("ConstantConditions", "UnusedDeclaration")
|
||||
abstract class ViewHolderTest : BaseAdapter() {
|
||||
override fun getCount() = 0
|
||||
override fun getItem(position: Int) = null
|
||||
override fun getItemId(position: Int) = 0L
|
||||
|
||||
class Adapter1 : ViewHolderTest() {
|
||||
override fun getView(position: Int, convertView: View, parent: ViewGroup) = null
|
||||
}
|
||||
|
||||
class Adapter2 : ViewHolderTest() {
|
||||
lateinit var mInflater: LayoutInflater
|
||||
|
||||
override fun getView(position: Int, convertView: View, parent: ViewGroup): View {
|
||||
var convertView = convertView
|
||||
// Should use View Holder pattern here
|
||||
convertView = mInflater.<warning descr="Unconditional layout inflation from view adapter: Should use View Holder pattern (use recycled view passed into this method as the second parameter) for smoother scrolling">inflate(R.layout.your_layout, null)</warning>
|
||||
|
||||
val text = convertView.findViewById(R.id.text) as TextView
|
||||
text.text = "Position " + position
|
||||
|
||||
return convertView
|
||||
}
|
||||
}
|
||||
|
||||
class Adapter3 : ViewHolderTest() {
|
||||
lateinit var mInflater: LayoutInflater
|
||||
|
||||
override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
|
||||
var convertView = convertView
|
||||
// Already using View Holder pattern
|
||||
if (convertView == null) {
|
||||
convertView = mInflater.inflate(R.layout.your_layout, null)
|
||||
}
|
||||
|
||||
val text = convertView!!.findViewById(R.id.text) as TextView
|
||||
text.text = "Position " + position
|
||||
|
||||
return convertView
|
||||
}
|
||||
}
|
||||
|
||||
class Adapter4 : ViewHolderTest() {
|
||||
lateinit var mInflater: LayoutInflater
|
||||
|
||||
override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
|
||||
var convertView = convertView
|
||||
// Already using View Holder pattern
|
||||
//noinspection StatementWithEmptyBody
|
||||
if (convertView != null) {
|
||||
} else {
|
||||
convertView = mInflater.inflate(R.layout.your_layout, null)
|
||||
}
|
||||
|
||||
val text = convertView!!.findViewById(R.id.text) as TextView
|
||||
text.text = "Position " + position
|
||||
|
||||
return convertView
|
||||
}
|
||||
}
|
||||
|
||||
class Adapter5 : ViewHolderTest() {
|
||||
lateinit var mInflater: LayoutInflater
|
||||
|
||||
override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
|
||||
var convertView = convertView
|
||||
// Already using View Holder pattern
|
||||
convertView = if (convertView == null) mInflater.inflate(R.layout.your_layout, null) else convertView
|
||||
|
||||
val text = convertView!!.findViewById(R.id.text) as TextView
|
||||
text.text = "Position " + position
|
||||
|
||||
return convertView
|
||||
}
|
||||
}
|
||||
|
||||
class Adapter6 : ViewHolderTest() {
|
||||
private val mContext: Context? = null
|
||||
private var mLayoutInflator: LayoutInflater? = null
|
||||
private lateinit var mLapTimes: ArrayList<Double>
|
||||
|
||||
override fun getView(position: Int, convertView: View, parent: ViewGroup): View {
|
||||
if (mLayoutInflator == null)
|
||||
mLayoutInflator = mContext!!.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater
|
||||
|
||||
var v: View? = convertView
|
||||
if (v == null) v = mLayoutInflator!!.inflate(R.layout.your_layout, null)
|
||||
|
||||
val listItemHolder = v!!.findViewById(R.id.laptimes_list_item_holder) as LinearLayout
|
||||
listItemHolder.removeAllViews()
|
||||
|
||||
for (i in 1..5) {
|
||||
val lapItemView = mLayoutInflator!!.inflate(R.layout.laptime_item, null)
|
||||
if (i == 0) {
|
||||
val t = lapItemView.findViewById(R.id.laptime_text) as TextView
|
||||
}
|
||||
|
||||
val t2 = lapItemView.findViewById(R.id.laptime_text2) as TextView
|
||||
if (i < mLapTimes.size - 1 && mLapTimes.size > 1) {
|
||||
var laptime = mLapTimes[i] - mLapTimes[i + 1]
|
||||
if (laptime < 0) laptime = mLapTimes[i]
|
||||
}
|
||||
|
||||
listItemHolder.addView(lapItemView)
|
||||
|
||||
}
|
||||
return v
|
||||
}
|
||||
}
|
||||
|
||||
class Adapter7 : ViewHolderTest() {
|
||||
lateinit var inflater: LayoutInflater
|
||||
|
||||
override fun getView(position: Int, convertView: View, parent: ViewGroup): View {
|
||||
var rootView: View? = convertView
|
||||
val itemViewType = getItemViewType(position)
|
||||
when (itemViewType) {
|
||||
0 -> {
|
||||
if (rootView != null)
|
||||
return rootView
|
||||
rootView = inflater.inflate(android.R.layout.simple_list_item_1, parent, false)
|
||||
}
|
||||
}
|
||||
return rootView!!
|
||||
}
|
||||
}
|
||||
|
||||
class R {
|
||||
object layout {
|
||||
val your_layout = 1
|
||||
val laptime_item = 2
|
||||
}
|
||||
|
||||
object id {
|
||||
val laptime_text = 1
|
||||
val laptime_text2 = 2
|
||||
val laptimes_list_item_holder = 3
|
||||
val text = 4
|
||||
}
|
||||
}
|
||||
}
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintLocalSuppressInspection
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.view.View
|
||||
|
||||
@Suppress("UsePropertyAccessSyntax", "UNUSED_VARIABLE", "unused", "UNUSED_PARAMETER", "DEPRECATION")
|
||||
class WrongAnnotation2 {
|
||||
@SuppressLint("NewApi")
|
||||
private val field1: Int = 0
|
||||
|
||||
@SuppressLint("NewApi")
|
||||
private val field2 = 5
|
||||
|
||||
companion object {
|
||||
@SuppressLint("NewApi") // Valid: class-file check on method
|
||||
fun foobar(view: View, @SuppressLint("NewApi") foo: Int) {
|
||||
// Invalid: class-file check
|
||||
@SuppressLint("NewApi") // Invalid
|
||||
val a: Boolean
|
||||
@SuppressLint("SdCardPath", "NewApi") // Invalid: class-file based check on local variable
|
||||
val b: Boolean
|
||||
@android.annotation.SuppressLint("SdCardPath", "NewApi") // Invalid (FQN)
|
||||
val c: Boolean
|
||||
@SuppressLint("SdCardPath") // Valid: AST-based check
|
||||
val d: Boolean
|
||||
}
|
||||
|
||||
init {
|
||||
// Local variable outside method: invalid
|
||||
@SuppressLint("NewApi")
|
||||
val localvar = 5
|
||||
}
|
||||
|
||||
private fun test() {
|
||||
@SuppressLint("NewApi") // Invalid
|
||||
val a = View.MEASURED_STATE_MASK
|
||||
}
|
||||
}
|
||||
}
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintSuspiciousImportInspection
|
||||
|
||||
//Warning
|
||||
<warning descr="Don't include `android.R` here; use a fully qualified name for each usage instead">import android.R</warning>
|
||||
|
||||
fun a() {
|
||||
R.id.button1
|
||||
}
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintWrongCallInspection
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.Canvas
|
||||
import android.util.AttributeSet
|
||||
import android.widget.FrameLayout
|
||||
import android.widget.LinearLayout
|
||||
|
||||
abstract class WrongViewCall(context: Context, attrs: AttributeSet, defStyle: Int) : LinearLayout(context, attrs, defStyle) {
|
||||
private val child: MyChild? = null
|
||||
|
||||
override fun onDraw(canvas: Canvas) {
|
||||
super.onDraw(canvas)
|
||||
child?.<error descr="Suspicious method call; should probably call \"`draw`\" rather than \"`onDraw`\"">onDraw</error>(canvas)
|
||||
}
|
||||
|
||||
private inner class MyChild(context: Context, attrs: AttributeSet, defStyle: Int) : FrameLayout(context, attrs, defStyle) {
|
||||
|
||||
public override fun onDraw(canvas: Canvas) {
|
||||
super.onDraw(canvas)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user