[FE 1.0] Prohibit confusing syntax inside when branches

^KT-48385 Fixed
This commit is contained in:
Dmitriy Novozhilov
2021-10-20 16:40:13 +03:00
committed by teamcityserver
parent 653fc85461
commit 1513e739c6
28 changed files with 1323 additions and 28 deletions
@@ -997,6 +997,7 @@ public interface Errors {
DiagnosticFactory0<KtElement> SENSELESS_NULL_IN_WHEN = DiagnosticFactory0.create(WARNING);
DiagnosticFactory0<PsiElement> INVALID_IF_AS_EXPRESSION = DiagnosticFactory0.create(ERROR);
DiagnosticFactoryForDeprecation0<PsiElement> CONFUSING_BRANCH_CONDITION = DiagnosticFactoryForDeprecation0.create(LanguageFeature.ProhibitConfusingSyntaxInWhenBranches);
// Nullability
@@ -833,6 +833,7 @@ public class DefaultErrorMessages {
MAP.put(SENSELESS_NULL_IN_WHEN, "Expression under 'when' is never equal to null");
MAP.put(INVALID_IF_AS_EXPRESSION, "'if' must have both main and 'else' branches if used as an expression");
MAP.put(CONFUSING_BRANCH_CONDITION, "The logical expressions may be understood ambiguously in when with subject branches. Please wrap it with parenthesis");
MAP.put(OVERRIDING_FINAL_MEMBER, "''{0}'' in ''{1}'' is final and cannot be overridden", NAME, NAME);
MAP.put(CANNOT_WEAKEN_ACCESS_PRIVILEGE, "Cannot weaken access privilege ''{0}'' for ''{1}'' in ''{2}''", VISIBILITY, NAME, NAME);
@@ -0,0 +1,51 @@
/*
* Copyright 2010-2021 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.resolve.checkers
import com.intellij.psi.tree.TokenSet
import org.jetbrains.kotlin.config.LanguageVersionSettings
import org.jetbrains.kotlin.diagnostics.Errors.CONFUSING_BRANCH_CONDITION
import org.jetbrains.kotlin.lexer.KtTokens.*
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.resolve.BindingTrace
object ConfusingWhenBranchSyntaxChecker {
private val prohibitedTokens = TokenSet.create(
IN_KEYWORD, NOT_IN,
LT, LTEQ, GT, GTEQ,
EQEQ, EXCLEQ, EQEQEQ, EXCLEQEQEQ,
ANDAND, OROR
)
fun check(whenExpression: KtWhenExpression, languageVersionSettings: LanguageVersionSettings, trace: BindingTrace) {
if (whenExpression.subjectExpression == null && whenExpression.subjectVariable == null) return
for (entry in whenExpression.entries) {
for (condition in entry.conditions) {
checkCondition(condition, languageVersionSettings, trace)
}
}
}
private fun checkCondition(condition: KtWhenCondition, languageVersionSettings: LanguageVersionSettings, trace: BindingTrace) {
when (condition) {
is KtWhenConditionWithExpression -> checkConditionExpression(condition.expression, languageVersionSettings, trace)
is KtWhenConditionInRange -> checkConditionExpression(condition.rangeExpression, languageVersionSettings, trace)
}
}
private fun checkConditionExpression(rawExpression: KtExpression?, languageVersionSettings: LanguageVersionSettings, trace: BindingTrace) {
if (rawExpression == null) return
if (rawExpression is KtParenthesizedExpression) return
val shouldReport = when (val expression = KtPsiUtil.safeDeparenthesize(rawExpression)) {
is KtIsExpression -> true
is KtBinaryExpression -> expression.operationToken in prohibitedTokens
else -> false
}
if (shouldReport) {
trace.report(CONFUSING_BRANCH_CONDITION.on(languageVersionSettings, rawExpression))
}
}
}
@@ -32,6 +32,7 @@ import org.jetbrains.kotlin.resolve.calls.checkers.RttiOperation
import org.jetbrains.kotlin.resolve.calls.context.ContextDependency.INDEPENDENT
import org.jetbrains.kotlin.resolve.calls.smartcasts.*
import org.jetbrains.kotlin.resolve.calls.util.CallMaker
import org.jetbrains.kotlin.resolve.checkers.ConfusingWhenBranchSyntaxChecker
import org.jetbrains.kotlin.resolve.checkers.PrimitiveNumericComparisonCallChecker
import org.jetbrains.kotlin.resolve.scopes.LexicalScope
import org.jetbrains.kotlin.resolve.scopes.LexicalScopeKind
@@ -249,6 +250,8 @@ class PatternMatchingTypingVisitor internal constructor(facade: ExpressionTyping
val branchesType = branchesTypeInfo.type ?: return noTypeInfo(resultDataFlowInfo)
val resultType = components.dataFlowAnalyzer.checkType(branchesType, expression, contextWithExpectedType)
ConfusingWhenBranchSyntaxChecker.check(expression, contextWithExpectedType.languageVersionSettings, trace)
return createTypeInfo(resultType, resultDataFlowInfo, branchesTypeInfo.jumpOutPossible, contextWithExpectedType.dataFlowInfo)
}