From 206e4395953f16d0717c7d7874fd9d933c54a402 Mon Sep 17 00:00:00 2001 From: Yan Zhulanow Date: Wed, 15 Jun 2016 21:22:25 +0300 Subject: [PATCH] KT-12023 Kotlin Lint: Cast doesn't trigger minSdk error Check Kotlin as/is types. (cherry picked from commit c0db8e0) --- .../android/tools/klint/checks/ApiDetector.kt | 9 ++++ .../expressions/UBinaryExpressionWithType.kt | 5 +++ .../JavaUInstanceCheckExpression.kt | 4 ++ .../expressions/JavaUTypeCastExpression.kt | 4 ++ .../kotlin/uast/KotlinUastLanguagePlugin.kt | 4 ++ .../uast/declarations/KotlinUTypeReference.kt | 42 +++++++++++++++++++ .../KotlinUBinaryExpressionWithType.kt | 4 ++ .../expressions/KotlinUSwitchExpression.kt | 4 +- .../expressions/KotlinUTypeCheckExpression.kt | 1 + plugins/uast-kotlin/testData/lint/apiCheck.kt | 6 ++- 10 files changed, 81 insertions(+), 2 deletions(-) create mode 100644 plugins/uast-kotlin/src/org/jetbrains/kotlin/uast/declarations/KotlinUTypeReference.kt diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/ApiDetector.kt b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/ApiDetector.kt index f2568da47f2..84917df3f38 100755 --- a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/ApiDetector.kt +++ b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/ApiDetector.kt @@ -68,6 +68,15 @@ open class ApiDetector : Detector(), UastScanner { return super.visitCallExpression(node) } + override fun visitBinaryExpressionWithType(node: UBinaryExpressionWithType): Boolean { + val typeRef = node.typeReference + if (typeRef != null) { + checkVersion(context, typeRef, node.type.resolve(context)) + } + + return super.visitBinaryExpressionWithType(node) + } + override fun visitSimpleReferenceExpression(node: USimpleReferenceExpression): Boolean { checkVersion(context, node, node.resolve(context) as? UVariable) return super.visitSimpleReferenceExpression(node) diff --git a/plugins/uast-common/src/org/jetbrains/uast/expressions/UBinaryExpressionWithType.kt b/plugins/uast-common/src/org/jetbrains/uast/expressions/UBinaryExpressionWithType.kt index 8d9fd5be3a7..be033e82305 100644 --- a/plugins/uast-common/src/org/jetbrains/uast/expressions/UBinaryExpressionWithType.kt +++ b/plugins/uast-common/src/org/jetbrains/uast/expressions/UBinaryExpressionWithType.kt @@ -36,6 +36,11 @@ interface UBinaryExpressionWithType : UExpression { */ val type: UType + /** + * Returns the type reference. + */ + val typeReference: UTypeReference? + override fun logString() = log("UBinaryExpressionWithType (${getExpressionType()?.name}, ${operationKind.name})", operand) override fun renderString() = "${operand.renderString()} ${operationKind.name} ${type.name}" diff --git a/plugins/uast-java/src/org/jetbrains/uast/java/expressions/JavaUInstanceCheckExpression.kt b/plugins/uast-java/src/org/jetbrains/uast/java/expressions/JavaUInstanceCheckExpression.kt index 981aaf5433d..791ac5cde4c 100644 --- a/plugins/uast-java/src/org/jetbrains/uast/java/expressions/JavaUInstanceCheckExpression.kt +++ b/plugins/uast-java/src/org/jetbrains/uast/java/expressions/JavaUInstanceCheckExpression.kt @@ -18,6 +18,7 @@ package org.jetbrains.uast.java import com.intellij.psi.PsiInstanceOfExpression import org.jetbrains.uast.UBinaryExpressionWithType import org.jetbrains.uast.UElement +import org.jetbrains.uast.UTypeReference import org.jetbrains.uast.UastBinaryExpressionWithTypeKind import org.jetbrains.uast.psi.PsiElementBacked @@ -27,6 +28,9 @@ class JavaUInstanceCheckExpression( ) : JavaAbstractUElement(), UBinaryExpressionWithType, PsiElementBacked, JavaUElementWithType, JavaEvaluatableUElement { override val operand by lz { JavaConverter.convertOrEmpty(psi.operand, this) } override val type by lz { JavaConverter.convert(psi.checkType?.type, this) } + + override val typeReference: UTypeReference? + get() = null override val operationKind: UastBinaryExpressionWithTypeKind.InstanceCheck get() = UastBinaryExpressionWithTypeKind.INSTANCE_CHECK diff --git a/plugins/uast-java/src/org/jetbrains/uast/java/expressions/JavaUTypeCastExpression.kt b/plugins/uast-java/src/org/jetbrains/uast/java/expressions/JavaUTypeCastExpression.kt index b27b955e3bb..7a6e759f3e1 100644 --- a/plugins/uast-java/src/org/jetbrains/uast/java/expressions/JavaUTypeCastExpression.kt +++ b/plugins/uast-java/src/org/jetbrains/uast/java/expressions/JavaUTypeCastExpression.kt @@ -18,6 +18,7 @@ package org.jetbrains.uast.java import com.intellij.psi.PsiTypeCastExpression import org.jetbrains.uast.UBinaryExpressionWithType import org.jetbrains.uast.UElement +import org.jetbrains.uast.UTypeReference import org.jetbrains.uast.UastBinaryExpressionWithTypeKind import org.jetbrains.uast.psi.PsiElementBacked @@ -28,6 +29,9 @@ class JavaUTypeCastExpression( override val operand by lz { JavaConverter.convertOrEmpty(psi.operand, this) } override val type by lz { JavaConverter.convert(psi.castType?.type, this) } + override val typeReference: UTypeReference? + get() = null + override val operationKind: UastBinaryExpressionWithTypeKind.TypeCast get() = UastBinaryExpressionWithTypeKind.TYPE_CAST } \ No newline at end of file diff --git a/plugins/uast-kotlin/src/org/jetbrains/kotlin/uast/KotlinUastLanguagePlugin.kt b/plugins/uast-kotlin/src/org/jetbrains/kotlin/uast/KotlinUastLanguagePlugin.kt index 5f632ef2c44..7e6f229bc2f 100644 --- a/plugins/uast-kotlin/src/org/jetbrains/kotlin/uast/KotlinUastLanguagePlugin.kt +++ b/plugins/uast-kotlin/src/org/jetbrains/kotlin/uast/KotlinUastLanguagePlugin.kt @@ -188,6 +188,10 @@ internal object KotlinConverter : UastConverter { val type = bindingContext[BindingContext.TYPE, typeReference] ?: return UastErrorType return KotlinUType(type, typeReference.project, parent, typeReference.typeElement) } + + internal fun convertTypeReference(typeReference: KtTypeReference, parent: UElement): UTypeReference { + return KotlinUTypeReference(typeReference, parent) + } internal fun asSimpleReference(element: PsiElement?, parent: UElement): USimpleReferenceExpression? { if (element == null) return null diff --git a/plugins/uast-kotlin/src/org/jetbrains/kotlin/uast/declarations/KotlinUTypeReference.kt b/plugins/uast-kotlin/src/org/jetbrains/kotlin/uast/declarations/KotlinUTypeReference.kt new file mode 100644 index 00000000000..7b5cdd730ef --- /dev/null +++ b/plugins/uast-kotlin/src/org/jetbrains/kotlin/uast/declarations/KotlinUTypeReference.kt @@ -0,0 +1,42 @@ +/* + * Copyright 2010-2016 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.uast + +import org.jetbrains.kotlin.descriptors.ClassDescriptor +import org.jetbrains.kotlin.idea.caches.resolve.analyze +import org.jetbrains.kotlin.psi.KtTypeReference +import org.jetbrains.kotlin.resolve.BindingContext +import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode +import org.jetbrains.uast.* +import org.jetbrains.uast.psi.PsiElementBacked + +class KotlinUTypeReference( + override val psi: KtTypeReference, + override val parent: UElement +) : KotlinAbstractUElement(), UTypeReference, PsiElementBacked { + override fun resolve(context: UastContext): UClass? { + val descriptor = psi.analyze(BodyResolveMode.PARTIAL)[BindingContext.TYPE, psi] + ?.constructor?.declarationDescriptor as? ClassDescriptor ?: return null + return context.convert(descriptor.toSource()) as? UClass + } + + override val nameElement: UElement? + get() = KotlinDumbUElement(psi, this) + + override val name: String + get() = psi.name.orAnonymous() +} \ No newline at end of file diff --git a/plugins/uast-kotlin/src/org/jetbrains/kotlin/uast/expressions/KotlinUBinaryExpressionWithType.kt b/plugins/uast-kotlin/src/org/jetbrains/kotlin/uast/expressions/KotlinUBinaryExpressionWithType.kt index f71fbaa7b6b..36688f681de 100644 --- a/plugins/uast-kotlin/src/org/jetbrains/kotlin/uast/expressions/KotlinUBinaryExpressionWithType.kt +++ b/plugins/uast-kotlin/src/org/jetbrains/kotlin/uast/expressions/KotlinUBinaryExpressionWithType.kt @@ -28,6 +28,7 @@ class KotlinUBinaryExpressionWithType( ) : KotlinAbstractUElement(), UBinaryExpressionWithType, PsiElementBacked, KotlinUElementWithType, KotlinEvaluatableUElement { override val operand by lz { KotlinConverter.convert(psi.left, this) } override val type by lz { KotlinConverter.convert(psi.right, this) } + override val typeReference by lz { psi.right?.let { KotlinConverter.convertTypeReference(it, this) } } override val operationKind = when (psi.operationReference.getReferencedNameElementType()) { KtTokens.AS_KEYWORD -> UastBinaryExpressionWithTypeKind.TYPE_CAST KtTokens.AS_SAFE -> KotlinBinaryExpressionWithTypeKinds.SAFE_TYPE_CAST @@ -47,4 +48,7 @@ class KotlinCustomUBinaryExpressionWithType( lateinit override var type: UType internal set + + override var typeReference: UTypeReference? = null + internal set } \ No newline at end of file diff --git a/plugins/uast-kotlin/src/org/jetbrains/kotlin/uast/expressions/KotlinUSwitchExpression.kt b/plugins/uast-kotlin/src/org/jetbrains/kotlin/uast/expressions/KotlinUSwitchExpression.kt index 1004c563971..346fdd9196c 100644 --- a/plugins/uast-kotlin/src/org/jetbrains/kotlin/uast/expressions/KotlinUSwitchExpression.kt +++ b/plugins/uast-kotlin/src/org/jetbrains/kotlin/uast/expressions/KotlinUSwitchExpression.kt @@ -64,7 +64,9 @@ class KotlinUSwitchEntry( it.isNegated -> KotlinBinaryExpressionWithTypeKinds.NEGATED_INSTANCE_CHECK else -> UastBinaryExpressionWithTypeKind.INSTANCE_CHECK } - type = KotlinConverter.convert(it.typeReference, this) + val typeRef = it.typeReference + type = KotlinConverter.convert(typeRef, this) + typeReference = typeRef?.let { KotlinConverter.convertTypeReference(it, this) } } is KtWhenConditionWithExpression -> KotlinConverter.convertOrEmpty(it.expression, this) else -> EmptyUExpression(this) diff --git a/plugins/uast-kotlin/src/org/jetbrains/kotlin/uast/expressions/KotlinUTypeCheckExpression.kt b/plugins/uast-kotlin/src/org/jetbrains/kotlin/uast/expressions/KotlinUTypeCheckExpression.kt index 19de370ded2..88a24702976 100644 --- a/plugins/uast-kotlin/src/org/jetbrains/kotlin/uast/expressions/KotlinUTypeCheckExpression.kt +++ b/plugins/uast-kotlin/src/org/jetbrains/kotlin/uast/expressions/KotlinUTypeCheckExpression.kt @@ -27,5 +27,6 @@ class KotlinUTypeCheckExpression( ) : KotlinAbstractUElement(), UBinaryExpressionWithType, PsiElementBacked, KotlinUElementWithType, KotlinEvaluatableUElement { override val operand by lz { KotlinConverter.convert(psi.leftHandSide, this) } override val type by lz { KotlinConverter.convert(psi.typeReference, this) } + override val typeReference by lz { psi.typeReference?.let { KotlinConverter.convertTypeReference(it, this) } } override val operationKind = KotlinBinaryExpressionWithTypeKinds.NEGATED_INSTANCE_CHECK } \ No newline at end of file diff --git a/plugins/uast-kotlin/testData/lint/apiCheck.kt b/plugins/uast-kotlin/testData/lint/apiCheck.kt index 6ddd00f8cde..74a6a6b3dcb 100644 --- a/plugins/uast-kotlin/testData/lint/apiCheck.kt +++ b/plugins/uast-kotlin/testData/lint/apiCheck.kt @@ -8,6 +8,7 @@ import org.w3c.dom.DOMError import org.w3c.dom.DOMErrorHandler import org.w3c.dom.DOMLocator +import android.view.ViewGroup import android.view.ViewGroup.LayoutParams import android.app.Activity import android.app.ApplicationErrorReport @@ -108,7 +109,10 @@ class ApiCallTest: Activity() { } } - fun test(priority: Boolean) { + fun test(priority: Boolean, layout: ViewGroup) { + 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 {