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:
@@ -166,8 +166,18 @@ open class ApiDetector : Detector(), UastScanner {
|
||||
|
||||
val parentClass = declaration.parent as? UClass ?: 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) {
|
||||
is UFunction -> {
|
||||
@@ -197,6 +207,18 @@ open class ApiDetector : Detector(), UastScanner {
|
||||
|
||||
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 {
|
||||
|
||||
@@ -21,6 +21,8 @@ import org.jetbrains.uast.visitor.UastVisitor
|
||||
* Represents a call expression (function call, constructor call, array initializer).
|
||||
*/
|
||||
interface UCallExpression : UExpression, UResolvable {
|
||||
val receiverType: UType?
|
||||
|
||||
/**
|
||||
* Returns the call kind.
|
||||
*/
|
||||
|
||||
@@ -27,6 +27,9 @@ class JavaUAssertExpression(
|
||||
val condition: UExpression by lz { JavaConverter.convertOrEmpty(psi.assertCondition, this) }
|
||||
val message: UExpression? by lz { JavaConverter.convertOrNull(psi.assertDescription, this) }
|
||||
|
||||
override val receiverType: UType?
|
||||
get() = null
|
||||
|
||||
override val functionReference: USimpleReferenceExpression?
|
||||
get() = null
|
||||
|
||||
|
||||
@@ -26,6 +26,13 @@ class JavaUCallExpression(
|
||||
override val psi: PsiMethodCallExpression,
|
||||
override val parent: UElement
|
||||
) : 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
|
||||
get() = UastCallKind.FUNCTION_CALL
|
||||
|
||||
@@ -63,6 +70,9 @@ class JavaConstructorUCallExpression(
|
||||
}
|
||||
}
|
||||
|
||||
override val receiverType: UType?
|
||||
get() = null
|
||||
|
||||
override val functionReference: USimpleReferenceExpression?
|
||||
get() = null
|
||||
|
||||
@@ -119,6 +129,9 @@ class JavaArrayInitializerUCallExpression(
|
||||
override val psi: PsiArrayInitializerExpression,
|
||||
override val parent: UElement
|
||||
) : JavaAbstractUElement(), UCallExpression, PsiElementBacked, JavaUElementWithType, JavaEvaluatableUElement {
|
||||
override val receiverType: UType?
|
||||
get() = null
|
||||
|
||||
override val functionReference: USimpleReferenceExpression?
|
||||
get() = null
|
||||
|
||||
@@ -150,6 +163,9 @@ class JavaAnnotationArrayInitializerUCallExpression(
|
||||
override val psi: PsiArrayInitializerMemberValue,
|
||||
override val parent: UElement
|
||||
) : JavaAbstractUElement(), UCallExpression, PsiElementBacked, JavaUElementWithType, JavaEvaluatableUElement {
|
||||
override val receiverType: UType?
|
||||
get() = null
|
||||
|
||||
override val kind: UastCallKind
|
||||
get() = UastCallKind.ARRAY_INITIALIZER
|
||||
|
||||
|
||||
+40
-14
@@ -30,7 +30,15 @@ class KotlinUFunctionCallExpression(
|
||||
override val psi: KtCallExpression,
|
||||
override val parent: UElement
|
||||
) : 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 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 kind by lz {
|
||||
when (resolveCall()?.resultingDescriptor) {
|
||||
when (resolvedCall?.resultingDescriptor) {
|
||||
is ConstructorDescriptor -> UastCallKind.CONSTRUCTOR_CALL
|
||||
else -> UastCallKind.FUNCTION_CALL
|
||||
}
|
||||
}
|
||||
|
||||
override fun resolve(context: UastContext): UFunction? {
|
||||
val resolvedCall = resolveCall()
|
||||
val descriptor = resolvedCall?.resultingDescriptor ?: return null
|
||||
val source = descriptor.toSource() ?: return null
|
||||
|
||||
@@ -74,9 +81,7 @@ class KotlinUFunctionCallExpression(
|
||||
}
|
||||
|
||||
return context.convert(source) as? UFunction
|
||||
}
|
||||
|
||||
private fun resolveCall() = psi.getResolvedCall(psi.analyze(BodyResolveMode.PARTIAL))
|
||||
}
|
||||
}
|
||||
|
||||
class KotlinUComponentFunctionCallExpression(
|
||||
@@ -84,14 +89,35 @@ class KotlinUComponentFunctionCallExpression(
|
||||
n: Int,
|
||||
override val parent: UElement
|
||||
) : UCallExpression, PsiElementBacked {
|
||||
override val valueArgumentCount = 0
|
||||
override val valueArguments = emptyList<UExpression>()
|
||||
override val typeArgumentCount = 0
|
||||
override val typeArguments = emptyList<UType>()
|
||||
override val classReference = null
|
||||
override val receiverType: UType?
|
||||
get() = null
|
||||
|
||||
override val valueArgumentCount: Int
|
||||
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 functionReference by lz { KotlinStringUSimpleReferenceExpression(functionName, this) }
|
||||
override val functionNameElement = null
|
||||
override val kind = UastCallKind.FUNCTION_CALL
|
||||
override fun resolve(context: UastContext) = null
|
||||
|
||||
override val functionNameElement: UElement?
|
||||
get() = null
|
||||
|
||||
override val kind: UastCallKind
|
||||
get() = UastCallKind.FUNCTION_CALL
|
||||
|
||||
override fun resolve(context: UastContext): UFunction? {
|
||||
return null
|
||||
}
|
||||
}
|
||||
+5
@@ -51,6 +51,11 @@ class PropertyAsCallAndroidUastVisitorExtension : UastVisitorExtension {
|
||||
override val parent = element.parent
|
||||
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(
|
||||
expr.psi, expr.identifier, expr.parent, accessorDescriptor)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user