diff --git a/compiler/fir/analysis-tests/testData/extendedCheckers/RedundantExplicitTypeChecker.kt b/compiler/fir/analysis-tests/testData/extendedCheckers/RedundantExplicitTypeChecker.kt new file mode 100644 index 00000000000..1e76dd656dc --- /dev/null +++ b/compiler/fir/analysis-tests/testData/extendedCheckers/RedundantExplicitTypeChecker.kt @@ -0,0 +1,44 @@ +interface Point { + val x: Int + val y: Int +} + +class PointImpl(override val x: Int, override val y: Int) : Point + +fun foo() { + 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 +} + +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() { + val id: Id = 11 +} + +typealias Id = Int \ No newline at end of file diff --git a/compiler/fir/analysis-tests/testData/extendedCheckers/RedundantExplicitTypeChecker.txt b/compiler/fir/analysis-tests/testData/extendedCheckers/RedundantExplicitTypeChecker.txt new file mode 100644 index 00000000000..2bc7999fdf5 --- /dev/null +++ b/compiler/fir/analysis-tests/testData/extendedCheckers/RedundantExplicitTypeChecker.txt @@ -0,0 +1,62 @@ +FILE: RedundantExplicitTypeChecker.kt + public abstract interface Point : R|kotlin/Any| { + public abstract val x: R|kotlin/Int| + public get(): R|kotlin/Int| + + public abstract val y: R|kotlin/Int| + public get(): R|kotlin/Int| + + } + public final class PointImpl : R|Point| { + public constructor(x: R|kotlin/Int|, y: R|kotlin/Int|): R|PointImpl| { + super() + } + + public final override val x: R|kotlin/Int| = R|/x| + public get(): R|kotlin/Int| + + public final override val y: R|kotlin/Int| = R|/y| + public get(): R|kotlin/Int| + + } + public final fun foo(): R|kotlin/Unit| { + 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) + } + public final class My : R|kotlin/Any| { + public constructor(): R|My| { + super() + } + + public final val x: R|kotlin/Int| = Int(1) + 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| { + lval id: R|Id| = Int(11) + } + public final typealias Id = R|kotlin/Int| diff --git a/compiler/fir/analysis-tests/tests/org/jetbrains/kotlin/fir/ExtendedFirDiagnosticsTestGenerated.java b/compiler/fir/analysis-tests/tests/org/jetbrains/kotlin/fir/ExtendedFirDiagnosticsTestGenerated.java index a4ff8ffedc5..c23444ba612 100644 --- a/compiler/fir/analysis-tests/tests/org/jetbrains/kotlin/fir/ExtendedFirDiagnosticsTestGenerated.java +++ b/compiler/fir/analysis-tests/tests/org/jetbrains/kotlin/fir/ExtendedFirDiagnosticsTestGenerated.java @@ -28,6 +28,11 @@ public class ExtendedFirDiagnosticsTestGenerated extends AbstractExtendedFirDiag KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/fir/analysis-tests/testData/extendedCheckers"), Pattern.compile("^([^.]+)\\.kt$"), null, true); } + @TestMetadata("RedundantExplicitTypeChecker.kt") + public void testRedundantExplicitTypeChecker() throws Exception { + runTest("compiler/fir/analysis-tests/testData/extendedCheckers/RedundantExplicitTypeChecker.kt"); + } + @TestMetadata("RedundantModalityModifierChecker.kt") public void testRedundantModalityModifierChecker() throws Exception { runTest("compiler/fir/analysis-tests/testData/extendedCheckers/RedundantModalityModifierChecker.kt"); diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/DefaultDeclarationCheckers.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/DefaultDeclarationCheckers.kt index 6551350f478..0d69956d9d9 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/DefaultDeclarationCheckers.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/DefaultDeclarationCheckers.kt @@ -5,6 +5,7 @@ package org.jetbrains.kotlin.fir.analysis.checkers.declaration +import org.jetbrains.kotlin.fir.analysis.checkers.extended.RedundantExplicitTypeChecker import org.jetbrains.kotlin.fir.analysis.checkers.extended.RedundantModalityModifierChecker import org.jetbrains.kotlin.fir.analysis.checkers.extended.RedundantReturnUnitType import org.jetbrains.kotlin.fir.analysis.checkers.extended.RedundantVisibilityModifierChecker @@ -20,7 +21,8 @@ object CommonDeclarationCheckers : DeclarationCheckers() { override val memberDeclarationCheckers: List = listOf( FirInfixFunctionDeclarationChecker, FirExposedVisibilityChecker, - RedundantModalityModifierChecker + RedundantModalityModifierChecker, + RedundantExplicitTypeChecker ) override val constructorCheckers: List = listOf( 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 new file mode 100644 index 00000000000..c25c21794c8 --- /dev/null +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/extended/RedundantExplicitTypeChecker.kt @@ -0,0 +1,87 @@ +/* + * Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.fir.analysis.checkers.extended + +import org.jetbrains.kotlin.KtNodeTypes +import org.jetbrains.kotlin.fir.FirFakeSourceElement +import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext +import org.jetbrains.kotlin.fir.analysis.checkers.declaration.FirMemberDeclarationChecker +import org.jetbrains.kotlin.fir.analysis.diagnostics.DiagnosticReporter +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.psi +import org.jetbrains.kotlin.fir.references.FirNamedReference +import org.jetbrains.kotlin.fir.symbols.StandardClassIds +import org.jetbrains.kotlin.fir.types.FirTypeRef +import org.jetbrains.kotlin.fir.types.classId +import org.jetbrains.kotlin.fir.types.coneType + +object RedundantExplicitTypeChecker : FirMemberDeclarationChecker() { + override fun check(declaration: FirMemberDeclaration, context: CheckerContext, reporter: DiagnosticReporter) { + if (declaration !is FirProperty) return + if (!declaration.isLocal) return + + val initializer = declaration.initializer ?: return + val typeReference = declaration.returnTypeRef + + if (typeReference.source is FirFakeSourceElement<*>) return + + val type = declaration.returnTypeRef.coneType + + if (typeReference is FirTypeAlias) return + + when (initializer) { + is FirConstExpression<*> -> { + when (initializer.source?.elementType) { + KtNodeTypes.BOOLEAN_CONSTANT -> { + if (type.classId != StandardClassIds.Boolean) return + } + KtNodeTypes.INTEGER_CONSTANT -> { + if (initializer.text?.endsWith("L") == true) { + if (type.classId != StandardClassIds.Long) return + } else { + if (type.classId != StandardClassIds.Int) return + } + } + KtNodeTypes.FLOAT_CONSTANT -> { + if (initializer.text?.endsWith("f", ignoreCase = true) == true) { + if (type.classId != StandardClassIds.Float) return + } else { + if (type.classId != StandardClassIds.Double) return + } + } + KtNodeTypes.CHARACTER_CONSTANT -> { + if (type.classId != StandardClassIds.Char) return + } + KtNodeTypes.STRING_TEMPLATE -> { + if (type.classId != StandardClassIds.String) return + } + else -> return + } + } + is FirNamedReference -> { + if (typeReference.text != initializer.name.identifier) return + } + is FirFunctionCall -> { + if (typeReference.text != initializer.calleeReference.name.identifier) return + } + } + + reporter.report(declaration.returnTypeRef.source, FirErrors.REDUNDANT_EXPLICIT_TYPE) + } + + private val FirExpression.text + get() = this.source.psi?.text + + private val FirTypeRef.text + get() = this.psi?.text + +} \ No newline at end of file diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrors.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrors.kt index 6eeb88e88a0..bb40fd27125 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrors.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrors.kt @@ -86,6 +86,7 @@ object FirErrors { val REDUNDANT_VISIBILITY_MODIFIER by warning0() val REDUNDANT_MODALITY_MODIFIER by warning0() val REDUNDANT_RETURN_UNIT_TYPE by warning0() + val REDUNDANT_EXPLICIT_TYPE by warning0() }