summaryrefslogtreecommitdiff
path: root/plugins/kotlin/fir/src/org/jetbrains/kotlin/idea/fir/highlighter/visitors/VariableReferenceHighlightingVisitor.kt
blob: 08d1776f9a5d5082c90550a2181a49f7cb0f6e69 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
// Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.

package org.jetbrains.kotlin.idea.fir.highlighter.visitors

import com.intellij.lang.annotation.AnnotationHolder
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiMethod
import com.intellij.psi.PsiModifier
import com.intellij.psi.PsiVariable
import com.intellij.psi.util.PsiUtilCore
import com.intellij.psi.util.parentOfType
import org.jetbrains.kotlin.idea.KotlinIdeaAnalysisBundle
import org.jetbrains.kotlin.idea.highlighter.textAttributesKeyForPropertyDeclaration
import org.jetbrains.kotlin.analysis.api.KtAnalysisSession
import org.jetbrains.kotlin.analysis.api.symbols.KtBackingFieldSymbol
import org.jetbrains.kotlin.analysis.api.symbols.KtSymbol
import org.jetbrains.kotlin.idea.highlighter.NameHighlighter
import org.jetbrains.kotlin.idea.references.mainReference
import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.idea.highlighter.KotlinHighlightingColors as Colors

internal class VariableReferenceHighlightingVisitor(
    analysisSession: KtAnalysisSession,
    holder: AnnotationHolder
) : FirAfterResolveHighlightingVisitor(analysisSession, holder) {
    override fun visitSimpleNameExpression(expression: KtSimpleNameExpression) {
        if (!NameHighlighter.namesHighlightingEnabled) return
        if (expression.isAssignmentReference()) return
        if (expression.isByNameArgumentReference()) return
        if (expression.parent is KtInstanceExpressionWithLabel) return

        if (expression.isAutoCreatedItParameter()) {
            createInfoAnnotation(
                expression,
                KotlinIdeaAnalysisBundle.message("automatically.declared.based.on.the.expected.type"),
                Colors.FUNCTION_LITERAL_DEFAULT_PARAMETER
            )
            return
        }

        with(analysisSession) {
            val targetSymbol = expression.mainReference.resolveToSymbol()
            val target = expression.mainReference.resolve()
            when {
                targetSymbol is KtBackingFieldSymbol -> Colors.BACKING_FIELD_VARIABLE
                target is PsiMethod -> Colors.SYNTHETIC_EXTENSION_PROPERTY
                target != null -> textAttributesKeyForPropertyDeclaration(target)
                else -> null
            }?.let { attribute ->
                highlightName(expression, attribute)
                if (target?.isMutableVariable() == true || targetSymbol != null && isBackingFieldReferencingMutableVariable(targetSymbol)) {
                    highlightName(expression, Colors.MUTABLE_VARIABLE)
                }
            }
        }
    }

    @Suppress("unused")
    private fun KtAnalysisSession.isBackingFieldReferencingMutableVariable(symbol: KtSymbol): Boolean {
        if (symbol !is KtBackingFieldSymbol) return false
        return !symbol.owningProperty.isVal
    }

    private fun KtSimpleNameExpression.isByNameArgumentReference() =
        parent is KtValueArgumentName


    private fun KtSimpleNameExpression.isAutoCreatedItParameter(): Boolean {
        return getReferencedName() == "it" // todo
    }
}

private fun PsiElement.isMutableVariable() = when {
    this is KtValVarKeywordOwner && PsiUtilCore.getElementType(valOrVarKeyword) == KtTokens.VAR_KEYWORD -> true
    this is PsiVariable && !hasModifierProperty(PsiModifier.FINAL) -> true
    else -> false
}

private fun KtSimpleNameExpression.isAssignmentReference(): Boolean {
    if (this !is KtOperationReferenceExpression) return false
    return operationSignTokenType == KtTokens.EQ
}