From 8395018de8c715d194d169558d69d87f6de16c7a Mon Sep 17 00:00:00 2001 From: Vladimir Sukharev Date: Tue, 24 Oct 2023 16:28:30 +0000 Subject: [PATCH] [FIR] Fix disappeared INVALID_CHARACTERS_NATIVE_ERROR https://youtrack.jetbrains.com/issue/KT-60003/K2-Disappeared-INVALIDCHARACTERSNATIVEERROR ^KT-60003 Fixed Merge-request: KT-MR-12686 Merged-by: Vladimir Sukharev --- .../checkers/FirNativeIdentifierChecker.kt | 2 +- .../FirNativePackageDirectiveChecker.kt | 29 +++++ .../checkers/NativeDeclarationCheckers.kt | 5 + compiler/fir/checkers/module.md | 5 +- .../kotlin/fir/analysis/FirSourceUtils.kt | 113 ++++++++++++++---- .../LighterTreeElementFinderByType.kt | 45 ------- .../fir/analysis/PsiElementFinderByType.kt | 43 ------- .../nativeTests/identifiers.fir.kt | 68 ----------- .../diagnostics/nativeTests/identifiers.kt | 4 + 9 files changed, 134 insertions(+), 180 deletions(-) create mode 100644 compiler/fir/checkers/checkers.native/src/org/jetbrains/kotlin/fir/analysis/native/checkers/FirNativePackageDirectiveChecker.kt delete mode 100644 compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/LighterTreeElementFinderByType.kt delete mode 100644 compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/PsiElementFinderByType.kt delete mode 100644 compiler/testData/diagnostics/nativeTests/identifiers.fir.kt diff --git a/compiler/fir/checkers/checkers.native/src/org/jetbrains/kotlin/fir/analysis/native/checkers/FirNativeIdentifierChecker.kt b/compiler/fir/checkers/checkers.native/src/org/jetbrains/kotlin/fir/analysis/native/checkers/FirNativeIdentifierChecker.kt index 0dcc854fc9c..f1cc5be8eca 100644 --- a/compiler/fir/checkers/checkers.native/src/org/jetbrains/kotlin/fir/analysis/native/checkers/FirNativeIdentifierChecker.kt +++ b/compiler/fir/checkers/checkers.native/src/org/jetbrains/kotlin/fir/analysis/native/checkers/FirNativeIdentifierChecker.kt @@ -37,7 +37,7 @@ object FirNativeIdentifierChecker : FirBasicDeclarationChecker() { } } - private fun checkNameAndReport(name: Name, source: KtSourceElement?, context: CheckerContext, reporter: DiagnosticReporter) { + internal fun checkNameAndReport(name: Name, source: KtSourceElement?, context: CheckerContext, reporter: DiagnosticReporter) { if (source != null && source.kind !is KtFakeSourceElementKind && !name.isSpecial) { val text = name.asString() val message = when { diff --git a/compiler/fir/checkers/checkers.native/src/org/jetbrains/kotlin/fir/analysis/native/checkers/FirNativePackageDirectiveChecker.kt b/compiler/fir/checkers/checkers.native/src/org/jetbrains/kotlin/fir/analysis/native/checkers/FirNativePackageDirectiveChecker.kt new file mode 100644 index 00000000000..ed6d39c6a5b --- /dev/null +++ b/compiler/fir/checkers/checkers.native/src/org/jetbrains/kotlin/fir/analysis/native/checkers/FirNativePackageDirectiveChecker.kt @@ -0,0 +1,29 @@ +/* + * Copyright 2010-2023 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.native.checkers + +import org.jetbrains.kotlin.KtNodeTypes.REFERENCE_EXPRESSION +import org.jetbrains.kotlin.diagnostics.DiagnosticReporter +import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext +import org.jetbrains.kotlin.fir.analysis.checkers.declaration.FirFileChecker +import org.jetbrains.kotlin.fir.analysis.forEachChildOfType +import org.jetbrains.kotlin.fir.analysis.native.checkers.FirNativeIdentifierChecker.checkNameAndReport +import org.jetbrains.kotlin.fir.declarations.FirFile +import org.jetbrains.kotlin.name.Name +import org.jetbrains.kotlin.text + +object FirNativePackageDirectiveChecker : FirFileChecker() { + override fun check(declaration: FirFile, context: CheckerContext, reporter: DiagnosticReporter) { + declaration.packageDirective.source?.forEachChildOfType(setOf(REFERENCE_EXPRESSION)) { + checkNameAndReport( + Name.identifier(it.text.toString()), + it, + context, + reporter + ) + } + } +} diff --git a/compiler/fir/checkers/checkers.native/src/org/jetbrains/kotlin/fir/analysis/native/checkers/NativeDeclarationCheckers.kt b/compiler/fir/checkers/checkers.native/src/org/jetbrains/kotlin/fir/analysis/native/checkers/NativeDeclarationCheckers.kt index 7693482d917..dabb1caf5f8 100644 --- a/compiler/fir/checkers/checkers.native/src/org/jetbrains/kotlin/fir/analysis/native/checkers/NativeDeclarationCheckers.kt +++ b/compiler/fir/checkers/checkers.native/src/org/jetbrains/kotlin/fir/analysis/native/checkers/NativeDeclarationCheckers.kt @@ -33,4 +33,9 @@ object NativeDeclarationCheckers : DeclarationCheckers() { FirNativeObjCRefinementAnnotationChecker, FirNativeHiddenFromObjCInheritanceChecker, ) + + override val fileCheckers: Set + get() = setOf( + FirNativePackageDirectiveChecker, + ) } diff --git a/compiler/fir/checkers/module.md b/compiler/fir/checkers/module.md index d6ed74c0b16..78bec5fcbf1 100644 --- a/compiler/fir/checkers/module.md +++ b/compiler/fir/checkers/module.md @@ -2,10 +2,11 @@ ## Checkers structure -There are four kinds of checkers: +There are six kinds of checkers: - [DeclarationChecker](./src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirDeclarationChecker.kt) - [ExpressionChecker](./src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirExpressionChecker.kt) - [FirTypeChecker](./src/org/jetbrains/kotlin/fir/analysis/checkers/type/FirTypeChecker.kt) +- [FirLanguageVersionSettingsChecker](./src/org/jetbrains/kotlin/fir/analysis/checkers/config/FirLanguageVersionSettingsChecker.kt) - [FirControlFlowChecker](./src/org/jetbrains/kotlin/fir/analysis/checkers/cfa/FirControlFlowChecker.kt) The first three kinds are typed and may be restricted to checking only a specific type of declaration/expression/type ref. To simplify working with checkers for different FIR elements, there is a number of typed typealiases: @@ -13,6 +14,8 @@ The first three kinds are typed and may be restricted to checking only a specifi - Expressions: [FirExpressionCheckerAliases.kt](./gen/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirExpressionCheckerAliases.kt) - Type refs: [FirTypeCheckerAliases.kt](./gen/org/jetbrains/kotlin/fir/analysis/checkers/type/FirTypeCheckerAliases.kt) +The next kind, `FirLanguageVersionSettingsChecker`, is to check language version settings independently of particular code pieces. + The last kind of checker, `FirControlFlowChecker`, is for checkers which perform Control Flow Analysis (CFA) and is supposed to work with every declaration that has its own Control Flow Graph (CFG) ## Checkers contracts diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/FirSourceUtils.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/FirSourceUtils.kt index b4bf9d1f510..8ee0d05d3ca 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/FirSourceUtils.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/FirSourceUtils.kt @@ -6,41 +6,110 @@ package org.jetbrains.kotlin.fir.analysis import com.intellij.lang.LighterASTNode +import com.intellij.psi.PsiElement import com.intellij.psi.tree.IElementType import com.intellij.psi.tree.TokenSet +import com.intellij.util.diff.FlyweightCapableTreeStructure import org.jetbrains.kotlin.* import org.jetbrains.kotlin.fir.declarations.FirImport -import org.jetbrains.kotlin.name.FqName +import org.jetbrains.kotlin.psi.psiUtil.allChildren +import org.jetbrains.kotlin.util.getChildren +import org.jetbrains.kotlin.utils.addToStdlib.butIf +import org.jetbrains.kotlin.utils.addToStdlib.popLast -fun KtSourceElement.getChild(type: IElementType, index: Int = 0, depth: Int = -1, reverse: Boolean = false): KtSourceElement? { - return getChild(setOf(type), index, depth, reverse) -} +fun KtSourceElement.getChild(type: IElementType, index: Int = 0, depth: Int = -1, reverse: Boolean = false): KtSourceElement? = + getChild(setOf(type), index, depth, reverse) -fun KtSourceElement.getChild(types: TokenSet, index: Int = 0, depth: Int = -1, reverse: Boolean = false): KtSourceElement? { - return getChild(types.types.toSet(), index, depth, reverse) -} +fun KtSourceElement.getChild(types: TokenSet, index: Int = 0, depth: Int = -1, reverse: Boolean = false): KtSourceElement? = + getChild(types.types.toSet(), index, depth, reverse) fun KtSourceElement.getChild(types: Set, index: Int = 0, depth: Int = -1, reverse: Boolean = false): KtSourceElement? { - return when (this) { - is KtPsiSourceElement -> { - getChild(types, index, depth, reverse) + var idx = index + + forEachChildOfType(types, depth, reverse) { + if (idx-- == 0) { + return it } - is KtLightSourceElement -> { - getChild(types, index, depth, reverse) - } - else -> null + } + + return null +} + +/** + * Iterates recursively over all children up to the given depth. + * `processChild` is invoked for each child having a type in the `types` set. + */ +inline fun KtSourceElement.forEachChildOfType( + types: Set, + depth: Int = -1, + reverse: Boolean = false, + processChild: (KtSourceElement) -> Unit, +) = when (this) { + is KtPsiSourceElement -> psi.forEachChildOfType(types, depth, reverse) { + processChild(it.toKtPsiSourceElement()) + } + is KtLightSourceElement -> lighterASTNode.forEachChildOfType(types, depth, reverse, treeStructure) { + processChild(it.toKtLightSourceElement(treeStructure)) } } -private fun KtPsiSourceElement.getChild(types: Set, index: Int, depth: Int, reverse: Boolean): KtSourceElement? { - val visitor = PsiElementFinderByType(types, index, depth, reverse) - return visitor.find(psi)?.toKtPsiSourceElement() -} +/** + * See [KtSourceElement.forEachChildOfType] + */ +inline fun PsiElement.forEachChildOfType( + types: Set, + depth: Int = -1, + reverse: Boolean = false, + processChild: (PsiElement) -> Unit, +) = forEachChildOfType( + this, types, depth, reverse, + getElementType = { it.node.elementType }, + getChildren = { it.allChildren.toList() }, + processChild, +) -private fun KtLightSourceElement.getChild(types: Set, index: Int, depth: Int, reverse: Boolean): KtSourceElement? { - val visitor = LighterTreeElementFinderByType(treeStructure, types, index, depth, reverse) - val childNode = visitor.find(lighterASTNode) ?: return null - return buildChildSourceElement(childNode) +/** + * See [KtSourceElement.forEachChildOfType] + */ +inline fun LighterASTNode.forEachChildOfType( + types: Set, + depth: Int = -1, + reverse: Boolean = false, + treeStructure: FlyweightCapableTreeStructure, + processChild: (LighterASTNode) -> Unit, +) = forEachChildOfType( + this, types, depth, reverse, + getElementType = { it.tokenType }, + getChildren = { it.getChildren(treeStructure) }, + processChild, +) + +inline fun forEachChildOfType( + root: T, + types: Set, + depth: Int = -1, + reverse: Boolean = false, + getElementType: (T) -> IElementType, + getChildren: (T) -> List, + processChild: (T) -> Unit, +) { + val stack = mutableListOf(root to 0) + + while (stack.isNotEmpty()) { + val (element, currentDepth) = stack.popLast() + + if (currentDepth != 0 && getElementType(element) in types) { + processChild(element) + } + + if (currentDepth == depth) { + continue + } + + getChildren(element).butIf(!reverse) { it.asReversed() }.forEach { child -> + stack += child to (currentDepth + 1) + } + } } /** diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/LighterTreeElementFinderByType.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/LighterTreeElementFinderByType.kt deleted file mode 100644 index 8674a816546..00000000000 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/LighterTreeElementFinderByType.kt +++ /dev/null @@ -1,45 +0,0 @@ -/* - * 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 - -import com.intellij.lang.LighterASTNode -import com.intellij.psi.tree.IElementType -import com.intellij.util.diff.FlyweightCapableTreeStructure -import org.jetbrains.kotlin.util.getChildren - -class LighterTreeElementFinderByType( - private val tree: FlyweightCapableTreeStructure, - private var types: Collection, - private var index: Int, - private val depth: Int, - private val reverse: Boolean, -) { - fun find(node: LighterASTNode?): LighterASTNode? { - if (node == null) return null - return visitNode(node, 0) - } - - private fun visitNode(node: LighterASTNode, currentDepth: Int): LighterASTNode? { - if (currentDepth != 0) { - if (node.tokenType in types) { - if (index == 0) { - return node - } - index-- - } - } - - if (currentDepth == depth) return null - - val children = if (reverse) node.getChildren(tree).asReversed() else node.getChildren(tree) - for (child in children) { - val result = visitNode(child, currentDepth + 1) - if (result != null) return result - } - - return null - } -} diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/PsiElementFinderByType.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/PsiElementFinderByType.kt deleted file mode 100644 index eb9cc748ef7..00000000000 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/PsiElementFinderByType.kt +++ /dev/null @@ -1,43 +0,0 @@ -/* - * 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 - -import com.intellij.psi.PsiElement -import com.intellij.psi.tree.IElementType -import org.jetbrains.kotlin.psi.psiUtil.allChildren - -class PsiElementFinderByType( - private val types: Collection, - private var index: Int, - private val depth: Int, - private val reverse: Boolean, -) { - fun find(root: PsiElement): PsiElement? { - return visitElement(root, 0) - } - - private fun visitElement(element: PsiElement, currentDepth: Int): PsiElement? { - if (currentDepth != 0) { - if (element.node.elementType in types) { - if (index == 0) { - return element - } - index-- - } - } - - if (currentDepth == depth) return null - - val children = if (reverse) element.allChildren.toList().asReversed().iterator() else element.allChildren.iterator() - for (child in children) { - val result = visitElement(child, currentDepth + 1) - if (result != null) return result - } - - return null - } - -} diff --git a/compiler/testData/diagnostics/nativeTests/identifiers.fir.kt b/compiler/testData/diagnostics/nativeTests/identifiers.fir.kt deleted file mode 100644 index afb8202a9f8..00000000000 --- a/compiler/testData/diagnostics/nativeTests/identifiers.fir.kt +++ /dev/null @@ -1,68 +0,0 @@ -// !DIAGNOSTICS: -UNUSED_PARAMETER -UNUSED_VARIABLE -MISSING_DEPENDENCY_SUPERCLASS - -// FIXME: rename identifiers.kt - -// FILE: 1.kt -package `check.pkg` - -// FILE: 2.kt -package totally.normal.pkg - -class `Check.Class` -class NormalClass { - fun `check$member`() {} -} - -object `Check;Object` -object NormalObject - -data class Pair(val first: Int, val `next,one`: Int) - -object Delegate { - operator fun getValue(thisRef: Any?, property: kotlin.reflect.KProperty<*>): Any? = null -} - -fun `check(function`() { - val `check)variable` = 1 - val `check[delegated[variable` by Delegate - - val normalVariable = 2 - val normalDelegatedVariable by Delegate - - val (check, `destructuring]declaration`) = Pair(1, 2) -} - -fun normalFunction() {} - -val `check{property` = 1 -val `check}delegated}property` by Delegate -val normalProperty = 2 -val normalDelegatedProperty by Delegate - -fun checkValueParameter(`check/parameter`: Int) {} - -fun <`check, normalTypeParameter> checkTypeParameter() {} - -enum class `Check>Enum>Entry` { - `CHECK:ENUM:ENTRY`; -} - -typealias `check\typealias` = Any - -fun `check&`() {} - -fun `check~`() {} - -fun `check*`() {} - -fun `check?`() {} - -fun `check#`() {} - -fun `check|`() {} - -fun `check§`() {} - -fun `check%`() {} - -fun `check@`() {} diff --git a/compiler/testData/diagnostics/nativeTests/identifiers.kt b/compiler/testData/diagnostics/nativeTests/identifiers.kt index d2f9fde42b5..f447b60f133 100644 --- a/compiler/testData/diagnostics/nativeTests/identifiers.kt +++ b/compiler/testData/diagnostics/nativeTests/identifiers.kt @@ -1,3 +1,4 @@ +// FIR_IDENTICAL // !DIAGNOSTICS: -UNUSED_PARAMETER -UNUSED_VARIABLE -MISSING_DEPENDENCY_SUPERCLASS // FIXME: rename identifiers.kt @@ -5,6 +6,9 @@ // FILE: 1.kt package `check.pkg` +// FILE: 11.kt +package one.`two.three`.four.`five.six`.seven + // FILE: 2.kt package totally.normal.pkg