[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.FirInlineBodyResolvedQualifierChecker
|
||||||
import org.jetbrains.kotlin.fir.analysis.checkers.declaration.FirInlineBodyVariableAssignmentChecker
|
import org.jetbrains.kotlin.fir.analysis.checkers.declaration.FirInlineBodyVariableAssignmentChecker
|
||||||
import org.jetbrains.kotlin.fir.analysis.checkers.expression.*
|
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.*
|
||||||
import org.jetbrains.kotlin.fir.analysis.checkers.syntax.FirConfusingWhenBranchSyntaxChecker
|
|
||||||
import org.jetbrains.kotlin.fir.analysis.checkers.syntax.FirUnderscoredTypeArgumentSyntaxChecker
|
|
||||||
|
|
||||||
object CommonExpressionCheckers : ExpressionCheckers() {
|
object CommonExpressionCheckers : ExpressionCheckers() {
|
||||||
override val annotationCallCheckers: Set<FirAnnotationCallChecker>
|
override val annotationCallCheckers: Set<FirAnnotationCallChecker>
|
||||||
@@ -27,6 +25,7 @@ object CommonExpressionCheckers : ExpressionCheckers() {
|
|||||||
FirDeprecationChecker,
|
FirDeprecationChecker,
|
||||||
FirRecursiveProblemChecker,
|
FirRecursiveProblemChecker,
|
||||||
FirOptInUsageAccessChecker,
|
FirOptInUsageAccessChecker,
|
||||||
|
FirPrefixAndSuffixSyntaxChecker,
|
||||||
)
|
)
|
||||||
|
|
||||||
override val throwExpressionCheckers: Set<FirThrowExpressionChecker>
|
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
|
// !DIAGNOSTICS: -UNUSED_PARAMETER -USELESS_CAST
|
||||||
|
|
||||||
infix fun Any?.foo(a: Any) {}
|
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
|
// !DIAGNOSTICS: -UNUSED_PARAMETER
|
||||||
|
|
||||||
infix fun Any?.foo(a: Any) {}
|
infix fun Any?.foo(a: Any) {}
|
||||||
|
|||||||
Reference in New Issue
Block a user