KT-12015 (Kotlin Lint False Positive for Bundle.getInt())

Java approach is not sufficient here, in Kotlin receivers can be implicit.
Use Kotlin extension/dispatch receivers to figure out the receiver type for our method call.
(cherry picked from commit cdedf1d)
This commit is contained in:
Yan Zhulanow
2016-06-10 21:35:41 +03:00
parent 530ab42de6
commit 1de05b965e
6 changed files with 90 additions and 16 deletions
@@ -166,8 +166,18 @@ open class ApiDetector : Detector(), UastScanner {
val parentClass = declaration.parent as? UClass ?: return val parentClass = declaration.parent as? UClass ?: return
if (!isSdkClass(parentClass) || checkAosp(parentClass)) return if (!isSdkClass(parentClass) || checkAosp(parentClass)) return
val parentInternalName = (node as? UCallExpression)?.getReceiver()?.getExpressionType()?.resolveClass(context)?.internalName
?: parentClass.internalName ?: return var parentInternalName = parentClass.internalName
if (node is UCallExpression) {
val clazz = node.receiverType?.resolveClass(context)?.let { getSupertypeFromAndroidSdk(context, it) }
val internalName = clazz?.internalName
if (internalName != null) {
parentInternalName = internalName
}
}
if (parentInternalName == null) return
when (declaration) { when (declaration) {
is UFunction -> { is UFunction -> {
@@ -197,6 +207,18 @@ open class ApiDetector : Detector(), UastScanner {
return mMinApi return mMinApi
} }
private fun getSupertypeFromAndroidSdk(context: UastContext, clazz: UClass): UClass? {
tailrec fun getSuperclassFromAndroidSdk(clazz: UClass): UClass? {
if (clazz.fqName?.startsWith("android.") ?: false) return clazz
return getSuperclassFromAndroidSdk(clazz.getSuperClass(context) ?: return null)
}
val superClass = getSuperclassFromAndroidSdk(clazz)
if (superClass != null) return superClass
return clazz.superTypes.firstOrNull { it.fqName?.startsWith("android.") ?: false }?.resolve(context)
}
} }
companion object { companion object {
@@ -21,6 +21,8 @@ import org.jetbrains.uast.visitor.UastVisitor
* Represents a call expression (function call, constructor call, array initializer). * Represents a call expression (function call, constructor call, array initializer).
*/ */
interface UCallExpression : UExpression, UResolvable { interface UCallExpression : UExpression, UResolvable {
val receiverType: UType?
/** /**
* Returns the call kind. * Returns the call kind.
*/ */
@@ -27,6 +27,9 @@ class JavaUAssertExpression(
val condition: UExpression by lz { JavaConverter.convertOrEmpty(psi.assertCondition, this) } val condition: UExpression by lz { JavaConverter.convertOrEmpty(psi.assertCondition, this) }
val message: UExpression? by lz { JavaConverter.convertOrNull(psi.assertDescription, this) } val message: UExpression? by lz { JavaConverter.convertOrNull(psi.assertDescription, this) }
override val receiverType: UType?
get() = null
override val functionReference: USimpleReferenceExpression? override val functionReference: USimpleReferenceExpression?
get() = null get() = null
@@ -26,6 +26,13 @@ class JavaUCallExpression(
override val psi: PsiMethodCallExpression, override val psi: PsiMethodCallExpression,
override val parent: UElement override val parent: UElement
) : JavaAbstractUElement(), UCallExpression, PsiElementBacked, JavaUElementWithType { ) : JavaAbstractUElement(), UCallExpression, PsiElementBacked, JavaUElementWithType {
override val receiverType: UType?
get() {
val qualifiedExpression = parent as? UQualifiedExpression ?: return null
if (qualifiedExpression.selector != this) return null
return qualifiedExpression.receiver.getExpressionType()
}
override val kind: UastCallKind override val kind: UastCallKind
get() = UastCallKind.FUNCTION_CALL get() = UastCallKind.FUNCTION_CALL
@@ -63,6 +70,9 @@ class JavaConstructorUCallExpression(
} }
} }
override val receiverType: UType?
get() = null
override val functionReference: USimpleReferenceExpression? override val functionReference: USimpleReferenceExpression?
get() = null get() = null
@@ -119,6 +129,9 @@ class JavaArrayInitializerUCallExpression(
override val psi: PsiArrayInitializerExpression, override val psi: PsiArrayInitializerExpression,
override val parent: UElement override val parent: UElement
) : JavaAbstractUElement(), UCallExpression, PsiElementBacked, JavaUElementWithType, JavaEvaluatableUElement { ) : JavaAbstractUElement(), UCallExpression, PsiElementBacked, JavaUElementWithType, JavaEvaluatableUElement {
override val receiverType: UType?
get() = null
override val functionReference: USimpleReferenceExpression? override val functionReference: USimpleReferenceExpression?
get() = null get() = null
@@ -150,6 +163,9 @@ class JavaAnnotationArrayInitializerUCallExpression(
override val psi: PsiArrayInitializerMemberValue, override val psi: PsiArrayInitializerMemberValue,
override val parent: UElement override val parent: UElement
) : JavaAbstractUElement(), UCallExpression, PsiElementBacked, JavaUElementWithType, JavaEvaluatableUElement { ) : JavaAbstractUElement(), UCallExpression, PsiElementBacked, JavaUElementWithType, JavaEvaluatableUElement {
override val receiverType: UType?
get() = null
override val kind: UastCallKind override val kind: UastCallKind
get() = UastCallKind.ARRAY_INITIALIZER get() = UastCallKind.ARRAY_INITIALIZER
@@ -30,7 +30,15 @@ class KotlinUFunctionCallExpression(
override val psi: KtCallExpression, override val psi: KtCallExpression,
override val parent: UElement override val parent: UElement
) : KotlinAbstractUElement(), UCallExpression, PsiElementBacked, KotlinUElementWithType { ) : KotlinAbstractUElement(), UCallExpression, PsiElementBacked, KotlinUElementWithType {
override val functionName: String? by lz { resolveCall()?.resultingDescriptor?.name?.asString().orAnonymous() } private val resolvedCall by lz { psi.getResolvedCall(psi.analyze(BodyResolveMode.PARTIAL)) }
override val receiverType by lz {
val resolvedCall = this.resolvedCall ?: return@lz null
val receiver = resolvedCall.extensionReceiver ?: resolvedCall.dispatchReceiver ?: return@lz null
KotlinConverter.convert(receiver.type, psi.project, null)
}
override val functionName: String? by lz { resolvedCall?.resultingDescriptor?.name?.asString().orAnonymous() }
override fun matchesFunctionName(name: String) = functionName == name override fun matchesFunctionName(name: String) = functionName == name
override val functionNameElement by lz { psi.calleeExpression?.let { KotlinConverter.convert(it, this) } } override val functionNameElement by lz { psi.calleeExpression?.let { KotlinConverter.convert(it, this) } }
@@ -56,14 +64,13 @@ class KotlinUFunctionCallExpression(
override val typeArguments by lz { psi.typeArguments.map { KotlinConverter.convert(it.typeReference, this) } } override val typeArguments by lz { psi.typeArguments.map { KotlinConverter.convert(it.typeReference, this) } }
override val kind by lz { override val kind by lz {
when (resolveCall()?.resultingDescriptor) { when (resolvedCall?.resultingDescriptor) {
is ConstructorDescriptor -> UastCallKind.CONSTRUCTOR_CALL is ConstructorDescriptor -> UastCallKind.CONSTRUCTOR_CALL
else -> UastCallKind.FUNCTION_CALL else -> UastCallKind.FUNCTION_CALL
} }
} }
override fun resolve(context: UastContext): UFunction? { override fun resolve(context: UastContext): UFunction? {
val resolvedCall = resolveCall()
val descriptor = resolvedCall?.resultingDescriptor ?: return null val descriptor = resolvedCall?.resultingDescriptor ?: return null
val source = descriptor.toSource() ?: return null val source = descriptor.toSource() ?: return null
@@ -74,9 +81,7 @@ class KotlinUFunctionCallExpression(
} }
return context.convert(source) as? UFunction return context.convert(source) as? UFunction
} }
private fun resolveCall() = psi.getResolvedCall(psi.analyze(BodyResolveMode.PARTIAL))
} }
class KotlinUComponentFunctionCallExpression( class KotlinUComponentFunctionCallExpression(
@@ -84,14 +89,35 @@ class KotlinUComponentFunctionCallExpression(
n: Int, n: Int,
override val parent: UElement override val parent: UElement
) : UCallExpression, PsiElementBacked { ) : UCallExpression, PsiElementBacked {
override val valueArgumentCount = 0 override val receiverType: UType?
override val valueArguments = emptyList<UExpression>() get() = null
override val typeArgumentCount = 0
override val typeArguments = emptyList<UType>() override val valueArgumentCount: Int
override val classReference = null get() = 0
override val valueArguments: List<UExpression>
get() = emptyList()
override val typeArgumentCount: Int
get() = 0
override val typeArguments: List<UType>
get() = emptyList()
override val classReference: USimpleReferenceExpression?
get() = null
override val functionName = "component$n" override val functionName = "component$n"
override val functionReference by lz { KotlinStringUSimpleReferenceExpression(functionName, this) } override val functionReference by lz { KotlinStringUSimpleReferenceExpression(functionName, this) }
override val functionNameElement = null
override val kind = UastCallKind.FUNCTION_CALL override val functionNameElement: UElement?
override fun resolve(context: UastContext) = null get() = null
override val kind: UastCallKind
get() = UastCallKind.FUNCTION_CALL
override fun resolve(context: UastContext): UFunction? {
return null
}
} }
@@ -51,6 +51,11 @@ class PropertyAsCallAndroidUastVisitorExtension : UastVisitorExtension {
override val parent = element.parent override val parent = element.parent
override val psi = ktElement override val psi = ktElement
override val receiverType by lz {
val type = (resolvedCall.extensionReceiver ?: resolvedCall.dispatchReceiver)?.type ?: return@lz null
KotlinConverter.convert(type, psi.project, null)
}
override val functionReference = KotlinNameUSimpleReferenceExpression( override val functionReference = KotlinNameUSimpleReferenceExpression(
expr.psi, expr.identifier, expr.parent, accessorDescriptor) expr.psi, expr.identifier, expr.parent, accessorDescriptor)