From a988234bbf9d07b04dcaeb2e8b3aa58b0321bdfe Mon Sep 17 00:00:00 2001 From: "vladislavf7@gmail.com" Date: Fri, 17 Jul 2020 10:55:33 +0300 Subject: [PATCH] [FIR] Fix redundant explicit type checker --- .../RedundantExplicitTypeChecker.kt | 66 ++++++++++++----- .../RedundantExplicitTypeChecker.txt | 74 ++++++++++++++----- .../extended/RedundantExplicitTypeChecker.kt | 40 +++++++--- 3 files changed, 134 insertions(+), 46 deletions(-) diff --git a/compiler/fir/analysis-tests/testData/extendedCheckers/RedundantExplicitTypeChecker.kt b/compiler/fir/analysis-tests/testData/extendedCheckers/RedundantExplicitTypeChecker.kt index 1e76dd656dc..f17ed45d2a5 100644 --- a/compiler/fir/analysis-tests/testData/extendedCheckers/RedundantExplicitTypeChecker.kt +++ b/compiler/fir/analysis-tests/testData/extendedCheckers/RedundantExplicitTypeChecker.kt @@ -1,3 +1,33 @@ +import kotlin.reflect.KClass + +@Target(AnnotationTarget.TYPE) +annotation class A + +fun annotated() { + val x: @A Int /* NOT redundant */ = 1 +} + +object SomeObj +fun fer() { + val x: Any /* NOT redundant */ = SomeObj +} + +fun f2(y: String?): String { + val f: KClass<*> = (y ?: return "")::class + return "" +} + +object Obj {} + +interface IA +interface IB : IA + +fun IA.extFun(x: IB) {} + +fun testWithExpectedType() { + val extFun_AB_A: IA.(IB) -> Unit = IA::extFun +} + interface Point { val x: Int val y: Int @@ -6,35 +36,37 @@ interface Point { class PointImpl(override val x: Int, override val y: Int) : Point fun foo() { + val s: String = "Hello ${10+1}" + val str: String? = "" + + val o: Obj = Obj + val p: Point = PointImpl(1, 2) val a: Boolean = true val i: Int = 2 * 2 val l: Long = 1234567890123L val s: String? = null val sh: Short = 42 + + val integer: Int = 42 + val piFloat: Float = 3.14f + val piDouble: Double = 3.14 + val charZ: Char = 'z' + var alpha: Int = 0 +} + +fun test(boolean: Boolean) { + val expectedLong: Long = if (boolean) { + 42 + } else { + return + } } class My { val x: Int = 1 } -object Obj {} - -fun bar() { - val o: Obj = Obj -} - -fun doo() { - val i: Int = 42 - val pi: Float = 3.14f - val pi2: Double = 3.14 - val ch: Char = 'z' -} - -fun soo() { - val s: String = "Hello ${10+1}" -} - val ZERO: Int = 0 fun main() { diff --git a/compiler/fir/analysis-tests/testData/extendedCheckers/RedundantExplicitTypeChecker.txt b/compiler/fir/analysis-tests/testData/extendedCheckers/RedundantExplicitTypeChecker.txt index 2bc7999fdf5..0bd8870bac8 100644 --- a/compiler/fir/analysis-tests/testData/extendedCheckers/RedundantExplicitTypeChecker.txt +++ b/compiler/fir/analysis-tests/testData/extendedCheckers/RedundantExplicitTypeChecker.txt @@ -1,4 +1,41 @@ FILE: RedundantExplicitTypeChecker.kt + @R|kotlin/annotation/Target|(vararg(Q|kotlin/annotation/AnnotationTarget|.R|kotlin/annotation/AnnotationTarget.TYPE|)) public final annotation class A : R|kotlin/Annotation| { + public constructor(): R|A| { + super() + } + + } + public final fun annotated(): R|kotlin/Unit| { + lval x: @R|A|() R|kotlin/Int| = Int(1) + } + public final object SomeObj : R|kotlin/Any| { + private constructor(): R|SomeObj| { + super() + } + + } + public final fun fer(): R|kotlin/Unit| { + lval x: R|kotlin/Any| = Q|SomeObj| + } + public final fun f2(y: R|kotlin/String?|): R|kotlin/String| { + lval f: R|kotlin/reflect/KClass<*>| = (R|/y| ?: ^f2 String()) + ^f2 String() + } + public final object Obj : R|kotlin/Any| { + private constructor(): R|Obj| { + super() + } + + } + public abstract interface IA : R|kotlin/Any| { + } + public abstract interface IB : R|IA| { + } + public final fun R|IA|.extFun(x: R|IB|): R|kotlin/Unit| { + } + public final fun testWithExpectedType(): R|kotlin/Unit| { + lval extFun_AB_A: R|IA.(IB) -> kotlin/Unit| = Q|IA|::R|/extFun| + } public abstract interface Point : R|kotlin/Any| { public abstract val x: R|kotlin/Int| public get(): R|kotlin/Int| @@ -20,12 +57,31 @@ FILE: RedundantExplicitTypeChecker.kt } public final fun foo(): R|kotlin/Unit| { + lval s: R|kotlin/String| = (String(Hello ), Int(10).R|kotlin/Int.plus|(Int(1)).R|kotlin/Any.toString|()) + lval str: R|kotlin/String?| = String() + lval o: R|Obj| = Q|Obj| lval p: R|Point| = R|/PointImpl.PointImpl|(Int(1), Int(2)) lval a: R|kotlin/Boolean| = Boolean(true) lval i: R|kotlin/Int| = Int(2).R|kotlin/Int.times|(Int(2)) lval l: R|kotlin/Long| = Long(1234567890123) lval s: R|kotlin/String?| = Null(null) lval sh: R|kotlin/Short| = Short(42) + lval integer: R|kotlin/Int| = Int(42) + lval piFloat: R|kotlin/Float| = Float(3.14) + lval piDouble: R|kotlin/Double| = Double(3.14) + lval charZ: R|kotlin/Char| = Char(z) + lvar alpha: R|kotlin/Int| = Int(0) + } + public final fun test(boolean: R|kotlin/Boolean|): R|kotlin/Unit| { + lval expectedLong: R|kotlin/Long| = when () { + R|/boolean| -> { + Int(42) + } + else -> { + ^test Unit + } + } + } public final class My : R|kotlin/Any| { public constructor(): R|My| { @@ -36,24 +92,6 @@ FILE: RedundantExplicitTypeChecker.kt public get(): R|kotlin/Int| } - public final object Obj : R|kotlin/Any| { - private constructor(): R|Obj| { - super() - } - - } - public final fun bar(): R|kotlin/Unit| { - lval o: R|Obj| = Q|Obj| - } - public final fun doo(): R|kotlin/Unit| { - lval i: R|kotlin/Int| = Int(42) - lval pi: R|kotlin/Float| = Float(3.14) - lval pi2: R|kotlin/Double| = Double(3.14) - lval ch: R|kotlin/Char| = Char(z) - } - public final fun soo(): R|kotlin/Unit| { - lval s: R|kotlin/String| = (String(Hello ), Int(10).R|kotlin/Int.plus|(Int(1)).R|kotlin/Any.toString|()) - } public final val ZERO: R|kotlin/Int| = Int(0) public get(): R|kotlin/Int| public final fun main(): R|kotlin/Unit| { diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/extended/RedundantExplicitTypeChecker.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/extended/RedundantExplicitTypeChecker.kt index c25c21794c8..e4b942d0d7e 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/extended/RedundantExplicitTypeChecker.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/extended/RedundantExplicitTypeChecker.kt @@ -14,15 +14,16 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors import org.jetbrains.kotlin.fir.declarations.FirMemberDeclaration import org.jetbrains.kotlin.fir.declarations.FirProperty import org.jetbrains.kotlin.fir.declarations.FirTypeAlias -import org.jetbrains.kotlin.fir.expressions.FirConstExpression -import org.jetbrains.kotlin.fir.expressions.FirExpression -import org.jetbrains.kotlin.fir.expressions.FirFunctionCall +import org.jetbrains.kotlin.fir.expressions.* import org.jetbrains.kotlin.fir.psi import org.jetbrains.kotlin.fir.references.FirNamedReference import org.jetbrains.kotlin.fir.symbols.StandardClassIds +import org.jetbrains.kotlin.fir.types.ConeKotlinType import org.jetbrains.kotlin.fir.types.FirTypeRef import org.jetbrains.kotlin.fir.types.classId import org.jetbrains.kotlin.fir.types.coneType +import org.jetbrains.kotlin.fir.types.impl.FirResolvedTypeRefImpl +import org.jetbrains.kotlin.name.ClassId object RedundantExplicitTypeChecker : FirMemberDeclarationChecker() { override fun check(declaration: FirMemberDeclaration, context: CheckerContext, reporter: DiagnosticReporter) { @@ -37,32 +38,33 @@ object RedundantExplicitTypeChecker : FirMemberDeclarationChecker() { val type = declaration.returnTypeRef.coneType if (typeReference is FirTypeAlias) return + if (typeReference.annotations.isNotEmpty()) return when (initializer) { is FirConstExpression<*> -> { when (initializer.source?.elementType) { KtNodeTypes.BOOLEAN_CONSTANT -> { - if (type.classId != StandardClassIds.Boolean) return + if (!type.isSame(StandardClassIds.Boolean)) return } KtNodeTypes.INTEGER_CONSTANT -> { if (initializer.text?.endsWith("L") == true) { - if (type.classId != StandardClassIds.Long) return + if (!type.isSame(StandardClassIds.Long)) return } else { - if (type.classId != StandardClassIds.Int) return + if (!type.isSame(StandardClassIds.Int)) return } } KtNodeTypes.FLOAT_CONSTANT -> { if (initializer.text?.endsWith("f", ignoreCase = true) == true) { - if (type.classId != StandardClassIds.Float) return + if (!type.isSame(StandardClassIds.Float)) return } else { - if (type.classId != StandardClassIds.Double) return + if (!type.isSame(StandardClassIds.Double)) return } } KtNodeTypes.CHARACTER_CONSTANT -> { - if (type.classId != StandardClassIds.Char) return + if (!type.isSame(StandardClassIds.Char)) return } KtNodeTypes.STRING_TEMPLATE -> { - if (type.classId != StandardClassIds.String) return + if (!type.isSame(StandardClassIds.String)) return } else -> return } @@ -71,8 +73,18 @@ object RedundantExplicitTypeChecker : FirMemberDeclarationChecker() { if (typeReference.text != initializer.name.identifier) return } is FirFunctionCall -> { - if (typeReference.text != initializer.calleeReference.name.identifier) return + if (typeReference.text != initializer.calleeReference.name.asString()) return } + is FirGetClassCall -> { + return + } + is FirResolvedQualifier -> { + if (!type.isSame(initializer.classId)) return + } + is FirStringConcatenationCall -> { + if (!type.isSame(StandardClassIds.String)) return + } + else -> return } reporter.report(declaration.returnTypeRef.source, FirErrors.REDUNDANT_EXPLICIT_TYPE) @@ -84,4 +96,10 @@ object RedundantExplicitTypeChecker : FirMemberDeclarationChecker() { private val FirTypeRef.text get() = this.psi?.text + private fun ConeKotlinType.isSame(other: ClassId?): Boolean { + if (this.nullability.isNullable) return false + if (this.type.classId == other) return true + return false + } + } \ No newline at end of file