All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.jetbrains.kotlin.diagnostics.PositioningStrategies.kt Maven / Gradle / Ivy

There is a newer version: 2.1.0-RC
Show newest version
/*
 * 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 {
            if (element is KtBinaryExpression && element.operationToken == KtTokens.EQ) {
                // Look for reference in LHS of variable assignment.
                element.left?.let { return mark(it) }
            }
            if (element is KtClassOrObject) {
                val nameIdentifier = (element as? KtNamedDeclaration)?.nameIdentifier
                if (nameIdentifier != null) {
                    val startElement = findStartingPsiElementForDeclarationName(element)
                    return markRange(startElement, nameIdentifier)
                }
            }
            var result: PsiElement = when (element) {
                is KtQualifiedExpression -> {
                    when (val selectorExpression = element.selectorExpression) {
                        is KtCallExpression -> selectorExpression.calleeExpression ?: selectorExpression
                        is KtReferenceExpression -> selectorExpression
                        else -> element
                    }
                }
                is KtCallableReferenceExpression -> element.callableReference
                is KtCallExpression -> element.calleeExpression ?: element
                is KtConstructorDelegationCall -> element.calleeExpression ?: element
                is KtSuperTypeCallEntry -> element.calleeExpression
                is KtOperationExpression -> element.operationReference
                is KtWhenConditionInRange -> element.operationReference
                is KtAnnotationEntry -> element.calleeExpression ?: element
                is KtTypeReference -> (element.typeElement as? KtNullableType)?.innerType ?: element
                is KtImportDirective -> element.importedReference ?: element
                else -> element
            }
            while (locateReferencedName && result is KtParenthesizedExpression) {
                result = result.expression ?: break
            }
            return super.mark(result)
        }
    }

    val TYPE_ARGUMENT_LIST_OR_SELF = object : PositioningStrategy() {
        override fun mark(element: PsiElement): List {
            element.getChildOfType()?.let { return markElement(it) }
            return super.mark(element)
        }
    }

    val PACKAGE_DIRECTIVE_NAME_EXPRESSION: PositioningStrategy = object : PositioningStrategy() {
        override fun mark(element: KtElement): List {
            val packageNameExpression = (element as? KtPackageDirective)?.packageNameExpression
            return super.mark(packageNameExpression ?: element)
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy