org.jetbrains.kotlin.diagnostics.PositioningStrategies.kt Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of kotlin-compiler-embeddable Show documentation
Show all versions of kotlin-compiler-embeddable Show documentation
the Kotlin compiler embeddable
/*
* 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.lang.ASTNode
import com.intellij.openapi.util.TextRange
import com.intellij.psi.*
import com.intellij.psi.tree.IElementType
import com.intellij.psi.tree.TokenSet
import com.intellij.psi.util.descendants
import org.jetbrains.kotlin.KtNodeTypes
import org.jetbrains.kotlin.lexer.KtModifierKeywordToken
import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.lexer.KtTokens.ENUM_KEYWORD
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.utils.addToStdlib.UnsafeCastFunction
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
import org.jetbrains.kotlin.utils.sure
object PositioningStrategies {
open class DeclarationHeader : PositioningStrategy() {
override fun isValid(element: T): Boolean {
if (element is KtNamedDeclaration &&
element !is KtObjectDeclaration &&
element !is KtSecondaryConstructor &&
element !is KtFunction
) {
if (element.nameIdentifier == null) {
return false
}
}
return super.isValid(element)
}
}
@JvmField
val DEFAULT: PositioningStrategy = object : PositioningStrategy() {
override fun mark(element: PsiElement): List {
when (element) {
is KtObjectLiteralExpression -> {
val objectDeclaration = element.objectDeclaration
val objectKeyword = objectDeclaration.getObjectKeyword()!!
val delegationSpecifierList = objectDeclaration.getSuperTypeList() ?: return markElement(objectKeyword)
return markRange(objectKeyword, delegationSpecifierList)
}
is KtObjectDeclaration -> {
return markRange(
element.getObjectKeyword()!!,
element.nameIdentifier ?: element.getObjectKeyword()!!
)
}
is KtConstructorDelegationCall -> {
return SECONDARY_CONSTRUCTOR_DELEGATION_CALL.mark(element)
}
else -> {
return super.mark(element)
}
}
}
}
@JvmField
val SUPERTYPES_LIST: PositioningStrategy = object : PositioningStrategy() {
override fun mark(element: PsiElement): List {
val supertypes = ((
element as? KtClassOrObject
) ?: return markElement(element)
).superTypeListEntries
return if (supertypes.isEmpty())
markElement(element)
else
markRange(supertypes[0], supertypes.last())
}
}
@JvmField
val DECLARATION_RETURN_TYPE: PositioningStrategy = object : PositioningStrategy() {
override fun mark(element: KtDeclaration): List {
return markElement(getElementToMark(element))
}
override fun isValid(element: KtDeclaration): Boolean {
return !hasSyntaxErrors(getElementToMark(element))
}
private fun getElementToMark(declaration: KtDeclaration): PsiElement {
val (returnTypeRef, nameIdentifierOrPlaceholder) = when (declaration) {
is KtCallableDeclaration -> Pair(declaration.typeReference, declaration.nameIdentifier)
is KtPropertyAccessor -> Pair(declaration.returnTypeReference, declaration.namePlaceholder)
else -> Pair(null, null)
}
if (returnTypeRef != null) return returnTypeRef
if (nameIdentifierOrPlaceholder != null) return nameIdentifierOrPlaceholder
return declaration
}
}
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() {
private fun PsiElement.firstNonCommentNonAnnotationLeaf(): PsiElement? {
var child: PsiElement? = firstChild ?: return this // this is leaf
while (true) {
if (child is PsiComment || child is PsiWhiteSpace || child is KtAnnotationEntry) {
child = child.nextSibling
continue
}
if (child == null) return null // no children of accepted type in this
val leaf = child.firstNonCommentNonAnnotationLeaf()
if (leaf == null) {
child = child.nextSibling
continue
}
return leaf
}
}
override fun mark(element: KtDeclaration): List {
val startElement = element.firstNonCommentNonAnnotationLeaf() ?: element
val nameIdentifier = (element as? KtNamedDeclaration)?.nameIdentifier
return if (nameIdentifier != null) {
markRange(startElement, nameIdentifier)
} else when (element) {
// companion object/constructors without name
is KtConstructor<*> -> {
markRange(startElement, element.getConstructorKeyword() ?: element)
}
is KtObjectDeclaration -> {
markRange(startElement, element.getObjectKeyword() ?: element)
}
else -> DEFAULT.mark(element)
}
}
}
@JvmField
val CONTEXT_KEYWORD: PositioningStrategy = object : PositioningStrategy() {
override fun mark(element: PsiElement): List =
element.descendants().firstIsInstanceOrNull()?.firstChild?.textRange?.let(::markRange)
?: DEFAULT.mark(element)
}
private fun findStartingPsiElementForDeclarationName(
element: KtNamedDeclaration,
): PsiElement =
element.getModifierList()?.getModifier(ENUM_KEYWORD)
?: element.node.findChildByType(TokenSet.create(KtTokens.CLASS_KEYWORD, KtTokens.OBJECT_KEYWORD))?.psi
?: element
@JvmField
val DECLARATION_NAME: PositioningStrategy = object : DeclarationHeader() {
override fun mark(element: KtDeclaration): List {
if (element is KtNamedDeclaration) {
val nameIdentifier = element.nameIdentifier
if (nameIdentifier != null) {
if (element is KtClassOrObject) {
val startElement = findStartingPsiElementForDeclarationName(element)
return markRange(startElement, nameIdentifier)
}
return markElement(nameIdentifier)
}
if (element is KtNamedFunction) {
return DECLARATION_SIGNATURE.mark(element)
}
} else if (element is KtPropertyAccessor) {
return markElement(element.namePlaceholder)
}
return DEFAULT.mark(element)
}
}
@JvmField
val DECLARATION_NAME_OR_ACCESSOR: PositioningStrategy = object : DeclarationHeader() {
override fun mark(element: KtDeclaration): List {
return when (element) {
is KtPropertyAccessor -> markElement(element.namePlaceholder)
is KtNamedDeclaration -> DECLARATION_NAME.mark(element)
else -> super.mark(element)
}
}
}
@JvmField
val DECLARATION_NAME_ONLY: PositioningStrategy = object : DeclarationHeader() {
override fun mark(element: KtNamedDeclaration): List {
val nameIdentifier = element.nameIdentifier
if (nameIdentifier != null) {
return markElement(nameIdentifier)
}
if (element is KtNamedFunction) {
return DECLARATION_SIGNATURE.mark(element)
}
return DEFAULT.mark(element)
}
}
@JvmField
val DECLARATION_SIGNATURE: PositioningStrategy = object : DeclarationHeader() {
override fun mark(element: KtDeclaration): List {
when (element) {
is KtConstructor<*> -> {
val begin = element.getConstructorKeyword() ?: element.getValueParameterList() ?: return markElement(element)
val end = element.getValueParameterList() ?: element.getConstructorKeyword() ?: return markElement(element)
return markRange(begin, end)
}
is KtFunction -> {
val endOfSignatureElement =
element.typeReference
?: element.valueParameterList
?: element.nameIdentifier
?: element
val startElement = if (element is KtFunctionLiteral) {
element.getReceiverTypeReference()
?: element.getValueParameterList()
?: element
} else element
return markRange(startElement, endOfSignatureElement)
}
is KtProperty -> {
val endOfSignatureElement = element.typeReference ?: element.nameIdentifier ?: element
return markRange(element, endOfSignatureElement)
}
is KtPropertyAccessor -> {
val endOfSignatureElement =
element.returnTypeReference
?: element.rightParenthesis
?: element.namePlaceholder
return markRange(element, endOfSignatureElement)
}
is KtClass -> {
val nameAsDeclaration = element.nameIdentifier ?: return markElement(element)
val primaryConstructorParameterList =
element.getPrimaryConstructorParameterList() ?: return markElement(nameAsDeclaration)
return markRange(nameAsDeclaration, primaryConstructorParameterList)
}
is KtObjectDeclaration -> {
return DECLARATION_NAME.mark(element)
}
is KtClassInitializer -> {
return markRange(element.initKeyword.textRange)
}
}
return super.mark(element)
}
}
@JvmField
val CALLABLE_DECLARATION_SIGNATURE_NO_MODIFIERS: PositioningStrategy = object : DeclarationHeader() {
override fun mark(element: KtDeclaration): List {
when (element) {
is KtNamedFunction -> {
val startElement = element.funKeyword ?: element
val endOfSignatureElement =
element.typeReference
?: element.valueParameterList
?: element.nameIdentifier
?: element
return markRange(startElement, endOfSignatureElement)
}
is KtProperty -> {
val endOfSignatureElement = element.typeReference ?: element.nameIdentifier ?: element
return markRange(element.valOrVarKeyword, endOfSignatureElement)
}
is KtPropertyAccessor -> {
val endOfSignatureElement =
element.returnTypeReference
?: element.rightParenthesis
?: element.namePlaceholder
return markRange(element.namePlaceholder, endOfSignatureElement)
}
}
return DECLARATION_SIGNATURE.mark(element)
}
}
@JvmField
val DECLARATION_SIGNATURE_OR_DEFAULT: PositioningStrategy = object : PositioningStrategy() {
override fun mark(element: PsiElement): List {
return if (element is KtDeclaration)
DECLARATION_SIGNATURE.mark(element)
else
DEFAULT.mark(element)
}
override fun isValid(element: PsiElement): Boolean {
return if (element is KtDeclaration)
DECLARATION_SIGNATURE.isValid(element)
else
DEFAULT.isValid(element)
}
}
@JvmField
val NOT_SUPPORTED_IN_INLINE_MOST_RELEVANT: PositioningStrategy = object : PositioningStrategy() {
override fun mark(element: KtDeclaration): List =
markElement(
when (element) {
is KtClassOrObject ->
element.getDeclarationKeyword() ?: element.nameIdentifier ?: element
is KtNamedFunction ->
element.modifierList?.getModifier(KtTokens.INLINE_KEYWORD) ?: element.funKeyword ?: element
else -> element
}
)
}
@JvmField
val TYPE_PARAMETERS_OR_DECLARATION_SIGNATURE: PositioningStrategy = object : PositioningStrategy() {
override fun mark(element: KtDeclaration): List {
if (element is KtTypeParameterListOwner) {
val ktTypeParameterList = element.typeParameterList
if (ktTypeParameterList != null) {
return markElement(ktTypeParameterList)
}
}
return DECLARATION_SIGNATURE.mark(element)
}
}
@JvmField
val ABSTRACT_MODIFIER: PositioningStrategy =
ModifierSetBasedPositioningStrategy(KtTokens.ABSTRACT_KEYWORD)
@JvmField
val OPEN_MODIFIER: PositioningStrategy =
ModifierSetBasedPositioningStrategy(KtTokens.OPEN_KEYWORD)
@JvmField
val OVERRIDE_MODIFIER: PositioningStrategy =
ModifierSetBasedPositioningStrategy(KtTokens.OVERRIDE_KEYWORD)
@JvmField
val PRIVATE_MODIFIER: PositioningStrategy =
ModifierSetBasedPositioningStrategy(KtTokens.PRIVATE_KEYWORD)
@JvmField
val LATEINIT_MODIFIER: PositioningStrategy =
ModifierSetBasedPositioningStrategy(KtTokens.LATEINIT_KEYWORD)
@JvmField
val VARIANCE_MODIFIER: PositioningStrategy = projectionPosition()
@JvmField
val CONST_MODIFIER: PositioningStrategy =
ModifierSetBasedPositioningStrategy(KtTokens.CONST_KEYWORD)
@JvmField
val FUN_MODIFIER: PositioningStrategy =
ModifierSetBasedPositioningStrategy(KtTokens.FUN_KEYWORD)
@JvmField
val SUSPEND_MODIFIER: PositioningStrategy =
ModifierSetBasedPositioningStrategy(KtTokens.SUSPEND_KEYWORD)
@JvmField
val DATA_MODIFIER: PositioningStrategy =
ModifierSetBasedPositioningStrategy(KtTokens.DATA_KEYWORD)
@JvmField
val OPERATOR_MODIFIER: PositioningStrategy =
ModifierSetBasedPositioningStrategy(KtTokens.OPERATOR_KEYWORD)
@JvmField
val INFIX_MODIFIER: PositioningStrategy =
ModifierSetBasedPositioningStrategy(KtTokens.INFIX_KEYWORD)
@JvmField
val ENUM_MODIFIER: PositioningStrategy =
ModifierSetBasedPositioningStrategy(KtTokens.ENUM_KEYWORD)
@JvmField
val TAILREC_MODIFIER: PositioningStrategy =
ModifierSetBasedPositioningStrategy(KtTokens.TAILREC_KEYWORD)
@JvmField
val EXTERNAL_MODIFIER: PositioningStrategy =
ModifierSetBasedPositioningStrategy(KtTokens.EXTERNAL_KEYWORD)
@JvmField
val EXPECT_ACTUAL_MODIFIER: PositioningStrategy =
ModifierSetBasedPositioningStrategy(KtTokens.EXPECT_KEYWORD, KtTokens.ACTUAL_KEYWORD)
@JvmField
val OBJECT_KEYWORD: PositioningStrategy = object : PositioningStrategy() {
override fun mark(element: KtObjectDeclaration): List {
return markElement(element.getObjectKeyword() ?: element)
}
}
@JvmField
val FIELD_KEYWORD: PositioningStrategy = object : DeclarationHeader() {
override fun mark(element: KtBackingField): List {
return markElement(element.fieldKeyword)
}
}
@JvmField
val PROPERTY_DELEGATE: PositioningStrategy = object : DeclarationHeader() {
override fun mark(element: KtProperty): List {
val delegate = element.delegate
return if (delegate != null) {
markElement(delegate)
} else {
DEFAULT.mark(element)
}
}
}
@JvmField
val FOR_REDECLARATION: PositioningStrategy = object : PositioningStrategy() {
override fun mark(element: PsiElement): List {
val nameIdentifier = when (element) {
is KtNamedDeclaration -> element.nameIdentifier
is KtFile -> element.packageDirective!!.nameIdentifier
else -> null
}
if (nameIdentifier == null && element is KtObjectDeclaration) return DEFAULT.mark(element)
return markElement(nameIdentifier ?: element)
}
}
@JvmField
val FOR_UNRESOLVED_REFERENCE: PositioningStrategy = object : PositioningStrategy() {
override fun mark(element: KtReferenceExpression): List {
if (element is KtArrayAccessExpression) {
val ranges = element.bracketRanges
if (!ranges.isEmpty()) {
return ranges
}
}
return listOf(element.textRange)
}
}
@JvmStatic
fun modifierSetPosition(vararg tokens: KtModifierKeywordToken): PositioningStrategy {
return object : PositioningStrategy() {
override fun mark(element: KtModifierListOwner): List {
val modifierList = element.modifierList ?: return DEFAULT.mark(element)
for (token in tokens) {
val modifier = modifierList.getModifier(token)
if (modifier != null) {
return markElement(modifier)
}
}
return DEFAULT.mark(element)
}
}
}
@JvmStatic
fun projectionPosition(): PositioningStrategy {
return object : PositioningStrategy() {
override fun mark(element: KtModifierListOwner): List {
if (element is KtTypeProjection && element.projectionKind == KtProjectionKind.STAR) {
return markElement(element)
}
val modifierList = element.modifierList.sure { "No modifier list, but modifier has been found by the analyzer" }
modifierList.getModifier(KtTokens.IN_KEYWORD)?.let { return markElement(it) }
modifierList.getModifier(KtTokens.OUT_KEYWORD)?.let { return markElement(it) }
throw IllegalStateException("None of the modifiers is found: in, out")
}
}
}
@JvmField
val ARRAY_ACCESS: PositioningStrategy = object : PositioningStrategy() {
override fun mark(element: KtArrayAccessExpression): List {
return markElement(element.indicesNode)
}
}
@JvmField
val SAFE_ACCESS: PositioningStrategy = object : PositioningStrategy() {
override fun mark(element: PsiElement): List {
return markElement(element.node.findChildByType(KtTokens.SAFE_ACCESS)?.psi ?: element)
}
}
private open class ModifierSetBasedPositioningStrategy(private val modifierSet: TokenSet) : PositioningStrategy() {
constructor(vararg tokens: IElementType) : this(TokenSet.create(*tokens))
protected fun markModifier(element: KtModifierListOwner?): List? =
modifierSet.types.mapNotNull {
element?.modifierList?.getModifier(it as KtModifierKeywordToken)?.textRange
}.takeIf { it.isNotEmpty() }
override fun mark(element: KtModifierListOwner): List {
val result = markModifier(element)
if (result != null) return result
// Try to resolve situation when there's no visibility modifiers written before element
if (element is PsiNameIdentifierOwner) {
val nameIdentifier = element.nameIdentifier
if (nameIdentifier != null) {
return markElement(nameIdentifier)
}
}
val elementToMark = when (element) {
is KtObjectDeclaration -> element.getObjectKeyword()!!
is KtPropertyAccessor -> element.namePlaceholder
is KtAnonymousInitializer, is KtPrimaryConstructor -> element
else -> throw IllegalArgumentException(
"Can't find text range for element '${element::class.java.canonicalName}' with the text '${element.text}'"
)
}
return markElement(elementToMark)
}
}
private class InlineFunPositioningStrategy : ModifierSetBasedPositioningStrategy(KtTokens.INLINE_KEYWORD) {
override fun mark(element: KtModifierListOwner): List {
if (element is KtProperty) {
return markModifier(element.getter) ?: markModifier(element.setter) ?: super.mark(element)
}
return super.mark(element)
}
}
@JvmField
val VISIBILITY_MODIFIER: PositioningStrategy = ModifierSetBasedPositioningStrategy(VISIBILITY_MODIFIERS)
@JvmField
val MODALITY_MODIFIER: PositioningStrategy = ModifierSetBasedPositioningStrategy(MODALITY_MODIFIERS)
@JvmField
val INLINE_OR_VALUE_MODIFIER: PositioningStrategy =
ModifierSetBasedPositioningStrategy(KtTokens.INLINE_KEYWORD, KtTokens.VALUE_KEYWORD)
@JvmField
val INNER_MODIFIER: PositioningStrategy =
ModifierSetBasedPositioningStrategy(KtTokens.INNER_KEYWORD)
@JvmField
val INLINE_PARAMETER_MODIFIER: PositioningStrategy =
ModifierSetBasedPositioningStrategy(KtTokens.NOINLINE_KEYWORD, KtTokens.CROSSINLINE_KEYWORD)
@JvmField
val INLINE_FUN_MODIFIER: PositioningStrategy = InlineFunPositioningStrategy()
@JvmField
val VARIANCE_IN_PROJECTION: PositioningStrategy = object : PositioningStrategy() {
override fun mark(element: KtTypeProjection): List {
return markElement(element.projectionToken!!)
}
}
@JvmField
val PARAMETER_DEFAULT_VALUE: PositioningStrategy = object : PositioningStrategy() {
override fun mark(element: KtParameter): List {
return markNode(element.defaultValue!!.node)
}
}
@JvmField
val PARAMETERS_WITH_DEFAULT_VALUE: PositioningStrategy = object : PositioningStrategy() {
override fun mark(element: KtFunction): List =
element.valueParameters.filter(KtParameter::hasDefaultValue).takeIf(List<*>::isNotEmpty)?.flatMap { markNode(it.node) }
?: element.valueParameterList?.let { markNode(it.node) }
?: element.nameIdentifier?.let { markNode(it.node) }
?: markNode(element.node)
}
@JvmField
val PARAMETER_VARARG_MODIFIER: PositioningStrategy = object : PositioningStrategy() {
override fun mark(element: KtParameter): List {
val varargModifier = element.modifierList!!.getModifier(KtTokens.VARARG_KEYWORD)!!
return markNode(varargModifier.node)
}
}
/**
* Mark the name of a named argument. If the given element is not a named argument or doesn't have a name, then the entire given element
* is marked instead.
*/
@JvmField
val NAME_OF_NAMED_ARGUMENT: PositioningStrategy = object : PositioningStrategy() {
override fun mark(element: KtValueArgument): List {
return markElement(element.getArgumentName() ?: element)
}
}
@JvmField
val CALL_ELEMENT: PositioningStrategy = object : PositioningStrategy() {
override fun mark(element: PsiElement): List {
return markElement((element as? KtCallElement)?.calleeExpression ?: element)
}
}
@JvmField
val CALL_ELEMENT_WITH_DOT: PositioningStrategy = object : PositioningStrategy() {
override fun mark(element: KtQualifiedExpression): List {
val callElementRanges = SELECTOR_BY_QUALIFIED.mark(element)
val callElementRange = when (callElementRanges.size) {
1 -> callElementRanges.first()
else -> return callElementRanges
}
val dotRanges = SAFE_ACCESS.mark(element)
val dotRange = when (dotRanges.size) {
1 -> dotRanges.first()
else -> return dotRanges
}
return listOf(TextRange(dotRange.startOffset, callElementRange.endOffset))
}
}
@JvmField
val DECLARATION_WITH_BODY: PositioningStrategy = object : PositioningStrategy() {
override fun mark(element: KtDeclarationWithBody): List {
val lastBracketRange = element.bodyBlockExpression?.lastBracketRange
return if (lastBracketRange != null)
markRange(lastBracketRange)
else
markElement(element)
}
override fun isValid(element: KtDeclarationWithBody): Boolean {
return super.isValid(element) && element.bodyBlockExpression?.lastBracketRange != null
}
}
@JvmField
val VAL_OR_VAR_NODE: PositioningStrategy = object : PositioningStrategy() {
override fun mark(element: KtDeclaration): List {
return when (element) {
is KtParameter -> markElement(element.valOrVarKeyword ?: element)
is KtProperty -> markElement(element.valOrVarKeyword)
is KtDestructuringDeclaration -> markElement(element.valOrVarKeyword ?: element)
else -> error("Declaration is neither a parameter nor a property: " + element.getElementTextWithContext())
}
}
}
@JvmField
val ELSE_ENTRY: PositioningStrategy = object : PositioningStrategy() {
override fun mark(element: KtWhenEntry): List {
return markElement(element.elseKeyword!!)
}
}
@JvmField
val WHEN_EXPRESSION: PositioningStrategy = object : PositioningStrategy() {
override fun mark(element: KtWhenExpression): List {
return markElement(element.whenKeyword)
}
}
@JvmField
val WHEN_GUARD: PositioningStrategy = object : PositioningStrategy() {
override fun mark(element: KtWhenEntry): List {
return markElement(element.guard ?: element)
}
}
@JvmField
val IF_EXPRESSION: PositioningStrategy = object : PositioningStrategy() {
override fun mark(element: KtIfExpression): List {
return markElement(element.ifKeyword)
}
}
@JvmField
val WHEN_CONDITION_IN_RANGE: PositioningStrategy = object : PositioningStrategy() {
override fun mark(element: KtWhenConditionInRange): List {
return markElement(element.operationReference)
}
}
@JvmField
val SPECIAL_CONSTRUCT_TOKEN: PositioningStrategy = object : PositioningStrategy() {
override fun mark(element: KtExpression): List =
when (element) {
is KtWhenExpression -> markElement(element.whenKeyword)
is KtIfExpression -> markElement(element.ifKeyword)
is KtOperationExpression -> markElement(element.operationReference)
else -> error("Expression is not an if, when or operation expression: ${element.getElementTextWithContext()}")
}
}
@JvmField
val REDUNDANT_NULLABLE: PositioningStrategy = object : PositioningStrategy() {
override fun mark(element: KtTypeReference): List {
var typeElement = element.typeElement
var question: ASTNode? = null
var prevQuestion: ASTNode? = null
var lastQuestion: ASTNode? = null
while (typeElement is KtNullableType) {
prevQuestion = question
question = typeElement.questionMarkNode
if (lastQuestion == null) {
lastQuestion = question
}
typeElement = typeElement.innerType
}
if (lastQuestion != null) {
return markRange((prevQuestion ?: lastQuestion).psi, lastQuestion.psi)
}
return super.mark(element)
}
}
@JvmField
val NULLABLE_TYPE: PositioningStrategy = object : PositioningStrategy() {
override fun mark(element: KtNullableType): List {
return markNode(element.questionMarkNode)
}
}
@JvmField
val QUESTION_MARK_BY_TYPE: PositioningStrategy = object : PositioningStrategy() {
override fun mark(element: KtTypeReference): List {
val typeElement = element.typeElement
if (typeElement is KtNullableType) {
return markNode(typeElement.questionMarkNode)
}
return super.mark(element)
}
}
@JvmField
val CALL_EXPRESSION: PositioningStrategy = object : PositioningStrategy() {
override fun mark(element: PsiElement): List {
if (element is KtCallExpression) {
return markRange(element, element.typeArgumentList ?: element.calleeExpression ?: element)
}
return markElement(element)
}
}
@JvmField
val VALUE_ARGUMENTS: PositioningStrategy = object : PositioningStrategy() {
override fun mark(element: KtElement): List {
if (element is KtBinaryExpression && element.operationToken in KtTokens.ALL_ASSIGNMENTS) {
element.left.let { left -> left.unwrapParenthesesLabelsAndAnnotations()?.let { return markElement(it) } }
}
val qualifiedAccess = when (element) {
is KtQualifiedExpression -> element.selectorExpression ?: element
is KtClassOrObject -> element.getSuperTypeList() ?: element
else -> element
}
val argumentList = qualifiedAccess as? KtValueArgumentList
?: qualifiedAccess.getChildOfType()
return when {
argumentList != null -> {
val rightParenthesis = argumentList.rightParenthesis ?: return markElement(qualifiedAccess)
val lastArgument = argumentList.children.findLast { it is KtValueArgument }
if (lastArgument != null) {
markRange(lastArgument, rightParenthesis)
} else {
val leftParenthesis = argumentList.leftParenthesis
markRange(leftParenthesis ?: qualifiedAccess, rightParenthesis)
}
}
qualifiedAccess is KtCallExpression -> markElement(
qualifiedAccess.getChildOfType() ?: qualifiedAccess
)
else -> markElement(qualifiedAccess)
}
}
}
@JvmField
val VALUE_ARGUMENTS_LIST: PositioningStrategy = object : PositioningStrategy() {
override fun mark(element: KtElement): List {
return markElement(element.getChildOfType() ?: element)
}
}
@JvmField
val FUNCTION_PARAMETERS: PositioningStrategy = object : PositioningStrategy() {
override fun mark(element: KtFunction): List {
val valueParameterList = element.valueParameterList
if (valueParameterList != null) {
return markElement(valueParameterList)
}
if (element is KtFunctionLiteral) {
return markNode(element.lBrace.node)
}
return DECLARATION_SIGNATURE_OR_DEFAULT.mark(element)
}
}
@JvmField
val CUT_CHAR_QUOTES: PositioningStrategy = object : PositioningStrategy() {
override fun mark(element: KtElement): List {
if (element is KtConstantExpression) {
if (element.node.elementType == KtNodeTypes.CHARACTER_CONSTANT) {
val elementTextRange = element.getTextRange()
return listOf(TextRange.create(elementTextRange.startOffset + 1, elementTextRange.endOffset - 1))
}
}
return markElement(element)
}
}
@JvmField
val LONG_LITERAL_SUFFIX: PositioningStrategy = object : PositioningStrategy() {
override fun mark(element: KtElement): List {
if (element is KtConstantExpression) {
if (element.node.elementType == KtNodeTypes.INTEGER_CONSTANT) {
val endOffset = element.endOffset
return listOf(TextRange.create(endOffset - 1, endOffset))
}
}
return markElement(element)
}
}
@JvmField
val AS_TYPE: PositioningStrategy = object : PositioningStrategy() {
override fun mark(element: KtBinaryExpressionWithTypeRHS): List {
return markRange(element.operationReference, element)
}
}
@JvmField
val COMPANION_OBJECT: PositioningStrategy =
ModifierSetBasedPositioningStrategy(KtTokens.COMPANION_KEYWORD)
@JvmField
val SECONDARY_CONSTRUCTOR_DELEGATION_CALL: PositioningStrategy =
object : PositioningStrategy() {
override fun mark(element: PsiElement): List {
return when (element) {
is KtSecondaryConstructor -> {
val valueParameterList = element.valueParameterList ?: return markElement(element)
markRange(element.getConstructorKeyword(), valueParameterList.lastChild)
}
is KtConstructorDelegationCall -> {
if (element.isImplicit) {
// TODO: [VD] FIR collects for some reason implicit KtConstructorDelegationCall
// check(!element.isImplicit) { "Implicit KtConstructorDelegationCall should not be collected directly" }
val constructor = element.getStrictParentOfType()!!
val valueParameterList = constructor.valueParameterList ?: return markElement(constructor)
return markRange(constructor.getConstructorKeyword(), valueParameterList.lastChild)
}
markElement(element.calleeExpression ?: element)
}
else -> markElement(element)
}
}
}
@JvmField
val DELEGATOR_SUPER_CALL: PositioningStrategy = object : PositioningStrategy() {
override fun mark(element: KtEnumEntry): List {
val specifiers = element.superTypeListEntries
return markElement(if (specifiers.isEmpty()) element else specifiers[0])
}
}
@JvmField
val UNUSED_VALUE: PositioningStrategy = object : PositioningStrategy() {
override fun mark(element: KtBinaryExpression): List {
return listOf(TextRange(element.left!!.startOffset, element.operationReference.endOffset))
}
}
@JvmField
val USELESS_ELVIS: PositioningStrategy = object : PositioningStrategy() {
override fun mark(element: KtBinaryExpression): List {
return listOf(TextRange(element.operationReference.startOffset, element.endOffset))
}
}
@JvmField
val IMPORT_ALIAS: PositioningStrategy = object : PositioningStrategy() {
override fun mark(element: KtImportDirective): List {
element.alias?.nameIdentifier?.let { return markElement(it) }
element.importedReference?.let {
if (it is KtQualifiedExpression) {
it.selectorExpression?.let { return markElement(it) }
}
return markElement(it)
}
return markElement(element)
}
}
@JvmField
val RETURN_WITH_LABEL: PositioningStrategy = object : PositioningStrategy() {
override fun mark(element: KtReturnExpression): List {
val labeledExpression = element.labeledExpression
if (labeledExpression != null) {
return markRange(element, labeledExpression)
}
return markElement(element.returnKeyword)
}
}
@JvmField
val RECEIVER: PositioningStrategy = object : DeclarationHeader() {
override fun mark(element: KtCallableDeclaration): List {
element.receiverTypeReference?.let { return markElement(it) }
return DEFAULT.mark(element)
}
}
val OPERATOR: PositioningStrategy = object : PositioningStrategy() {
override fun mark(element: KtExpression): List {
return when (element) {
is KtBinaryExpression -> mark(element.operationReference)
is KtBinaryExpressionWithTypeRHS -> mark(element.operationReference)
is KtUnaryExpression -> mark(element.operationReference)
else -> super.mark(element)
}
}
}
val DOT_BY_QUALIFIED: PositioningStrategy = object : PositioningStrategy() {
override fun mark(element: PsiElement): List {
if (element is KtBinaryExpression && element.operationToken in KtTokens.ALL_ASSIGNMENTS) {
element.left?.let { left ->
left.findDescendantOfType()?.let { return mark(it) }
}
}
if (element is KtDotQualifiedExpression) {
return mark(element.operationTokenNode.psi)
}
// Fallback to mark the callee reference.
return REFERENCE_BY_QUALIFIED.mark(element)
}
}
val SELECTOR_BY_QUALIFIED: PositioningStrategy = object : PositioningStrategy() {
override fun mark(element: PsiElement): List {
if (element is KtBinaryExpression && element.operationToken in KtTokens.ALL_ASSIGNMENTS) {
element.left?.let { return mark(it) }
}
if (element is KtQualifiedExpression) {
when (val selectorExpression = element.selectorExpression) {
is KtElement -> return mark(selectorExpression)
}
}
if (element is KtImportDirective) {
element.alias?.nameIdentifier?.let { return mark(it) }
element.importedReference?.let { return mark(it) }
}
if (element is KtTypeReference) {
element.typeElement?.getReferencedTypeExpression()?.let { return mark(it) }
}
return super.mark(element)
}
}
private fun KtTypeElement.getReferencedTypeExpression(): KtElement? {
return when (this) {
is KtUserType -> referenceExpression
is KtNullableType -> innerType?.getReferencedTypeExpression()
else -> null
}
}
val NAME_IDENTIFIER: PositioningStrategy = object : PositioningStrategy() {
override fun mark(element: PsiElement): List {
if (element is PsiNameIdentifierOwner) {
val nameIdentifier = element.nameIdentifier
if (nameIdentifier != null) {
return super.mark(nameIdentifier)
}
} else if (element is KtLabelReferenceExpression) {
return super.mark(element.getReferencedNameElement())
} else if (element is KtPackageDirective) {
element.packageNameExpression?.let {
return super.mark(it)
}
}
return DEFAULT.mark(element)
}
}
val SPREAD_OPERATOR: PositioningStrategy = object : PositioningStrategy() {
override fun mark(element: PsiElement): List {
return super.mark((element as? KtValueArgument)?.getSpreadElement()?.node?.psi ?: element)
}
}
@JvmField
val FUN_INTERFACE: PositioningStrategy = object : PositioningStrategy() {
override fun mark(element: KtDeclaration): List {
return when (element) {
is KtClass -> FUN_MODIFIER.mark(element)
is KtProperty -> markElement(element.valOrVarKeyword)
is KtNamedFunction -> {
val typeParameterList = element.typeParameterList
when {
typeParameterList != null -> markElement(typeParameterList)
element.hasModifier(KtTokens.SUSPEND_KEYWORD) -> SUSPEND_MODIFIER.mark(element)
else -> markElement(element.funKeyword ?: element)
}
}
else -> markElement(element)
}
}
}
val REFERENCE_BY_QUALIFIED: PositioningStrategy = FindReferencePositioningStrategy(false)
val REFERENCED_NAME_BY_QUALIFIED: PositioningStrategy = FindReferencePositioningStrategy(true)
val REIFIED_MODIFIER: PositioningStrategy =
ModifierSetBasedPositioningStrategy(KtTokens.REIFIED_KEYWORD)
val PROPERTY_INITIALIZER: PositioningStrategy = object : PositioningStrategy() {
override fun mark(element: KtNamedDeclaration): List {
return markElement(
when (element) {
is KtProperty -> element.initializer ?: element
// Type reference is used as a target for loop variable type mismatches
is KtParameter -> element.defaultValue ?: element.typeReference ?: element
else -> element
}
)
}
}
val WHOLE_ELEMENT: PositioningStrategy = object : PositioningStrategy() {}
val TYPE_PARAMETERS_LIST: PositioningStrategy = object : PositioningStrategy() {
override fun mark(element: KtDeclaration): List {
if (element is KtTypeParameterListOwner) {
return markElement(element.typeParameterList ?: element)
}
return markElement(element)
}
}
val ANNOTATION_USE_SITE: PositioningStrategy = object : PositioningStrategy() {
override fun mark(element: KtAnnotationEntry): List {
return markElement(element.useSiteTarget ?: element)
}
}
val IMPORT_LAST_NAME: PositioningStrategy = object : PositioningStrategy() {
override fun isValid(element: PsiElement): Boolean {
if (element is PsiErrorElement) return false
return !element.children.any { !isValid(it) }
}
override fun mark(element: PsiElement): List {
if (element is KtImportDirective) {
val importedReference = element.importedReference
if (importedReference is KtDotQualifiedExpression) {
importedReference.selectorExpression?.let { return super.mark(it) }
}
return super.mark(element.importedReference ?: element)
}
return super.mark(element)
}
}
@OptIn(UnsafeCastFunction::class)
val IMPORT_LAST_BUT_ONE_NAME: PositioningStrategy = object : PositioningStrategy() {
override fun mark(element: KtImportDirective): List {
element.importedReference
?.safeAs()
?.receiverExpression
?.safeAs()
?.selectorExpression
?.let { return markElement(it) }
return super.mark(element)
}
}
val LABEL: PositioningStrategy = object : PositioningStrategy() {
override fun mark(element: KtElement): List {
return super.mark((element as? KtExpressionWithLabel)?.labelQualifier ?: element)
}
}
val COMMAS: PositioningStrategy = object : PositioningStrategy() {
override fun mark(element: PsiElement): List {
return buildList {
for (child in element.allChildren) {
if (child.node.elementType == KtTokens.COMMA) {
add(markSingleElement(child))
}
}
}
}
override fun isValid(element: PsiElement): Boolean = true
}
val NON_FINAL_MODIFIER_OR_NAME: PositioningStrategy =
ModifierSetBasedPositioningStrategy(KtTokens.ABSTRACT_KEYWORD, KtTokens.OPEN_KEYWORD, KtTokens.SEALED_KEYWORD)
val DELEGATED_SUPERTYPE_BY_KEYWORD: PositioningStrategy = object : PositioningStrategy() {
override fun mark(element: KtTypeReference): List {
val parent = element.parent as? KtDelegatedSuperTypeEntry ?: return super.mark(element)
return markElement(parent.byKeywordNode.psi ?: element)
}
}
@JvmField
val TYPEALIAS_TYPE_REFERENCE = object : PositioningStrategy() {
override fun mark(element: KtTypeAlias): List {
return markElement(element.getTypeReference() ?: element)
}
}
@JvmField
val SUPERTYPE_INITIALIZED_IN_EXPECTED_CLASS_DIAGNOSTIC = object : PositioningStrategy() {
override fun mark(element: KtElement): List {
val elementToMark = when (element) {
is KtEnumEntry -> element.initializerList ?: element
is KtTypeReference -> {
val superTypeCallEntry = (element.parent as? KtConstructorCalleeExpression)?.parent as? KtSuperTypeCallEntry
superTypeCallEntry?.valueArgumentList ?: element
}
else -> element
}
return markElement(elementToMark)
}
}
/**
* @param locateReferencedName whether to remove any nested parentheses while locating the reference element. This is useful for
* diagnostics on super and unresolved references. For example, with the following, only the part inside the parentheses should be
* highlighted.
*
* ```
* fun foo() {
* (super)()
* ^^^^^
* (random123)()
* ^^^^^^^^^
* }
* ```
*/
class FindReferencePositioningStrategy(val locateReferencedName: Boolean) : PositioningStrategy() {
override fun mark(element: PsiElement): List