[K2] Disappeared UNSUPPORTED
Forbid prefixes and suffixes for numeric literals and string ^KT-59881
This commit is contained in:
committed by
Space Team
parent
c5248fc5f4
commit
b83d8595e5
+2
-3
@@ -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>
|
||||
|
||||
+124
@@ -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) {}
|
||||
|
||||
Reference in New Issue
Block a user