From 850ca1916faaf9425c61d36787f9361176f8f3b1 Mon Sep 17 00:00:00 2001 From: Dmitriy Novozhilov Date: Wed, 8 Sep 2021 18:04:34 +0300 Subject: [PATCH] [FE] Commonize positioning strategies and move them to :frontend-psi module --- .../SourceElementPositioningStrategies.kt | 2 +- .../diagnostics/PositioningStrategies.kt | 107 +---------------- .../kotlin/diagnostics/PositioningStrategy.kt | 20 +--- .../resolve/jvm/diagnostics/ErrorsJvm.java | 1 - .../ClassicPositioningStrategies.kt | 113 ++++++++++++++++++ .../DiagnosticFactoryWithPsiElement.java | 3 +- .../jetbrains/kotlin/diagnostics/Errors.java | 4 +- .../diagnostics/JsCodePositioningStrategy.kt | 8 +- 8 files changed, 130 insertions(+), 128 deletions(-) rename compiler/{frontend => frontend.common-psi}/src/org/jetbrains/kotlin/diagnostics/PositioningStrategies.kt (88%) rename compiler/{frontend => frontend.common-psi}/src/org/jetbrains/kotlin/diagnostics/PositioningStrategy.kt (74%) create mode 100644 compiler/frontend/src/org/jetbrains/kotlin/diagnostics/ClassicPositioningStrategies.kt diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/SourceElementPositioningStrategies.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/SourceElementPositioningStrategies.kt index f0f60913388..0be0422962b 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/SourceElementPositioningStrategies.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/SourceElementPositioningStrategies.kt @@ -284,7 +284,7 @@ object SourceElementPositioningStrategies { val ACTUAL_DECLARATION_NAME = SourceElementPositioningStrategy( LightTreePositioningStrategies.ACTUAL_DECLARATION_NAME, - PositioningStrategies.ACTUAL_DECLARATION_NAME + PositioningStrategies.DEFAULT // TODO ) val LABEL = SourceElementPositioningStrategy( diff --git a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/PositioningStrategies.kt b/compiler/frontend.common-psi/src/org/jetbrains/kotlin/diagnostics/PositioningStrategies.kt similarity index 88% rename from compiler/frontend/src/org/jetbrains/kotlin/diagnostics/PositioningStrategies.kt rename to compiler/frontend.common-psi/src/org/jetbrains/kotlin/diagnostics/PositioningStrategies.kt index 4ae337c9d48..407741ba09b 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/PositioningStrategies.kt +++ b/compiler/frontend.common-psi/src/org/jetbrains/kotlin/diagnostics/PositioningStrategies.kt @@ -1,5 +1,5 @@ /* - * Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors. + * 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. */ @@ -9,22 +9,16 @@ import com.intellij.openapi.util.TextRange import com.intellij.psi.* import com.intellij.psi.tree.TokenSet import org.jetbrains.kotlin.KtNodeTypes -import org.jetbrains.kotlin.cfg.UnreachableCode -import org.jetbrains.kotlin.descriptors.MemberDescriptor -import org.jetbrains.kotlin.diagnostics.Errors.ACTUAL_WITHOUT_EXPECT -import org.jetbrains.kotlin.diagnostics.Errors.NO_ACTUAL_FOR_EXPECT import org.jetbrains.kotlin.lexer.KtModifierKeywordToken import org.jetbrains.kotlin.lexer.KtTokens import org.jetbrains.kotlin.lexer.KtTokens.MODALITY_MODIFIERS import org.jetbrains.kotlin.lexer.KtTokens.VISIBILITY_MODIFIERS import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.psiUtil.* -import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualCompatibility.Incompatible -import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualCompatibility.Incompatible.* import org.jetbrains.kotlin.utils.sure object PositioningStrategies { - private open class DeclarationHeader : PositioningStrategy() { + open class DeclarationHeader : PositioningStrategy() { override fun isValid(element: T): Boolean { if (element is KtNamedDeclaration && element !is KtObjectDeclaration && @@ -102,93 +96,8 @@ object PositioningStrategies { } } - @JvmField - val ACTUAL_DECLARATION_NAME: PositioningStrategy = object : DeclarationHeader() { - override fun mark(element: KtNamedDeclaration): List { - val nameIdentifier = element.nameIdentifier - return when { - nameIdentifier != null -> markElement(nameIdentifier) - element is KtNamedFunction -> DECLARATION_SIGNATURE.mark(element) - else -> DEFAULT.mark(element) - } - } - } - - private val ParametrizedDiagnostic.firstIncompatibility: Incompatible? - get() { - val map = when (factory) { - NO_ACTUAL_FOR_EXPECT -> - NO_ACTUAL_FOR_EXPECT.cast(this).c - ACTUAL_WITHOUT_EXPECT -> - ACTUAL_WITHOUT_EXPECT.cast(this).b - else -> - return null - } - return map.keys.firstOrNull() - } - - private val propertyKindTokens = TokenSet.create(KtTokens.VAL_KEYWORD, KtTokens.VAR_KEYWORD) - - private val classKindTokens = TokenSet.create(KtTokens.CLASS_KEYWORD, KtTokens.OBJECT_KEYWORD, KtTokens.INTERFACE_KEYWORD) - - @JvmField - val INCOMPATIBLE_DECLARATION: PositioningStrategy = object : DeclarationHeader() { - override fun markDiagnostic(diagnostic: ParametrizedDiagnostic): List { - val element = diagnostic.psiElement - val callableDeclaration = element as? KtCallableDeclaration - val incompatibility = diagnostic.firstIncompatibility - return when (incompatibility) { - null, Unknown, is ClassScopes, EnumEntries -> null - ClassKind -> { - val startElement = - element.modifierList?.getModifier(KtTokens.ENUM_KEYWORD) - ?: element.modifierList?.getModifier(KtTokens.ANNOTATION_KEYWORD) - val endElement = - element.node.findChildByType(classKindTokens)?.psi - ?: element.nameIdentifier - if (startElement != null && endElement != null) { - return markRange(startElement, endElement) - } else { - endElement - } - } - TypeParameterNames, TypeParameterCount, - TypeParameterUpperBounds, TypeParameterVariance, TypeParameterReified -> { - (element as? KtTypeParameterListOwner)?.typeParameterList - } - CallableKind -> { - (callableDeclaration as? KtNamedFunction)?.funKeyword - ?: (callableDeclaration as? KtProperty)?.valOrVarKeyword - } - ParameterShape -> { - callableDeclaration?.let { it.receiverTypeReference ?: it.valueParameterList } - } - ParameterCount, ParameterTypes, ParameterNames, - ValueParameterVararg, ValueParameterNoinline, ValueParameterCrossinline -> { - callableDeclaration?.valueParameterList - } - ReturnType -> { - callableDeclaration?.typeReference - } - FunctionModifiersDifferent, FunctionModifiersNotSubset, - PropertyModifiers, ClassModifiers -> { - element.modifierList - } - PropertyKind -> { - element.node.findChildByType(propertyKindTokens)?.psi - } - Supertypes -> { - (element as? KtClassOrObject)?.getSuperTypeList() - } - Modality -> { - element.modalityModifier() - } - Visibility -> { - element.visibilityModifier() - } - }?.let { markElement(it) } ?: ACTUAL_DECLARATION_NAME.mark(element) - } - } + val propertyKindTokens = TokenSet.create(KtTokens.VAL_KEYWORD, KtTokens.VAR_KEYWORD) + val classKindTokens = TokenSet.create(KtTokens.CLASS_KEYWORD, KtTokens.OBJECT_KEYWORD, KtTokens.INTERFACE_KEYWORD) @JvmField val DECLARATION_START_TO_NAME: PositioningStrategy = object : DeclarationHeader() { @@ -693,14 +602,6 @@ object PositioningStrategies { } } - @JvmField - val UNREACHABLE_CODE: PositioningStrategy = object : PositioningStrategy() { - override fun markDiagnostic(diagnostic: ParametrizedDiagnostic): List { - val unreachableCode = Errors.UNREACHABLE_CODE.cast(diagnostic) - return UnreachableCode.getUnreachableTextRanges(unreachableCode.psiElement, unreachableCode.a, unreachableCode.b) - } - } - @JvmField val AS_TYPE: PositioningStrategy = object : PositioningStrategy() { override fun mark(element: KtBinaryExpressionWithTypeRHS): List { diff --git a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/PositioningStrategy.kt b/compiler/frontend.common-psi/src/org/jetbrains/kotlin/diagnostics/PositioningStrategy.kt similarity index 74% rename from compiler/frontend/src/org/jetbrains/kotlin/diagnostics/PositioningStrategy.kt rename to compiler/frontend.common-psi/src/org/jetbrains/kotlin/diagnostics/PositioningStrategy.kt index 879d7ebeec9..83e5666d152 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/PositioningStrategy.kt +++ b/compiler/frontend.common-psi/src/org/jetbrains/kotlin/diagnostics/PositioningStrategy.kt @@ -1,17 +1,6 @@ /* - * Copyright 2010-2015 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * 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.diagnostics @@ -26,8 +15,9 @@ import org.jetbrains.kotlin.psi.psiUtil.endOffset import org.jetbrains.kotlin.psi.psiUtil.startOffset open class PositioningStrategy { - open fun markDiagnostic(diagnostic: ParametrizedDiagnostic): List { - return mark(diagnostic.psiElement) + open fun markDiagnostic(diagnostic: DiagnosticMarker): List { + @Suppress("UNCHECKED_CAST") + return mark(diagnostic.psiElement as E) } open fun mark(element: E): List { diff --git a/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/diagnostics/ErrorsJvm.java b/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/diagnostics/ErrorsJvm.java index cb9dca2be16..fc7256346e3 100644 --- a/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/diagnostics/ErrorsJvm.java +++ b/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/diagnostics/ErrorsJvm.java @@ -13,7 +13,6 @@ import org.jetbrains.kotlin.descriptors.DeclarationDescriptor; import org.jetbrains.kotlin.descriptors.Named; import org.jetbrains.kotlin.diagnostics.*; import org.jetbrains.kotlin.name.FqName; -import org.jetbrains.kotlin.name.Name; import org.jetbrains.kotlin.psi.*; import org.jetbrains.kotlin.types.KotlinType; diff --git a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/ClassicPositioningStrategies.kt b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/ClassicPositioningStrategies.kt new file mode 100644 index 00000000000..230746d5a11 --- /dev/null +++ b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/ClassicPositioningStrategies.kt @@ -0,0 +1,113 @@ +/* + * 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.diagnostics + +import com.intellij.openapi.util.TextRange +import com.intellij.psi.PsiElement +import org.jetbrains.kotlin.cfg.UnreachableCode +import org.jetbrains.kotlin.descriptors.MemberDescriptor +import org.jetbrains.kotlin.lexer.KtTokens +import org.jetbrains.kotlin.psi.* +import org.jetbrains.kotlin.psi.psiUtil.modalityModifier +import org.jetbrains.kotlin.psi.psiUtil.visibilityModifier +import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualCompatibility + +object ClassicPositioningStrategies { + @JvmField + val ACTUAL_DECLARATION_NAME: PositioningStrategy = + object : PositioningStrategies.DeclarationHeader() { + override fun mark(element: KtNamedDeclaration): List { + val nameIdentifier = element.nameIdentifier + return when { + nameIdentifier != null -> markElement(nameIdentifier) + element is KtNamedFunction -> PositioningStrategies.DECLARATION_SIGNATURE.mark(element) + else -> PositioningStrategies.DEFAULT.mark(element) + } + } + } + + // TODO: move to specific strategies + private val DiagnosticMarker.firstIncompatibility: ExpectActualCompatibility.Incompatible? + get() { + @Suppress("UNCHECKED_CAST") + val map = when (factoryName) { + Errors.NO_ACTUAL_FOR_EXPECT.name -> (this as DiagnosticWithParameters3Marker<*, *, *>).c + Errors.ACTUAL_WITHOUT_EXPECT.name -> (this as DiagnosticWithParameters2Marker<*, *>).b + else -> null + } as? Map, Collection> ?: return null + return map.keys.firstOrNull() + } + + @JvmField + val INCOMPATIBLE_DECLARATION: PositioningStrategy = + object : PositioningStrategies.DeclarationHeader() { + override fun markDiagnostic(diagnostic: DiagnosticMarker): List { + val element = diagnostic.psiElement as KtNamedDeclaration + val callableDeclaration = element as? KtCallableDeclaration + val incompatibility = diagnostic.firstIncompatibility + return when (incompatibility) { + null, ExpectActualCompatibility.Incompatible.Unknown, is ExpectActualCompatibility.Incompatible.ClassScopes, ExpectActualCompatibility.Incompatible.EnumEntries -> null + ExpectActualCompatibility.Incompatible.ClassKind -> { + val startElement = + element.modifierList?.getModifier(KtTokens.ENUM_KEYWORD) + ?: element.modifierList?.getModifier(KtTokens.ANNOTATION_KEYWORD) + val endElement = + element.node.findChildByType(PositioningStrategies.classKindTokens)?.psi + ?: element.nameIdentifier + if (startElement != null && endElement != null) { + return markRange(startElement, endElement) + } else { + endElement + } + } + ExpectActualCompatibility.Incompatible.TypeParameterNames, ExpectActualCompatibility.Incompatible.TypeParameterCount, + ExpectActualCompatibility.Incompatible.TypeParameterUpperBounds, ExpectActualCompatibility.Incompatible.TypeParameterVariance, ExpectActualCompatibility.Incompatible.TypeParameterReified -> { + (element as? KtTypeParameterListOwner)?.typeParameterList + } + ExpectActualCompatibility.Incompatible.CallableKind -> { + (callableDeclaration as? KtNamedFunction)?.funKeyword + ?: (callableDeclaration as? KtProperty)?.valOrVarKeyword + } + ExpectActualCompatibility.Incompatible.ParameterShape -> { + callableDeclaration?.let { it.receiverTypeReference ?: it.valueParameterList } + } + ExpectActualCompatibility.Incompatible.ParameterCount, ExpectActualCompatibility.Incompatible.ParameterTypes, ExpectActualCompatibility.Incompatible.ParameterNames, + ExpectActualCompatibility.Incompatible.ValueParameterVararg, ExpectActualCompatibility.Incompatible.ValueParameterNoinline, ExpectActualCompatibility.Incompatible.ValueParameterCrossinline -> { + callableDeclaration?.valueParameterList + } + ExpectActualCompatibility.Incompatible.ReturnType -> { + callableDeclaration?.typeReference + } + ExpectActualCompatibility.Incompatible.FunctionModifiersDifferent, ExpectActualCompatibility.Incompatible.FunctionModifiersNotSubset, + ExpectActualCompatibility.Incompatible.PropertyModifiers, ExpectActualCompatibility.Incompatible.ClassModifiers -> { + element.modifierList + } + ExpectActualCompatibility.Incompatible.PropertyKind -> { + element.node.findChildByType(PositioningStrategies.propertyKindTokens)?.psi + } + ExpectActualCompatibility.Incompatible.Supertypes -> { + (element as? KtClassOrObject)?.getSuperTypeList() + } + ExpectActualCompatibility.Incompatible.Modality -> { + element.modalityModifier() + } + ExpectActualCompatibility.Incompatible.Visibility -> { + element.visibilityModifier() + } + }?.let { markElement(it) } ?: ACTUAL_DECLARATION_NAME.mark(element) + } + } + + @JvmField + val UNREACHABLE_CODE: PositioningStrategy = object : PositioningStrategy() { + override fun markDiagnostic(diagnostic: DiagnosticMarker): List { + @Suppress("UNCHECKED_CAST") + val unreachableCode = diagnostic as DiagnosticWithParameters2Marker, Set> + return UnreachableCode.getUnreachableTextRanges(unreachableCode.psiElement as KtElement, unreachableCode.a, unreachableCode.b) + } + } + +} diff --git a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/DiagnosticFactoryWithPsiElement.java b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/DiagnosticFactoryWithPsiElement.java index f77f6a04caa..42ef41af565 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/DiagnosticFactoryWithPsiElement.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/DiagnosticFactoryWithPsiElement.java @@ -31,7 +31,8 @@ public abstract class DiagnosticFactoryWithPsiElement getTextRanges(ParametrizedDiagnostic diagnostic) { - return positioningStrategy.markDiagnostic(diagnostic); + // TODO: it's strange that java requires cast here, because ParametrizedDiagnostic inherits DiagnosticMarker + return positioningStrategy.markDiagnostic((DiagnosticMarker) diagnostic); } protected boolean isValid(ParametrizedDiagnostic diagnostic) { diff --git a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java index 4bdeb831ac6..fa644c1c86b 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java @@ -41,6 +41,8 @@ import java.util.List; import java.util.Map; import java.util.Set; +import static org.jetbrains.kotlin.diagnostics.ClassicPositioningStrategies.ACTUAL_DECLARATION_NAME; +import static org.jetbrains.kotlin.diagnostics.ClassicPositioningStrategies.INCOMPATIBLE_DECLARATION; import static org.jetbrains.kotlin.diagnostics.PositioningStrategies.*; import static org.jetbrains.kotlin.diagnostics.Severity.*; @@ -953,7 +955,7 @@ public interface Errors { // Control flow / Data flow DiagnosticFactory2, Set> UNREACHABLE_CODE = DiagnosticFactory2.create( - WARNING, PositioningStrategies.UNREACHABLE_CODE); + WARNING, ClassicPositioningStrategies.UNREACHABLE_CODE); DiagnosticFactory0 VARIABLE_WITH_NO_TYPE_NO_INITIALIZER = DiagnosticFactory0.create(ERROR, DECLARATION_NAME); diff --git a/js/js.frontend/src/org/jetbrains/kotlin/js/resolve/diagnostics/JsCodePositioningStrategy.kt b/js/js.frontend/src/org/jetbrains/kotlin/js/resolve/diagnostics/JsCodePositioningStrategy.kt index 385056e4e19..699e0d81e8a 100644 --- a/js/js.frontend/src/org/jetbrains/kotlin/js/resolve/diagnostics/JsCodePositioningStrategy.kt +++ b/js/js.frontend/src/org/jetbrains/kotlin/js/resolve/diagnostics/JsCodePositioningStrategy.kt @@ -18,16 +18,12 @@ package org.jetbrains.kotlin.js.resolve.diagnostics import com.intellij.openapi.util.TextRange import com.intellij.psi.PsiElement -import org.jetbrains.kotlin.diagnostics.PositioningStrategy +import org.jetbrains.kotlin.diagnostics.* import org.jetbrains.kotlin.psi.KtExpression -import org.jetbrains.kotlin.diagnostics.DiagnosticWithParameters3 -import org.jetbrains.kotlin.diagnostics.ParametrizedDiagnostic import org.jetbrains.kotlin.js.resolve.diagnostics.JsCallData -import org.jetbrains.kotlin.diagnostics.DiagnosticFactory1 -import org.jetbrains.kotlin.diagnostics.DiagnosticWithParameters1 object JsCodePositioningStrategy : PositioningStrategy() { - override fun markDiagnostic(diagnostic: ParametrizedDiagnostic): List { + override fun markDiagnostic(diagnostic: DiagnosticMarker): List { @Suppress("UNCHECKED_CAST") val diagnosticWithParameters = diagnostic as DiagnosticWithParameters1 val textRange = diagnosticWithParameters.a.reportRange