[K2] Disappeared UNSUPPORTED

Forbid prefixes and suffixes for numeric literals and string

^KT-59881
This commit is contained in:
Anastasia.Nekrasova
2023-12-02 11:19:42 +02:00
committed by Space Team
parent c5248fc5f4
commit b83d8595e5
6 changed files with 128 additions and 97 deletions
@@ -9,9 +9,7 @@ import org.jetbrains.kotlin.fir.analysis.checkers.declaration.FirInlineBodyQuali
import org.jetbrains.kotlin.fir.analysis.checkers.declaration.FirInlineBodyResolvedQualifierChecker
import org.jetbrains.kotlin.fir.analysis.checkers.declaration.FirInlineBodyVariableAssignmentChecker
import org.jetbrains.kotlin.fir.analysis.checkers.expression.*
import org.jetbrains.kotlin.fir.analysis.checkers.syntax.FirCommaInWhenConditionChecker
import org.jetbrains.kotlin.fir.analysis.checkers.syntax.FirConfusingWhenBranchSyntaxChecker
import org.jetbrains.kotlin.fir.analysis.checkers.syntax.FirUnderscoredTypeArgumentSyntaxChecker
import org.jetbrains.kotlin.fir.analysis.checkers.syntax.*
object CommonExpressionCheckers : ExpressionCheckers() {
override val annotationCallCheckers: Set<FirAnnotationCallChecker>
@@ -27,6 +25,7 @@ object CommonExpressionCheckers : ExpressionCheckers() {
FirDeprecationChecker,
FirRecursiveProblemChecker,
FirOptInUsageAccessChecker,
FirPrefixAndSuffixSyntaxChecker,
)
override val throwExpressionCheckers: Set<FirThrowExpressionChecker>
@@ -0,0 +1,124 @@
/*
* 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.fir.analysis.checkers.syntax
import com.intellij.lang.LighterASTNode
import com.intellij.psi.PsiElement
import com.intellij.psi.tree.IElementType
import com.intellij.util.diff.FlyweightCapableTreeStructure
import org.jetbrains.kotlin.*
import org.jetbrains.kotlin.KtNodeTypes.BINARY_EXPRESSION
import org.jetbrains.kotlin.diagnostics.DiagnosticReporter
import org.jetbrains.kotlin.diagnostics.reportOn
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors
import org.jetbrains.kotlin.fir.expressions.FirStatement
import org.jetbrains.kotlin.lexer.KtKeywordToken
import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.psi.KtExpression
import org.jetbrains.kotlin.psi.psiUtil.nextLeaf
import org.jetbrains.kotlin.psi.psiUtil.prevLeaf
import org.jetbrains.kotlin.util.getChildren
object FirPrefixAndSuffixSyntaxChecker : FirExpressionSyntaxChecker<FirStatement, KtExpression>() {
private val literalConstants = listOf(KtNodeTypes.CHARACTER_CONSTANT, KtNodeTypes.FLOAT_CONSTANT, KtNodeTypes.INTEGER_CONSTANT)
override fun isApplicable(element: FirStatement, source: KtSourceElement): Boolean =
source.kind !is KtFakeSourceElementKind && (source.elementType == KtNodeTypes.STRING_TEMPLATE || source.elementType in literalConstants)
override fun checkPsi(
element: FirStatement,
source: KtPsiSourceElement,
psi: KtExpression,
context: CheckerContext,
reporter: DiagnosticReporter,
) {
psi.prevLeaf()?.let { checkLiteralPrefixOrSuffix(it, context, reporter) }
psi.nextLeaf()?.let { checkLiteralPrefixOrSuffix(it, context, reporter) }
}
override fun checkLightTree(
element: FirStatement,
source: KtLightSourceElement,
context: CheckerContext,
reporter: DiagnosticReporter,
) {
source.lighterASTNode.prevLeaf(source.treeStructure)
?.let { checkLiteralPrefixOrSuffix(it, source, context, reporter) }
source.lighterASTNode.nextLeaf(source.treeStructure)
?.let { checkLiteralPrefixOrSuffix(it, source, context, reporter) }
}
private enum class Direction(val offset: Int) {
PREVIOUS(-1),
NEXT(1)
}
private fun LighterASTNode.getLeaf(
direction: Direction,
treeStructure: FlyweightCapableTreeStructure<LighterASTNode>,
): LighterASTNode? {
val parent = treeStructure.getParent(this) ?: return null
val children = parent.getChildren(treeStructure)
val index = children.indexOf(this)
val leaf = children.getOrNull(index - direction.offset)
return when {
// Necessary for finding the next leaf in complex binary expressions, for example 'a foo"asdsfsa"foo a'
leaf == null && parent.tokenType == BINARY_EXPRESSION -> parent.getLeaf(direction, treeStructure)
leaf == null -> return null
else -> {
// This is necessary to obtain the simplest node, as the found leaf can be a complex expression
var result = leaf
var resultChildren = leaf.getChildren(treeStructure)
while (resultChildren.isNotEmpty()) {
result = if (direction == Direction.PREVIOUS) resultChildren.first() else resultChildren.last()
resultChildren = result.getChildren(treeStructure)
}
result
}
}
}
private fun LighterASTNode.prevLeaf(treeStructure: FlyweightCapableTreeStructure<LighterASTNode>): LighterASTNode? {
return getLeaf(Direction.PREVIOUS, treeStructure)
}
private fun LighterASTNode.nextLeaf(treeStructure: FlyweightCapableTreeStructure<LighterASTNode>): LighterASTNode? {
return getLeaf(Direction.NEXT, treeStructure)
}
private fun checkLiteralPrefixOrSuffix(
prefixOrSuffix: PsiElement,
context: CheckerContext,
reporter: DiagnosticReporter,
) {
if (illegalLiteralPrefixOrSuffix(prefixOrSuffix.node.elementType)) {
report(prefixOrSuffix.toKtPsiSourceElement(), context, reporter)
}
}
private fun checkLiteralPrefixOrSuffix(
prefixOrSuffix: LighterASTNode,
source: KtSourceElement,
context: CheckerContext,
reporter: DiagnosticReporter,
) {
val elementType = prefixOrSuffix.tokenType ?: return
if (illegalLiteralPrefixOrSuffix(elementType)) {
report(prefixOrSuffix.toKtLightSourceElement(source.treeStructure), context, reporter)
}
}
private fun illegalLiteralPrefixOrSuffix(elementType: IElementType): Boolean =
(elementType === KtTokens.IDENTIFIER || elementType === KtTokens.INTEGER_LITERAL || elementType === KtTokens.FLOAT_LITERAL || elementType is KtKeywordToken)
private fun report(source: KtSourceElement, context: CheckerContext, reporter: DiagnosticReporter) {
reporter.reportOn(source, FirErrors.UNSUPPORTED, "literal prefixes and suffixes", context)
}
}
@@ -1,54 +0,0 @@
// !DIAGNOSTICS: -UNUSED_PARAMETER -USELESS_CAST
infix fun Any?.foo(a: Any) {}
infix fun Any?.zoo(a: Any) {}
infix fun Any?.Loo(a: Any) {}
infix fun Any?.doo(a: Any) {}
infix fun Any?.ddoo(a: Any) {}
operator fun Any?.contains(a: Any): Boolean = true
fun test(a: Any) {
1f<!UNRESOLVED_REFERENCE!>oo<!> a
1ffoo a
1doo a
1ddoo a
1<!INFIX_MODIFIER_REQUIRED!>contains<!> a
1Lfoo a
1L<!UNRESOLVED_REFERENCE!>oo<!> a
1LLoo a
0b1foo a
0b1Lfoo a
0b1L<!UNRESOLVED_REFERENCE!>oo<!> a
0b1LLoo a
0xf<!UNRESOLVED_REFERENCE!>oo<!> a
0xff<!UNRESOLVED_REFERENCE!>oo<!> a
0xfLLoo a
1.0f<!UNRESOLVED_REFERENCE!>oo<!> a
1.0ffoo a
1.0doo a
1.0ddoo a
.0f<!UNRESOLVED_REFERENCE!>oo<!> a
.0ffoo a
.0doo a
.0ddoo a
1in a
1.0in a
1.0fin a
1.0<!UNRESOLVED_REFERENCE!>din<!> a
.0in a
.0fin a
.0<!UNRESOLVED_REFERENCE!>din<!> a
<!USELESS_IS_CHECK!>1is Any<!>
1as Any
1as? Any
<!USELESS_IS_CHECK!>1!is Any<!>
1!in a
}
@@ -1,3 +1,4 @@
// FIR_IDENTICAL
// !DIAGNOSTICS: -UNUSED_PARAMETER -USELESS_CAST
infix fun Any?.foo(a: Any) {}
@@ -1,40 +0,0 @@
// !DIAGNOSTICS: -UNUSED_PARAMETER
infix fun Any?.foo(a: Any) {}
operator fun Any?.contains(a: Any): Boolean = true
fun test(a: Any) {
a foo""
a foo"asd"
a foo"$a"
a foo"asd${a}sfsa"
a foo"""sdf"""
a foo'd'
a foo<!EMPTY_CHARACTER_LITERAL!>''<!>
a foo""foo a
a foo"asd"foo a
a foo"$a"foo a
a foo"asd${a}sfsa"foo a
a foo"""sdf"""foo a
a foo'd'foo a
a foo<!EMPTY_CHARACTER_LITERAL!>''<!>foo a
a in"foo"
a in"""foo"""
a in's'
a in<!EMPTY_CHARACTER_LITERAL!>''<!>
a !in"foo"
a !in"""foo"""
a !in's'
a !in<!EMPTY_CHARACTER_LITERAL!>''<!>
if(<!USELESS_IS_CHECK!>"s"is Any<!>) {}
if(<!USELESS_IS_CHECK!>"s"is Any<!>) {}
test("s"as Any)
a foo""<!SYNTAX!>1<!>
a foo""<!SYNTAX!>1.0<!>
}
@@ -1,3 +1,4 @@
// FIR_IDENTICAL
// !DIAGNOSTICS: -UNUSED_PARAMETER
infix fun Any?.foo(a: Any) {}