[FE] Commonize positioning strategies and move them to :frontend-psi module

This commit is contained in:
Dmitriy Novozhilov
2021-09-08 18:04:34 +03:00
committed by TeamCityServer
parent 7536dadb65
commit 850ca1916f
8 changed files with 130 additions and 128 deletions
@@ -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(
@@ -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<T : KtDeclaration> : PositioningStrategy<T>() {
open class DeclarationHeader<T : KtDeclaration> : PositioningStrategy<T>() {
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<KtNamedDeclaration> = object : DeclarationHeader<KtNamedDeclaration>() {
override fun mark(element: KtNamedDeclaration): List<TextRange> {
val nameIdentifier = element.nameIdentifier
return when {
nameIdentifier != null -> markElement(nameIdentifier)
element is KtNamedFunction -> DECLARATION_SIGNATURE.mark(element)
else -> DEFAULT.mark(element)
}
}
}
private val ParametrizedDiagnostic<out KtNamedDeclaration>.firstIncompatibility: Incompatible<MemberDescriptor>?
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<KtNamedDeclaration> = object : DeclarationHeader<KtNamedDeclaration>() {
override fun markDiagnostic(diagnostic: ParametrizedDiagnostic<out KtNamedDeclaration>): List<TextRange> {
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<KtDeclaration> = object : DeclarationHeader<KtDeclaration>() {
@@ -693,14 +602,6 @@ object PositioningStrategies {
}
}
@JvmField
val UNREACHABLE_CODE: PositioningStrategy<PsiElement> = object : PositioningStrategy<PsiElement>() {
override fun markDiagnostic(diagnostic: ParametrizedDiagnostic<out PsiElement>): List<TextRange> {
val unreachableCode = Errors.UNREACHABLE_CODE.cast(diagnostic)
return UnreachableCode.getUnreachableTextRanges(unreachableCode.psiElement, unreachableCode.a, unreachableCode.b)
}
}
@JvmField
val AS_TYPE: PositioningStrategy<KtBinaryExpressionWithTypeRHS> = object : PositioningStrategy<KtBinaryExpressionWithTypeRHS>() {
override fun mark(element: KtBinaryExpressionWithTypeRHS): List<TextRange> {
@@ -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<in E : PsiElement> {
open fun markDiagnostic(diagnostic: ParametrizedDiagnostic<out E>): List<TextRange> {
return mark(diagnostic.psiElement)
open fun markDiagnostic(diagnostic: DiagnosticMarker): List<TextRange> {
@Suppress("UNCHECKED_CAST")
return mark(diagnostic.psiElement as E)
}
open fun mark(element: E): List<TextRange> {
@@ -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;
@@ -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<KtNamedDeclaration> =
object : PositioningStrategies.DeclarationHeader<KtNamedDeclaration>() {
override fun mark(element: KtNamedDeclaration): List<TextRange> {
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<MemberDescriptor>?
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<ExpectActualCompatibility.Incompatible<MemberDescriptor>, Collection<MemberDescriptor>> ?: return null
return map.keys.firstOrNull()
}
@JvmField
val INCOMPATIBLE_DECLARATION: PositioningStrategy<KtNamedDeclaration> =
object : PositioningStrategies.DeclarationHeader<KtNamedDeclaration>() {
override fun markDiagnostic(diagnostic: DiagnosticMarker): List<TextRange> {
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<PsiElement> = object : PositioningStrategy<PsiElement>() {
override fun markDiagnostic(diagnostic: DiagnosticMarker): List<TextRange> {
@Suppress("UNCHECKED_CAST")
val unreachableCode = diagnostic as DiagnosticWithParameters2Marker<Set<KtElement>, Set<KtElement>>
return UnreachableCode.getUnreachableTextRanges(unreachableCode.psiElement as KtElement, unreachableCode.a, unreachableCode.b)
}
}
}
@@ -31,7 +31,8 @@ public abstract class DiagnosticFactoryWithPsiElement<E extends PsiElement, D ex
}
protected List<TextRange> getTextRanges(ParametrizedDiagnostic<E> diagnostic) {
return positioningStrategy.markDiagnostic(diagnostic);
// TODO: it's strange that java requires cast here, because ParametrizedDiagnostic<E> inherits DiagnosticMarker
return positioningStrategy.markDiagnostic((DiagnosticMarker) diagnostic);
}
protected boolean isValid(ParametrizedDiagnostic<E> diagnostic) {
@@ -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<KtElement, Set<KtElement>, Set<KtElement>> UNREACHABLE_CODE = DiagnosticFactory2.create(
WARNING, PositioningStrategies.UNREACHABLE_CODE);
WARNING, ClassicPositioningStrategies.UNREACHABLE_CODE);
DiagnosticFactory0<KtVariableDeclaration> VARIABLE_WITH_NO_TYPE_NO_INITIALIZER = DiagnosticFactory0.create(ERROR, DECLARATION_NAME);
@@ -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<PsiElement>() {
override fun markDiagnostic(diagnostic: ParametrizedDiagnostic<out PsiElement>): List<TextRange> {
override fun markDiagnostic(diagnostic: DiagnosticMarker): List<TextRange> {
@Suppress("UNCHECKED_CAST")
val diagnosticWithParameters = diagnostic as DiagnosticWithParameters1<KtExpression, JsCallData>
val textRange = diagnosticWithParameters.a.reportRange