diff --git a/compiler/fir/analysis-tests/testData/extendedCheckers/RedundantSingleExpressionStringTemplateChecker.kt b/compiler/fir/analysis-tests/testData/extendedCheckers/RedundantSingleExpressionStringTemplateChecker.kt new file mode 100644 index 00000000000..373bb48ad33 --- /dev/null +++ b/compiler/fir/analysis-tests/testData/extendedCheckers/RedundantSingleExpressionStringTemplateChecker.kt @@ -0,0 +1,11 @@ +val x = "Hello" + +val y = "$x" + +val z = "${y.hashCode()}" + +fun toString(x: String) = "IC$x" + +data class ProductGroup(val short_name: String, val parent: ProductGroup?) { + val name: String = if (parent == null) short_name else "${parent.name} $short_name" +} \ No newline at end of file diff --git a/compiler/fir/analysis-tests/testData/extendedCheckers/RedundantSingleExpressionStringTemplateChecker.txt b/compiler/fir/analysis-tests/testData/extendedCheckers/RedundantSingleExpressionStringTemplateChecker.txt new file mode 100644 index 00000000000..17162b3a946 --- /dev/null +++ b/compiler/fir/analysis-tests/testData/extendedCheckers/RedundantSingleExpressionStringTemplateChecker.txt @@ -0,0 +1,39 @@ +FILE: RedundantSingleExpressionStringTemplateChecker.kt + public final val x: R|kotlin/String| = String(Hello) + public get(): R|kotlin/String| + public final val y: R|kotlin/String| = R|/x|.R|kotlin/Any.toString|() + public get(): R|kotlin/String| + public final val z: R|kotlin/String| = R|/y|.R|kotlin/Any.hashCode|().R|kotlin/Any.toString|() + public get(): R|kotlin/String| + public final fun toString(x: R|kotlin/String|): R|kotlin/String| { + ^toString (String(IC), R|/x|.R|kotlin/Any.toString|()) + } + public final data class ProductGroup : R|kotlin/Any| { + public constructor(short_name: R|kotlin/String|, parent: R|ProductGroup?|): R|ProductGroup| { + super() + } + + public final val short_name: R|kotlin/String| = R|/short_name| + public get(): R|kotlin/String| + + public final val parent: R|ProductGroup?| = R|/parent| + public get(): R|ProductGroup?| + + public final val name: R|kotlin/String| = when () { + ==(R|/parent|, Null(null)) -> { + R|/short_name| + } + else -> { + (R|/parent|.R|/ProductGroup.name|.R|kotlin/Any.toString|(), String( ), R|/short_name|.R|kotlin/Any.toString|()) + } + } + + public get(): R|kotlin/String| + + public final fun component1(): R|kotlin/String| + + public final fun component2(): R|ProductGroup?| + + public final fun copy(short_name: R|kotlin/String| = this@R|/ProductGroup|.R|/ProductGroup.short_name|, parent: R|ProductGroup?| = this@R|/ProductGroup|.R|/ProductGroup.parent|): R|ProductGroup| + + } 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 c4e76d6f416..4d4ad5bdb40 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 @@ -48,6 +48,11 @@ public class ExtendedFirDiagnosticsTestGenerated extends AbstractExtendedFirDiag runTest("compiler/fir/analysis-tests/testData/extendedCheckers/RedundantReturnUnitTypeChecker.kt"); } + @TestMetadata("RedundantSingleExpressionStringTemplateChecker.kt") + public void testRedundantSingleExpressionStringTemplateChecker() throws Exception { + runTest("compiler/fir/analysis-tests/testData/extendedCheckers/RedundantSingleExpressionStringTemplateChecker.kt"); + } + @TestMetadata("RedundantVisibilityModifierChecker.kt") public void testRedundantVisibilityModifierChecker() throws Exception { runTest("compiler/fir/analysis-tests/testData/extendedCheckers/RedundantVisibilityModifierChecker.kt"); diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/ExtendedExpressionCheckers.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/ExtendedExpressionCheckers.kt index b43e8e1a4bd..53e1b7833bf 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/ExtendedExpressionCheckers.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/ExtendedExpressionCheckers.kt @@ -7,10 +7,12 @@ package org.jetbrains.kotlin.fir.analysis.checkers.expression import org.jetbrains.kotlin.fir.analysis.checkers.extended.ArrayEqualityCanBeReplacedWithEquals import org.jetbrains.kotlin.fir.analysis.checkers.extended.CanBeReplacedWithOperatorAssignmentChecker +import org.jetbrains.kotlin.fir.analysis.checkers.extended.RedundantSingleExpressionStringTemplateChecker object ExtendedExpressionCheckers : ExpressionCheckers() { override val expressionCheckers: List = listOf( - ArrayEqualityCanBeReplacedWithEquals + ArrayEqualityCanBeReplacedWithEquals, + RedundantSingleExpressionStringTemplateChecker ) override val variableAssignmentCheckers: List = listOf( CanBeReplacedWithOperatorAssignmentChecker diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/extended/RedundantSingleExpressionStringTemplateChecker.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/extended/RedundantSingleExpressionStringTemplateChecker.kt new file mode 100644 index 00000000000..e7ce1fae66c --- /dev/null +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/extended/RedundantSingleExpressionStringTemplateChecker.kt @@ -0,0 +1,39 @@ +/* + * 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 com.intellij.psi.PsiElement +import org.jetbrains.kotlin.fir.FirFakeSourceElementKind +import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext +import org.jetbrains.kotlin.fir.analysis.checkers.expression.FirBasicExpresionChecker +import org.jetbrains.kotlin.fir.analysis.diagnostics.DiagnosticReporter +import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.REDUNDANT_SINGLE_EXPRESSION_STRING_TEMPLATE +import org.jetbrains.kotlin.fir.expressions.FirFunctionCall +import org.jetbrains.kotlin.fir.expressions.FirStatement +import org.jetbrains.kotlin.fir.psi +import org.jetbrains.kotlin.fir.symbols.StandardClassIds +import org.jetbrains.kotlin.fir.types.classId +import org.jetbrains.kotlin.fir.types.coneType +import org.jetbrains.kotlin.psi.KtStringTemplateExpression + +object RedundantSingleExpressionStringTemplateChecker : FirBasicExpresionChecker() { + override fun check(expression: FirStatement, context: CheckerContext, reporter: DiagnosticReporter) { + if (expression.source?.kind != FirFakeSourceElementKind.GeneratedToStringCallOnTemplateEntry) return + if (expression !is FirFunctionCall) return + if ( + expression.explicitReceiver?.typeRef?.coneType?.classId == StandardClassIds.String + && expression.psi?.findStringParent()?.children?.size == 1 // there is no more children in original string template + ) { + reporter.report(expression.source, REDUNDANT_SINGLE_EXPRESSION_STRING_TEMPLATE) + } + } + + private fun PsiElement.findStringParent(): KtStringTemplateExpression? { + if (this is KtStringTemplateExpression) return this + return if (this.parent != null) this.parent.findStringParent() + else null + } +} \ 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 8be77f65dbd..e7d40476d29 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 @@ -89,7 +89,10 @@ object FirErrors { val REDUNDANT_MODALITY_MODIFIER by warning0() val REDUNDANT_RETURN_UNIT_TYPE by warning0() val REDUNDANT_EXPLICIT_TYPE by warning0() + val REDUNDANT_SINGLE_EXPRESSION_STRING_TEMPLATE by warning0() val CAN_BE_VAL by warning0() val CAN_BE_REPLACED_WITH_OPERATOR_ASSIGNMENT by warning0() val ARRAY_EQUALITY_OPERATOR_CAN_BE_REPLACED_WITH_EQUALS by warning0() } + +