mirror of
https://github.com/jlengrand/kotlin.git
synced 2026-04-21 08:31:30 +00:00
FIR IDE: Begin implementing semantic highlighting via FIR
* Introduce frontend api module & implement api for FIR * Implement some basic declaration highlighting for FIR
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Copyright 2010-2020 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.
|
||||
*/
|
||||
|
||||
@@ -923,9 +923,13 @@ fun main(args: Array<String>) {
|
||||
model("fir/multiModule", recursive = false, extension = null)
|
||||
}
|
||||
|
||||
testClass<AbstractFirLazyResolveTest> {
|
||||
model("fir/lazyResolve", extension = "test", singleClass = true, filenameStartsLowerCase = true)
|
||||
}
|
||||
testClass<AbstractFirHighlightingTest> {
|
||||
model("highlighter")
|
||||
}
|
||||
|
||||
testClass<AbstractFirLazyResolveTest> {
|
||||
model("fir/lazyResolve", extension = "test", singleClass = true, filenameStartsLowerCase = true)
|
||||
}
|
||||
|
||||
testClass<AbstractFirReferenceResolveTest> {
|
||||
model("resolve/references", pattern = KT_WITHOUT_DOTS_IN_NAME)
|
||||
|
||||
@@ -25,6 +25,7 @@ dependencies {
|
||||
testRuntime(intellijPluginDep("java"))
|
||||
}
|
||||
|
||||
testCompile(projectTests(":idea:idea-test-framework"))
|
||||
testRuntime(intellijRuntimeAnnotations())
|
||||
testRuntime(project(":plugins:kapt3-idea")) { isTransitive = false }
|
||||
testRuntime(project(":kotlin-reflect"))
|
||||
@@ -48,7 +49,6 @@ dependencies {
|
||||
testRuntime(project(":idea:idea-android")) { isTransitive = false }
|
||||
testRuntime(project(":plugins:lint")) { isTransitive = false }
|
||||
testRuntime(project(":plugins:uast-kotlin"))
|
||||
testRuntime(project(":nj2k:nj2k-services")) { isTransitive = false }
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
/*
|
||||
* Copyright 2010-2020 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.idea.highlighter
|
||||
|
||||
import org.jetbrains.kotlin.idea.test.ProjectDescriptorWithStdlibSources
|
||||
import org.jetbrains.kotlin.test.InTextDirectivesUtils
|
||||
|
||||
abstract class AbstractFirHighlightingTest : AbstractHighlightingTest() {
|
||||
override fun getDefaultProjectDescriptor() = ProjectDescriptorWithStdlibSources.INSTANCE
|
||||
|
||||
override fun checkHighlighting(fileText: String) {
|
||||
val checkInfos = !InTextDirectivesUtils.isDirectiveDefined(fileText, NO_CHECK_INFOS_PREFIX);
|
||||
|
||||
// warnings are not supported yet
|
||||
myFixture.checkHighlighting(/* checkWarnings= */ false, checkInfos, /* checkWeakWarnings= */ false)
|
||||
}
|
||||
}
|
||||
253
idea/idea-fir/tests/org/jetbrains/kotlin/idea/highlighter/FirHighlightingTestGenerated.java
generated
Normal file
253
idea/idea-fir/tests/org/jetbrains/kotlin/idea/highlighter/FirHighlightingTestGenerated.java
generated
Normal file
@@ -0,0 +1,253 @@
|
||||
/*
|
||||
* Copyright 2010-2020 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.idea.highlighter;
|
||||
|
||||
import com.intellij.testFramework.TestDataPath;
|
||||
import org.jetbrains.kotlin.test.JUnit3RunnerWithInners;
|
||||
import org.jetbrains.kotlin.test.KotlinTestUtils;
|
||||
import org.jetbrains.kotlin.test.TestMetadata;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/** This class is generated by {@link org.jetbrains.kotlin.generators.tests.TestsPackage}. DO NOT MODIFY MANUALLY */
|
||||
@SuppressWarnings("all")
|
||||
@TestMetadata("idea/testData/highlighter")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
public class FirHighlightingTestGenerated extends AbstractFirHighlightingTest {
|
||||
private void runTest(String testDataFilePath) throws Exception {
|
||||
KotlinTestUtils.runTest(this::doTest, this, testDataFilePath);
|
||||
}
|
||||
|
||||
public void testAllFilesPresentInHighlighter() throws Exception {
|
||||
KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("idea/testData/highlighter"), Pattern.compile("^(.+)\\.kt$"), null, true);
|
||||
}
|
||||
|
||||
@TestMetadata("Annotations.kt")
|
||||
public void testAnnotations() throws Exception {
|
||||
runTest("idea/testData/highlighter/Annotations.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("AutoCreatedItParameter.kt")
|
||||
public void testAutoCreatedItParameter() throws Exception {
|
||||
runTest("idea/testData/highlighter/AutoCreatedItParameter.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("Destructuring.kt")
|
||||
public void testDestructuring() throws Exception {
|
||||
runTest("idea/testData/highlighter/Destructuring.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("Dynamic.kt")
|
||||
public void testDynamic() throws Exception {
|
||||
runTest("idea/testData/highlighter/Dynamic.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("Enums.kt")
|
||||
public void testEnums() throws Exception {
|
||||
runTest("idea/testData/highlighter/Enums.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("Field.kt")
|
||||
public void testField() throws Exception {
|
||||
runTest("idea/testData/highlighter/Field.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("Functions.kt")
|
||||
public void testFunctions() throws Exception {
|
||||
runTest("idea/testData/highlighter/Functions.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("InvokeCall.kt")
|
||||
public void testInvokeCall() throws Exception {
|
||||
runTest("idea/testData/highlighter/InvokeCall.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("JavaTypes.kt")
|
||||
public void testJavaTypes() throws Exception {
|
||||
runTest("idea/testData/highlighter/JavaTypes.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("KDoc.kt")
|
||||
public void testKDoc() throws Exception {
|
||||
runTest("idea/testData/highlighter/KDoc.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("KotlinInjection.kt")
|
||||
public void testKotlinInjection() throws Exception {
|
||||
runTest("idea/testData/highlighter/KotlinInjection.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("Labels.kt")
|
||||
public void testLabels() throws Exception {
|
||||
runTest("idea/testData/highlighter/Labels.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("NamedArguments.kt")
|
||||
public void testNamedArguments() throws Exception {
|
||||
runTest("idea/testData/highlighter/NamedArguments.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("NonNullAssertion.kt")
|
||||
public void testNonNullAssertion() throws Exception {
|
||||
runTest("idea/testData/highlighter/NonNullAssertion.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("Object.kt")
|
||||
public void testObject() throws Exception {
|
||||
runTest("idea/testData/highlighter/Object.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("PropertiesWithPropertyDeclarations.kt")
|
||||
public void testPropertiesWithPropertyDeclarations() throws Exception {
|
||||
runTest("idea/testData/highlighter/PropertiesWithPropertyDeclarations.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("SmartCast.kt")
|
||||
public void testSmartCast() throws Exception {
|
||||
runTest("idea/testData/highlighter/SmartCast.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("Suspend.kt")
|
||||
public void testSuspend() throws Exception {
|
||||
runTest("idea/testData/highlighter/Suspend.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("SyntheticExtensionProperty.kt")
|
||||
public void testSyntheticExtensionProperty() throws Exception {
|
||||
runTest("idea/testData/highlighter/SyntheticExtensionProperty.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("Todo.kt")
|
||||
public void testTodo() throws Exception {
|
||||
runTest("idea/testData/highlighter/Todo.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("TopLevelDestructuring.kt")
|
||||
public void testTopLevelDestructuring() throws Exception {
|
||||
runTest("idea/testData/highlighter/TopLevelDestructuring.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("TopLevelOpenSuspendFun.kt")
|
||||
public void testTopLevelOpenSuspendFun() throws Exception {
|
||||
runTest("idea/testData/highlighter/TopLevelOpenSuspendFun.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("TypeAlias.kt")
|
||||
public void testTypeAlias() throws Exception {
|
||||
runTest("idea/testData/highlighter/TypeAlias.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("TypesAndAnnotations.kt")
|
||||
public void testTypesAndAnnotations() throws Exception {
|
||||
runTest("idea/testData/highlighter/TypesAndAnnotations.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("Variables.kt")
|
||||
public void testVariables() throws Exception {
|
||||
runTest("idea/testData/highlighter/Variables.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("VariablesAsFunctions.kt")
|
||||
public void testVariablesAsFunctions() throws Exception {
|
||||
runTest("idea/testData/highlighter/VariablesAsFunctions.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("idea/testData/highlighter/deprecated")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
public static class Deprecated extends AbstractFirHighlightingTest {
|
||||
private void runTest(String testDataFilePath) throws Exception {
|
||||
KotlinTestUtils.runTest(this::doTest, this, testDataFilePath);
|
||||
}
|
||||
|
||||
public void testAllFilesPresentInDeprecated() throws Exception {
|
||||
KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("idea/testData/highlighter/deprecated"), Pattern.compile("^(.+)\\.kt$"), null, true);
|
||||
}
|
||||
|
||||
@TestMetadata("Class.kt")
|
||||
public void testClass() throws Exception {
|
||||
runTest("idea/testData/highlighter/deprecated/Class.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("ClassObject.kt")
|
||||
public void testClassObject() throws Exception {
|
||||
runTest("idea/testData/highlighter/deprecated/ClassObject.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("Constructor.kt")
|
||||
public void testConstructor() throws Exception {
|
||||
runTest("idea/testData/highlighter/deprecated/Constructor.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("ExtensionFunction.kt")
|
||||
public void testExtensionFunction() throws Exception {
|
||||
runTest("idea/testData/highlighter/deprecated/ExtensionFunction.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("Function.kt")
|
||||
public void testFunction() throws Exception {
|
||||
runTest("idea/testData/highlighter/deprecated/Function.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("Get.kt")
|
||||
public void testGet() throws Exception {
|
||||
runTest("idea/testData/highlighter/deprecated/Get.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("Getter.kt")
|
||||
public void testGetter() throws Exception {
|
||||
runTest("idea/testData/highlighter/deprecated/Getter.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("Inc.kt")
|
||||
public void testInc() throws Exception {
|
||||
runTest("idea/testData/highlighter/deprecated/Inc.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("Invalid.kt")
|
||||
public void testInvalid() throws Exception {
|
||||
runTest("idea/testData/highlighter/deprecated/Invalid.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("Invoke.kt")
|
||||
public void testInvoke() throws Exception {
|
||||
runTest("idea/testData/highlighter/deprecated/Invoke.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("Operation.kt")
|
||||
public void testOperation() throws Exception {
|
||||
runTest("idea/testData/highlighter/deprecated/Operation.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("Property.kt")
|
||||
public void testProperty() throws Exception {
|
||||
runTest("idea/testData/highlighter/deprecated/Property.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("RangeTo.kt")
|
||||
public void testRangeTo() throws Exception {
|
||||
runTest("idea/testData/highlighter/deprecated/RangeTo.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("Setter.kt")
|
||||
public void testSetter() throws Exception {
|
||||
runTest("idea/testData/highlighter/deprecated/Setter.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("SuperCall.kt")
|
||||
public void testSuperCall() throws Exception {
|
||||
runTest("idea/testData/highlighter/deprecated/SuperCall.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("Trait.kt")
|
||||
public void testTrait() throws Exception {
|
||||
runTest("idea/testData/highlighter/deprecated/Trait.kt");
|
||||
}
|
||||
}
|
||||
}
|
||||
34
idea/idea-frontend-api/build.gradle.kts
Normal file
34
idea/idea-frontend-api/build.gradle.kts
Normal file
@@ -0,0 +1,34 @@
|
||||
plugins {
|
||||
kotlin("jvm")
|
||||
id("jps-compatible")
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compileOnly(project(":compiler:psi"))
|
||||
compileOnly(project(":compiler:frontend"))
|
||||
compileOnly(project(":core:type-system"))
|
||||
compileOnly(project(":idea:idea-frontend-independent"))
|
||||
compileOnly(project(":compiler:psi"))
|
||||
compileOnly(intellijCoreDep())
|
||||
compileOnly(intellijDep())
|
||||
|
||||
Platform[191].orLower {
|
||||
compileOnly(intellijDep()) { includeJars("java-api", "java-impl") }
|
||||
}
|
||||
|
||||
Platform[192].orHigher {
|
||||
compileOnly(intellijPluginDep("java")) { includeJars("java-api", "java-impl") }
|
||||
}
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
"main" { projectDefault() }
|
||||
"test" { projectDefault() }
|
||||
}
|
||||
|
||||
testsJar()
|
||||
|
||||
projectTest {
|
||||
dependsOn(":dist")
|
||||
workingDir = rootDir
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* Copyright 2010-2020 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.idea.frontend.api
|
||||
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.PsiMethod
|
||||
import org.jetbrains.kotlin.lexer.KtTokens
|
||||
import org.jetbrains.kotlin.psi.KtNamedFunction
|
||||
|
||||
sealed class CallInfo {
|
||||
abstract val isSuspendCall: Boolean
|
||||
abstract val targetFunction: PsiElement?
|
||||
}
|
||||
|
||||
data class VariableAsFunctionCallInfo(val target: PsiElement, override val isSuspendCall: Boolean) : CallInfo() {
|
||||
override val targetFunction: PsiElement? = null
|
||||
}
|
||||
|
||||
data class VariableAsFunctionLikeCallInfo(val target: PsiElement, val invokeFunction: KtNamedFunction) : CallInfo() {
|
||||
override val isSuspendCall: Boolean get() = invokeFunction.hasModifier(KtTokens.SUSPEND_KEYWORD)
|
||||
override val targetFunction: PsiElement? get() = invokeFunction
|
||||
}
|
||||
|
||||
// SimpleFunctionCallInfo
|
||||
|
||||
sealed class SimpleFunctionCallInfo : CallInfo() {
|
||||
abstract override val targetFunction: PsiElement
|
||||
}
|
||||
|
||||
data class SimpleKtFunctionCallInfo(override val targetFunction: KtNamedFunction) : SimpleFunctionCallInfo() {
|
||||
override val isSuspendCall: Boolean get() = targetFunction.hasModifier(KtTokens.SUSPEND_KEYWORD)
|
||||
}
|
||||
|
||||
data class SimpleJavaFunctionCallInfo(override val targetFunction: PsiMethod) : SimpleFunctionCallInfo() {
|
||||
override val isSuspendCall: Boolean = false
|
||||
}
|
||||
|
||||
|
||||
// ConstructorCallInfo
|
||||
|
||||
//TODO
|
||||
object ConstructorCallInfo : CallInfo() {
|
||||
// abstract val targetConstructor: PsiElement?
|
||||
final override val isSuspendCall: Boolean = false
|
||||
override val targetFunction: PsiElement? = null
|
||||
}
|
||||
|
||||
//data class SimpleKtConstructorCallInfo(override val targetConstructor: KtConstructor<*>) : ConstructorCallInfo()
|
||||
|
||||
//data class SimpleJavaConstructorCallInfo(override val targetConstructor: PsiMethod) : ConstructorCallInfo()
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright 2010-2020 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.idea.frontend.api
|
||||
|
||||
import org.jetbrains.kotlin.diagnostics.Diagnostic
|
||||
import org.jetbrains.kotlin.name.ClassId
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.types.model.KotlinTypeMarker
|
||||
|
||||
abstract class FrontendAnalysisSession {
|
||||
abstract fun getSmartCastedToTypes(expression: KtExpression): Collection<KotlinTypeMarker>?
|
||||
|
||||
abstract fun getImplicitReceiverSmartCasts(expression: KtExpression): Collection<ImplicitReceiverSmartCast>
|
||||
|
||||
abstract fun getReturnTypeForKtDeclaration(declaration: KtDeclaration): KotlinTypeMarker?
|
||||
|
||||
abstract fun renderType(type: KotlinTypeMarker): String
|
||||
|
||||
abstract fun getKtExpressionType(expression: KtExpression): KotlinTypeMarker?
|
||||
|
||||
abstract fun isSubclassOf(klass: KtClassOrObject, superClassId: ClassId): Boolean
|
||||
|
||||
abstract fun getDiagnosticsForElement(element: KtElement): Collection<Diagnostic>
|
||||
|
||||
abstract fun resolveCall(call: KtCallExpression): CallInfo?
|
||||
|
||||
abstract fun resolveCall(call: KtBinaryExpression): CallInfo?
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
/*
|
||||
* Copyright 2010-2020 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.idea.frontend.api
|
||||
|
||||
import org.jetbrains.kotlin.types.model.KotlinTypeMarker
|
||||
|
||||
data class ImplicitReceiverSmartCast(val types: Collection<KotlinTypeMarker>, val kind: ImplicitReceiverSmartcastKind)
|
||||
|
||||
enum class ImplicitReceiverSmartcastKind {
|
||||
DISPATCH, EXTENSION
|
||||
}
|
||||
@@ -6,6 +6,7 @@ plugins {
|
||||
dependencies {
|
||||
compileOnly(project(":compiler:psi"))
|
||||
compileOnly(project(":idea:idea-frontend-independent"))
|
||||
implementation(project(":idea:idea-frontend-api"))
|
||||
compileOnly(project(":idea:idea-core"))
|
||||
compileOnly(project(":compiler:fir:fir2ir"))
|
||||
compileOnly(project(":compiler:fir:resolve"))
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
* Copyright 2010-2020 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.idea.fir
|
||||
|
||||
import org.jetbrains.kotlin.fir.expressions.FirFunctionCall
|
||||
import org.jetbrains.kotlin.fir.expressions.FirQualifiedAccessExpression
|
||||
import org.jetbrains.kotlin.fir.references.FirResolvedNamedReference
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirNamedFunctionSymbol
|
||||
|
||||
fun FirFunctionCall.isImplicitFunctionCall(): Boolean {
|
||||
if (dispatchReceiver !is FirQualifiedAccessExpression) return false
|
||||
val resolvedCalleeSymbol = (calleeReference as? FirResolvedNamedReference)?.resolvedSymbol
|
||||
return (resolvedCalleeSymbol as? FirNamedFunctionSymbol)?.fir?.name?.asString() == "invoke"
|
||||
}
|
||||
@@ -0,0 +1,154 @@
|
||||
/*
|
||||
* Copyright 2010-2020 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.idea.frontend.api.fir
|
||||
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.PsiField
|
||||
import com.intellij.psi.PsiMethod
|
||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||
import org.jetbrains.kotlin.diagnostics.Diagnostic
|
||||
import org.jetbrains.kotlin.fir.FirElement
|
||||
import org.jetbrains.kotlin.fir.declarations.*
|
||||
import org.jetbrains.kotlin.fir.expressions.*
|
||||
import org.jetbrains.kotlin.fir.references.FirResolvedNamedReference
|
||||
import org.jetbrains.kotlin.fir.resolve.transformers.firClassLike
|
||||
import org.jetbrains.kotlin.fir.symbols.CallableId
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirConstructorSymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirNamedFunctionSymbol
|
||||
import org.jetbrains.kotlin.fir.types.*
|
||||
import org.jetbrains.kotlin.idea.fir.*
|
||||
import org.jetbrains.kotlin.idea.frontend.api.*
|
||||
import org.jetbrains.kotlin.idea.references.FirReferenceResolveHelper
|
||||
import org.jetbrains.kotlin.idea.references.FirReferenceResolveHelper.toTargetPsi
|
||||
import org.jetbrains.kotlin.name.ClassId
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.types.model.KotlinTypeMarker
|
||||
|
||||
class AnalysisSessionFirImpl internal constructor(
|
||||
private val state: FirModuleResolveState
|
||||
) : FrontendAnalysisSession() {
|
||||
|
||||
override fun getSmartCastedToTypes(expression: KtExpression): Collection<KotlinTypeMarker>? {
|
||||
// TODO filter out not used smartcasts
|
||||
return (expression.toFir() as? FirExpressionWithSmartcast)?.typesFromSmartCast
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalStdlibApi::class)
|
||||
override fun getImplicitReceiverSmartCasts(expression: KtExpression): Collection<ImplicitReceiverSmartCast> {
|
||||
// TODO filter out not used smartcasts
|
||||
val qualifiedExpression = expression.getOrBuildFir(state) as? FirQualifiedAccessExpression ?: return emptyList()
|
||||
if (qualifiedExpression.dispatchReceiver !is FirExpressionWithSmartcast
|
||||
&& qualifiedExpression.extensionReceiver !is FirExpressionWithSmartcast
|
||||
) return emptyList()
|
||||
return buildList {
|
||||
(qualifiedExpression.dispatchReceiver as? FirExpressionWithSmartcast)?.let { smartCasted ->
|
||||
ImplicitReceiverSmartCast(smartCasted.typesFromSmartCast, ImplicitReceiverSmartcastKind.DISPATCH)
|
||||
}?.let(::add)
|
||||
(qualifiedExpression.extensionReceiver as? FirExpressionWithSmartcast)?.let { smartCasted ->
|
||||
ImplicitReceiverSmartCast(smartCasted.typesFromSmartCast, ImplicitReceiverSmartcastKind.EXTENSION)
|
||||
}?.let(::add)
|
||||
}
|
||||
}
|
||||
|
||||
override fun renderType(type: KotlinTypeMarker): String =
|
||||
type.asConeType().render()
|
||||
|
||||
override fun getReturnTypeForKtDeclaration(declaration: KtDeclaration): KotlinTypeMarker? =
|
||||
declaration.toFir<FirCallableDeclaration<*>>()?.returnTypeRef?.coneTypeSafe()
|
||||
|
||||
override fun getKtExpressionType(expression: KtExpression): ConeKotlinType? =
|
||||
expression.toFir<FirExpression>()?.typeRef?.coneTypeSafe()
|
||||
|
||||
override fun isSubclassOf(klass: KtClassOrObject, superClassId: ClassId): Boolean {
|
||||
var result = false
|
||||
forEachSubClass(klass.toFir() ?: return false) { type ->
|
||||
result = result || type.firClassLike(state.getSession(klass))?.symbol?.classId == superClassId
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
override fun getDiagnosticsForElement(element: KtElement): Collection<Diagnostic> {
|
||||
element.containingKtFile.getOrBuildFirWithDiagnostics(state)
|
||||
return state.getDiagnostics(element)
|
||||
}
|
||||
|
||||
override fun resolveCall(call: KtBinaryExpression): CallInfo? {
|
||||
val firCall = call.toFir<FirFunctionCall>() ?: return null
|
||||
return resolveCall(firCall, call)
|
||||
}
|
||||
|
||||
override fun resolveCall(call: KtCallExpression): CallInfo? {
|
||||
val firCall = call.toFir<FirFunctionCall>() ?: return null
|
||||
return resolveCall(firCall, call)
|
||||
}
|
||||
|
||||
private fun resolveCall(firCall: FirFunctionCall, callExpression: KtExpression): CallInfo? {
|
||||
val session = callExpression.getSession()
|
||||
val resolvedFunctionPsi = firCall.calleeReference.toTargetPsi(session)
|
||||
val resolvedCalleeSymbol = (firCall.calleeReference as? FirResolvedNamedReference)?.resolvedSymbol
|
||||
return when {
|
||||
resolvedCalleeSymbol is FirConstructorSymbol -> {
|
||||
ConstructorCallInfo//todo use proper constructor info
|
||||
}
|
||||
firCall.dispatchReceiver is FirQualifiedAccessExpression && firCall.isImplicitFunctionCall() -> {
|
||||
val target = with(FirReferenceResolveHelper) {
|
||||
val calleeReference = (firCall.dispatchReceiver as FirQualifiedAccessExpression).calleeReference
|
||||
calleeReference.toTargetPsi(session)
|
||||
}
|
||||
when (target) {
|
||||
null -> null
|
||||
is KtValVarKeywordOwner, is PsiField -> {
|
||||
val functionSymbol =
|
||||
(firCall.calleeReference as? FirResolvedNamedReference)?.resolvedSymbol as? FirNamedFunctionSymbol
|
||||
when (functionSymbol?.callableId) {
|
||||
null -> null
|
||||
in kotlinFunctionInvokeCallableIds -> VariableAsFunctionCallInfo(target, functionSymbol.fir.isSuspend)
|
||||
else -> (resolvedFunctionPsi as? KtNamedFunction)?.let { VariableAsFunctionLikeCallInfo(target, it) }
|
||||
}
|
||||
}
|
||||
else -> resolvedFunctionPsi?.asSimpleFunctionCall()
|
||||
}
|
||||
}
|
||||
else -> resolvedFunctionPsi?.asSimpleFunctionCall()
|
||||
}
|
||||
}
|
||||
|
||||
private fun PsiElement.asSimpleFunctionCall() = when (this) {
|
||||
is KtNamedFunction -> SimpleKtFunctionCallInfo(this)
|
||||
is PsiMethod -> SimpleJavaFunctionCallInfo(this)
|
||||
else -> null
|
||||
}
|
||||
|
||||
private fun KtElement.getSession() = state.getSession(this)
|
||||
|
||||
private inline fun <reified F : FirElement> KtElement.toFir(phase: FirResolvePhase = FirResolvePhase.BODY_RESOLVE): F? =
|
||||
getOrBuildFir(state, phase) as? F
|
||||
|
||||
private fun forEachSubClass(firClass: FirClass<*>, action: (FirResolvedTypeRef) -> Unit) {
|
||||
firClass.superTypeRefs.forEach { superType ->
|
||||
(superType as? FirResolvedTypeRef)?.let(action)
|
||||
(superType.firClassLike(firClass.session) as? FirClass<*>?)?.let { forEachSubClass(it, action) }
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun forElement(element: KtElement): FrontendAnalysisSession =
|
||||
AnalysisSessionFirImpl(element.firResolveState())
|
||||
|
||||
private fun KotlinTypeMarker.asConeType(): ConeKotlinType =
|
||||
this as? ConeKotlinType ?: error("$this should be ConeKotlinType")
|
||||
|
||||
private val kotlinFunctionInvokeCallableIds = (0..23).flatMapTo(hashSetOf()) { arity ->
|
||||
listOf(
|
||||
CallableId(KotlinBuiltIns.getFunctionClassId(arity), Name.identifier("invoke")),
|
||||
CallableId(KotlinBuiltIns.getSuspendFunctionClassId(arity), Name.identifier("invoke"))
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* Copyright 2010-2020 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.idea.highlighter
|
||||
|
||||
import com.intellij.lang.jvm.JvmModifier
|
||||
import com.intellij.openapi.editor.colors.TextAttributesKey
|
||||
import com.intellij.psi.*
|
||||
import com.intellij.psi.impl.source.PsiFieldImpl
|
||||
import com.intellij.psi.util.elementType
|
||||
import org.jetbrains.kotlin.lexer.KtTokens
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.psi.psiUtil.isAbstract
|
||||
import org.jetbrains.kotlin.psi.psiUtil.isExtensionDeclaration
|
||||
import org.jetbrains.kotlin.idea.highlighter.KotlinHighlightingColors as Colors
|
||||
|
||||
internal fun textAttributesKeyForPropertyDeclaration(declaration: PsiElement): TextAttributesKey? = when {
|
||||
declaration is KtProperty && declaration.isExtensionDeclaration() -> Colors.EXTENSION_PROPERTY
|
||||
declaration is KtProperty && declaration.isLocal || declaration is PsiLocalVariable ->
|
||||
Colors.LOCAL_VARIABLE
|
||||
declaration is KtParameter -> {
|
||||
if (declaration.valOrVarKeyword != null) Colors.INSTANCE_PROPERTY
|
||||
else Colors.PARAMETER
|
||||
}
|
||||
declaration is PsiParameter -> Colors.PARAMETER
|
||||
declaration is KtProperty && declaration.isTopLevel -> {
|
||||
if (declaration.isCustomPropertyDeclaration()) Colors.PACKAGE_PROPERTY_CUSTOM_PROPERTY_DECLARATION
|
||||
else Colors.PACKAGE_PROPERTY
|
||||
}
|
||||
declaration is KtProperty -> {
|
||||
if (declaration.isCustomPropertyDeclaration()) Colors.INSTANCE_PROPERTY_CUSTOM_PROPERTY_DECLARATION
|
||||
else Colors.INSTANCE_PROPERTY
|
||||
}
|
||||
declaration is PsiField -> Colors.INSTANCE_PROPERTY
|
||||
else -> null
|
||||
}
|
||||
|
||||
private fun KtProperty.isCustomPropertyDeclaration() =
|
||||
getter?.bodyExpression != null || setter?.bodyExpression != null
|
||||
|
||||
@Suppress("UnstableApiUsage")
|
||||
internal fun textAttributesKeyForTypeDeclaration(declaration: PsiElement): TextAttributesKey? = when {
|
||||
declaration is KtTypeParameter || declaration is PsiTypeParameter -> Colors.TYPE_PARAMETER
|
||||
declaration is KtTypeAlias -> Colors.TYPE_ALIAS
|
||||
declaration is KtClass && declaration.isInterface()
|
||||
|| declaration is PsiClass && declaration.isInterface && !declaration.isAnnotationType -> Colors.TRAIT
|
||||
declaration.isAnnotationClass() -> Colors.ANNOTATION
|
||||
declaration is KtObjectDeclaration -> Colors.OBJECT
|
||||
declaration is KtEnumEntry || declaration is PsiEnumConstant -> Colors.ENUM_ENTRY
|
||||
declaration is KtClass && declaration.isAbstract()
|
||||
|| declaration is PsiClass && declaration.hasModifier(JvmModifier.ABSTRACT) -> Colors.ABSTRACT_CLASS
|
||||
declaration is KtClass || declaration is PsiClass -> Colors.CLASS
|
||||
else -> null
|
||||
}
|
||||
|
||||
internal fun PsiElement.isAnnotationClass() =
|
||||
this is KtClass && isAnnotation() || this is PsiClass && isAnnotationType
|
||||
@@ -8,8 +8,9 @@ package org.jetbrains.kotlin.idea.highlighter
|
||||
import com.intellij.lang.annotation.AnnotationHolder
|
||||
import com.intellij.psi.PsiElement
|
||||
import org.jetbrains.kotlin.diagnostics.Diagnostic
|
||||
import org.jetbrains.kotlin.idea.fir.firResolveState
|
||||
import org.jetbrains.kotlin.idea.fir.getOrBuildFirWithDiagnostics
|
||||
import org.jetbrains.kotlin.idea.frontend.api.FrontendAnalysisSession
|
||||
import org.jetbrains.kotlin.idea.frontend.api.fir.AnalysisSessionFirImpl
|
||||
import org.jetbrains.kotlin.idea.highlighter.visitors.FirAfterResolveHighlightingVisitor
|
||||
import org.jetbrains.kotlin.psi.KtElement
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
|
||||
@@ -20,12 +21,21 @@ class KotlinFirPsiChecker : AbstractKotlinPsiChecker() {
|
||||
|
||||
override fun annotateElement(element: PsiElement, containingFile: KtFile, holder: AnnotationHolder) {
|
||||
if (element !is KtElement) return
|
||||
val state = containingFile.firResolveState()
|
||||
containingFile.getOrBuildFirWithDiagnostics(state)
|
||||
val analysisSession = AnalysisSessionFirImpl.forElement(element)
|
||||
|
||||
val diagnostics = state.getDiagnostics(element)
|
||||
highlightDiagnostics(element, analysisSession, holder)
|
||||
|
||||
FirAfterResolveHighlightingVisitor
|
||||
.createListOfVisitors(analysisSession, holder)
|
||||
.forEach(element::accept)
|
||||
}
|
||||
|
||||
private fun highlightDiagnostics(element: KtElement, analysisSession: FrontendAnalysisSession, holder: AnnotationHolder) {
|
||||
val diagnostics = analysisSession.getDiagnosticsForElement(element)
|
||||
if (diagnostics.isEmpty()) return
|
||||
|
||||
if (diagnostics.none(Diagnostic::isValid)) return
|
||||
|
||||
if (shouldHighlightErrors(element)) {
|
||||
highlightDiagnostics(diagnostics, holder)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* Copyright 2010-2020 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.idea.highlighter.visitors
|
||||
|
||||
import com.intellij.lang.annotation.AnnotationHolder
|
||||
import com.intellij.psi.util.elementType
|
||||
import org.jetbrains.kotlin.idea.frontend.api.FrontendAnalysisSession
|
||||
import org.jetbrains.kotlin.idea.highlighter.textAttributesKeyForPropertyDeclaration
|
||||
import org.jetbrains.kotlin.idea.highlighter.textAttributesKeyForTypeDeclaration
|
||||
import org.jetbrains.kotlin.lexer.KtTokens
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.idea.highlighter.KotlinHighlightingColors as Colors
|
||||
|
||||
internal class DeclarationHighlightingVisitor(
|
||||
analysisSession: FrontendAnalysisSession,
|
||||
holder: AnnotationHolder
|
||||
) : FirAfterResolveHighlightingVisitor(analysisSession, holder) {
|
||||
override fun visitNamedFunction(function: KtNamedFunction) {
|
||||
highlightNamedDeclaration(function, Colors.FUNCTION_DECLARATION)
|
||||
super.visitNamedFunction(function)
|
||||
}
|
||||
|
||||
override fun visitTypeAlias(typeAlias: KtTypeAlias) {
|
||||
highlightNamedDeclaration(typeAlias, Colors.TYPE_ALIAS)
|
||||
super.visitTypeAlias(typeAlias)
|
||||
}
|
||||
|
||||
override fun visitClassOrObject(classOrObject: KtClassOrObject) {
|
||||
textAttributesKeyForTypeDeclaration(classOrObject)?.let { attributes -> highlightNamedDeclaration(classOrObject, attributes) }
|
||||
super.visitClassOrObject(classOrObject)
|
||||
}
|
||||
|
||||
override fun visitTypeParameter(parameter: KtTypeParameter) {
|
||||
highlightNamedDeclaration(parameter, Colors.TYPE_PARAMETER)
|
||||
super.visitTypeParameter(parameter)
|
||||
}
|
||||
|
||||
override fun visitNamedDeclaration(declaration: KtNamedDeclaration) {
|
||||
if (declaration is KtValVarKeywordOwner) {
|
||||
textAttributesKeyForPropertyDeclaration(declaration)?.let { attributes ->
|
||||
highlightNamedDeclaration(declaration, attributes)
|
||||
}
|
||||
if (declaration.valOrVarKeyword?.elementType == KtTokens.VAR_KEYWORD) {
|
||||
highlightNamedDeclaration(declaration, Colors.MUTABLE_VARIABLE)
|
||||
}
|
||||
}
|
||||
super.visitNamedDeclaration(declaration)
|
||||
}
|
||||
|
||||
|
||||
override fun visitSuperTypeCallEntry(call: KtSuperTypeCallEntry) {
|
||||
val calleeExpression = call.calleeExpression
|
||||
val typeElement = calleeExpression.typeReference?.typeElement
|
||||
if (typeElement is KtUserType) {
|
||||
typeElement.referenceExpression?.let { highlightName(it, Colors.CONSTRUCTOR_CALL) }
|
||||
}
|
||||
super.visitSuperTypeCallEntry(call)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* Copyright 2010-2020 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.idea.highlighter.visitors
|
||||
|
||||
import com.intellij.lang.annotation.AnnotationHolder
|
||||
import com.intellij.psi.PsiElement
|
||||
import org.jetbrains.kotlin.idea.KotlinIdeaAnalysisBundle
|
||||
import org.jetbrains.kotlin.idea.frontend.api.FrontendAnalysisSession
|
||||
import org.jetbrains.kotlin.idea.frontend.api.ImplicitReceiverSmartcastKind
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
|
||||
internal class ExpressionsSmartcastHighlightingVisitor(
|
||||
analysisSession: FrontendAnalysisSession,
|
||||
holder: AnnotationHolder
|
||||
) : FirAfterResolveHighlightingVisitor(analysisSession, holder) {
|
||||
override fun visitExpression(expression: KtExpression) {
|
||||
analysisSession.getImplicitReceiverSmartCasts(expression).forEach { (types, kind) ->
|
||||
val receiverName = when (kind) {
|
||||
ImplicitReceiverSmartcastKind.EXTENSION -> KotlinIdeaAnalysisBundle.message("extension.implicit.receiver")
|
||||
ImplicitReceiverSmartcastKind.DISPATCH -> KotlinIdeaAnalysisBundle.message("implicit.receiver")
|
||||
}
|
||||
|
||||
types.forEach { type ->
|
||||
createInfoAnnotation(
|
||||
expression,
|
||||
KotlinIdeaAnalysisBundle.message(
|
||||
"0.smart.cast.to.1",
|
||||
receiverName,
|
||||
analysisSession.renderType(type)
|
||||
)
|
||||
).textAttributes = org.jetbrains.kotlin.idea.highlighter.KotlinHighlightingColors.SMART_CAST_RECEIVER
|
||||
}
|
||||
}
|
||||
analysisSession.getSmartCastedToTypes(expression)?.forEach { type ->
|
||||
createInfoAnnotation(
|
||||
getSmartCastTarget(expression),
|
||||
KotlinIdeaAnalysisBundle.message(
|
||||
"smart.cast.to.0",
|
||||
analysisSession.renderType(type)
|
||||
)
|
||||
).textAttributes = org.jetbrains.kotlin.idea.highlighter.KotlinHighlightingColors.SMART_CAST_VALUE
|
||||
}
|
||||
|
||||
//todo smartcast to null
|
||||
|
||||
super.visitExpression(expression)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private fun getSmartCastTarget(expression: KtExpression): PsiElement {
|
||||
var target: PsiElement = expression
|
||||
if (target is KtParenthesizedExpression) {
|
||||
target = KtPsiUtil.deparenthesize(target) ?: expression
|
||||
}
|
||||
return when (target) {
|
||||
is KtIfExpression -> target.ifKeyword
|
||||
is KtWhenExpression -> target.whenKeyword
|
||||
is KtBinaryExpression -> target.operationReference
|
||||
else -> target
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright 2010-2020 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.idea.highlighter.visitors
|
||||
|
||||
import com.intellij.lang.annotation.AnnotationHolder
|
||||
import org.jetbrains.kotlin.idea.frontend.api.FrontendAnalysisSession
|
||||
import org.jetbrains.kotlin.idea.highlighter.HighlightingVisitor
|
||||
|
||||
abstract class FirAfterResolveHighlightingVisitor(
|
||||
protected val analysisSession: FrontendAnalysisSession,
|
||||
protected val holder: AnnotationHolder
|
||||
) : HighlightingVisitor(holder) {
|
||||
|
||||
protected fun highlightNamedDeclaration(declaration: KtNamedDeclaration, key: TextAttributesKey) {
|
||||
declaration.nameIdentifier?.let { highlightName(it, key) }
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun createListOfVisitors(
|
||||
analysisSession: FrontendAnalysisSession,
|
||||
holder: AnnotationHolder
|
||||
): List<FirAfterResolveHighlightingVisitor> = listOf(
|
||||
TypeHighlightingVisitor(analysisSession, holder),
|
||||
DeclarationHighlightingVisitor(analysisSession, holder),
|
||||
FunctionCallHighlightingVisitor(analysisSession, holder),
|
||||
ExpressionsSmartcastHighlightingVisitor(analysisSession, holder),
|
||||
VariableReferenceHighlightingVisitor(analysisSession, holder),
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
* Copyright 2010-2020 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.idea.highlighter.visitors
|
||||
|
||||
import com.intellij.lang.annotation.AnnotationHolder
|
||||
import com.intellij.openapi.editor.colors.TextAttributesKey
|
||||
import org.jetbrains.kotlin.idea.frontend.api.*
|
||||
import org.jetbrains.kotlin.idea.refactoring.fqName.getKotlinFqName
|
||||
import org.jetbrains.kotlin.lexer.KtTokens
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.psi.psiUtil.isExtensionDeclaration
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameOrNull
|
||||
import org.jetbrains.kotlin.serialization.deserialization.KOTLIN_SUSPEND_BUILT_IN_FUNCTION_FQ_NAME
|
||||
import org.jetbrains.kotlin.idea.highlighter.KotlinHighlightingColors as Colors
|
||||
|
||||
internal class FunctionCallHighlightingVisitor(
|
||||
analysisSession: FrontendAnalysisSession,
|
||||
holder: AnnotationHolder
|
||||
) : FirAfterResolveHighlightingVisitor(analysisSession, holder) {
|
||||
override fun visitBinaryExpression(expression: KtBinaryExpression) {
|
||||
(expression.operationReference as? KtReferenceExpression)
|
||||
?.takeIf {
|
||||
// do not highlight assignment statement
|
||||
(it as? KtOperationReferenceExpression)?.operationSignTokenType != KtTokens.EQ
|
||||
}?.let { callee ->
|
||||
analysisSession.resolveCall(expression)
|
||||
?.takeIf { callInfo ->
|
||||
// ignore arithmetic-like operator calls
|
||||
(callInfo.targetFunction as? KtNamedFunction)?.hasModifier(KtTokens.OPERATOR_KEYWORD) != true
|
||||
}
|
||||
?.let { callInfo ->
|
||||
getTextAttributesForCal(callInfo)?.let { attributes ->
|
||||
highlightName(callee, attributes)
|
||||
}
|
||||
}
|
||||
}
|
||||
super.visitBinaryExpression(expression)
|
||||
}
|
||||
|
||||
override fun visitCallExpression(expression: KtCallExpression) {
|
||||
expression.calleeExpression
|
||||
?.takeUnless { it is KtLambdaExpression }
|
||||
?.takeUnless { it is KtCallExpression /* KT-16159 */}
|
||||
?.let { callee ->
|
||||
analysisSession.resolveCall(expression)?.let { callInfo ->
|
||||
getTextAttributesForCal(callInfo)?.let { attributes ->
|
||||
highlightName(callee, attributes)
|
||||
}
|
||||
}
|
||||
}
|
||||
super.visitCallExpression(expression)
|
||||
}
|
||||
|
||||
private fun getTextAttributesForCal(callInfo: CallInfo): TextAttributesKey? = when {
|
||||
callInfo.isSuspendCall -> Colors.SUSPEND_FUNCTION_CALL
|
||||
callInfo is ConstructorCallInfo ->
|
||||
Colors.CONSTRUCTOR_CALL
|
||||
callInfo is SimpleKtFunctionCallInfo -> when {
|
||||
callInfo.targetFunction.getKotlinFqName() == KOTLIN_SUSPEND_BUILT_IN_FUNCTION_FQ_NAME ->Colors.KEYWORD
|
||||
callInfo.targetFunction.isExtensionDeclaration() -> Colors.EXTENSION_FUNCTION_CALL
|
||||
callInfo.targetFunction.parent is KtFile -> Colors.PACKAGE_FUNCTION_CALL
|
||||
else -> Colors.FUNCTION_CALL
|
||||
}
|
||||
callInfo is SimpleJavaFunctionCallInfo -> Colors.FUNCTION_CALL
|
||||
callInfo is VariableAsFunctionCallInfo -> Colors.VARIABLE_AS_FUNCTION_CALL
|
||||
callInfo is VariableAsFunctionLikeCallInfo -> Colors.VARIABLE_AS_FUNCTION_LIKE_CALL
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
* Copyright 2010-2020 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.idea.highlighter.visitors
|
||||
|
||||
import com.intellij.lang.annotation.AnnotationHolder
|
||||
import com.intellij.openapi.util.TextRange
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.util.PsiTreeUtil
|
||||
import org.jetbrains.kotlin.idea.frontend.api.FrontendAnalysisSession
|
||||
import org.jetbrains.kotlin.idea.highlighter.NameHighlighter
|
||||
import org.jetbrains.kotlin.idea.highlighter.isAnnotationClass
|
||||
import org.jetbrains.kotlin.idea.highlighter.textAttributesKeyForTypeDeclaration
|
||||
import org.jetbrains.kotlin.idea.references.mainReference
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.idea.highlighter.KotlinHighlightingColors as Colors
|
||||
|
||||
internal class TypeHighlightingVisitor(
|
||||
analysisSession: FrontendAnalysisSession,
|
||||
holder: AnnotationHolder
|
||||
) : FirAfterResolveHighlightingVisitor(analysisSession, holder) {
|
||||
override fun visitSimpleNameExpression(expression: KtSimpleNameExpression) {
|
||||
if (!NameHighlighter.namesHighlightingEnabled) return
|
||||
if (expression.isCalleeExpression()) return
|
||||
val parent = expression.parent
|
||||
|
||||
if (parent is KtInstanceExpressionWithLabel) {
|
||||
// Do nothing: 'super' and 'this' are highlighted as a keyword
|
||||
return
|
||||
}
|
||||
val target = expression.mainReference.resolve() ?: return
|
||||
textAttributesKeyForTypeDeclaration(target)?.let { key ->
|
||||
if (expression.isConstructorCallReference() && key != Colors.ANNOTATION) {
|
||||
// Do not highlight constructor call as class reference
|
||||
return@let
|
||||
}
|
||||
highlightName(computeHighlightingRangeForUsage(expression, target), key)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private fun computeHighlightingRangeForUsage(expression: KtSimpleNameExpression, target: PsiElement): TextRange {
|
||||
val expressionRange = expression.textRange
|
||||
|
||||
if (!target.isAnnotationClass()) return expressionRange
|
||||
|
||||
// include '@' symbol if the reference is the first segment of KtAnnotationEntry
|
||||
// if "Deprecated" is highlighted then '@' should be highlighted too in "@Deprecated"
|
||||
val annotationEntry = PsiTreeUtil.getParentOfType(
|
||||
expression, KtAnnotationEntry::class.java, /* strict = */false, KtValueArgumentList::class.java
|
||||
)
|
||||
val atSymbol = annotationEntry?.atSymbol ?: return expressionRange
|
||||
return TextRange(atSymbol.textRange.startOffset, expression.textRange.endOffset)
|
||||
}
|
||||
}
|
||||
|
||||
private fun KtSimpleNameExpression.isCalleeExpression() =
|
||||
(parent as? KtCallExpression)?.calleeExpression == this
|
||||
|
||||
private fun KtSimpleNameExpression.isConstructorCallReference(): Boolean {
|
||||
val type = parent as? KtUserType ?: return false
|
||||
val typeReference = type.parent as? KtTypeReference ?: return false
|
||||
val constructorCallee = typeReference.parent as? KtConstructorCalleeExpression ?: return false
|
||||
return constructorCallee.constructorReferenceExpression == this
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
/*
|
||||
* Copyright 2010-2020 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.idea.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.frontend.api.FrontendAnalysisSession
|
||||
import org.jetbrains.kotlin.idea.highlighter.NameHighlighter
|
||||
import org.jetbrains.kotlin.idea.highlighter.textAttributesKeyForPropertyDeclaration
|
||||
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: FrontendAnalysisSession,
|
||||
holder: AnnotationHolder
|
||||
) : FirAfterResolveHighlightingVisitor(analysisSession, holder) {
|
||||
override fun visitSimpleNameExpression(expression: KtSimpleNameExpression) {
|
||||
if (!NameHighlighter.namesHighlightingEnabled) return
|
||||
if (expression.isAssignmentReference()) return
|
||||
if (expression.parent is KtInstanceExpressionWithLabel) return
|
||||
|
||||
if (expression.isAutoCreatedItParameter()) {
|
||||
createInfoAnnotation(
|
||||
expression,
|
||||
Colors.FUNCTION_LITERAL_DEFAULT_PARAMETER,
|
||||
KotlinIdeaAnalysisBundle.message("automatically.declared.based.on.the.expected.type")
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
val target = expression.mainReference.resolve()
|
||||
|
||||
when {
|
||||
target != null && expression.isBackingField(target) -> 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) {
|
||||
highlightName(expression, Colors.MUTABLE_VARIABLE)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
private fun KtSimpleNameExpression.isBackingField(target: PsiElement): Boolean {
|
||||
if (getReferencedName() != "field") return false
|
||||
if (target !is KtProperty) return false
|
||||
val accessor = parentOfType<KtPropertyAccessor>() ?: return false
|
||||
return accessor.parent == target
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2000-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Copyright 2010-2020 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.
|
||||
*/
|
||||
|
||||
@@ -200,13 +200,15 @@ abstract class KotlinLightCodeInsightFixtureTestCase : KotlinLightCodeInsightFix
|
||||
InTextDirectivesUtils.isDirectiveDefined(fileText, "ENABLE_MULTIPLATFORM") ->
|
||||
KotlinProjectDescriptorWithFacet.KOTLIN_STABLE_WITH_MULTIPLATFORM
|
||||
|
||||
else -> KotlinLightProjectDescriptor.INSTANCE
|
||||
else -> getDefaultProjectDescriptor()
|
||||
}
|
||||
} catch (e: IOException) {
|
||||
throw rethrow(e)
|
||||
}
|
||||
}
|
||||
|
||||
protected open fun getDefaultProjectDescriptor(): KotlinLightProjectDescriptor = KotlinLightProjectDescriptor.INSTANCE
|
||||
|
||||
protected fun isAllFilesPresentInTest(): Boolean = KotlinTestUtils.isAllFilesPresentTest(getTestName(false))
|
||||
|
||||
protected fun performNotWriteEditorAction(actionId: String): Boolean {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Copyright 2010-2020 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.
|
||||
*/
|
||||
|
||||
@@ -10,6 +10,7 @@ import com.intellij.codeInsight.daemon.impl.HighlightInfo;
|
||||
import com.intellij.openapi.util.io.FileUtil;
|
||||
import com.intellij.rt.execution.junit.FileComparisonFailure;
|
||||
import com.intellij.testFramework.ExpectedHighlightingData;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.kotlin.idea.test.KotlinLightCodeInsightFixtureTestCase;
|
||||
import org.jetbrains.kotlin.test.InTextDirectivesUtils;
|
||||
import org.jetbrains.kotlin.test.TagsTestDataUtil;
|
||||
@@ -24,18 +25,23 @@ public abstract class AbstractHighlightingTest extends KotlinLightCodeInsightFix
|
||||
public static final String NO_CHECK_WARNINGS_PREFIX = "// NO_CHECK_WARNINGS";
|
||||
public static final String EXPECTED_DUPLICATED_HIGHLIGHTING_PREFIX = "// EXPECTED_DUPLICATED_HIGHLIGHTING";
|
||||
|
||||
protected void doTest(String unused) throws Exception {
|
||||
String fileText = FileUtil.loadFile(new File(testPath()), true);
|
||||
protected void checkHighlighting(@NotNull String fileText) {
|
||||
boolean checkInfos = !InTextDirectivesUtils.isDirectiveDefined(fileText, NO_CHECK_INFOS_PREFIX);
|
||||
boolean checkWeakWarnings = !InTextDirectivesUtils.isDirectiveDefined(fileText, NO_CHECK_WEAK_WARNINGS_PREFIX);
|
||||
boolean checkWarnings = !InTextDirectivesUtils.isDirectiveDefined(fileText, NO_CHECK_WARNINGS_PREFIX);
|
||||
|
||||
myFixture.checkHighlighting(checkWarnings, checkInfos, checkWeakWarnings);
|
||||
}
|
||||
|
||||
protected void doTest(String unused) throws Exception {
|
||||
String fileText = FileUtil.loadFile(new File(testPath()), true);
|
||||
boolean expectedDuplicatedHighlighting = InTextDirectivesUtils.isDirectiveDefined(fileText, EXPECTED_DUPLICATED_HIGHLIGHTING_PREFIX);
|
||||
|
||||
myFixture.configureByFile(fileName());
|
||||
|
||||
withExpectedDuplicatedHighlighting(expectedDuplicatedHighlighting, () -> {
|
||||
try {
|
||||
myFixture.checkHighlighting(checkWarnings, checkInfos, checkWeakWarnings);
|
||||
checkHighlighting(fileText);
|
||||
}
|
||||
catch (FileComparisonFailure e) {
|
||||
List<HighlightInfo> highlights =
|
||||
|
||||
@@ -95,6 +95,7 @@ val projectsToShadow by extra(listOf(
|
||||
":idea:idea-jps-common",
|
||||
":idea:idea-frontend-independent",
|
||||
":idea:idea-frontend-fir",
|
||||
":idea:idea-frontend-api",
|
||||
*if (Ide.IJ())
|
||||
arrayOf(
|
||||
":idea:idea-maven",
|
||||
|
||||
@@ -185,6 +185,7 @@ include ":kotlin-build-common",
|
||||
":idea:scripting-support",
|
||||
":idea:idea-frontend-independent",
|
||||
":idea:idea-frontend-fir",
|
||||
":idea:idea-frontend-api",
|
||||
":libraries:tools:new-project-wizard",
|
||||
":idea:idea-new-project-wizard",
|
||||
":libraries:tools:new-project-wizard:new-project-wizard-cli",
|
||||
|
||||
Reference in New Issue
Block a user