Delete Kotlin IntelliJ IDEA plugin sources

Kotlin plugin sources were migrated to intellij-community:
https://github.com/JetBrains/intellij-community/tree/master/plugins/kotlin

Preserve `jps-plugin/testData/incremental`
because it's used in `compiler/incremental-compilation-impl/test`

Preserve `idea/testData/multiModuleHighlighting/multiplatform`
because it's used in `MppHighlightingTestDataWithGradleIT`
This commit is contained in:
Nikita Bobko
2021-07-19 18:33:33 +02:00
parent b8d74698f1
commit 39fa2b0baf
49333 changed files with 0 additions and 1160202 deletions

View File

@@ -1,42 +0,0 @@
plugins {
kotlin("jvm")
id("jps-compatible")
}
sourceSets {
"main" { java.srcDirs("main") }
"test" { projectDefault() }
}
dependencies {
compile(kotlinStdlib("jdk8"))
testCompile(projectTests(":idea:idea-maven"))
testCompile(projectTests(":idea:idea-fir"))
testCompile(projectTests(":idea:idea-fir-performance-tests"))
testCompile(projectTests(":idea-frontend-fir"))
testCompile(projectTests(":idea-frontend-fir:idea-fir-low-level-api"))
testCompile(projectTests(":idea:idea-frontend-fir:fir-low-level-api-ide-impl"))
testCompile(projectTests(":idea:idea-fir-fe10-binding"))
testCompile(projectTests(":j2k"))
testCompile(projectTests(":nj2k"))
if (Ide.IJ()) {
testCompile(projectTests(":libraries:tools:new-project-wizard:new-project-wizard-cli"))
testCompile(projectTests(":idea:idea-new-project-wizard"))
}
testCompile(projectTests(":idea:performanceTests"))
testCompile(projectTests(":idea:scripting-support"))
testCompile(projectTests(":jps-plugin"))
testCompile(projectTests(":plugins:uast-kotlin"))
testCompile(projectTests(":plugins:uast-kotlin-fir"))
testCompile(projectTests(":idea:jvm-debugger:jvm-debugger-test"))
testCompile(projectTests(":idea"))
testCompile(projectTests(":idea:idea-android"))
testCompile(projectTests(":generators:test-generator"))
testCompile(projectTests(":plugins:parcelize:parcelize-ide"))
testCompile(projectTests(":kotlinx-serialization-ide-plugin"))
testCompile(projectTests(":compiler:tests-common"))
testCompile(projectTests(":compiler:tests-spec"))
testApiJUnit5()
}
val generateIdeaTests by generator("org.jetbrains.kotlin.generators.tests.idea.GenerateTestsKt")

View File

@@ -1,225 +0,0 @@
import org.apache.tools.ant.filters.ReplaceTokens
plugins {
kotlin("jvm")
id("jps-compatible")
}
val kotlinVersion: String by rootProject.extra
sourceSets {
"main" {
projectDefault()
java.srcDirs(
"idea-completion/src",
"idea-live-templates/src",
"idea-repl/src"
)
resources.srcDirs(
"idea-completion/resources",
"idea-live-templates/resources",
"idea-repl/resources",
"resources-en"
)
if (kotlinBuildProperties.useFirIdeaPlugin) {
resources.srcDir("resources-fir")
} else {
resources.srcDir("resources-descriptors")
}
}
"test" {
projectDefault()
java.srcDirs(
"idea-completion/tests",
"idea-live-templates/tests"
)
}
}
dependencies {
testRuntime(intellijDep())
testRuntime(intellijRuntimeAnnotations())
compile(kotlinStdlib("jdk8"))
compileOnly(project(":kotlin-reflect-api"))
compile(project(":core:descriptors"))
compile(project(":core:descriptors.jvm"))
compile(project(":compiler:backend"))
compile(project(":compiler:frontend"))
compile(project(":compiler:frontend.common"))
compile(project(":compiler:frontend.java"))
compile(project(":compiler:ir.backend.common")) // TODO: fix import (workaround for jps build)
compile(project(":js:js.frontend"))
compile(project(":js:js.serializer"))
compile(project(":compiler:light-classes"))
compile(project(":compiler:util"))
compile(project(":kotlin-build-common"))
compile(project(":daemon-common"))
compile(project(":daemon-common-new"))
compile(projectRuntimeJar(":kotlin-daemon-client"))
compile(project(":compiler:plugin-api"))
compile(project(":idea:jvm-debugger:jvm-debugger-util"))
compile(project(":idea:jvm-debugger:jvm-debugger-core"))
compile(project(":idea:jvm-debugger:jvm-debugger-evaluation"))
compile(project(":idea:jvm-debugger:jvm-debugger-sequence"))
compile(project(":idea:jvm-debugger:jvm-debugger-coroutine"))
compile(project(":j2k"))
compile(project(":idea:idea-j2k"))
compile(project(":idea:formatter"))
compile(project(":compiler:fir:fir2ir"))
compile(project(":compiler:fir:resolve"))
compile(project(":compiler:fir:checkers"))
compile(project(":compiler:fir:checkers:checkers.jvm"))
compile(project(":compiler:fir:java"))
compile(project(":compiler:fir:jvm"))
compile(project(":idea:idea-core"))
compile(project(":idea:idea-frontend-independent"))
compile(project(":idea:ide-common"))
compile(project(":idea:idea-jps-common"))
compile(project(":idea:kotlin-gradle-tooling"))
compile(project(":idea:line-indent-provider"))
compile(project(":plugins:uast-kotlin-base"))
compile(project(":plugins:uast-kotlin"))
compile(project(":plugins:uast-kotlin-idea"))
compile(project(":plugins:uast-kotlin-idea-base"))
compile(project(":kotlin-script-util")) { isTransitive = false }
compile(project(":kotlin-scripting-intellij"))
compile(project(":compiler:backend.jvm")) // Do not delete, for Pill
compile(project(":compiler:backend.jvm:backend.jvm.entrypoint"))
compile(commonDep("org.jetbrains.kotlinx", "kotlinx-coroutines-core")) { isTransitive = false }
compileOnly(project(":kotlin-daemon-client"))
compileOnly(intellijDep())
compileOnly(intellijPluginDep("java"))
testCompileOnly(intellijPluginDep("java"))
testRuntime(intellijPluginDep("java"))
implementation(commonDep("org.jetbrains.intellij.deps.completion", "completion-ranking-kotlin"))
Ide.IJ {
implementation(intellijPluginDep("stats-collector"))
}
compileOnly(commonDep("org.jetbrains", "markdown"))
compileOnly(commonDep("com.google.code.findbugs", "jsr305"))
compileOnly(intellijPluginDep("IntelliLang"))
compileOnly(intellijPluginDep("copyright"))
compileOnly(intellijPluginDep("properties"))
compileOnly(intellijPluginDep("java-i18n"))
compileOnly(intellijPluginDep("gradle"))
testCompileOnly(toolsJar())
testCompileOnly(project(":kotlin-reflect-api")) // TODO: fix import (workaround for jps build)
testCompile(project(":kotlin-test:kotlin-test-junit"))
testCompile(projectTests(":compiler:tests-common"))
testCompile(projectTests(":idea:idea-test-framework")) { isTransitive = false }
testImplementation(projectTests(":idea:idea-frontend-independent")) { isTransitive = false }
testCompile(project(":idea:idea-jvm")) { isTransitive = false }
testCompile(project(":idea:idea-gradle")) { isTransitive = false }
testCompile(project(":idea:idea-maven")) { isTransitive = false }
testCompile(project(":idea:idea-native")) { isTransitive = false }
testCompile(project(":idea:idea-gradle-native")) { isTransitive = false }
testCompile(commonDep("junit:junit"))
testCompileOnly(intellijPluginDep("coverage"))
testRuntimeOnly(toolsJar())
testRuntime(commonDep("org.jetbrains", "markdown"))
testRuntime(project(":plugins:kapt3-idea")) { isTransitive = false }
testRuntime(project(":kotlin-reflect"))
testRuntime(project(":kotlin-preloader"))
testCompile(project(":kotlin-sam-with-receiver-compiler-plugin")) { isTransitive = false }
testRuntime(project(":plugins:android-extensions-compiler"))
testRuntimeOnly(project(":kotlin-android-extensions-runtime")) // TODO: fix import (workaround for jps build)
testRuntime(project(":plugins:android-extensions-ide")) { isTransitive = false }
testRuntime(project(":allopen-ide-plugin")) { isTransitive = false }
testRuntime(project(":kotlin-allopen-compiler-plugin"))
testRuntime(project(":noarg-ide-plugin")) { isTransitive = false }
testRuntime(project(":kotlin-noarg-compiler-plugin"))
testRuntime(project(":plugins:annotation-based-compiler-plugins-ide-support")) { isTransitive = false }
testRuntime(project(":plugins:base-compiler-plugins-ide-support")) { isTransitive = false }
testRuntime(project(":plugins:parcelize:parcelize-compiler"))
testRuntime(project(":plugins:parcelize:parcelize-ide")) { isTransitive = false }
testRuntime(project(":plugins:base-compiler-plugins-ide-support")) { isTransitive = false }
testRuntime(project(":plugins:lombok:lombok-compiler-plugin"))
testRuntime(project(":plugins:lombok:lombok-ide-plugin")) { isTransitive = false }
testRuntime(project(":kotlin-scripting-idea")) { isTransitive = false }
testRuntime(project(":kotlin-scripting-compiler-impl"))
testRuntime(project(":sam-with-receiver-ide-plugin")) { isTransitive = false }
testRuntime(project(":kotlinx-serialization-compiler-plugin"))
testRuntime(project(":kotlinx-serialization-ide-plugin")) { isTransitive = false }
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 }
(rootProject.extra["compilerModules"] as Array<String>).forEach {
testRuntime(project(it))
}
testCompile(intellijPluginDep("IntelliLang"))
testCompile(intellijPluginDep("copyright"))
testCompile(intellijPluginDep("properties"))
testCompile(intellijPluginDep("java-i18n"))
testCompile(intellijPluginDep("junit"))
testCompileOnly(intellijDep())
testCompileOnly(commonDep("com.google.code.findbugs", "jsr305"))
testCompileOnly(intellijPluginDep("gradle"))
testCompileOnly(intellijPluginDep("Groovy"))
if (Ide.IJ()) {
testCompileOnly(intellijPluginDep("maven"))
testRuntime(intellijPluginDep("maven"))
testRuntime(intellijPluginDep("repository-search"))
}
testRuntime(intellijPluginDep("junit"))
testRuntime(intellijPluginDep("gradle"))
testRuntime(intellijPluginDep("Groovy"))
testRuntime(intellijPluginDep("coverage"))
testRuntime(intellijPluginDep("android"))
testRuntime(intellijPluginDep("smali"))
testRuntime(intellijPluginDep("testng"))
if (kotlinBuildProperties.useFirIdeaPlugin) {
testRuntime(project(":idea:idea-fir"))
}
if (Ide.AS()) {
testRuntime(intellijPluginDep("android-layoutlib"))
testRuntime(intellijPluginDep("git4idea"))
testRuntime(intellijPluginDep("google-cloud-tools-core-as"))
testRuntime(intellijPluginDep("google-login-as"))
testRuntime(intellijPluginDep("platform-images"))
}
}
tasks.named<Copy>("processResources") {
val currentIde = IdeVersionConfigurator.currentIde
val pluginPatchNumber = findProperty("pluginPatchNumber") as String? ?: "1"
val defaultPluginVersion = "$kotlinVersion-${currentIde.displayVersion}-$pluginPatchNumber"
val pluginVersion = findProperty("pluginVersion") as String? ?: defaultPluginVersion
inputs.property("pluginVersion", pluginVersion)
filesMatching("META-INF/plugin.xml") {
filter<ReplaceTokens>("tokens" to mapOf("snapshot" to pluginVersion))
}
from(provider { project(":compiler:cli-common").mainSourceSet.resources }) {
include("META-INF/extensions/compiler.xml")
}
}
projectTest(parallel = true) {
dependsOn(":dist")
workingDir = rootDir
}
configureFormInstrumentation()
testsJar()

View File

@@ -1,18 +0,0 @@
plugins {
kotlin("jvm")
id("jps-compatible")
}
dependencies {
compile(project(":compiler:util"))
compile(project(":compiler:frontend"))
compileOnly(intellijDep())
compileOnly(intellijPluginDep("java"))
}
sourceSets {
"main" { projectDefault() }
"test" {}
}

View File

@@ -1,196 +0,0 @@
/*
* Copyright 2010-2019 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.core.formatter;
import com.intellij.application.options.CodeStyle;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.components.ServiceManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.InvalidDataException;
import com.intellij.openapi.util.WriteExternalException;
import com.intellij.psi.codeStyle.CodeStyleSettings;
import com.intellij.psi.codeStyle.CustomCodeStyleSettings;
import org.jdom.Element;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.idea.formatter.KotlinObsoleteCodeStyle;
import org.jetbrains.kotlin.idea.formatter.KotlinStyleGuideCodeStyle;
import org.jetbrains.kotlin.idea.util.FormatterUtilKt;
import org.jetbrains.kotlin.idea.util.ReflectionUtil;
import static com.intellij.util.ReflectionUtil.copyFields;
public class KotlinCodeStyleSettings extends CustomCodeStyleSettings {
public final KotlinPackageEntryTable PACKAGES_TO_USE_STAR_IMPORTS = new KotlinPackageEntryTable();
public final KotlinPackageEntryTable PACKAGES_IMPORT_LAYOUT = new KotlinPackageEntryTable();
public boolean SPACE_AROUND_RANGE = false;
public boolean SPACE_BEFORE_TYPE_COLON = false;
public boolean SPACE_AFTER_TYPE_COLON = true;
public boolean SPACE_BEFORE_EXTEND_COLON = true;
public boolean SPACE_AFTER_EXTEND_COLON = true;
public boolean INSERT_WHITESPACES_IN_SIMPLE_ONE_LINE_METHOD = true;
public boolean ALIGN_IN_COLUMNS_CASE_BRANCH = false;
public boolean SPACE_AROUND_FUNCTION_TYPE_ARROW = true;
public boolean SPACE_AROUND_WHEN_ARROW = true;
public boolean SPACE_BEFORE_LAMBDA_ARROW = true;
public boolean SPACE_BEFORE_WHEN_PARENTHESES = true;
public boolean LBRACE_ON_NEXT_LINE = false;
public int NAME_COUNT_TO_USE_STAR_IMPORT = ApplicationManager.getApplication().isUnitTestMode() ? Integer.MAX_VALUE : 5;
public int NAME_COUNT_TO_USE_STAR_IMPORT_FOR_MEMBERS = ApplicationManager.getApplication().isUnitTestMode() ? Integer.MAX_VALUE : 3;
public boolean IMPORT_NESTED_CLASSES = false;
public boolean CONTINUATION_INDENT_IN_PARAMETER_LISTS = true;
public boolean CONTINUATION_INDENT_IN_ARGUMENT_LISTS = true;
public boolean CONTINUATION_INDENT_FOR_EXPRESSION_BODIES = true;
public boolean CONTINUATION_INDENT_FOR_CHAINED_CALLS = true;
public boolean CONTINUATION_INDENT_IN_SUPERTYPE_LISTS = true;
public boolean CONTINUATION_INDENT_IN_IF_CONDITIONS = true;
public boolean CONTINUATION_INDENT_IN_ELVIS = true;
public int BLANK_LINES_AROUND_BLOCK_WHEN_BRANCHES = 0;
public int WRAP_EXPRESSION_BODY_FUNCTIONS = 0;
public int WRAP_ELVIS_EXPRESSIONS = 1;
public boolean IF_RPAREN_ON_NEW_LINE = false;
public boolean ALLOW_TRAILING_COMMA = false;
public boolean ALLOW_TRAILING_COMMA_ON_CALL_SITE = false;
public int BLANK_LINES_BEFORE_DECLARATION_WITH_COMMENT_OR_ANNOTATION_ON_SEPARATE_LINE = 1;
@ReflectionUtil.SkipInEquals
public String CODE_STYLE_DEFAULTS = null;
/**
* Load settings with previous IDEA defaults to have an ability to restore them.
*/
@Nullable
private KotlinCodeStyleSettings settingsAgainstPreviousDefaults = null;
private final boolean isTempForDeserialize;
public KotlinCodeStyleSettings(CodeStyleSettings container) {
this(container, false);
}
private KotlinCodeStyleSettings(CodeStyleSettings container, boolean isTempForDeserialize) {
super("JetCodeStyleSettings", container);
this.isTempForDeserialize = isTempForDeserialize;
// defaults in IDE but not in tests
if (!ApplicationManager.getApplication().isUnitTestMode()) {
PACKAGES_TO_USE_STAR_IMPORTS.addEntry(new KotlinPackageEntry("java.util", false));
PACKAGES_TO_USE_STAR_IMPORTS.addEntry(new KotlinPackageEntry("kotlinx.android.synthetic", true));
PACKAGES_TO_USE_STAR_IMPORTS.addEntry(new KotlinPackageEntry("io.ktor", true));
}
// Many of test data actually depend on this order of imports,
// that is why we put it here even for test mode
PACKAGES_IMPORT_LAYOUT.addEntry(KotlinPackageEntry.ALL_OTHER_IMPORTS_ENTRY);
PACKAGES_IMPORT_LAYOUT.addEntry(new KotlinPackageEntry("java", true));
PACKAGES_IMPORT_LAYOUT.addEntry(new KotlinPackageEntry("javax", true));
PACKAGES_IMPORT_LAYOUT.addEntry(new KotlinPackageEntry("kotlin", true));
PACKAGES_IMPORT_LAYOUT.addEntry(KotlinPackageEntry.ALL_OTHER_ALIAS_IMPORTS_ENTRY);
}
public static KotlinCodeStyleSettings getInstance(Project project) {
return CodeStyle.getSettings(project).getCustomSettings(KotlinCodeStyleSettings.class);
}
@SuppressWarnings("MethodDoesntCallSuperMethod")
@Override
public Object clone() {
return cloneSettings();
}
@NotNull
public KotlinCodeStyleSettings cloneSettings() {
KotlinCodeStyleSettings clone = new KotlinCodeStyleSettings(getContainer());
clone.copyFrom(this);
clone.settingsAgainstPreviousDefaults = this.settingsAgainstPreviousDefaults;
return clone;
}
private void copyFrom(@NotNull KotlinCodeStyleSettings from) {
copyFields(getClass().getFields(), from, this);
PACKAGES_TO_USE_STAR_IMPORTS.copyFrom(from.PACKAGES_TO_USE_STAR_IMPORTS);
PACKAGES_IMPORT_LAYOUT.copyFrom(from.PACKAGES_IMPORT_LAYOUT);
}
@Override
public boolean equals(Object obj) {
if (!(obj instanceof KotlinCodeStyleSettings)) return false;
if (!ReflectionUtil.comparePublicNonFinalFieldsWithSkip(this, obj)) return false;
return true;
}
@Override
public void writeExternal(Element parentElement, @NotNull CustomCodeStyleSettings parentSettings) throws WriteExternalException {
if (CODE_STYLE_DEFAULTS != null) {
KotlinCodeStyleSettings defaultKotlinCodeStyle = (KotlinCodeStyleSettings) parentSettings.clone();
if (KotlinStyleGuideCodeStyle.CODE_STYLE_ID.equals(CODE_STYLE_DEFAULTS)) {
KotlinStyleGuideCodeStyle.Companion.applyToKotlinCustomSettings(defaultKotlinCodeStyle, false);
}
else if (KotlinObsoleteCodeStyle.CODE_STYLE_ID.equals(CODE_STYLE_DEFAULTS)) {
KotlinObsoleteCodeStyle.Companion.applyToKotlinCustomSettings(defaultKotlinCodeStyle, false);
}
parentSettings = defaultKotlinCodeStyle;
}
super.writeExternal(parentElement, parentSettings);
}
@Override
public void readExternal(Element parentElement) throws InvalidDataException {
if (isTempForDeserialize) {
super.readExternal(parentElement);
return;
}
KotlinCodeStyleSettings tempSettings = readExternalToTemp(parentElement);
String customDefaults = tempSettings.CODE_STYLE_DEFAULTS;
if (KotlinStyleGuideCodeStyle.CODE_STYLE_ID.equals(customDefaults)) {
KotlinStyleGuideCodeStyle.Companion.applyToKotlinCustomSettings(this, true);
}
else if (KotlinObsoleteCodeStyle.CODE_STYLE_ID.equals(customDefaults)) {
KotlinObsoleteCodeStyle.Companion.applyToKotlinCustomSettings(this, true);
}
else if (customDefaults == null && FormatterUtilKt.isDefaultOfficialCodeStyle()) {
// Temporary load settings against previous defaults
settingsAgainstPreviousDefaults = new KotlinCodeStyleSettings(null, true);
KotlinObsoleteCodeStyle.Companion.applyToKotlinCustomSettings(settingsAgainstPreviousDefaults, true);
settingsAgainstPreviousDefaults.readExternal(parentElement);
}
// Actual read
super.readExternal(parentElement);
}
private static KotlinCodeStyleSettings readExternalToTemp(Element parentElement) {
// Read to temp
KotlinCodeStyleSettings tempSettings = new KotlinCodeStyleSettings(null, true);
tempSettings.readExternal(parentElement);
return tempSettings;
}
public boolean canRestore() {
return settingsAgainstPreviousDefaults != null;
}
public void restore() {
if (settingsAgainstPreviousDefaults != null) {
copyFrom(settingsAgainstPreviousDefaults);
}
}
public static KotlinCodeStyleSettings defaultSettings() {
return ServiceManager.getService(KotlinCodeStyleSettingsHolder.class).defaultSettings;
}
public static final class KotlinCodeStyleSettingsHolder {
private final KotlinCodeStyleSettings defaultSettings = new KotlinCodeStyleSettings(new CodeStyleSettings());
}
}

View File

@@ -1,40 +0,0 @@
/*
* 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.core.formatter
import com.intellij.openapi.application.ApplicationBundle
class KotlinPackageEntry(
packageName: String,
val withSubpackages: Boolean
) {
val packageName = packageName.removeSuffix(".*")
companion object {
@JvmField
val ALL_OTHER_IMPORTS_ENTRY =
KotlinPackageEntry(ApplicationBundle.message("listbox.import.all.other.imports"), withSubpackages = true)
@JvmField
val ALL_OTHER_ALIAS_IMPORTS_ENTRY = KotlinPackageEntry("<all other alias imports>", withSubpackages = true)
}
fun matchesPackageName(otherPackageName: String): Boolean {
if (otherPackageName.startsWith(packageName)) {
if (otherPackageName.length == packageName.length) return true
if (withSubpackages) {
if (otherPackageName[packageName.length] == '.') return true
}
}
return false
}
val isSpecial: Boolean get() = this == ALL_OTHER_IMPORTS_ENTRY || this == ALL_OTHER_ALIAS_IMPORTS_ENTRY
override fun toString(): String {
return packageName
}
}

View File

@@ -1,92 +0,0 @@
/*
* 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.core.formatter
import com.intellij.openapi.util.InvalidDataException
import com.intellij.openapi.util.JDOMExternalizable
import org.jdom.Element
class KotlinPackageEntryTable : JDOMExternalizable, Cloneable {
private val entries = mutableListOf<KotlinPackageEntry>()
val entryCount: Int get() = entries.size
public override fun clone(): KotlinPackageEntryTable {
val clone = KotlinPackageEntryTable()
clone.copyFrom(this)
return clone
}
fun copyFrom(packageTable: KotlinPackageEntryTable) {
entries.clear()
entries.addAll(packageTable.entries)
}
fun getEntries(): Array<KotlinPackageEntry> {
return entries.toTypedArray()
}
fun insertEntryAt(entry: KotlinPackageEntry, index: Int) {
entries.add(index, entry)
}
fun removeEntryAt(index: Int) {
entries.removeAt(index)
}
fun getEntryAt(index: Int): KotlinPackageEntry {
return entries[index]
}
fun setEntryAt(entry: KotlinPackageEntry, index: Int) {
entries[index] = entry
}
operator fun contains(packageName: String): Boolean {
return entries.any { !it.isSpecial && it.matchesPackageName(packageName) }
}
fun removeEmptyPackages() {
entries.removeAll { it.packageName.isBlank() }
}
fun addEntry(entry: KotlinPackageEntry) {
entries.add(entry)
}
override fun readExternal(element: Element) {
entries.clear()
element.children.forEach {
if (it.name == "package") {
val packageName = it.getAttributeValue("name") ?: throw InvalidDataException()
val alias = it.getAttributeValue("alias")?.toBoolean() ?: false
val withSubpackages = it.getAttributeValue("withSubpackages")?.toBoolean() ?: false
val entry = when {
packageName.isEmpty() && !alias -> KotlinPackageEntry.ALL_OTHER_IMPORTS_ENTRY
packageName.isEmpty() && alias -> KotlinPackageEntry.ALL_OTHER_ALIAS_IMPORTS_ENTRY
else -> KotlinPackageEntry(packageName, withSubpackages)
}
entries.add(entry)
}
}
}
override fun writeExternal(parentNode: Element) {
for (entry in entries) {
val element = Element("package")
parentNode.addContent(element)
val name = if (entry.isSpecial) "" else entry.packageName
val alias = (entry == KotlinPackageEntry.ALL_OTHER_ALIAS_IMPORTS_ENTRY)
element.setAttribute("name", name)
element.setAttribute("alias", alias.toString())
element.setAttribute("withSubpackages", entry.withSubpackages.toString())
}
}
}

View File

@@ -1,24 +0,0 @@
/*
* Copyright 2010-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.idea.formatter
import com.intellij.formatting.Alignment
import com.intellij.lang.ASTNode
abstract class CommonAlignmentStrategy {
abstract fun getAlignment(node: ASTNode): Alignment?
}

View File

@@ -1,273 +0,0 @@
/*
* Copyright 2000-2019 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.formatter;
import com.intellij.lang.Language;
import com.intellij.openapi.util.DefaultJDOMExternalizer;
import com.intellij.openapi.util.DifferenceFilter;
import com.intellij.openapi.util.InvalidDataException;
import com.intellij.openapi.util.WriteExternalException;
import com.intellij.psi.codeStyle.CodeStyleSettings;
import com.intellij.psi.codeStyle.CommonCodeStyleSettings;
import com.intellij.psi.codeStyle.LanguageCodeStyleSettingsProvider;
import com.intellij.psi.codeStyle.arrangement.ArrangementSettings;
import com.intellij.psi.codeStyle.arrangement.ArrangementUtil;
import com.intellij.util.xmlb.XmlSerializer;
import kotlin.collections.ArraysKt;
import org.jdom.Element;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.idea.KotlinLanguage;
import org.jetbrains.kotlin.idea.util.FormatterUtilKt;
import org.jetbrains.kotlin.idea.util.ReflectionUtil;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.List;
import java.util.Set;
@SuppressWarnings("UnnecessaryFinalOnLocalVariableOrParameter")
public class KotlinCommonCodeStyleSettings extends CommonCodeStyleSettings {
@ReflectionUtil.SkipInEquals
public String CODE_STYLE_DEFAULTS = null;
/**
* Load settings with previous IDEA defaults to have an ability to restore them.
*/
@Nullable
private KotlinCommonCodeStyleSettings settingsAgainstPreviousDefaults = null;
private final boolean isTempForDeserialize;
public KotlinCommonCodeStyleSettings() {
this(false);
}
private KotlinCommonCodeStyleSettings(boolean isTempForDeserialize) {
super(KotlinLanguage.INSTANCE);
this.isTempForDeserialize = isTempForDeserialize;
}
private static KotlinCommonCodeStyleSettings createForTempDeserialize() {
return new KotlinCommonCodeStyleSettings(true);
}
@Override
public void readExternal(Element element) throws InvalidDataException {
if (isTempForDeserialize) {
super.readExternal(element);
return;
}
KotlinCommonCodeStyleSettings tempDeserialize = createForTempDeserialize();
tempDeserialize.readExternal(element);
String customDefaults = tempDeserialize.CODE_STYLE_DEFAULTS;
if (KotlinStyleGuideCodeStyle.CODE_STYLE_ID.equals(customDefaults)) {
KotlinStyleGuideCodeStyle.Companion.applyToCommonSettings(this, true);
}
else if (KotlinObsoleteCodeStyle.CODE_STYLE_ID.equals(customDefaults)) {
KotlinObsoleteCodeStyle.Companion.applyToCommonSettings(this, true);
}
else if (customDefaults == null && FormatterUtilKt.isDefaultOfficialCodeStyle()) {
// Temporary load settings against previous defaults
settingsAgainstPreviousDefaults = createForTempDeserialize();
KotlinObsoleteCodeStyle.Companion.applyToCommonSettings(settingsAgainstPreviousDefaults, true);
settingsAgainstPreviousDefaults.readExternal(element);
}
readExternalBase(element);
}
@Override
public void writeExternal(Element element) throws WriteExternalException {
CommonCodeStyleSettings defaultSettings = getDefaultSettings();
if (defaultSettings != null) {
if (KotlinStyleGuideCodeStyle.CODE_STYLE_ID.equals(CODE_STYLE_DEFAULTS)) {
KotlinStyleGuideCodeStyle.Companion.applyToCommonSettings(defaultSettings, false);
}
else if (KotlinObsoleteCodeStyle.CODE_STYLE_ID.equals(CODE_STYLE_DEFAULTS)) {
KotlinObsoleteCodeStyle.Companion.applyToCommonSettings(defaultSettings, false);
}
}
writeExternalBase(element, defaultSettings);
}
//<editor-fold desc="Copied and adapted from CommonCodeStyleSettings ">
private void readExternalBase(Element element) throws InvalidDataException {
super.readExternal(element);
}
private void writeExternalBase(Element element, CommonCodeStyleSettings defaultSettings) throws WriteExternalException {
Set<String> supportedFields = getSupportedFields();
if (supportedFields != null) {
supportedFields.add("FORCE_REARRANGE_MODE");
supportedFields.add("CODE_STYLE_DEFAULTS");
}
//noinspection deprecation
DefaultJDOMExternalizer.writeExternal(this, element, new SupportedFieldsDiffFilter(this, supportedFields, defaultSettings));
List<Integer> softMargins = getSoftMargins();
serializeInto(softMargins, element);
IndentOptions myIndentOptions = getIndentOptions();
if (myIndentOptions != null) {
IndentOptions defaultIndentOptions = defaultSettings != null ? defaultSettings.getIndentOptions() : null;
Element indentOptionsElement = new Element(INDENT_OPTIONS_TAG);
myIndentOptions.serialize(indentOptionsElement, defaultIndentOptions);
if (!indentOptionsElement.getChildren().isEmpty()) {
element.addContent(indentOptionsElement);
}
}
ArrangementSettings myArrangementSettings = getArrangementSettings();
if (myArrangementSettings != null) {
Element container = new Element(ARRANGEMENT_ELEMENT_NAME);
ArrangementUtil.writeExternal(container, myArrangementSettings, myLanguage);
if (!container.getChildren().isEmpty()) {
element.addContent(container);
}
}
}
@Override
public CommonCodeStyleSettings clone(@NotNull CodeStyleSettings rootSettings) {
KotlinCommonCodeStyleSettings commonSettings = new KotlinCommonCodeStyleSettings();
commonSettings.settingsAgainstPreviousDefaults = settingsAgainstPreviousDefaults;
copyPublicFieldsOwn(this, commonSettings);
try {
Method setRootSettingsMethod = CommonCodeStyleSettings.class.getDeclaredMethod("setRootSettings", CodeStyleSettings.class);
setRootSettingsMethod.setAccessible(true);
setRootSettingsMethod.invoke(commonSettings, rootSettings);
}
catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
throw new IllegalStateException(e);
}
commonSettings.setForceArrangeMenuAvailable(isForceArrangeMenuAvailable());
IndentOptions indentOptions = getIndentOptions();
if (indentOptions != null) {
IndentOptions targetIndentOptions = commonSettings.initIndentOptions();
targetIndentOptions.copyFrom(indentOptions);
}
ArrangementSettings arrangementSettings = getArrangementSettings();
if (arrangementSettings != null) {
commonSettings.setArrangementSettings(arrangementSettings.clone());
}
try {
Method setRootSettingsMethod = ArraysKt.singleOrNull(
CommonCodeStyleSettings.class.getDeclaredMethods(),
method -> "setSoftMargins".equals(method.getName()));
if (setRootSettingsMethod != null) {
// Method was introduced in 173
setRootSettingsMethod.setAccessible(true);
setRootSettingsMethod.invoke(commonSettings, getSoftMargins());
}
}
catch (IllegalAccessException | InvocationTargetException e) {
throw new IllegalStateException(e);
}
return commonSettings;
}
@Override
public boolean equals(Object obj) {
if (!(obj instanceof KotlinCommonCodeStyleSettings)) {
return false;
}
if (!ReflectionUtil.comparePublicNonFinalFieldsWithSkip(this, obj)) {
return false;
}
CommonCodeStyleSettings other = (CommonCodeStyleSettings) obj;
if (!getSoftMargins().equals(other.getSoftMargins())) {
return false;
}
IndentOptions options = getIndentOptions();
if ((options == null && other.getIndentOptions() != null) ||
(options != null && !options.equals(other.getIndentOptions()))) {
return false;
}
return arrangementSettingsEqual(other);
}
// SoftMargins.serializeInfo
private void serializeInto(@NotNull List<Integer> softMargins, @NotNull Element element) {
if (softMargins.size() > 0) {
XmlSerializer.serializeInto(this, element);
}
}
//</editor-fold>
//<editor-fold desc="Copied from CommonCodeStyleSettings">
private static final String INDENT_OPTIONS_TAG = "indentOptions";
private static final String ARRANGEMENT_ELEMENT_NAME = "arrangement";
private final Language myLanguage = KotlinLanguage.INSTANCE;
@Nullable
private CommonCodeStyleSettings getDefaultSettings() {
return LanguageCodeStyleSettingsProvider.getDefaultCommonSettings(myLanguage);
}
@Nullable
private Set<String> getSupportedFields() {
final LanguageCodeStyleSettingsProvider provider = LanguageCodeStyleSettingsProvider.forLanguage(myLanguage);
return provider == null ? null : provider.getSupportedFields();
}
public boolean canRestore() {
return settingsAgainstPreviousDefaults != null;
}
public void restore() {
if (settingsAgainstPreviousDefaults != null) {
copyFrom(settingsAgainstPreviousDefaults);
}
}
private static class SupportedFieldsDiffFilter extends DifferenceFilter<CommonCodeStyleSettings> {
private final Set<String> mySupportedFieldNames;
public SupportedFieldsDiffFilter(
final CommonCodeStyleSettings object,
Set<String> supportedFiledNames,
final CommonCodeStyleSettings parentObject
) {
super(object, parentObject);
mySupportedFieldNames = supportedFiledNames;
}
@Override
public boolean isAccept(@NotNull Field field) {
if (mySupportedFieldNames == null ||
mySupportedFieldNames.contains(field.getName())) {
return super.isAccept(field);
}
return false;
}
}
// Can't use super.copyPublicFields because the method is internal in 181
private static void copyPublicFieldsOwn(Object from, Object to) {
assert from != to;
com.intellij.util.ReflectionUtil.copyFields(to.getClass().getFields(), from, to);
}
//</editor-fold>
}

View File

@@ -1,70 +0,0 @@
/*
* 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.formatter
import com.intellij.psi.codeStyle.CodeStyleSettings
import com.intellij.psi.codeStyle.CommonCodeStyleSettings
import org.jetbrains.kotlin.idea.KotlinLanguage
import org.jetbrains.kotlin.idea.core.formatter.KotlinCodeStyleSettings
class KotlinObsoleteCodeStyle : KotlinPredefinedCodeStyle(CODE_STYLE_TITLE, KotlinLanguage.INSTANCE) {
override val codeStyleId: String = CODE_STYLE_ID
override fun apply(settings: CodeStyleSettings) {
Companion.apply(settings)
}
companion object {
val INSTANCE = KotlinObsoleteCodeStyle()
const val CODE_STYLE_ID = "KOTLIN_OLD_DEFAULTS"
const val CODE_STYLE_SETTING = "obsolete"
const val CODE_STYLE_TITLE = "Kotlin obsolete IntelliJ IDEA codestyle"
fun apply(settings: CodeStyleSettings) {
applyToKotlinCustomSettings(settings.kotlinCustomSettings)
applyToCommonSettings(settings.kotlinCommonSettings)
}
fun applyToKotlinCustomSettings(kotlinCustomSettings: KotlinCodeStyleSettings, modifyCodeStyle: Boolean = true) {
kotlinCustomSettings.apply {
if (modifyCodeStyle) {
CODE_STYLE_DEFAULTS = CODE_STYLE_ID
}
CONTINUATION_INDENT_IN_PARAMETER_LISTS = true
CONTINUATION_INDENT_IN_ARGUMENT_LISTS = true
CONTINUATION_INDENT_FOR_EXPRESSION_BODIES = true
CONTINUATION_INDENT_FOR_CHAINED_CALLS = true
CONTINUATION_INDENT_IN_SUPERTYPE_LISTS = true
CONTINUATION_INDENT_IN_IF_CONDITIONS = true
CONTINUATION_INDENT_IN_ELVIS = true
WRAP_EXPRESSION_BODY_FUNCTIONS = CodeStyleSettings.DO_NOT_WRAP
IF_RPAREN_ON_NEW_LINE = false
}
}
fun applyToCommonSettings(commonSettings: CommonCodeStyleSettings, modifyCodeStyle: Boolean = true) {
commonSettings.apply {
CALL_PARAMETERS_WRAP = CodeStyleSettings.DO_NOT_WRAP
CALL_PARAMETERS_LPAREN_ON_NEXT_LINE = false
CALL_PARAMETERS_RPAREN_ON_NEXT_LINE = false
METHOD_PARAMETERS_WRAP = CodeStyleSettings.DO_NOT_WRAP
METHOD_PARAMETERS_LPAREN_ON_NEXT_LINE = false
METHOD_PARAMETERS_RPAREN_ON_NEXT_LINE = false
EXTENDS_LIST_WRAP = CodeStyleSettings.DO_NOT_WRAP
METHOD_CALL_CHAIN_WRAP = CodeStyleSettings.DO_NOT_WRAP
ASSIGNMENT_WRAP = CodeStyleSettings.DO_NOT_WRAP
}
if (modifyCodeStyle && commonSettings is KotlinCommonCodeStyleSettings) {
commonSettings.CODE_STYLE_DEFAULTS = CODE_STYLE_ID
}
}
}
}

View File

@@ -1,13 +0,0 @@
/*
* Copyright 2010-2018 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.formatter
import com.intellij.lang.Language
import com.intellij.psi.codeStyle.PredefinedCodeStyle
abstract class KotlinPredefinedCodeStyle(name: String, language: Language) : PredefinedCodeStyle(name, language) {
abstract val codeStyleId: String
}

View File

@@ -1,215 +0,0 @@
/*
* Copyright 2010-2019 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.formatter
import com.intellij.formatting.*
import com.intellij.formatting.DependentSpacingRule.Anchor
import com.intellij.formatting.DependentSpacingRule.Trigger
import com.intellij.lang.ASTNode
import com.intellij.openapi.util.TextRange
import com.intellij.psi.PsiComment
import com.intellij.psi.codeStyle.CommonCodeStyleSettings
import com.intellij.psi.tree.IElementType
import com.intellij.psi.tree.TokenSet
import org.jetbrains.kotlin.KtNodeTypes
import org.jetbrains.kotlin.idea.util.requireNode
import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.psi.psiUtil.children
import org.jetbrains.kotlin.psi.stubs.elements.KtModifierListElementType
import kotlin.math.max
fun CommonCodeStyleSettings.createSpaceBeforeRBrace(numSpacesOtherwise: Int, textRange: TextRange): Spacing? {
return Spacing.createDependentLFSpacing(
numSpacesOtherwise, numSpacesOtherwise, textRange,
KEEP_LINE_BREAKS,
KEEP_BLANK_LINES_BEFORE_RBRACE
)
}
class KotlinSpacingBuilder(val commonCodeStyleSettings: CommonCodeStyleSettings, val spacingBuilderUtil: KotlinSpacingBuilderUtil) {
private val builders = ArrayList<Builder>()
private interface Builder {
fun getSpacing(parent: ASTBlock, left: ASTBlock, right: ASTBlock): Spacing?
}
inner class BasicSpacingBuilder : SpacingBuilder(commonCodeStyleSettings), Builder {
override fun getSpacing(parent: ASTBlock, left: ASTBlock, right: ASTBlock): Spacing? {
return super.getSpacing(parent, left, right)
}
}
private data class Condition(
val parent: IElementType? = null,
val left: IElementType? = null,
val right: IElementType? = null,
val parentSet: TokenSet? = null,
val leftSet: TokenSet? = null,
val rightSet: TokenSet? = null
) : (ASTBlock, ASTBlock, ASTBlock) -> Boolean {
override fun invoke(p: ASTBlock, l: ASTBlock, r: ASTBlock): Boolean =
(parent == null || p.requireNode().elementType == parent) &&
(left == null || l.requireNode().elementType == left) &&
(right == null || r.requireNode().elementType == right) &&
(parentSet == null || parentSet.contains(p.requireNode().elementType)) &&
(leftSet == null || leftSet.contains(l.requireNode().elementType)) &&
(rightSet == null || rightSet.contains(r.requireNode().elementType))
}
private data class Rule(
val conditions: List<Condition>,
val action: (ASTBlock, ASTBlock, ASTBlock) -> Spacing?
) : (ASTBlock, ASTBlock, ASTBlock) -> Spacing? {
override fun invoke(p: ASTBlock, l: ASTBlock, r: ASTBlock): Spacing? =
if (conditions.all { it(p, l, r) }) action(p, l, r) else null
}
inner class CustomSpacingBuilder : Builder {
private val rules = ArrayList<Rule>()
private var conditions = ArrayList<Condition>()
override fun getSpacing(parent: ASTBlock, left: ASTBlock, right: ASTBlock): Spacing? {
for (rule in rules) {
val spacing = rule(parent, left, right)
if (spacing != null) {
return spacing
}
}
return null
}
fun inPosition(
parent: IElementType? = null, left: IElementType? = null, right: IElementType? = null,
parentSet: TokenSet? = null, leftSet: TokenSet? = null, rightSet: TokenSet? = null
): CustomSpacingBuilder {
conditions.add(Condition(parent, left, right, parentSet, leftSet, rightSet))
return this
}
fun lineBreakIfLineBreakInParent(numSpacesOtherwise: Int, allowBlankLines: Boolean = true) {
newRule { p, _, _ ->
Spacing.createDependentLFSpacing(
numSpacesOtherwise, numSpacesOtherwise, p.textRange,
commonCodeStyleSettings.KEEP_LINE_BREAKS,
if (allowBlankLines) commonCodeStyleSettings.KEEP_BLANK_LINES_IN_CODE else 0
)
}
}
fun emptyLinesIfLineBreakInLeft(emptyLines: Int, numberOfLineFeedsOtherwise: Int = 1, numSpacesOtherwise: Int = 0) {
newRule { _: ASTBlock, left: ASTBlock, _: ASTBlock ->
val lastChild = left.node?.psi?.lastChild
val leftEndsWithComment = lastChild is PsiComment && lastChild.tokenType == KtTokens.EOL_COMMENT
val dependentSpacingRule = DependentSpacingRule(Trigger.HAS_LINE_FEEDS).registerData(Anchor.MIN_LINE_FEEDS, emptyLines + 1)
val textRange = left.node
?.startOfDeclaration()
?.startOffset
?.let { TextRange.create(it, left.textRange.endOffset) }
?: left.textRange
spacingBuilderUtil.createLineFeedDependentSpacing(
numSpacesOtherwise,
numSpacesOtherwise,
if (leftEndsWithComment) max(1, numberOfLineFeedsOtherwise) else numberOfLineFeedsOtherwise,
commonCodeStyleSettings.KEEP_LINE_BREAKS,
commonCodeStyleSettings.KEEP_BLANK_LINES_IN_DECLARATIONS,
textRange,
dependentSpacingRule
)
}
}
fun spacing(spacing: Spacing) {
newRule { _, _, _ -> spacing }
}
fun customRule(block: (parent: ASTBlock, left: ASTBlock, right: ASTBlock) -> Spacing?) {
newRule(block)
}
private fun newRule(rule: (ASTBlock, ASTBlock, ASTBlock) -> Spacing?) {
val savedConditions = ArrayList(conditions)
rules.add(Rule(savedConditions, rule))
conditions.clear()
}
}
fun getSpacing(parent: Block, child1: Block?, child2: Block): Spacing? {
if (parent !is ASTBlock || child1 !is ASTBlock || child2 !is ASTBlock) {
return null
}
for (builder in builders) {
val spacing = builder.getSpacing(parent, child1, child2)
if (spacing != null) {
// TODO: it's a severe hack but I don't know how to implement it in other way
if (child1.requireNode().elementType == KtTokens.EOL_COMMENT && spacing.toString().contains("minLineFeeds=0")) {
val isBeforeBlock =
child2.requireNode().elementType == KtNodeTypes.BLOCK || child2.requireNode().firstChildNode
?.elementType == KtNodeTypes.BLOCK
val keepBlankLines = if (isBeforeBlock) 0 else commonCodeStyleSettings.KEEP_BLANK_LINES_IN_CODE
return createSpacing(0, minLineFeeds = 1, keepLineBreaks = true, keepBlankLines = keepBlankLines)
}
return spacing
}
}
return null
}
fun simple(init: BasicSpacingBuilder.() -> Unit) {
val builder = BasicSpacingBuilder()
builder.init()
builders.add(builder)
}
fun custom(init: CustomSpacingBuilder.() -> Unit) {
val builder = CustomSpacingBuilder()
builder.init()
builders.add(builder)
}
fun createSpacing(
minSpaces: Int,
maxSpaces: Int = minSpaces,
minLineFeeds: Int = 0,
keepLineBreaks: Boolean = commonCodeStyleSettings.KEEP_LINE_BREAKS,
keepBlankLines: Int = commonCodeStyleSettings.KEEP_BLANK_LINES_IN_CODE
): Spacing {
return Spacing.createSpacing(minSpaces, maxSpaces, minLineFeeds, keepLineBreaks, keepBlankLines)
}
}
interface KotlinSpacingBuilderUtil {
fun createLineFeedDependentSpacing(
minSpaces: Int,
maxSpaces: Int,
minimumLineFeeds: Int,
keepLineBreaks: Boolean,
keepBlankLines: Int,
dependency: TextRange,
rule: DependentSpacingRule
): Spacing
fun getPreviousNonWhitespaceLeaf(node: ASTNode?): ASTNode?
fun isWhitespaceOrEmpty(node: ASTNode?): Boolean
}
fun rules(
commonCodeStyleSettings: CommonCodeStyleSettings,
builderUtil: KotlinSpacingBuilderUtil,
init: KotlinSpacingBuilder.() -> Unit
): KotlinSpacingBuilder {
val builder = KotlinSpacingBuilder(commonCodeStyleSettings, builderUtil)
builder.init()
return builder
}
internal fun ASTNode.startOfDeclaration(): ASTNode? = children().firstOrNull {
val elementType = it.elementType
elementType !is KtModifierListElementType<*> && elementType !in KtTokens.WHITE_SPACE_OR_COMMENT_BIT_SET
}

View File

@@ -1,81 +0,0 @@
/*
* 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.formatter
import com.intellij.psi.codeStyle.CodeStyleSettings
import com.intellij.psi.codeStyle.CommonCodeStyleSettings
import org.jetbrains.kotlin.idea.KotlinLanguage
import org.jetbrains.kotlin.idea.core.formatter.KotlinCodeStyleSettings
class KotlinStyleGuideCodeStyle : KotlinPredefinedCodeStyle("Kotlin style guide", KotlinLanguage.INSTANCE) {
override val codeStyleId: String = CODE_STYLE_ID
override fun apply(settings: CodeStyleSettings) {
Companion.apply(settings)
}
companion object {
val INSTANCE = KotlinStyleGuideCodeStyle()
const val CODE_STYLE_ID = "KOTLIN_OFFICIAL"
const val CODE_STYLE_SETTING = "official"
const val CODE_STYLE_TITLE = "Kotlin Coding Conventions"
fun apply(settings: CodeStyleSettings) {
applyToKotlinCustomSettings(settings.kotlinCustomSettings)
applyToCommonSettings(settings.kotlinCommonSettings)
}
fun applyToKotlinCustomSettings(
kotlinCustomSettings: KotlinCodeStyleSettings,
modifyCodeStyle: Boolean = true
) {
kotlinCustomSettings.apply {
if (modifyCodeStyle) {
CODE_STYLE_DEFAULTS = CODE_STYLE_ID
}
CONTINUATION_INDENT_IN_PARAMETER_LISTS = false
CONTINUATION_INDENT_IN_ARGUMENT_LISTS = false
CONTINUATION_INDENT_FOR_EXPRESSION_BODIES = false
CONTINUATION_INDENT_FOR_CHAINED_CALLS = false
CONTINUATION_INDENT_IN_SUPERTYPE_LISTS = false
CONTINUATION_INDENT_IN_IF_CONDITIONS = false
CONTINUATION_INDENT_IN_ELVIS = false
WRAP_EXPRESSION_BODY_FUNCTIONS = CodeStyleSettings.WRAP_AS_NEEDED
IF_RPAREN_ON_NEW_LINE = true
ALLOW_TRAILING_COMMA = false
}
}
fun applyToCommonSettings(commonSettings: CommonCodeStyleSettings, modifyCodeStyle: Boolean = true) {
commonSettings.apply {
WHILE_ON_NEW_LINE = false
ELSE_ON_NEW_LINE = false
CATCH_ON_NEW_LINE = false
FINALLY_ON_NEW_LINE = false
CALL_PARAMETERS_WRAP = CodeStyleSettings.WRAP_AS_NEEDED + CodeStyleSettings.WRAP_ON_EVERY_ITEM
CALL_PARAMETERS_LPAREN_ON_NEXT_LINE = true
CALL_PARAMETERS_RPAREN_ON_NEXT_LINE = true
METHOD_PARAMETERS_WRAP = CodeStyleSettings.WRAP_AS_NEEDED + CodeStyleSettings.WRAP_ON_EVERY_ITEM
METHOD_PARAMETERS_LPAREN_ON_NEXT_LINE = true
METHOD_PARAMETERS_RPAREN_ON_NEXT_LINE = true
EXTENDS_LIST_WRAP = CodeStyleSettings.WRAP_AS_NEEDED
METHOD_CALL_CHAIN_WRAP = CodeStyleSettings.WRAP_AS_NEEDED
ASSIGNMENT_WRAP = CodeStyleSettings.WRAP_AS_NEEDED
ALIGN_MULTILINE_BINARY_OPERATION = false
}
if (modifyCodeStyle && commonSettings is KotlinCommonCodeStyleSettings) {
commonSettings.CODE_STYLE_DEFAULTS = CODE_STYLE_ID
}
}
}
}

View File

@@ -1,50 +0,0 @@
/*
* Copyright 2010-2018 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.formatter
import com.intellij.application.options.CodeStyle
import com.intellij.openapi.project.Project
import com.intellij.psi.codeStyle.CodeStyleSettings
import org.jetbrains.kotlin.idea.KotlinLanguage
import org.jetbrains.kotlin.idea.core.formatter.KotlinCodeStyleSettings
data class KtCodeStyleSettings(
val custom: KotlinCodeStyleSettings,
val common: KotlinCommonCodeStyleSettings,
val all: CodeStyleSettings
)
fun KtCodeStyleSettings.canRestore(): Boolean {
return custom.canRestore() || common.canRestore()
}
fun KtCodeStyleSettings.hasDefaultLoadScheme(): Boolean {
return custom.CODE_STYLE_DEFAULTS == null || common.CODE_STYLE_DEFAULTS == null
}
fun KtCodeStyleSettings.restore() {
custom.restore()
common.restore()
}
fun ktCodeStyleSettings(project: Project): KtCodeStyleSettings? {
val settings = CodeStyle.getSettings(project)
val ktCommonSettings = settings.getCommonSettings(KotlinLanguage.INSTANCE) as KotlinCommonCodeStyleSettings
val ktCustomSettings = settings.getCustomSettings(KotlinCodeStyleSettings::class.java)
return KtCodeStyleSettings(ktCustomSettings, ktCommonSettings, settings)
}
val CodeStyleSettings.kotlinCommonSettings: KotlinCommonCodeStyleSettings
get() = getCommonSettings(KotlinLanguage.INSTANCE) as KotlinCommonCodeStyleSettings
val CodeStyleSettings.kotlinCustomSettings: KotlinCodeStyleSettings
get() = getCustomSettings(KotlinCodeStyleSettings::class.java)
fun CodeStyleSettings.kotlinCodeStyleDefaults(): String? {
return kotlinCustomSettings.CODE_STYLE_DEFAULTS ?: kotlinCommonSettings.CODE_STYLE_DEFAULTS
}

View File

@@ -1,144 +0,0 @@
/*
* Copyright 2010-2019 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.formatter
import com.intellij.formatting.Indent
import com.intellij.lang.ASTNode
import com.intellij.psi.codeStyle.CodeStyleSettings
import com.intellij.psi.tree.IElementType
import com.intellij.psi.tree.TokenSet
import org.jetbrains.annotations.NonNls
import kotlin.collections.ArrayList
abstract class NodeIndentStrategy {
abstract fun getIndent(node: ASTNode, settings: CodeStyleSettings): Indent?
class ConstIndentStrategy(private val indent: Indent) : NodeIndentStrategy() {
override fun getIndent(node: ASTNode, settings: CodeStyleSettings): Indent? {
return indent
}
}
class PositionStrategy(private val debugInfo: String?) : NodeIndentStrategy() {
private var indentCallback: (CodeStyleSettings) -> Indent = { Indent.getNoneIndent() }
private val within = ArrayList<IElementType>()
private var withinCallback: ((ASTNode) -> Boolean)? = null
private val notIn = ArrayList<IElementType>()
private val forElement = ArrayList<IElementType>()
private val notForElement = ArrayList<IElementType>()
private var forElementCallback: ((ASTNode) -> Boolean)? = null
override fun toString(): String {
return "PositionStrategy " + (debugInfo ?: "No debug info")
}
fun set(indent: Indent): PositionStrategy {
indentCallback = { indent }
return this
}
fun set(indentCallback: (CodeStyleSettings) -> Indent): PositionStrategy {
this.indentCallback = indentCallback
return this
}
fun within(parents: TokenSet): PositionStrategy {
val types = parents.types
if (types.isEmpty()) {
throw IllegalArgumentException("Empty token set is unexpected")
}
fillTypes(within, types[0], types.copyOfRange(1, types.size))
return this
}
fun within(parentType: IElementType, vararg orParentTypes: IElementType): PositionStrategy {
fillTypes(within, parentType, orParentTypes)
return this
}
fun within(callback: (ASTNode) -> Boolean): PositionStrategy {
withinCallback = callback
return this
}
fun notWithin(parentType: IElementType, vararg orParentTypes: IElementType): PositionStrategy {
fillTypes(notIn, parentType, orParentTypes)
return this
}
fun withinAny(): PositionStrategy {
within.clear()
notIn.clear()
return this
}
fun forType(elementType: IElementType, vararg otherTypes: IElementType): PositionStrategy {
fillTypes(forElement, elementType, otherTypes)
return this
}
fun notForType(elementType: IElementType, vararg otherTypes: IElementType): PositionStrategy {
fillTypes(notForElement, elementType, otherTypes)
return this
}
fun forAny(): PositionStrategy {
forElement.clear()
notForElement.clear()
return this
}
fun forElement(callback: (ASTNode) -> Boolean): PositionStrategy {
forElementCallback = callback
return this
}
override fun getIndent(node: ASTNode, settings: CodeStyleSettings): Indent? {
if (!isValidIndent(forElement, notForElement, node, forElementCallback)) return null
val parent = node.treeParent
if (parent != null) {
if (!isValidIndent(within, notIn, parent, withinCallback)) return null
} else if (within.isNotEmpty()) return null
return indentCallback(settings)
}
private fun fillTypes(resultCollection: MutableList<IElementType>, singleType: IElementType, otherTypes: Array<out IElementType>) {
resultCollection.clear()
resultCollection.add(singleType)
resultCollection.addAll(otherTypes)
}
}
companion object {
fun constIndent(indent: Indent): NodeIndentStrategy {
return ConstIndentStrategy(indent)
}
fun strategy(@NonNls debugInfo: String?): PositionStrategy {
return PositionStrategy(debugInfo)
}
}
}
private fun isValidIndent(
elements: ArrayList<IElementType>,
excludeElements: ArrayList<IElementType>,
node: ASTNode,
callback: ((ASTNode) -> Boolean)?
): Boolean {
if (elements.isNotEmpty() && !elements.contains(node.elementType)) return false
if (excludeElements.contains(node.elementType)) return false
if (callback?.invoke(node) == false) return false
return true
}

View File

@@ -1,48 +0,0 @@
/*
* Copyright 2010-2019 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.formatter
import com.intellij.application.options.CodeStyle
import com.intellij.openapi.project.Project
import com.intellij.psi.codeStyle.CodeStyleSettings
import com.intellij.psi.codeStyle.CodeStyleSettingsManager
object ProjectCodeStyleImporter {
fun apply(project: Project, codeStyleStr: String?): Boolean {
return when (codeStyleStr) {
KotlinObsoleteCodeStyle.CODE_STYLE_SETTING -> {
apply(project, KotlinObsoleteCodeStyle.INSTANCE)
true
}
KotlinStyleGuideCodeStyle.CODE_STYLE_SETTING -> {
apply(project, KotlinStyleGuideCodeStyle.INSTANCE)
true
}
else -> false
}
}
fun apply(project: Project, predefinedCodeStyle: KotlinPredefinedCodeStyle) {
val settingsManager = CodeStyleSettingsManager.getInstance(project)
val customSettings = CodeStyle.getSettings(project)
if (predefinedCodeStyle.codeStyleId == customSettings.kotlinCodeStyleDefaults()) {
// Don't bother user that already have correct code style
return
}
val projectSettingsUpdated: CodeStyleSettings = if (settingsManager.USE_PER_PROJECT_SETTINGS) {
customSettings.clone()
} else {
CodeStyleSettings()
}
settingsManager.USE_PER_PROJECT_SETTINGS = true
predefinedCodeStyle.apply(projectSettingsUpdated)
settingsManager.mainProjectCodeStyle = projectSettingsUpdated
}
}

View File

@@ -1,63 +0,0 @@
/*
* Copyright 2010-2019 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.formatter
import com.intellij.formatting.*
import com.intellij.lang.ASTNode
import com.intellij.openapi.util.TextRange
class SyntheticKotlinBlock(
private val node: ASTNode,
private val subBlocks: List<ASTBlock>,
private val alignment: Alignment?,
private val indent: Indent?,
private val wrap: Wrap?,
private val spacingBuilder: KotlinSpacingBuilder,
private val createParentSyntheticSpacingBlock: (ASTNode) -> ASTBlock
) : ASTBlock {
private val textRange = TextRange(
subBlocks.first().textRange.startOffset,
subBlocks.last().textRange.endOffset
)
override fun getTextRange(): TextRange = textRange
override fun getSubBlocks() = subBlocks
override fun getWrap() = wrap
override fun getIndent() = indent
override fun getAlignment() = alignment
override fun getChildAttributes(newChildIndex: Int) = ChildAttributes(getIndent(), null)
override fun isIncomplete() = getSubBlocks().last().isIncomplete
override fun isLeaf() = false
override fun getNode() = node
override fun getSpacing(child1: Block?, child2: Block): Spacing? {
return spacingBuilder.getSpacing(createParentSyntheticSpacingBlock(node), child1, child2)
}
override fun toString(): String {
var child = subBlocks.first()
var treeNode: ASTNode? = null
loop@
while (treeNode == null) when (child) {
is SyntheticKotlinBlock -> child = child.getSubBlocks().first()
else -> treeNode = child.node
}
val textRange = getTextRange()
val psi = treeNode.psi
if (psi != null) {
val file = psi.containingFile
if (file != null) {
return file.text!!.subSequence(textRange.startOffset, textRange.endOffset).toString() + " " + textRange
}
}
return this::class.java.name + ": " + textRange
}
}

View File

@@ -1,690 +0,0 @@
/*
* Copyright 2000-2018 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.formatter
import com.intellij.formatting.ASTBlock
import com.intellij.formatting.DependentSpacingRule
import com.intellij.formatting.Spacing
import com.intellij.formatting.SpacingBuilder
import com.intellij.formatting.SpacingBuilder.RuleBuilder
import com.intellij.lang.ASTNode
import com.intellij.openapi.util.TextRange
import com.intellij.openapi.util.text.StringUtil
import com.intellij.psi.PsiComment
import com.intellij.psi.PsiWhiteSpace
import com.intellij.psi.codeStyle.CodeStyleSettings
import com.intellij.psi.tree.IElementType
import com.intellij.psi.tree.TokenSet
import com.intellij.util.text.TextRangeUtil
import org.jetbrains.kotlin.KtNodeTypes.*
import org.jetbrains.kotlin.idea.formatter.KotlinSpacingBuilder.CustomSpacingBuilder
import org.jetbrains.kotlin.idea.util.requireNode
import org.jetbrains.kotlin.lexer.KtTokens.*
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.children
import org.jetbrains.kotlin.psi.psiUtil.isObjectLiteral
import org.jetbrains.kotlin.psi.psiUtil.siblings
import org.jetbrains.kotlin.psi.psiUtil.textRangeWithoutComments
val MODIFIERS_LIST_ENTRIES = TokenSet.orSet(TokenSet.create(ANNOTATION_ENTRY, ANNOTATION), MODIFIER_KEYWORDS)
val EXTEND_COLON_ELEMENTS =
TokenSet.create(TYPE_CONSTRAINT, CLASS, OBJECT_DECLARATION, TYPE_PARAMETER, ENUM_ENTRY, SECONDARY_CONSTRUCTOR)
val DECLARATIONS = TokenSet.create(PROPERTY, FUN, CLASS, OBJECT_DECLARATION, ENUM_ENTRY, SECONDARY_CONSTRUCTOR, CLASS_INITIALIZER)
fun SpacingBuilder.beforeInside(element: IElementType, tokenSet: TokenSet, spacingFun: RuleBuilder.() -> Unit) {
tokenSet.types.forEach { inType -> beforeInside(element, inType).spacingFun() }
}
fun SpacingBuilder.afterInside(element: IElementType, tokenSet: TokenSet, spacingFun: RuleBuilder.() -> Unit) {
tokenSet.types.forEach { inType -> afterInside(element, inType).spacingFun() }
}
fun RuleBuilder.spacesNoLineBreak(spaces: Int): SpacingBuilder? =
spacing(spaces, spaces, 0, false, 0)
fun createSpacingBuilder(settings: CodeStyleSettings, builderUtil: KotlinSpacingBuilderUtil): KotlinSpacingBuilder {
val kotlinCommonSettings = settings.kotlinCommonSettings
val kotlinCustomSettings = settings.kotlinCustomSettings
return rules(kotlinCommonSettings, builderUtil) {
simple {
before(FILE_ANNOTATION_LIST).lineBreakInCode()
between(IMPORT_DIRECTIVE, IMPORT_DIRECTIVE).lineBreakInCode()
after(IMPORT_LIST).blankLines(1)
}
custom {
fun commentSpacing(minSpaces: Int): Spacing {
if (kotlinCommonSettings.KEEP_FIRST_COLUMN_COMMENT) {
return Spacing.createKeepingFirstColumnSpacing(
minSpaces,
Int.MAX_VALUE,
settings.KEEP_LINE_BREAKS,
kotlinCommonSettings.KEEP_BLANK_LINES_IN_CODE
)
}
return Spacing.createSpacing(
minSpaces,
Int.MAX_VALUE,
0,
settings.KEEP_LINE_BREAKS,
kotlinCommonSettings.KEEP_BLANK_LINES_IN_CODE
)
}
// Several line comments happened to be generated in one line
inPosition(parent = null, left = EOL_COMMENT, right = EOL_COMMENT).customRule { _, _, right ->
val nodeBeforeRight = right.requireNode().treePrev
if (nodeBeforeRight is PsiWhiteSpace && !nodeBeforeRight.textContains('\n')) {
createSpacing(0, minLineFeeds = 1)
} else {
null
}
}
inPosition(right = BLOCK_COMMENT).spacing(commentSpacing(0))
inPosition(right = EOL_COMMENT).spacing(commentSpacing(1))
inPosition(parent = FUNCTION_LITERAL, right = BLOCK).customRule { _, _, right ->
when (right.node?.children()?.firstOrNull()?.elementType) {
BLOCK_COMMENT -> commentSpacing(0)
EOL_COMMENT -> commentSpacing(1)
else -> null
}
}
}
simple {
after(FILE_ANNOTATION_LIST).blankLines(1)
after(PACKAGE_DIRECTIVE).blankLines(1)
}
custom {
inPosition(leftSet = DECLARATIONS, rightSet = DECLARATIONS).customRule(fun(
_: ASTBlock,
_: ASTBlock,
right: ASTBlock
): Spacing? {
val node = right.node ?: return null
val elementStart = node.startOfDeclaration() ?: return null
return if (StringUtil.containsLineBreak(
node.text.subSequence(0, elementStart.startOffset - node.startOffset).trimStart()
)
)
createSpacing(0, minLineFeeds = 1 + kotlinCustomSettings.BLANK_LINES_BEFORE_DECLARATION_WITH_COMMENT_OR_ANNOTATION_ON_SEPARATE_LINE)
else
null
})
inPosition(left = CLASS, right = CLASS).emptyLinesIfLineBreakInLeft(1)
inPosition(left = CLASS, right = OBJECT_DECLARATION).emptyLinesIfLineBreakInLeft(1)
inPosition(left = OBJECT_DECLARATION, right = OBJECT_DECLARATION).emptyLinesIfLineBreakInLeft(1)
inPosition(left = OBJECT_DECLARATION, right = CLASS).emptyLinesIfLineBreakInLeft(1)
inPosition(left = FUN, right = FUN).emptyLinesIfLineBreakInLeft(1)
inPosition(left = PROPERTY, right = FUN).emptyLinesIfLineBreakInLeft(1)
inPosition(left = FUN, right = PROPERTY).emptyLinesIfLineBreakInLeft(1)
inPosition(left = SECONDARY_CONSTRUCTOR, right = SECONDARY_CONSTRUCTOR).emptyLinesIfLineBreakInLeft(1)
inPosition(left = TYPEALIAS, right = TYPEALIAS).emptyLinesIfLineBreakInLeft(1)
// Case left for alternative constructors
inPosition(left = FUN, right = CLASS).emptyLinesIfLineBreakInLeft(1)
inPosition(left = ENUM_ENTRY, right = ENUM_ENTRY).emptyLinesIfLineBreakInLeft(
emptyLines = 0, numberOfLineFeedsOtherwise = 0, numSpacesOtherwise = 1
)
inPosition(parent = CLASS_BODY, left = SEMICOLON).customRule { parent, _, right ->
val klass = parent.requireNode().treeParent.psi as? KtClass ?: return@customRule null
if (klass.isEnum() && right.requireNode().elementType in DECLARATIONS) {
createSpacing(0, minLineFeeds = 2, keepBlankLines = kotlinCommonSettings.KEEP_BLANK_LINES_IN_DECLARATIONS)
} else null
}
inPosition(parent = CLASS_BODY, left = LBRACE).customRule { parent, left, right ->
if (right.requireNode().elementType == RBRACE) {
return@customRule createSpacing(0)
}
val classBody = parent.requireNode().psi as KtClassBody
val parentPsi = classBody.parent as? KtClassOrObject ?: return@customRule null
if (kotlinCommonSettings.BLANK_LINES_AFTER_CLASS_HEADER == 0 || parentPsi.isObjectLiteral()) {
null
} else {
val minLineFeeds = if (right.requireNode().elementType == FUN || right.requireNode().elementType == PROPERTY)
kotlinCommonSettings.BLANK_LINES_AFTER_CLASS_HEADER + 1
else
0
builderUtil.createLineFeedDependentSpacing(
1,
1,
minLineFeeds,
commonCodeStyleSettings.KEEP_LINE_BREAKS,
commonCodeStyleSettings.KEEP_BLANK_LINES_IN_DECLARATIONS,
TextRange(parentPsi.textRange.startOffset, left.requireNode().psi.textRange.startOffset),
DependentSpacingRule(DependentSpacingRule.Trigger.HAS_LINE_FEEDS)
.registerData(
DependentSpacingRule.Anchor.MIN_LINE_FEEDS,
kotlinCommonSettings.BLANK_LINES_AFTER_CLASS_HEADER + 1
)
)
}
}
val parameterWithDocCommentRule = { _: ASTBlock, _: ASTBlock, right: ASTBlock ->
if (right.requireNode().firstChildNode.elementType == DOC_COMMENT) {
createSpacing(0, minLineFeeds = 1, keepLineBreaks = true, keepBlankLines = settings.KEEP_BLANK_LINES_IN_DECLARATIONS)
} else {
null
}
}
inPosition(parent = VALUE_PARAMETER_LIST, right = VALUE_PARAMETER).customRule(parameterWithDocCommentRule)
inPosition(parent = PROPERTY, right = PROPERTY_ACCESSOR).customRule { parent, _, _ ->
val startNode = parent.requireNode().psi.firstChild
.siblings()
.dropWhile { it is PsiComment || it is PsiWhiteSpace }.firstOrNull() ?: parent.requireNode().psi
Spacing.createDependentLFSpacing(
1, 1,
TextRange(startNode.textRange.startOffset, parent.textRange.endOffset),
false, 0
)
}
if (!kotlinCustomSettings.ALLOW_TRAILING_COMMA) {
inPosition(parent = VALUE_ARGUMENT_LIST, left = LPAR).customRule { parent, _, _ ->
if (kotlinCommonSettings.CALL_PARAMETERS_LPAREN_ON_NEXT_LINE && needWrapArgumentList(parent.requireNode().psi)) {
Spacing.createDependentLFSpacing(
0, 0,
excludeLambdasAndObjects(parent),
commonCodeStyleSettings.KEEP_LINE_BREAKS,
commonCodeStyleSettings.KEEP_BLANK_LINES_IN_CODE
)
} else {
createSpacing(0)
}
}
inPosition(parent = VALUE_ARGUMENT_LIST, right = RPAR).customRule { parent, left, _ ->
when {
kotlinCommonSettings.CALL_PARAMETERS_RPAREN_ON_NEXT_LINE ->
Spacing.createDependentLFSpacing(
0, 0,
excludeLambdasAndObjects(parent),
commonCodeStyleSettings.KEEP_LINE_BREAKS,
commonCodeStyleSettings.KEEP_BLANK_LINES_IN_CODE
)
left.requireNode().elementType == COMMA -> // incomplete call being edited
createSpacing(1)
else ->
createSpacing(0)
}
}
}
inPosition(left = CONDITION, right = RPAR).customRule { _, left, _ ->
if (kotlinCustomSettings.IF_RPAREN_ON_NEW_LINE) {
Spacing.createDependentLFSpacing(
0, 0,
excludeLambdasAndObjects(left),
commonCodeStyleSettings.KEEP_LINE_BREAKS,
commonCodeStyleSettings.KEEP_BLANK_LINES_IN_CODE
)
} else {
createSpacing(0)
}
}
inPosition(left = VALUE_PARAMETER, right = COMMA).customRule { _, left, _ ->
if (left.node?.lastChildNode?.elementType === EOL_COMMENT)
createSpacing(0, minLineFeeds = 1)
else
null
}
inPosition(parent = LONG_STRING_TEMPLATE_ENTRY, right = LONG_TEMPLATE_ENTRY_END).lineBreakIfLineBreakInParent(0)
inPosition(parent = LONG_STRING_TEMPLATE_ENTRY, left = LONG_TEMPLATE_ENTRY_START).lineBreakIfLineBreakInParent(0)
}
simple {
// ============ Line breaks ==============
before(DOC_COMMENT).lineBreakInCode()
between(PROPERTY, PROPERTY).lineBreakInCode()
// CLASS - CLASS, CLASS - OBJECT_DECLARATION are exception
between(CLASS, DECLARATIONS).blankLines(1)
// FUN - FUN, FUN - PROPERTY, FUN - CLASS are exceptions
between(FUN, DECLARATIONS).blankLines(1)
// PROPERTY - PROPERTY, PROPERTY - FUN are exceptions
between(PROPERTY, DECLARATIONS).blankLines(1)
// OBJECT_DECLARATION - OBJECT_DECLARATION, CLASS - OBJECT_DECLARATION are exception
between(OBJECT_DECLARATION, DECLARATIONS).blankLines(1)
between(SECONDARY_CONSTRUCTOR, DECLARATIONS).blankLines(1)
between(CLASS_INITIALIZER, DECLARATIONS).blankLines(1)
// TYPEALIAS - TYPEALIAS is an exception
between(TYPEALIAS, DECLARATIONS).blankLines(1)
// ENUM_ENTRY - ENUM_ENTRY is exception
between(ENUM_ENTRY, DECLARATIONS).blankLines(1)
between(ENUM_ENTRY, SEMICOLON).spaces(0)
between(COMMA, SEMICOLON).lineBreakInCodeIf(kotlinCustomSettings.ALLOW_TRAILING_COMMA)
beforeInside(FUN, TokenSet.create(BODY, CLASS_BODY)).lineBreakInCode()
beforeInside(SECONDARY_CONSTRUCTOR, TokenSet.create(BODY, CLASS_BODY)).lineBreakInCode()
beforeInside(CLASS, TokenSet.create(BODY, CLASS_BODY)).lineBreakInCode()
beforeInside(OBJECT_DECLARATION, TokenSet.create(BODY, CLASS_BODY)).lineBreakInCode()
beforeInside(PROPERTY, WHEN).spaces(0)
beforeInside(PROPERTY, LABELED_EXPRESSION).spacesNoLineBreak(1)
before(PROPERTY).lineBreakInCode()
after(DOC_COMMENT).lineBreakInCode()
// =============== Spacing ================
between(EOL_COMMENT, COMMA).lineBreakInCode()
before(COMMA).spacesNoLineBreak(if (kotlinCommonSettings.SPACE_BEFORE_COMMA) 1 else 0)
after(COMMA).spaceIf(kotlinCommonSettings.SPACE_AFTER_COMMA)
val spacesAroundAssignment = if (kotlinCommonSettings.SPACE_AROUND_ASSIGNMENT_OPERATORS) 1 else 0
beforeInside(EQ, PROPERTY).spacesNoLineBreak(spacesAroundAssignment)
beforeInside(EQ, FUN).spacing(spacesAroundAssignment, spacesAroundAssignment, 0, false, 0)
around(
TokenSet.create(EQ, MULTEQ, DIVEQ, PLUSEQ, MINUSEQ, PERCEQ)
).spaceIf(kotlinCommonSettings.SPACE_AROUND_ASSIGNMENT_OPERATORS)
around(TokenSet.create(ANDAND, OROR)).spaceIf(kotlinCommonSettings.SPACE_AROUND_LOGICAL_OPERATORS)
around(TokenSet.create(EQEQ, EXCLEQ, EQEQEQ, EXCLEQEQEQ)).spaceIf(kotlinCommonSettings.SPACE_AROUND_EQUALITY_OPERATORS)
aroundInside(
TokenSet.create(LT, GT, LTEQ, GTEQ), BINARY_EXPRESSION
).spaceIf(kotlinCommonSettings.SPACE_AROUND_RELATIONAL_OPERATORS)
aroundInside(TokenSet.create(PLUS, MINUS), BINARY_EXPRESSION).spaceIf(kotlinCommonSettings.SPACE_AROUND_ADDITIVE_OPERATORS)
aroundInside(
TokenSet.create(MUL, DIV, PERC), BINARY_EXPRESSION
).spaceIf(kotlinCommonSettings.SPACE_AROUND_MULTIPLICATIVE_OPERATORS)
around(
TokenSet.create(PLUSPLUS, MINUSMINUS, EXCLEXCL, MINUS, PLUS, EXCL)
).spaceIf(kotlinCommonSettings.SPACE_AROUND_UNARY_OPERATOR)
before(ELVIS).spaces(1)
after(ELVIS).spacesNoLineBreak(1)
around(RANGE).spaceIf(kotlinCustomSettings.SPACE_AROUND_RANGE)
after(MODIFIER_LIST).spaces(1)
beforeInside(IDENTIFIER, CLASS).spaces(1)
beforeInside(IDENTIFIER, OBJECT_DECLARATION).spaces(1)
after(VAL_KEYWORD).spaces(1)
after(VAR_KEYWORD).spaces(1)
betweenInside(TYPE_PARAMETER_LIST, IDENTIFIER, PROPERTY).spaces(1)
betweenInside(TYPE_REFERENCE, DOT, PROPERTY).spacing(0, 0, 0, false, 0)
betweenInside(DOT, IDENTIFIER, PROPERTY).spacing(0, 0, 0, false, 0)
betweenInside(RETURN_KEYWORD, LABEL_QUALIFIER, RETURN).spaces(0)
afterInside(RETURN_KEYWORD, RETURN).spaces(1)
afterInside(LABEL_QUALIFIER, RETURN).spaces(1)
betweenInside(LABEL_QUALIFIER, EOL_COMMENT, LABELED_EXPRESSION).spacing(
0, Int.MAX_VALUE, 0, true, kotlinCommonSettings.KEEP_BLANK_LINES_IN_CODE
)
betweenInside(LABEL_QUALIFIER, BLOCK_COMMENT, LABELED_EXPRESSION).spacing(
0, Int.MAX_VALUE, 0, true, kotlinCommonSettings.KEEP_BLANK_LINES_IN_CODE
)
betweenInside(LABEL_QUALIFIER, LAMBDA_EXPRESSION, LABELED_EXPRESSION).spaces(0)
afterInside(LABEL_QUALIFIER, LABELED_EXPRESSION).spaces(1)
betweenInside(FUN_KEYWORD, VALUE_PARAMETER_LIST, FUN).spacing(0, 0, 0, false, 0)
after(FUN_KEYWORD).spaces(1)
betweenInside(TYPE_PARAMETER_LIST, TYPE_REFERENCE, FUN).spaces(1)
betweenInside(TYPE_PARAMETER_LIST, IDENTIFIER, FUN).spaces(1)
betweenInside(TYPE_REFERENCE, DOT, FUN).spacing(0, 0, 0, false, 0)
betweenInside(DOT, IDENTIFIER, FUN).spacing(0, 0, 0, false, 0)
afterInside(IDENTIFIER, FUN).spacing(0, 0, 0, false, 0)
aroundInside(DOT, USER_TYPE).spaces(0)
around(AS_KEYWORD).spaces(1)
around(AS_SAFE).spaces(1)
around(IS_KEYWORD).spaces(1)
around(NOT_IS).spaces(1)
around(IN_KEYWORD).spaces(1)
around(NOT_IN).spaces(1)
aroundInside(IDENTIFIER, BINARY_EXPRESSION).spaces(1)
// before LPAR in constructor(): this() {}
after(CONSTRUCTOR_DELEGATION_REFERENCE).spacing(0, 0, 0, false, 0)
// class A() - no space before LPAR of PRIMARY_CONSTRUCTOR
// class A private() - one space before modifier
custom {
inPosition(right = PRIMARY_CONSTRUCTOR).customRule { _, _, r ->
val spacesCount = if (r.requireNode().findLeafElementAt(0)?.elementType != LPAR) 1 else 0
createSpacing(spacesCount, minLineFeeds = 0, keepLineBreaks = true, keepBlankLines = 0)
}
}
afterInside(CONSTRUCTOR_KEYWORD, PRIMARY_CONSTRUCTOR).spaces(0)
betweenInside(IDENTIFIER, TYPE_PARAMETER_LIST, CLASS).spaces(0)
beforeInside(DOT, DOT_QUALIFIED_EXPRESSION).spaces(0)
afterInside(DOT, DOT_QUALIFIED_EXPRESSION).spacesNoLineBreak(0)
beforeInside(SAFE_ACCESS, SAFE_ACCESS_EXPRESSION).spaces(0)
afterInside(SAFE_ACCESS, SAFE_ACCESS_EXPRESSION).spacesNoLineBreak(0)
between(MODIFIERS_LIST_ENTRIES, MODIFIERS_LIST_ENTRIES).spaces(1)
after(LBRACKET).spaces(0)
before(RBRACKET).spaces(0)
afterInside(LPAR, VALUE_PARAMETER_LIST).spaces(0, kotlinCommonSettings.METHOD_PARAMETERS_LPAREN_ON_NEXT_LINE)
beforeInside(RPAR, VALUE_PARAMETER_LIST).spaces(0, kotlinCommonSettings.METHOD_PARAMETERS_RPAREN_ON_NEXT_LINE)
afterInside(LT, TYPE_PARAMETER_LIST).spaces(0)
beforeInside(GT, TYPE_PARAMETER_LIST).spaces(0)
afterInside(LT, TYPE_ARGUMENT_LIST).spaces(0)
beforeInside(GT, TYPE_ARGUMENT_LIST).spaces(0)
before(TYPE_ARGUMENT_LIST).spaces(0)
after(LPAR).spaces(0)
before(RPAR).spaces(0)
betweenInside(FOR_KEYWORD, LPAR, FOR).spaceIf(kotlinCommonSettings.SPACE_BEFORE_FOR_PARENTHESES)
betweenInside(IF_KEYWORD, LPAR, IF).spaceIf(kotlinCommonSettings.SPACE_BEFORE_IF_PARENTHESES)
betweenInside(WHILE_KEYWORD, LPAR, WHILE).spaceIf(kotlinCommonSettings.SPACE_BEFORE_WHILE_PARENTHESES)
betweenInside(WHILE_KEYWORD, LPAR, DO_WHILE).spaceIf(kotlinCommonSettings.SPACE_BEFORE_WHILE_PARENTHESES)
betweenInside(WHEN_KEYWORD, LPAR, WHEN).spaceIf(kotlinCustomSettings.SPACE_BEFORE_WHEN_PARENTHESES)
betweenInside(CATCH_KEYWORD, VALUE_PARAMETER_LIST, CATCH).spaceIf(kotlinCommonSettings.SPACE_BEFORE_CATCH_PARENTHESES)
betweenInside(LPAR, VALUE_PARAMETER, FOR).spaces(0)
betweenInside(LPAR, DESTRUCTURING_DECLARATION, FOR).spaces(0)
betweenInside(LOOP_RANGE, RPAR, FOR).spaces(0)
afterInside(ANNOTATION_ENTRY, ANNOTATED_EXPRESSION).spaces(1)
before(SEMICOLON).spaces(0)
beforeInside(INITIALIZER_LIST, ENUM_ENTRY).spaces(0)
beforeInside(QUEST, NULLABLE_TYPE).spaces(0)
val TYPE_COLON_ELEMENTS = TokenSet.create(PROPERTY, FUN, VALUE_PARAMETER, DESTRUCTURING_DECLARATION_ENTRY, FUNCTION_LITERAL)
beforeInside(COLON, TYPE_COLON_ELEMENTS) { spaceIf(kotlinCustomSettings.SPACE_BEFORE_TYPE_COLON) }
afterInside(COLON, TYPE_COLON_ELEMENTS) { spaceIf(kotlinCustomSettings.SPACE_AFTER_TYPE_COLON) }
afterInside(COLON, EXTEND_COLON_ELEMENTS) { spaceIf(kotlinCustomSettings.SPACE_AFTER_EXTEND_COLON) }
beforeInside(ARROW, FUNCTION_LITERAL).spaceIf(kotlinCustomSettings.SPACE_BEFORE_LAMBDA_ARROW)
aroundInside(ARROW, FUNCTION_TYPE).spaceIf(kotlinCustomSettings.SPACE_AROUND_FUNCTION_TYPE_ARROW)
before(VALUE_ARGUMENT_LIST).spaces(0)
between(VALUE_ARGUMENT_LIST, LAMBDA_ARGUMENT).spaces(1)
betweenInside(REFERENCE_EXPRESSION, LAMBDA_ARGUMENT, CALL_EXPRESSION).spaces(1)
betweenInside(TYPE_ARGUMENT_LIST, LAMBDA_ARGUMENT, CALL_EXPRESSION).spaces(1)
around(COLONCOLON).spaces(0)
around(BY_KEYWORD).spaces(1)
betweenInside(IDENTIFIER, PROPERTY_DELEGATE, PROPERTY).spaces(1)
betweenInside(TYPE_REFERENCE, PROPERTY_DELEGATE, PROPERTY).spaces(1)
before(INDICES).spaces(0)
before(WHERE_KEYWORD).spaces(1)
afterInside(GET_KEYWORD, PROPERTY_ACCESSOR).spaces(0)
afterInside(SET_KEYWORD, PROPERTY_ACCESSOR).spaces(0)
}
custom {
fun CustomSpacingBuilder.ruleForKeywordOnNewLine(
shouldBeOnNewLine: Boolean,
keyword: IElementType,
parent: IElementType,
afterBlockFilter: (wordParent: ASTNode, block: ASTNode) -> Boolean = { _, _ -> true }
) {
if (shouldBeOnNewLine) {
inPosition(parent = parent, right = keyword)
.lineBreakIfLineBreakInParent(numSpacesOtherwise = 1, allowBlankLines = false)
} else {
inPosition(parent = parent, right = keyword).customRule { _, _, right ->
val previousLeaf = builderUtil.getPreviousNonWhitespaceLeaf(right.requireNode())
val leftBlock = if (
previousLeaf != null &&
previousLeaf.elementType == RBRACE &&
previousLeaf.treeParent?.elementType == BLOCK
) {
previousLeaf.treeParent!!
} else null
val removeLineBreaks = leftBlock != null && afterBlockFilter(right.node?.treeParent!!, leftBlock)
createSpacing(1, minLineFeeds = 0, keepLineBreaks = !removeLineBreaks, keepBlankLines = 0)
}
}
}
ruleForKeywordOnNewLine(kotlinCommonSettings.ELSE_ON_NEW_LINE, keyword = ELSE_KEYWORD, parent = IF) { keywordParent, block ->
block.treeParent?.elementType == THEN && block.treeParent?.treeParent == keywordParent
}
ruleForKeywordOnNewLine(
kotlinCommonSettings.WHILE_ON_NEW_LINE,
keyword = WHILE_KEYWORD,
parent = DO_WHILE
) { keywordParent, block ->
block.treeParent?.elementType == BODY && block.treeParent?.treeParent == keywordParent
}
ruleForKeywordOnNewLine(kotlinCommonSettings.CATCH_ON_NEW_LINE, keyword = CATCH, parent = TRY)
ruleForKeywordOnNewLine(kotlinCommonSettings.FINALLY_ON_NEW_LINE, keyword = FINALLY, parent = TRY)
fun spacingForLeftBrace(block: ASTNode?, blockType: IElementType = BLOCK): Spacing? {
if (block != null && block.elementType == blockType) {
val leftBrace = block.findChildByType(LBRACE)
if (leftBrace != null) {
val previousLeaf = builderUtil.getPreviousNonWhitespaceLeaf(leftBrace)
val isAfterEolComment = previousLeaf != null && (previousLeaf.elementType == EOL_COMMENT)
val keepLineBreaks = kotlinCustomSettings.LBRACE_ON_NEXT_LINE || isAfterEolComment
val minimumLF = if (kotlinCustomSettings.LBRACE_ON_NEXT_LINE) 1 else 0
return createSpacing(1, minLineFeeds = minimumLF, keepLineBreaks = keepLineBreaks, keepBlankLines = 0)
}
}
return createSpacing(1)
}
fun leftBraceRule(blockType: IElementType = BLOCK) = { _: ASTBlock, _: ASTBlock, right: ASTBlock ->
spacingForLeftBrace(right.node, blockType)
}
val leftBraceRuleIfBlockIsWrapped = { _: ASTBlock, _: ASTBlock, right: ASTBlock ->
spacingForLeftBrace(right.requireNode().firstChildNode)
}
// Add space after a semicolon if there is another child at the same line
inPosition(left = SEMICOLON).customRule { _, left, _ ->
val nodeAfterLeft = left.requireNode().treeNext
if (nodeAfterLeft is PsiWhiteSpace && !nodeAfterLeft.textContains('\n')) {
createSpacing(1)
} else {
null
}
}
inPosition(parent = IF, right = THEN).customRule(leftBraceRuleIfBlockIsWrapped)
inPosition(parent = IF, right = ELSE).customRule(leftBraceRuleIfBlockIsWrapped)
inPosition(parent = FOR, right = BODY).customRule(leftBraceRuleIfBlockIsWrapped)
inPosition(parent = WHILE, right = BODY).customRule(leftBraceRuleIfBlockIsWrapped)
inPosition(parent = DO_WHILE, right = BODY).customRule(leftBraceRuleIfBlockIsWrapped)
inPosition(parent = TRY, right = BLOCK).customRule(leftBraceRule())
inPosition(parent = CATCH, right = BLOCK).customRule(leftBraceRule())
inPosition(parent = FINALLY, right = BLOCK).customRule(leftBraceRule())
inPosition(parent = FUN, right = BLOCK).customRule(leftBraceRule())
inPosition(parent = SECONDARY_CONSTRUCTOR, right = BLOCK).customRule(leftBraceRule())
inPosition(parent = CLASS_INITIALIZER, right = BLOCK).customRule(leftBraceRule())
inPosition(parent = PROPERTY_ACCESSOR, right = BLOCK).customRule(leftBraceRule())
inPosition(right = CLASS_BODY).customRule(leftBraceRule(blockType = CLASS_BODY))
inPosition(left = WHEN_ENTRY, right = WHEN_ENTRY).customRule { _, left, right ->
val leftEntry = left.requireNode().psi as KtWhenEntry
val rightEntry = right.requireNode().psi as KtWhenEntry
val blankLines = if (leftEntry.expression is KtBlockExpression || rightEntry.expression is KtBlockExpression)
settings.kotlinCustomSettings.BLANK_LINES_AROUND_BLOCK_WHEN_BRANCHES
else
0
createSpacing(0, minLineFeeds = blankLines + 1)
}
inPosition(parent = WHEN_ENTRY, right = BLOCK).customRule(leftBraceRule())
inPosition(parent = WHEN, right = LBRACE).customRule { parent, _, _ ->
spacingForLeftBrace(block = parent.requireNode(), blockType = WHEN)
}
inPosition(left = LBRACE, right = WHEN_ENTRY).customRule { _, _, _ ->
createSpacing(0, minLineFeeds = 1)
}
val spacesInSimpleFunction = if (kotlinCustomSettings.INSERT_WHITESPACES_IN_SIMPLE_ONE_LINE_METHOD) 1 else 0
inPosition(
parent = FUNCTION_LITERAL,
left = LBRACE,
right = BLOCK
).lineBreakIfLineBreakInParent(numSpacesOtherwise = spacesInSimpleFunction)
inPosition(
parent = FUNCTION_LITERAL,
left = ARROW,
right = BLOCK
).lineBreakIfLineBreakInParent(numSpacesOtherwise = 1)
inPosition(
parent = FUNCTION_LITERAL,
left = LBRACE,
right = RBRACE
).spacing(createSpacing(minSpaces = 0, maxSpaces = 1))
inPosition(
parent = FUNCTION_LITERAL,
right = RBRACE
).lineBreakIfLineBreakInParent(numSpacesOtherwise = spacesInSimpleFunction)
inPosition(
parent = FUNCTION_LITERAL,
left = LBRACE
).customRule { _, _, right ->
val rightNode = right.requireNode()
val rightType = rightNode.elementType
if (rightType == VALUE_PARAMETER_LIST) {
createSpacing(spacesInSimpleFunction, keepLineBreaks = false)
} else {
createSpacing(spacesInSimpleFunction)
}
}
inPosition(parent = CLASS_BODY, right = RBRACE).customRule { parent, _, _ ->
kotlinCommonSettings.createSpaceBeforeRBrace(1, parent.textRange)
}
inPosition(parent = BLOCK, right = RBRACE).customRule { block, left, _ ->
val psiElement = block.requireNode().treeParent.psi
val empty = left.requireNode().elementType == LBRACE
when (psiElement) {
is KtFunction -> {
if (psiElement.name != null && !empty) return@customRule null
}
is KtPropertyAccessor ->
if (!empty) return@customRule null
else ->
return@customRule null
}
val spaces = if (empty) 0 else spacesInSimpleFunction
kotlinCommonSettings.createSpaceBeforeRBrace(spaces, psiElement.textRangeWithoutComments)
}
inPosition(parent = BLOCK, left = LBRACE).customRule { parent, _, _ ->
val psiElement = parent.requireNode().treeParent.psi
val funNode = psiElement as? KtFunction ?: return@customRule null
if (funNode.name != null) return@customRule null
// Empty block is covered in above rule
Spacing.createDependentLFSpacing(
spacesInSimpleFunction, spacesInSimpleFunction, funNode.textRangeWithoutComments,
kotlinCommonSettings.KEEP_LINE_BREAKS,
kotlinCommonSettings.KEEP_BLANK_LINES_IN_CODE
)
}
inPosition(parentSet = EXTEND_COLON_ELEMENTS, left = PRIMARY_CONSTRUCTOR, right = COLON).customRule { _, left, _ ->
val primaryConstructor = left.requireNode().psi as KtPrimaryConstructor
val rightParenthesis = primaryConstructor.valueParameterList?.rightParenthesis
val prevSibling = rightParenthesis?.prevSibling
val spaces = if (kotlinCustomSettings.SPACE_BEFORE_EXTEND_COLON) 1 else 0
// TODO This should use DependentSpacingRule, but it doesn't set keepLineBreaks to false if max LFs is 0
if ((prevSibling as? PsiWhiteSpace)?.textContains('\n') == true || kotlinCommonSettings
.METHOD_PARAMETERS_RPAREN_ON_NEXT_LINE
) {
createSpacing(spaces, keepLineBreaks = false)
} else {
createSpacing(spaces)
}
}
inPosition(
parent = CLASS_BODY,
left = LBRACE,
right = ENUM_ENTRY
).lineBreakIfLineBreakInParent(numSpacesOtherwise = 1)
}
simple {
afterInside(LBRACE, BLOCK).lineBreakInCode()
beforeInside(RBRACE, BLOCK).spacing(
1, 0, 1,
kotlinCommonSettings.KEEP_LINE_BREAKS,
kotlinCommonSettings.KEEP_BLANK_LINES_BEFORE_RBRACE
)
between(LBRACE, ENUM_ENTRY).spacing(1, 0, 0, true, kotlinCommonSettings.KEEP_BLANK_LINES_IN_CODE)
beforeInside(RBRACE, WHEN).lineBreakInCode()
between(RPAR, BODY).spaces(1)
// if when entry has block, spacing after arrow should be set by lbrace rule
aroundInside(ARROW, WHEN_ENTRY).spaceIf(kotlinCustomSettings.SPACE_AROUND_WHEN_ARROW)
beforeInside(COLON, EXTEND_COLON_ELEMENTS) { spaceIf(kotlinCustomSettings.SPACE_BEFORE_EXTEND_COLON) }
after(EOL_COMMENT).lineBreakInCode()
}
}
}
private fun excludeLambdasAndObjects(parent: ASTBlock): List<TextRange> {
val rangesToExclude = mutableListOf<TextRange>()
parent.requireNode().psi.accept(object : KtTreeVisitorVoid() {
override fun visitLambdaExpression(lambdaExpression: KtLambdaExpression) {
super.visitLambdaExpression(lambdaExpression)
rangesToExclude.add(lambdaExpression.textRange)
}
override fun visitObjectLiteralExpression(expression: KtObjectLiteralExpression) {
super.visitObjectLiteralExpression(expression)
rangesToExclude.add(expression.textRange)
}
override fun visitNamedFunction(function: KtNamedFunction) {
super.visitNamedFunction(function)
if (function.name == null) {
rangesToExclude.add(function.textRange)
}
}
})
return TextRangeUtil.excludeRanges(parent.textRange, rangesToExclude).toList()
}

View File

@@ -1,30 +0,0 @@
/*
* 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.formatter.trailingComma
import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.idea.core.formatter.KotlinCodeStyleSettings
import org.jetbrains.kotlin.psi.KtElement
class TrailingCommaContext private constructor(val element: PsiElement, val state: TrailingCommaState) {
/**
* Return [KtElement] if [state] != [TrailingCommaState.NOT_APPLICABLE]
*/
val ktElement: KtElement get() = element as? KtElement ?: error("State is NOT_APPLICABLE")
companion object {
fun create(element: PsiElement): TrailingCommaContext = TrailingCommaContext(
element,
TrailingCommaState.stateForElement(element),
)
}
}
fun TrailingCommaContext.commaExistsOrMayExist(settings: KotlinCodeStyleSettings): Boolean = when (state) {
TrailingCommaState.EXISTS -> true
TrailingCommaState.MISSING -> settings.addTrailingCommaIsAllowedFor(element)
else -> false
}

View File

@@ -1,86 +0,0 @@
/*
* 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.formatter.trailingComma
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiErrorElement
import com.intellij.psi.codeStyle.CodeStyleSettings
import com.intellij.psi.tree.TokenSet
import com.intellij.psi.util.PsiUtil
import org.jetbrains.kotlin.idea.formatter.kotlinCustomSettings
import org.jetbrains.kotlin.idea.util.isComma
import org.jetbrains.kotlin.idea.util.isLineBreak
import org.jetbrains.kotlin.idea.util.leafIgnoringWhitespace
import org.jetbrains.kotlin.idea.util.leafIgnoringWhitespaceAndComments
import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.getPrevSiblingIgnoringWhitespaceAndComments
import org.jetbrains.kotlin.psi.psiUtil.prevLeaf
import org.jetbrains.kotlin.psi.psiUtil.siblings
import org.jetbrains.kotlin.utils.addToStdlib.cast
object TrailingCommaHelper {
fun findInvalidCommas(commaOwner: KtElement): List<PsiElement> = commaOwner.firstChild
?.siblings(withItself = false)
?.filter { it.isComma }
?.filter {
it.prevLeaf(true)?.isLineBreak() == true || it.leafIgnoringWhitespace(false) != it.leafIgnoringWhitespaceAndComments(false)
}?.toList().orEmpty()
fun trailingCommaExistsOrCanExist(psiElement: PsiElement, settings: CodeStyleSettings): Boolean =
TrailingCommaContext.create(psiElement).commaExistsOrMayExist(settings.kotlinCustomSettings)
fun trailingCommaExists(commaOwner: KtElement): Boolean = when (commaOwner) {
is KtFunctionLiteral -> commaOwner.valueParameterList?.trailingComma != null
is KtWhenEntry -> commaOwner.trailingComma != null
is KtDestructuringDeclaration -> commaOwner.trailingComma != null
else -> trailingCommaOrLastElement(commaOwner)?.isComma == true
}
fun trailingCommaOrLastElement(commaOwner: KtElement): PsiElement? {
val lastChild = commaOwner.lastSignificantChild ?: return null
val withSelf = when (PsiUtil.getElementType(lastChild)) {
KtTokens.COMMA -> return lastChild
in RIGHT_BARRIERS -> false
else -> true
}
return lastChild.getPrevSiblingIgnoringWhitespaceAndComments(withSelf)?.takeIf {
PsiUtil.getElementType(it) !in LEFT_BARRIERS
}?.takeIfIsNotError()
}
fun elementBeforeFirstElement(commaOwner: KtElement): PsiElement? = when (commaOwner) {
is KtParameterList -> {
val parent = commaOwner.parent
if (parent is KtFunctionLiteral) parent.lBrace else commaOwner.leftParenthesis
}
is KtWhenEntry -> commaOwner.parent.cast<KtWhenExpression>().openBrace
is KtDestructuringDeclaration -> commaOwner.lPar
else -> commaOwner.firstChild?.takeIfIsNotError()
}
fun elementAfterLastElement(commaOwner: KtElement): PsiElement? = when (commaOwner) {
is KtParameterList -> {
val parent = commaOwner.parent
if (parent is KtFunctionLiteral) parent.arrow else commaOwner.rightParenthesis
}
is KtWhenEntry -> commaOwner.arrow
is KtDestructuringDeclaration -> commaOwner.rPar
else -> commaOwner.lastChild?.takeIfIsNotError()
}
private fun PsiElement.takeIfIsNotError(): PsiElement? = takeIf { it !is PsiErrorElement }
private val RIGHT_BARRIERS = TokenSet.create(KtTokens.RBRACKET, KtTokens.RPAR, KtTokens.RBRACE, KtTokens.GT, KtTokens.ARROW)
private val LEFT_BARRIERS = TokenSet.create(KtTokens.LBRACKET, KtTokens.LPAR, KtTokens.LBRACE, KtTokens.LT)
private val PsiElement.lastSignificantChild: PsiElement?
get() = when (this) {
is KtWhenEntry -> arrow
is KtDestructuringDeclaration -> rPar
else -> lastChild
}
}

View File

@@ -1,90 +0,0 @@
/*
* 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.formatter.trailingComma
import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.idea.util.containsLineBreakInChild
import org.jetbrains.kotlin.idea.util.isMultiline
import org.jetbrains.kotlin.psi.KtDestructuringDeclaration
import org.jetbrains.kotlin.psi.KtElement
import org.jetbrains.kotlin.psi.KtFunctionLiteral
import org.jetbrains.kotlin.psi.KtWhenEntry
import org.jetbrains.kotlin.psi.psiUtil.endOffset
import org.jetbrains.kotlin.psi.psiUtil.startOffset
enum class TrailingCommaState {
/**
* The trailing comma is needed and exists
*/
EXISTS,
/**
* The trailing comma is needed and doesn't exists
*/
MISSING,
/**
* The trailing comma isn't needed and doesn't exists
*/
NOT_EXISTS,
/**
* The trailing comma isn't needed, but exists
*/
REDUNDANT,
/**
* The trailing comma isn't applicable for this element
*/
NOT_APPLICABLE,
;
companion object {
fun stateForElement(element: PsiElement): TrailingCommaState = when {
element !is KtElement || !element.canAddTrailingComma() -> NOT_APPLICABLE
isMultiline(element) ->
if (TrailingCommaHelper.trailingCommaExists(element))
EXISTS
else
MISSING
else ->
if (TrailingCommaHelper.trailingCommaExists(element))
REDUNDANT
else
NOT_EXISTS
}
}
}
private fun isMultiline(ktElement: KtElement): Boolean = when {
ktElement.parent is KtFunctionLiteral -> isMultiline(ktElement.parent as KtElement)
ktElement is KtFunctionLiteral -> ktElement.isMultiline(
startOffsetGetter = { valueParameterList?.startOffset },
endOffsetGetter = { arrow?.endOffset },
)
ktElement is KtWhenEntry -> ktElement.isMultiline(
startOffsetGetter = { startOffset },
endOffsetGetter = { arrow?.endOffset },
)
ktElement is KtDestructuringDeclaration -> ktElement.isMultiline(
startOffsetGetter = { lPar?.startOffset },
endOffsetGetter = { rPar?.endOffset },
)
else -> ktElement.isMultiline()
}
private fun <T : PsiElement> T.isMultiline(
startOffsetGetter: T.() -> Int?,
endOffsetGetter: T.() -> Int?,
): Boolean {
val startOffset = startOffsetGetter() ?: startOffset
val endOffset = endOffsetGetter() ?: endOffset
return containsLineBreakInChild(startOffset, endOffset)
}

View File

@@ -1,66 +0,0 @@
/*
* 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.formatter.trailingComma
import com.intellij.lang.ASTNode
import com.intellij.openapi.util.registry.Registry
import com.intellij.psi.PsiElement
import com.intellij.psi.tree.IElementType
import com.intellij.psi.tree.TokenSet
import com.intellij.psi.util.PsiUtilCore
import org.jetbrains.kotlin.KtNodeTypes
import org.jetbrains.kotlin.idea.core.formatter.KotlinCodeStyleSettings
import org.jetbrains.kotlin.psi.KtFunctionLiteral
import org.jetbrains.kotlin.psi.KtWhenEntry
import org.jetbrains.kotlin.psi.KtWhenExpression
import org.jetbrains.kotlin.utils.addToStdlib.cast
fun trailingCommaIsAllowedOnCallSite(): Boolean = Registry.`is`("kotlin.formatter.allowTrailingCommaOnCallSite")
private val TYPES_WITH_TRAILING_COMMA_ON_DECLARATION_SITE = TokenSet.create(
KtNodeTypes.TYPE_PARAMETER_LIST,
KtNodeTypes.DESTRUCTURING_DECLARATION,
KtNodeTypes.WHEN_ENTRY,
KtNodeTypes.FUNCTION_LITERAL,
KtNodeTypes.VALUE_PARAMETER_LIST,
)
private val TYPES_WITH_TRAILING_COMMA_ON_CALL_SITE = TokenSet.create(
KtNodeTypes.COLLECTION_LITERAL_EXPRESSION,
KtNodeTypes.TYPE_ARGUMENT_LIST,
KtNodeTypes.INDICES,
KtNodeTypes.VALUE_ARGUMENT_LIST,
)
private val TYPES_WITH_TRAILING_COMMA = TokenSet.orSet(
TYPES_WITH_TRAILING_COMMA_ON_DECLARATION_SITE,
TYPES_WITH_TRAILING_COMMA_ON_CALL_SITE,
)
fun PsiElement.canAddTrailingCommaWithRegistryCheck(): Boolean {
val type = PsiUtilCore.getElementType(this) ?: return false
return type in TYPES_WITH_TRAILING_COMMA_ON_DECLARATION_SITE ||
trailingCommaIsAllowedOnCallSite() && type in TYPES_WITH_TRAILING_COMMA_ON_CALL_SITE
}
fun KotlinCodeStyleSettings.addTrailingCommaIsAllowedFor(node: ASTNode): Boolean =
addTrailingCommaIsAllowedFor(PsiUtilCore.getElementType(node))
fun KotlinCodeStyleSettings.addTrailingCommaIsAllowedFor(element: PsiElement): Boolean =
addTrailingCommaIsAllowedFor(PsiUtilCore.getElementType(element))
private fun KotlinCodeStyleSettings.addTrailingCommaIsAllowedFor(type: IElementType?): Boolean = when (type) {
null -> false
in TYPES_WITH_TRAILING_COMMA_ON_DECLARATION_SITE -> ALLOW_TRAILING_COMMA
in TYPES_WITH_TRAILING_COMMA_ON_CALL_SITE -> ALLOW_TRAILING_COMMA_ON_CALL_SITE || trailingCommaIsAllowedOnCallSite()
else -> false
}
fun PsiElement.canAddTrailingComma(): Boolean = when {
this is KtWhenEntry && (isElse || parent.cast<KtWhenExpression>().leftParenthesis == null) -> false
this is KtFunctionLiteral && arrow == null -> false
else -> PsiUtilCore.getElementType(this) in TYPES_WITH_TRAILING_COMMA
}

View File

@@ -1,68 +0,0 @@
/*
* 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.util
import com.intellij.formatting.ASTBlock
import com.intellij.openapi.util.TextRange
import com.intellij.openapi.util.text.StringUtil
import com.intellij.psi.PsiComment
import com.intellij.psi.PsiDocumentManager
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiWhiteSpace
import com.intellij.psi.util.PsiUtil
import org.jetbrains.kotlin.idea.core.formatter.KotlinCodeStyleSettings
import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.psi.psiUtil.*
/*
* ASTBlock.node is nullable, this extension was introduced to minimize changes
*/
fun ASTBlock.requireNode() = node ?: error("ASTBlock.getNode() returned null")
/**
* Can be removed with all usages after moving master to 1.3 with new default code style settings.
*/
val isDefaultOfficialCodeStyle by lazy { !KotlinCodeStyleSettings.defaultSettings().CONTINUATION_INDENT_FOR_CHAINED_CALLS }
// Copied from idea-core
fun PsiElement.getLineCount(): Int {
val spaceRange = textRange ?: TextRange.EMPTY_RANGE
return getLineCountByDocument(spaceRange.startOffset, spaceRange.endOffset)
?: StringUtil.getLineBreakCount(text ?: error("Cannot count number of lines")) + 1
}
fun PsiElement.getLineCountByDocument(startOffset: Int, endOffset: Int): Int? {
val doc = containingFile?.let { PsiDocumentManager.getInstance(project).getDocument(it) } ?: return null
if (endOffset > doc.textLength || startOffset >= endOffset) return null
val startLine = doc.getLineNumber(startOffset)
val endLine = doc.getLineNumber(endOffset)
return endLine - startLine + 1
}
fun PsiElement.isMultiline() = getLineCount() > 1
fun PsiElement?.isLineBreak() = this is PsiWhiteSpace && StringUtil.containsLineBreak(text)
fun PsiElement.leafIgnoringWhitespace(forward: Boolean = true, skipEmptyElements: Boolean = true) =
leaf(forward) { (!skipEmptyElements || it.textLength != 0) && it !is PsiWhiteSpace }
fun PsiElement.leafIgnoringWhitespaceAndComments(forward: Boolean = true, skipEmptyElements: Boolean = true) =
leaf(forward) { (!skipEmptyElements || it.textLength != 0) && it !is PsiWhiteSpace && it !is PsiComment }
fun PsiElement.leaf(forward: Boolean = true, filter: (PsiElement) -> Boolean): PsiElement? =
if (forward) nextLeaf(filter)
else prevLeaf(filter)
val PsiElement.isComma: Boolean get() = PsiUtil.getElementType(this) == KtTokens.COMMA
fun PsiElement.containsLineBreakInChild(globalStartOffset: Int, globalEndOffset: Int): Boolean =
getLineCountByDocument(globalStartOffset, globalEndOffset)?.let { it > 1 }
?: firstChild.siblings(forward = true, withItself = true)
.dropWhile { it.startOffset < globalStartOffset }
.takeWhile { it.endOffset <= globalEndOffset }
.any { it.textContains('\n') || it.textContains('\r') }

View File

@@ -1,61 +0,0 @@
/*
* Copyright 2010-2019 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.util;
import com.intellij.openapi.util.Comparing;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.containers.Predicate;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.Set;
public class ReflectionUtil {
@Retention(RetentionPolicy.RUNTIME)
public @interface SkipInEquals {
}
public static boolean comparePublicNonFinalFieldsWithSkip(@NotNull Object first, @NotNull Object second) {
return comparePublicNonFinalFields(first, second, field -> field.getAnnotation(SkipInEquals.class) == null);
}
private static boolean comparePublicNonFinalFields(
@NotNull Object first,
@NotNull Object second,
@Nullable Predicate<Field> acceptPredicate
) {
Set<Field> firstFields = ContainerUtil.newHashSet(first.getClass().getFields());
for (Field field : second.getClass().getFields()) {
if (firstFields.contains(field)) {
if (isPublic(field) && !isFinal(field) && (acceptPredicate == null || acceptPredicate.apply(field))) {
try {
if (!Comparing.equal(field.get(first), field.get(second))) {
return false;
}
}
catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
}
}
}
return true;
}
private static boolean isPublic(Field field) {
return (field.getModifiers() & Modifier.PUBLIC) != 0;
}
private static boolean isFinal(Field field) {
return (field.getModifiers() & Modifier.FINAL) != 0;
}
}

View File

@@ -1,24 +0,0 @@
plugins {
kotlin("jvm")
id("jps-compatible")
}
dependencies {
compileOnly(project(":compiler:util"))
compileOnly(project(":compiler:frontend"))
compileOnly(project(":compiler:frontend.java"))
compileOnly(project(":idea:idea-frontend-independent"))
compileOnly(project(":js:js.frontend"))
compileOnly(project(":js:js.serializer"))
compileOnly(intellijCoreDep()) { includeJars("intellij-core", "guava", rootProject = rootProject) }
}
sourceSets {
"main" { projectDefault() }
"test" {}
}
sourcesJar()
javadocJar()

View File

@@ -1,62 +0,0 @@
/*
* Copyright 2010-2019 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.caches.project
import com.intellij.openapi.module.Module
import com.intellij.openapi.project.Project
import com.intellij.openapi.roots.ProjectRootModificationTracker
import com.intellij.openapi.util.UserDataHolder
import com.intellij.psi.util.CachedValueProvider
import com.intellij.psi.util.CachedValuesManager
fun <T> Module.cacheByClass(classForKey: Class<*>, vararg dependencies: Any, provider: () -> T): T {
return CachedValuesManager.getManager(project).cache(this, dependencies, classForKey, provider)
}
fun <T> Module.cacheByClassInvalidatingOnRootModifications(classForKey: Class<*>, provider: () -> T): T {
return cacheByClass(classForKey, ProjectRootModificationTracker.getInstance(project), provider = provider)
}
/**
* Note that it uses lambda's class for caching (essentially, anonymous class), which means that all invocations will be cached
* by the one and the same key.
* It is encouraged to use explicit class, just for the sake of readability.
*/
fun <T> Module.cacheInvalidatingOnRootModifications(provider: () -> T): T {
return cacheByClassInvalidatingOnRootModifications(provider::class.java, provider)
}
fun <T> Project.cacheByClass(classForKey: Class<*>, vararg dependencies: Any, provider: () -> T): T {
return CachedValuesManager.getManager(this).cache(this, dependencies, classForKey, provider)
}
fun <T> Project.cacheByClassInvalidatingOnRootModifications(classForKey: Class<*>, provider: () -> T): T {
return cacheByClass(classForKey, ProjectRootModificationTracker.getInstance(this), provider = provider)
}
/**
* Note that it uses lambda's class for caching (essentially, anonymous class), which means that all invocations will be cached
* by the one and the same key.
* It is encouraged to use explicit class, just for the sake of readability.
*/
fun <T> Project.cacheInvalidatingOnRootModifications(provider: () -> T): T {
return cacheByClassInvalidatingOnRootModifications(provider::class.java, provider)
}
private fun <T> CachedValuesManager.cache(
holder: UserDataHolder,
dependencies: Array<out Any>,
classForKey: Class<*>,
provider: () -> T
): T {
return getCachedValue(
holder,
getKeyForClass(classForKey),
{ CachedValueProvider.Result.create(provider(), dependencies) },
false
)
}

View File

@@ -1,41 +0,0 @@
/*
* Copyright 2010-2015 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.caches.resolve
import com.intellij.openapi.components.ServiceManager
import com.intellij.openapi.project.Project
import com.intellij.psi.PsiFile
import org.jetbrains.kotlin.analyzer.ModuleInfo
import org.jetbrains.kotlin.idea.resolve.ResolutionFacade
import org.jetbrains.kotlin.platform.TargetPlatform
import org.jetbrains.kotlin.psi.KtElement
import org.jetbrains.kotlin.resolve.diagnostics.KotlinSuppressCache
interface KotlinCacheService {
companion object {
fun getInstance(project: Project): KotlinCacheService = ServiceManager.getService(project, KotlinCacheService::class.java)!!
}
fun getResolutionFacade(elements: List<KtElement>): ResolutionFacade
fun getResolutionFacade(elements: List<KtElement>, platform: TargetPlatform): ResolutionFacade
fun getResolutionFacadeByFile(file: PsiFile, platform: TargetPlatform): ResolutionFacade?
fun getSuppressionCache(): KotlinSuppressCache
fun getResolutionFacadeByModuleInfo(moduleInfo: ModuleInfo, platform: TargetPlatform): ResolutionFacade?
fun getResolutionFacadeByModuleInfo(moduleInfo: ModuleInfo, settings: PlatformAnalysisSettings): ResolutionFacade?
}

View File

@@ -1,22 +0,0 @@
/*
* 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.caches.resolve
/**
* Regulates which sources should be analyzed together.
*
* There are exactly two descendants, which are in strong one-to-one correspondence with [ResolutionModeComponent.Mode] (meaning
* that after checking value of ResolutionMode, it's safe to downcast settings instance to the respective type):
* - [PlatformAnalysisSettingsImpl] should be used iff we're working under [Mode.SEPARATE], and will create separate
* facade for each platforms, sdk, builtIns settings and other stuff.
* This is the old and stable mode, which should be used by default.
*
* - [CompositeAnalysisSettings] should be used iff we're working under [Mode.COMPOSITE], and will analyze all sources
* together, in one facade.
* This mode is new and experimental, and works only together with TypeRefinement facilities in the compiler's frontend.
* This mode is currently enabled only for HMPP projects
*/
interface PlatformAnalysisSettings

View File

@@ -1,110 +0,0 @@
/*
* Copyright 2010-2019 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.analysis
import org.jetbrains.kotlin.idea.FrontendInternals
import org.jetbrains.kotlin.idea.caches.resolve.getResolutionFacade
import org.jetbrains.kotlin.idea.resolve.ResolutionFacade
import org.jetbrains.kotlin.idea.resolve.frontendService
import org.jetbrains.kotlin.idea.util.getResolutionScope
import org.jetbrains.kotlin.psi.KtExpression
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.BindingTrace
import org.jetbrains.kotlin.resolve.BindingTraceContext
import org.jetbrains.kotlin.resolve.DelegatingBindingTrace
import org.jetbrains.kotlin.resolve.bindingContextUtil.getDataFlowInfoBefore
import org.jetbrains.kotlin.resolve.bindingContextUtil.isUsedAsStatement
import org.jetbrains.kotlin.resolve.calls.components.InferenceSession
import org.jetbrains.kotlin.resolve.calls.context.ContextDependency
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowInfo
import org.jetbrains.kotlin.resolve.scopes.LexicalScope
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.TypeUtils
import org.jetbrains.kotlin.types.expressions.ExpressionTypingServices
import org.jetbrains.kotlin.types.expressions.KotlinTypeInfo
import org.jetbrains.kotlin.types.expressions.PreliminaryDeclarationVisitor
@JvmOverloads
@OptIn(FrontendInternals::class)
fun KtExpression.computeTypeInfoInContext(
scope: LexicalScope,
contextExpression: KtExpression = this,
trace: BindingTrace = BindingTraceContext(),
dataFlowInfo: DataFlowInfo = DataFlowInfo.EMPTY,
expectedType: KotlinType = TypeUtils.NO_EXPECTED_TYPE,
isStatement: Boolean = false,
contextDependency: ContextDependency = ContextDependency.INDEPENDENT,
expressionTypingServices: ExpressionTypingServices = contextExpression.getResolutionFacade().frontendService<ExpressionTypingServices>()
): KotlinTypeInfo {
PreliminaryDeclarationVisitor.createForExpression(this, trace, expressionTypingServices.languageVersionSettings)
return expressionTypingServices.getTypeInfo(
scope, this, expectedType, dataFlowInfo, InferenceSession.default, trace, isStatement, contextExpression, contextDependency
)
}
@JvmOverloads
@OptIn(FrontendInternals::class)
fun KtExpression.analyzeInContext(
scope: LexicalScope,
contextExpression: KtExpression = this,
trace: BindingTrace = BindingTraceContext(),
dataFlowInfo: DataFlowInfo = DataFlowInfo.EMPTY,
expectedType: KotlinType = TypeUtils.NO_EXPECTED_TYPE,
isStatement: Boolean = false,
contextDependency: ContextDependency = ContextDependency.INDEPENDENT,
expressionTypingServices: ExpressionTypingServices = contextExpression.getResolutionFacade().frontendService<ExpressionTypingServices>()
): BindingContext {
computeTypeInfoInContext(
scope,
contextExpression,
trace,
dataFlowInfo,
expectedType,
isStatement,
contextDependency,
expressionTypingServices
)
return trace.bindingContext
}
@JvmOverloads
fun KtExpression.computeTypeInContext(
scope: LexicalScope,
contextExpression: KtExpression = this,
trace: BindingTrace = BindingTraceContext(),
dataFlowInfo: DataFlowInfo = DataFlowInfo.EMPTY,
expectedType: KotlinType = TypeUtils.NO_EXPECTED_TYPE
): KotlinType? = computeTypeInfoInContext(scope, contextExpression, trace, dataFlowInfo, expectedType).type
@JvmOverloads
fun KtExpression.analyzeAsReplacement(
expressionToBeReplaced: KtExpression,
bindingContext: BindingContext,
scope: LexicalScope,
trace: BindingTrace = DelegatingBindingTrace(bindingContext, "Temporary trace for analyzeAsReplacement()"),
contextDependency: ContextDependency = ContextDependency.INDEPENDENT
): BindingContext = analyzeInContext(
scope,
expressionToBeReplaced,
dataFlowInfo = bindingContext.getDataFlowInfoBefore(expressionToBeReplaced),
expectedType = bindingContext[BindingContext.EXPECTED_EXPRESSION_TYPE, expressionToBeReplaced] ?: TypeUtils.NO_EXPECTED_TYPE,
isStatement = expressionToBeReplaced.isUsedAsStatement(bindingContext),
trace = trace,
contextDependency = contextDependency
)
@JvmOverloads
fun KtExpression.analyzeAsReplacement(
expressionToBeReplaced: KtExpression,
bindingContext: BindingContext,
resolutionFacade: ResolutionFacade = expressionToBeReplaced.getResolutionFacade(),
trace: BindingTrace = DelegatingBindingTrace(bindingContext, "Temporary trace for analyzeAsReplacement()"),
contextDependency: ContextDependency = ContextDependency.INDEPENDENT
): BindingContext {
val scope = expressionToBeReplaced.getResolutionScope(bindingContext, resolutionFacade)
return analyzeAsReplacement(expressionToBeReplaced, bindingContext, scope, trace, contextDependency)
}

View File

@@ -1,119 +0,0 @@
/*
* Copyright 2010-2019 JetBrains s.r.o. 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.caches.resolve
import org.jetbrains.kotlin.analyzer.AnalysisResult
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor
import org.jetbrains.kotlin.idea.resolve.ResolutionFacade
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
import org.jetbrains.kotlin.resolve.lazy.NoDescriptorForDeclarationException
/**
* This function throws exception when resolveToDescriptorIfAny returns null, otherwise works equivalently.
*/
fun KtDeclaration.unsafeResolveToDescriptor(
resolutionFacade: ResolutionFacade,
bodyResolveMode: BodyResolveMode = BodyResolveMode.FULL
): DeclarationDescriptor =
resolveToDescriptorIfAny(resolutionFacade, bodyResolveMode) ?: throw NoDescriptorForDeclarationException(this)
/**
* This function first uses declaration resolvers to resolve this declaration and/or additional declarations (e.g. its parent),
* and then takes the relevant descriptor from binding context.
* The exact set of declarations to resolve depends on bodyResolveMode
*/
fun KtDeclaration.resolveToDescriptorIfAny(
resolutionFacade: ResolutionFacade,
bodyResolveMode: BodyResolveMode = BodyResolveMode.PARTIAL
): DeclarationDescriptor? {
//TODO: BodyResolveMode.PARTIAL is not quite safe!
val context = analyze(resolutionFacade, bodyResolveMode)
return if (this is KtParameter && hasValOrVar()) {
context.get(BindingContext.PRIMARY_CONSTRUCTOR_PARAMETER, this)
// It is incorrect to have `val/var` parameters outside the primary constructor (e.g., `fun foo(val x: Int)`)
// but we still want to try to resolve in such cases.
?: context.get(BindingContext.DECLARATION_TO_DESCRIPTOR, this)
} else {
context.get(BindingContext.DECLARATION_TO_DESCRIPTOR, this)
}
}
fun KtAnnotationEntry.resolveToDescriptorIfAny(
resolutionFacade: ResolutionFacade,
bodyResolveMode: BodyResolveMode = BodyResolveMode.PARTIAL
): AnnotationDescriptor? {
//TODO: BodyResolveMode.PARTIAL is not quite safe!
val context = analyze(resolutionFacade, bodyResolveMode)
return context.get(BindingContext.ANNOTATION, this)
}
fun KtClassOrObject.resolveToDescriptorIfAny(
resolutionFacade: ResolutionFacade,
bodyResolveMode: BodyResolveMode = BodyResolveMode.PARTIAL
): ClassDescriptor? {
return (this as KtDeclaration).resolveToDescriptorIfAny(resolutionFacade, bodyResolveMode) as? ClassDescriptor
}
fun KtNamedFunction.resolveToDescriptorIfAny(
resolutionFacade: ResolutionFacade,
bodyResolveMode: BodyResolveMode = BodyResolveMode.PARTIAL
): FunctionDescriptor? {
return (this as KtDeclaration).resolveToDescriptorIfAny(resolutionFacade, bodyResolveMode) as? FunctionDescriptor
}
fun KtProperty.resolveToDescriptorIfAny(
resolutionFacade: ResolutionFacade,
bodyResolveMode: BodyResolveMode = BodyResolveMode.PARTIAL
): VariableDescriptor? {
return (this as KtDeclaration).resolveToDescriptorIfAny(resolutionFacade, bodyResolveMode) as? VariableDescriptor
}
fun KtParameter.resolveToParameterDescriptorIfAny(
resolutionFacade: ResolutionFacade,
bodyResolveMode: BodyResolveMode = BodyResolveMode.PARTIAL
): ValueParameterDescriptor? {
val context = analyze(resolutionFacade, bodyResolveMode)
return context.get(BindingContext.VALUE_PARAMETER, this) as? ValueParameterDescriptor
}
fun KtElement.resolveToCall(
resolutionFacade: ResolutionFacade,
bodyResolveMode: BodyResolveMode = BodyResolveMode.PARTIAL
): ResolvedCall<out CallableDescriptor>? =
getResolvedCall(analyze(resolutionFacade, bodyResolveMode))
@JvmOverloads
fun KtElement.analyze(
resolutionFacade: ResolutionFacade,
bodyResolveMode: BodyResolveMode = BodyResolveMode.FULL
): BindingContext =
resolutionFacade.analyze(this, bodyResolveMode)
fun KtElement.analyzeAndGetResult(resolutionFacade: ResolutionFacade): AnalysisResult =
AnalysisResult.success(resolutionFacade.analyze(this), resolutionFacade.moduleDescriptor)
// This function is used on declarations to make analysis not only declaration itself but also it content:
// body for declaration with body, initializer & accessors for properties
fun KtElement.analyzeWithContentAndGetResult(resolutionFacade: ResolutionFacade): AnalysisResult =
resolutionFacade.analyzeWithAllCompilerChecks(listOf(this))
// This function is used on declarations to make analysis not only declaration itself but also it content:
// body for declaration with body, initializer & accessors for properties
fun KtDeclaration.analyzeWithContent(resolutionFacade: ResolutionFacade): BindingContext =
resolutionFacade.analyzeWithAllCompilerChecks(listOf(this)).bindingContext
// This function is used to make full analysis of declaration container.
// All its declarations, including their content (see above), are analyzed.
inline fun <reified T> T.analyzeWithContent(resolutionFacade: ResolutionFacade): BindingContext where T : KtDeclarationContainer, T : KtElement =
resolutionFacade.analyzeWithAllCompilerChecks(listOf(this)).bindingContext

View File

@@ -1,218 +0,0 @@
/*
* Copyright 2010-2019 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.
*/
@file:JvmName("ResolutionUtils")
package org.jetbrains.kotlin.idea.caches.resolve
import org.jetbrains.kotlin.analyzer.AnalysisResult
import org.jetbrains.kotlin.caches.resolve.KotlinCacheService
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor
import org.jetbrains.kotlin.idea.FrontendInternals
import org.jetbrains.kotlin.diagnostics.Diagnostic
import org.jetbrains.kotlin.idea.resolve.ResolutionFacade
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.BindingTraceContext
import org.jetbrains.kotlin.resolve.ImportPath
import org.jetbrains.kotlin.resolve.QualifiedExpressionResolver
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
fun KtElement.getResolutionFacade(): ResolutionFacade =
KotlinCacheService.getInstance(project).getResolutionFacade(listOf(this))
/**
* For local declarations is equivalent to unsafeResolveToDescriptor(bodyResolveMode)
*
* But for non-local declarations it ignores bodyResolveMode and uses LazyDeclarationResolver directly
*/
@Deprecated(
message = "This function has unclear semantics. Please use either unsafeResolveToDescriptor or resolveToDescriptorIfAny instead",
replaceWith = ReplaceWith("unsafeResolveToDescriptor")
)
fun KtDeclaration.resolveToDescriptor(bodyResolveMode: BodyResolveMode = BodyResolveMode.FULL): DeclarationDescriptor =
getResolutionFacade().resolveToDescriptor(this, bodyResolveMode)
/**
* This function throws exception when resolveToDescriptorIfAny returns null, otherwise works equivalently.
*
* **Please, use overload with providing resolutionFacade for stable results of subsequent calls**
*/
fun KtDeclaration.unsafeResolveToDescriptor(
bodyResolveMode: BodyResolveMode = BodyResolveMode.FULL
): DeclarationDescriptor =
unsafeResolveToDescriptor(getResolutionFacade(), bodyResolveMode)
/**
* This function first uses declaration resolvers to resolve this declaration and/or additional declarations (e.g. its parent),
* and then takes the relevant descriptor from binding context.
* The exact set of declarations to resolve depends on bodyResolveMode
*
* **Please, use overload with providing resolutionFacade for stable results of subsequent calls**
*/
fun KtDeclaration.resolveToDescriptorIfAny(
bodyResolveMode: BodyResolveMode = BodyResolveMode.PARTIAL
): DeclarationDescriptor? =
resolveToDescriptorIfAny(getResolutionFacade(), bodyResolveMode)
/**
* **Please, use overload with providing resolutionFacade for stable results of subsequent calls**
*/
fun KtClassOrObject.resolveToDescriptorIfAny(bodyResolveMode: BodyResolveMode = BodyResolveMode.PARTIAL) =
resolveToDescriptorIfAny(getResolutionFacade(), bodyResolveMode)
/**
* **Please, use overload with providing resolutionFacade for stable results of subsequent calls**
*/
fun KtNamedFunction.resolveToDescriptorIfAny(bodyResolveMode: BodyResolveMode = BodyResolveMode.PARTIAL) =
resolveToDescriptorIfAny(getResolutionFacade(), bodyResolveMode)
/**
* **Please, use overload with providing resolutionFacade for stable results of subsequent calls**
*/
fun KtProperty.resolveToDescriptorIfAny(bodyResolveMode: BodyResolveMode = BodyResolveMode.PARTIAL) =
resolveToDescriptorIfAny(getResolutionFacade(), bodyResolveMode)
/**
* **Please, use overload with providing resolutionFacade for stable results of subsequent calls**
*/
fun KtParameter.resolveToParameterDescriptorIfAny(bodyResolveMode: BodyResolveMode = BodyResolveMode.PARTIAL) =
resolveToParameterDescriptorIfAny(getResolutionFacade(), bodyResolveMode)
/**
* **Please, use overload with providing resolutionFacade for stable results of subsequent calls**
*/
fun KtElement.resolveToCall(bodyResolveMode: BodyResolveMode = BodyResolveMode.PARTIAL) =
resolveToCall(getResolutionFacade(), bodyResolveMode)
fun KtFile.resolveImportReference(fqName: FqName): Collection<DeclarationDescriptor> {
val facade = getResolutionFacade()
return facade.resolveImportReference(facade.moduleDescriptor, fqName)
}
fun KtAnnotationEntry.resolveToDescriptorIfAny(
bodyResolveMode: BodyResolveMode = BodyResolveMode.PARTIAL_NO_ADDITIONAL
): AnnotationDescriptor? =
resolveToDescriptorIfAny(getResolutionFacade(), bodyResolveMode)
// This and next functions are used for 'normal' element analysis
// This analysis *should* provide all information extractable from this KtElement except:
// - for declarations, it does not analyze their bodies
// - for classes, it does not analyze their content
// - for member / top-level properties, it does not analyze initializers / accessors
// This information includes related descriptors, resolved calls (but not inside body, see above!)
// and many other binding context slices.
// Normally, the function is used on local declarations or statements / expressions
// Any usage on non-local declaration is a bit suspicious,
// consider replacing it with resolveToDescriptorIfAny and
// remember that body / content is not analyzed;
// if it's necessary, use analyzeWithContent()
//
// If you need diagnostics in result context, use BodyResolveMode.PARTIAL_WITH_DIAGNOSTICS.
// BodyResolveMode.FULL analyzes all statements on the level of KtElement and above.
// BodyResolveMode.PARTIAL analyzes only statements necessary for this KtElement precise analysis.
//
// See also: ResolveSessionForBodies, ResolveElementCache
/**
* **Please, use overload with providing resolutionFacade for stable results of subsequent calls**
*/
@JvmOverloads
fun KtElement.analyze(
bodyResolveMode: BodyResolveMode = BodyResolveMode.FULL
): BindingContext =
analyze(getResolutionFacade(), bodyResolveMode)
/**
* **Please, use overload with providing resolutionFacade for stable results of subsequent calls**
*/
fun KtElement.analyzeAndGetResult(): AnalysisResult {
return analyzeAndGetResult(getResolutionFacade())
}
/**
* **Please, use overload with providing resolutionFacade for stable results of subsequent calls**
*/
fun KtElement.analyzeWithContentAndGetResult(): AnalysisResult =
analyzeWithContentAndGetResult(getResolutionFacade())
fun KtElement.findModuleDescriptor(): ModuleDescriptor = getResolutionFacade().moduleDescriptor
// This function is used on declarations to make analysis not only declaration itself but also it content:
// body for declaration with body, initializer & accessors for properties
/**
* **Please, use overload with providing resolutionFacade for stable results of subsequent calls**
*/
fun KtDeclaration.analyzeWithContent(): BindingContext =
analyzeWithContent(getResolutionFacade())
// This function is used to make full analysis of declaration container.
// All its declarations, including their content (see above), are analyzed.
/**
* **Please, use overload with providing resolutionFacade for stable results of subsequent calls**
*/
inline fun <reified T> T.analyzeWithContent(): BindingContext where T : KtDeclarationContainer, T : KtElement =
analyzeWithContent(getResolutionFacade())
/**
* This function is expected to produce the same result as compiler for the whole file content (including diagnostics,
* trace slices, descriptors, etc.).
*
* It's not recommended to call this function without real need.
*
* @ref [KotlinCacheService]
* @ref [org.jetbrains.kotlin.idea.caches.resolve.PerFileAnalysisCache]
*/
fun KtFile.analyzeWithAllCompilerChecks(vararg extraFiles: KtFile): AnalysisResult =
this.analyzeWithAllCompilerChecks(null, *extraFiles)
fun KtFile.analyzeWithAllCompilerChecks(callback: ((Diagnostic) -> Unit)?, vararg extraFiles: KtFile): AnalysisResult =
KotlinCacheService.getInstance(project).getResolutionFacade(listOf(this) + extraFiles.toList())
.analyzeWithAllCompilerChecks(listOf(this), callback)
/**
* This function is expected to produce the same result as compiler for the given element and its children (including diagnostics,
* trace slices, descriptors, etc.). For some expression element it actually performs analyze for some parent (usually declaration).
*
* It's not recommended to call this function without real need.
*
* NB: for statements / expressions, usually should be replaced with analyze(),
* for declarations, analyzeWithContent() will do what you want.
*
* @ref [KotlinCacheService]
* @ref [org.jetbrains.kotlin.idea.caches.resolve.PerFileAnalysisCache]
*/
@Deprecated(
"Use either KtFile.analyzeWithAllCompilerChecks() or KtElement.analyzeAndGetResult()",
ReplaceWith("analyzeAndGetResult()")
)
fun KtElement.analyzeWithAllCompilerChecks(): AnalysisResult = getResolutionFacade().analyzeWithAllCompilerChecks(listOf(this))
// this method don't check visibility and collect all descriptors with given fqName
@OptIn(FrontendInternals::class)
fun ResolutionFacade.resolveImportReference(
moduleDescriptor: ModuleDescriptor,
fqName: FqName
): Collection<DeclarationDescriptor> {
val importDirective = KtPsiFactory(project).createImportDirective(ImportPath(fqName, false))
val qualifiedExpressionResolver = this.getFrontendService(moduleDescriptor, QualifiedExpressionResolver::class.java)
return qualifiedExpressionResolver.processImportReference(
importDirective,
moduleDescriptor,
BindingTraceContext(),
excludedImportNames = emptyList(),
packageFragmentForVisibilityCheck = null
)?.getContributedDescriptors() ?: emptyList()
}
@Suppress("DEPRECATION")
@Deprecated(
"This method is going to be removed in 1.3.0 release",
ReplaceWith("analyzeWithAllCompilerChecks().bindingContext"),
DeprecationLevel.ERROR
)
fun KtElement.analyzeFully(): BindingContext = analyzeWithAllCompilerChecks().bindingContext

View File

@@ -1,198 +0,0 @@
/*
* Copyright 2010-2015 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.idea.core.quickfix;
import com.intellij.extapi.psi.ASTDelegatePsiElement;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiWhiteSpace;
import com.intellij.psi.util.PsiTreeUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.descriptors.CallableDescriptor;
import org.jetbrains.kotlin.descriptors.ClassifierDescriptor;
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor;
import org.jetbrains.kotlin.diagnostics.Diagnostic;
import org.jetbrains.kotlin.idea.caches.resolve.ResolutionUtils;
import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers;
import org.jetbrains.kotlin.name.FqName;
import org.jetbrains.kotlin.psi.*;
import org.jetbrains.kotlin.renderer.DescriptorRenderer;
import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils;
import org.jetbrains.kotlin.resolve.DescriptorUtils;
import org.jetbrains.kotlin.resolve.calls.callUtil.CallUtilKt;
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall;
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode;
import org.jetbrains.kotlin.types.DeferredType;
import org.jetbrains.kotlin.types.KotlinType;
import org.jetbrains.kotlin.types.checker.KotlinTypeChecker;
public class QuickFixUtil {
private QuickFixUtil() {
}
public static boolean removePossiblyWhiteSpace(ASTDelegatePsiElement element, PsiElement possiblyWhiteSpace) {
if (possiblyWhiteSpace instanceof PsiWhiteSpace) {
element.deleteChildInternal(possiblyWhiteSpace.getNode());
return true;
}
return false;
}
@Nullable
public static <T extends PsiElement> T getParentElementOfType(Diagnostic diagnostic, Class<T> aClass) {
return PsiTreeUtil.getParentOfType(diagnostic.getPsiElement(), aClass, false);
}
@Nullable
public static KotlinType getDeclarationReturnType(KtNamedDeclaration declaration) {
PsiFile file = declaration.getContainingFile();
if (!(file instanceof KtFile)) return null;
DeclarationDescriptor descriptor = ResolutionUtils.unsafeResolveToDescriptor(declaration, BodyResolveMode.FULL);
if (!(descriptor instanceof CallableDescriptor)) return null;
KotlinType type = ((CallableDescriptor) descriptor).getReturnType();
if (type instanceof DeferredType) {
type = ((DeferredType) type).getDelegate();
}
return type;
}
@Nullable
public static KotlinType findLowerBoundOfOverriddenCallablesReturnTypes(@NotNull CallableDescriptor descriptor) {
KotlinType matchingReturnType = null;
for (CallableDescriptor overriddenDescriptor : ((CallableDescriptor) descriptor).getOverriddenDescriptors()) {
KotlinType overriddenReturnType = overriddenDescriptor.getReturnType();
if (overriddenReturnType == null) {
return null;
}
if (matchingReturnType == null || KotlinTypeChecker.DEFAULT.isSubtypeOf(overriddenReturnType, matchingReturnType)) {
matchingReturnType = overriddenReturnType;
}
else if (!KotlinTypeChecker.DEFAULT.isSubtypeOf(matchingReturnType, overriddenReturnType)) {
return null;
}
}
return matchingReturnType;
}
@Nullable
public static PsiElement safeGetDeclaration(@Nullable CallableDescriptor descriptor) {
//do not create fix if descriptor has more than one overridden declaration
if (descriptor == null || descriptor.getOverriddenDescriptors().size() > 1) return null;
return DescriptorToSourceUtils.descriptorToDeclaration(descriptor);
}
@Nullable
public static KtParameter getParameterDeclarationForValueArgument(
@NotNull ResolvedCall<?> resolvedCall,
@Nullable ValueArgument valueArgument
) {
PsiElement declaration = safeGetDeclaration(CallUtilKt.getParameterForArgument(resolvedCall, valueArgument));
return declaration instanceof KtParameter ? (KtParameter) declaration : null;
}
private static boolean equalOrLastInBlock(KtExpression block, KtExpression expression) {
if (block == expression) return true;
return block instanceof KtBlockExpression && expression.getParent() == block &&
PsiTreeUtil.getNextSiblingOfType(expression, KtExpression.class) == null;
}
@Nullable
public static KtIfExpression getParentIfForBranch(@Nullable KtExpression expression) {
KtIfExpression ifExpression = PsiTreeUtil.getParentOfType(expression, KtIfExpression.class, true);
if (ifExpression == null) return null;
if (equalOrLastInBlock(ifExpression.getThen(), expression)
|| equalOrLastInBlock(ifExpression.getElse(), expression)) {
return ifExpression;
}
return null;
}
@Nullable
private static KtWhenExpression getParentWhenForBranch(@Nullable KtExpression expression) {
KtWhenEntry whenEntry = PsiTreeUtil.getParentOfType(expression, KtWhenEntry.class, true);
if (whenEntry == null) return null;
KtExpression whenEntryExpression = whenEntry.getExpression();
if (whenEntryExpression == null) return null;
if (!equalOrLastInBlock(whenEntryExpression, expression)) return null;
return PsiTreeUtil.getParentOfType(whenEntry, KtWhenExpression.class, true);
}
@Nullable
private static KtExpression getParentForBranch(@Nullable KtExpression expression) {
KtExpression parent = getParentIfForBranch(expression);
if (parent != null) return parent;
return getParentWhenForBranch(expression);
}
// Returns true iff parent's value always or sometimes is evaluable to child's value, e.g.
// parent = (x), child = x;
// parent = if (...) x else y, child = x;
// parent = y.x, child = x
public static boolean canEvaluateTo(KtExpression parent, KtExpression child) {
if (parent == null || child == null) {
return false;
}
while (parent != child) {
PsiElement childParent = child.getParent();
if (childParent instanceof KtParenthesizedExpression) {
child = (KtExpression) childParent;
continue;
}
if (childParent instanceof KtDotQualifiedExpression &&
(child instanceof KtCallExpression || child instanceof KtDotQualifiedExpression)) {
child = (KtExpression) childParent;
continue;
}
child = getParentForBranch(child);
if (child == null) return false;
}
return true;
}
public static boolean canFunctionOrGetterReturnExpression(@NotNull KtDeclaration functionOrGetter, @NotNull KtExpression expression) {
if (functionOrGetter instanceof KtFunctionLiteral) {
KtBlockExpression functionLiteralBody = ((KtFunctionLiteral) functionOrGetter).getBodyExpression();
PsiElement returnedElement = null;
if (functionLiteralBody != null) {
PsiElement[] children = functionLiteralBody.getChildren();
int length = children.length;
if (length > 0) {
returnedElement = children[length - 1];
}
}
return returnedElement instanceof KtExpression && canEvaluateTo((KtExpression) returnedElement, expression);
}
else {
if (functionOrGetter instanceof KtDeclarationWithInitializer && canEvaluateTo(((KtDeclarationWithInitializer) functionOrGetter).getInitializer(), expression)) {
return true;
}
KtReturnExpression returnExpression = PsiTreeUtil.getParentOfType(expression, KtReturnExpression.class);
return returnExpression != null && canEvaluateTo(returnExpression.getReturnedExpression(), expression);
}
}
public static String renderTypeWithFqNameOnClash(KotlinType type, String nameToCheckAgainst) {
FqName fqNameToCheckAgainst = new FqName(nameToCheckAgainst);
ClassifierDescriptor typeClassifierDescriptor = type.getConstructor().getDeclarationDescriptor();
FqName typeFqName = typeClassifierDescriptor != null ? DescriptorUtils.getFqNameSafe(typeClassifierDescriptor) : fqNameToCheckAgainst;
DescriptorRenderer renderer = typeFqName.shortName().equals(fqNameToCheckAgainst.shortName())
? IdeDescriptorRenderers.SOURCE_CODE
: IdeDescriptorRenderers.SOURCE_CODE_SHORT_NAMES_NO_ANNOTATIONS;
return renderer.renderType(type);
}
}

View File

@@ -1,372 +0,0 @@
/*
* 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.imports
import com.intellij.openapi.progress.ProgressManager
import org.jetbrains.annotations.TestOnly
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.descriptors.TypeAliasDescriptor
import org.jetbrains.kotlin.descriptors.impl.TypeAliasConstructorDescriptor
import org.jetbrains.kotlin.idea.FrontendInternals
import org.jetbrains.kotlin.idea.analysis.analyzeAsReplacement
import org.jetbrains.kotlin.idea.caches.resolve.analyze
import org.jetbrains.kotlin.idea.caches.resolve.getResolutionFacade
import org.jetbrains.kotlin.idea.resolve.ResolutionFacade
import org.jetbrains.kotlin.idea.resolve.frontendService
import org.jetbrains.kotlin.idea.util.ImportInsertHelper
import org.jetbrains.kotlin.idea.util.getResolutionScope
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.renderer.render
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.BindingTraceContext
import org.jetbrains.kotlin.resolve.ImportPath
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
import org.jetbrains.kotlin.resolve.lazy.FileScopeProvider
import org.jetbrains.kotlin.resolve.scopes.ImportingScope
import org.jetbrains.kotlin.resolve.scopes.utils.findClassifier
import org.jetbrains.kotlin.resolve.scopes.utils.parentsWithSelf
import org.jetbrains.kotlin.resolve.scopes.utils.replaceImportingScopes
class OptimizedImportsBuilder(
private val file: KtFile,
private val data: InputData,
private val options: Options
) {
companion object {
@get:TestOnly
@set:TestOnly
var testLog: StringBuilder? = null
}
interface AbstractReference {
val element: KtElement
val dependsOnNames: Collection<Name>
fun resolve(bindingContext: BindingContext): Collection<DeclarationDescriptor>
}
class InputData(
val descriptorsToImport: Set<DeclarationDescriptor>,
val namesToImport: Map<FqName, Set<Name>>,
val references: Collection<AbstractReference>,
val unresolvedNames: Set<Name>,
)
class Options(
val nameCountToUseStarImport: Int,
val nameCountToUseStarImportForMembers: Int,
val isInPackagesToUseStarImport: (FqName) -> Boolean
)
private val importInsertHelper = ImportInsertHelper.getInstance(file.project)
private sealed class ImportRule {
// force presence of this import
data class Add(val importPath: ImportPath) : ImportRule() {
override fun toString() = "+$importPath"
}
// force absence of this import
data class DoNotAdd(val importPath: ImportPath) : ImportRule() {
override fun toString() = "-$importPath"
}
}
private val importRules = HashSet<ImportRule>()
fun buildOptimizedImports(): List<ImportPath>? {
val facade = file.getResolutionFacade()
file.importDirectives
.asSequence()
.filter { it.mayReferToSomeUnresolvedName() || it.isExistedUnresolvedName(facade) }
.mapNotNull { it.importPath }
.mapNotNullTo(importRules) { ImportRule.Add(it) }
while (true) {
ProgressManager.checkCanceled()
val importRulesBefore = importRules.size
val result = tryBuildOptimizedImports()
if (importRules.size == importRulesBefore) return result
testLog?.append("Trying to build import list again with import rules: ${importRules.joinToString()}\n")
}
}
private fun KtImportDirective.mayReferToSomeUnresolvedName() = isAllUnder && data.unresolvedNames.isNotEmpty()
private fun KtImportDirective.isExistedUnresolvedName(facade: ResolutionFacade) =
importedName in data.unresolvedNames && !canResolve(facade)
private fun getExpressionToAnalyze(element: KtElement): KtExpression? {
val parent = element.parent
return when {
parent is KtQualifiedExpression && element == parent.selectorExpression -> parent
parent is KtCallExpression && element == parent.calleeExpression -> getExpressionToAnalyze(parent)
parent is KtOperationExpression && element == parent.operationReference -> parent
parent is KtUserType -> null //TODO: is it always correct?
else -> element as? KtExpression //TODO: what if not expression? Example: KtPropertyDelegationMethodsReference
}
}
private fun tryBuildOptimizedImports(): List<ImportPath>? {
val importsToGenerate = hashSetOf<ImportPath>()
importRules.filterIsInstance<ImportRule.Add>().mapTo(importsToGenerate) { it.importPath }
val descriptorsByParentFqName = HashMap<FqName, MutableSet<DeclarationDescriptor>>()
for (descriptor in data.descriptorsToImport) {
val fqName = descriptor.importableFqName!!
for (name in data.namesToImport.getValue(fqName)) {
val alias = if (name != fqName.shortName()) name else null
val explicitImportPath = ImportPath(fqName, false, alias)
if (explicitImportPath in importsToGenerate) continue
val parentFqName = fqName.parent()
if (alias == null && canUseStarImport(descriptor, fqName) && ImportPath(parentFqName, true).isAllowedByRules()) {
descriptorsByParentFqName.getOrPut(parentFqName) { hashSetOf() }.add(descriptor)
} else {
importsToGenerate.add(explicitImportPath)
}
}
}
val classNamesToCheck = hashSetOf<FqName>()
for (parentFqName in descriptorsByParentFqName.keys) {
ProgressManager.checkCanceled()
val starImportPath = ImportPath(parentFqName, true)
if (starImportPath in importsToGenerate) continue
val descriptors = descriptorsByParentFqName[parentFqName]!!
val fqNames = descriptors.map { it.importableFqName!! }.toSet()
val nameCountToUseStar = descriptors.first().nameCountToUseStar()
val useExplicitImports = fqNames.size < nameCountToUseStar && !options.isInPackagesToUseStarImport(parentFqName)
|| !starImportPath.isAllowedByRules()
if (useExplicitImports) {
fqNames.filter(this::needExplicitImport).mapTo(importsToGenerate) { ImportPath(it, false) }
} else {
descriptors.asSequence()
.filterIsInstance<ClassDescriptor>()
.map { it.importableFqName!! }
.filterTo(classNamesToCheck, this::needExplicitImport)
if (fqNames.all(this::needExplicitImport)) {
importsToGenerate.add(starImportPath)
}
}
}
// now check that there are no conflicts and all classes are really imported
addExplicitImportsForClassesWhenRequired(classNamesToCheck, descriptorsByParentFqName, importsToGenerate, file)
val sortedImportsToGenerate = importsToGenerate.sortedWith(importInsertHelper.importSortComparator)
// check if no changes to imports required
val oldImports = file.importDirectives
if (oldImports.size == sortedImportsToGenerate.size && oldImports.map { it.importPath } == sortedImportsToGenerate) return null
val originalFileScope = file.getFileResolutionScope()
val newFileScope = buildScopeByImports(file, sortedImportsToGenerate)
var references = data.references
if (testLog != null) {
// to make log the same for all runs
references = references.sortedBy { it.toString() }
}
for ((names, refs) in references.groupBy { it.dependsOnNames }) {
if (!areScopeSlicesEqual(originalFileScope, newFileScope, names)) {
for (ref in refs) {
ProgressManager.checkCanceled()
val element = ref.element
val bindingContext = element.analyze(BodyResolveMode.PARTIAL)
val expressionToAnalyze = getExpressionToAnalyze(element) ?: continue
val newScope = element.getResolutionScope(
bindingContext,
file.getResolutionFacade()
).replaceImportingScopes(newFileScope)
val newBindingContext = expressionToAnalyze.analyzeAsReplacement(
expressionToAnalyze,
bindingContext,
newScope,
trace = BindingTraceContext()
)
testLog?.append("Additional checking of reference $ref\n")
val oldTargets = ref.resolve(bindingContext)
val newTargets = ref.resolve(newBindingContext)
if (!areTargetsEqual(oldTargets, newTargets)) {
testLog?.append("Changed resolve of $ref\n")
(oldTargets + newTargets).forEach {
lockImportForDescriptor(it, names)
}
}
}
}
}
return sortedImportsToGenerate
}
private fun lockImportForDescriptor(descriptor: DeclarationDescriptor, existingNames: Collection<Name>) {
val fqName = descriptor.importableFqName ?: return
val names = data.namesToImport.getOrElse(fqName) { listOf(descriptor.name) }.intersect(existingNames)
val starImportPath = ImportPath(fqName.parent(), true)
val importPaths = file.importDirectives.map { it.importPath }
for (name in names) {
val alias = if (name != fqName.shortName()) name else null
val explicitImportPath = ImportPath(fqName, false, alias)
when {
explicitImportPath in importPaths ->
importRules.add(ImportRule.Add(explicitImportPath))
alias == null && starImportPath in importPaths ->
importRules.add(ImportRule.Add(starImportPath))
else -> // there is no import for this descriptor in the original import list, so do not allow to import it by star-import
importRules.add(ImportRule.DoNotAdd(starImportPath))
}
}
}
private fun addExplicitImportsForClassesWhenRequired(
classNamesToCheck: Collection<FqName>,
descriptorsByParentFqName: Map<FqName, MutableSet<DeclarationDescriptor>>,
importsToGenerate: MutableSet<ImportPath>,
originalFile: KtFile
) {
val scope = buildScopeByImports(originalFile, importsToGenerate.filter { it.isAllUnder })
for (fqName in classNamesToCheck) {
if (scope.findClassifier(fqName.shortName(), NoLookupLocation.FROM_IDE)?.importableFqName != fqName) {
// add explicit import if failed to import with * (or from current package)
importsToGenerate.add(ImportPath(fqName, false))
val parentFqName = fqName.parent()
val siblingsToImport = descriptorsByParentFqName.getValue(parentFqName)
for (descriptor in siblingsToImport.filter { it.importableFqName == fqName }) {
siblingsToImport.remove(descriptor)
}
if (siblingsToImport.isEmpty()) { // star import is not really needed
importsToGenerate.remove(ImportPath(parentFqName, true))
}
}
}
}
private fun buildScopeByImports(originalFile: KtFile, importsToGenerate: Collection<ImportPath>): ImportingScope {
val fileText = buildString {
append("package ")
append(originalFile.packageFqName.toUnsafe().render())
append("\n")
for (importPath in importsToGenerate) {
append("import ")
append(importPath.pathStr)
if (importPath.hasAlias()) {
append("=")
append(importPath.alias!!.render())
}
append("\n")
}
}
val fileWithImports = KtPsiFactory(originalFile).createAnalyzableFile("Dummy_" + originalFile.name, fileText, originalFile)
if (file.isScript()) {
fileWithImports.originalFile = originalFile
}
return fileWithImports.getFileResolutionScope()
}
@OptIn(FrontendInternals::class)
private fun KtFile.getFileResolutionScope() =
getResolutionFacade().frontendService<FileScopeProvider>().getFileScopes(this).importingScope
private fun areScopeSlicesEqual(scope1: ImportingScope, scope2: ImportingScope, names: Collection<Name>): Boolean {
val tower1 = scope1.extractSliceTower(names)
val tower2 = scope2.extractSliceTower(names)
val iterator1 = tower1.iterator()
val iterator2 = tower2.iterator()
while (true) {
when {
!iterator1.hasNext() -> return !iterator2.hasNext()
!iterator2.hasNext() -> return false
else -> if (!areTargetsEqual(iterator1.next(), iterator2.next())) return false
}
}
}
private fun ImportingScope.extractSliceTower(names: Collection<Name>): Sequence<Collection<DeclarationDescriptor>> {
return parentsWithSelf
.map { scope ->
names.flatMap { name ->
ProgressManager.checkCanceled()
val contributedFunctions = scope.getContributedFunctions(name, NoLookupLocation.FROM_IDE)
ProgressManager.checkCanceled()
val contributedVariables = scope.getContributedVariables(name, NoLookupLocation.FROM_IDE)
ProgressManager.checkCanceled()
val contributedClassifier = scope.getContributedClassifier(name, NoLookupLocation.FROM_IDE)
contributedFunctions + contributedVariables + listOfNotNull(contributedClassifier)
}
}
.filter { it.isNotEmpty() }
}
private fun canUseStarImport(descriptor: DeclarationDescriptor, fqName: FqName): Boolean = when {
fqName.parent().isRoot -> false
(descriptor.containingDeclaration as? ClassDescriptor)?.kind == ClassKind.OBJECT -> false
else -> true
}
private fun needExplicitImport(fqName: FqName): Boolean = hasAlias(fqName) || !isImportedByDefault(fqName)
private fun hasAlias(fqName: FqName) = data.namesToImport[fqName]?.let {
it.singleOrNull() == null
} ?: false
private fun isImportedByDefault(fqName: FqName) = importInsertHelper.isImportedWithDefault(ImportPath(fqName, false), file)
private fun isImportedByLowPriorityDefault(fqName: FqName) =
importInsertHelper.isImportedWithLowPriorityDefaultImport(ImportPath(fqName, false), file)
private fun DeclarationDescriptor.nameCountToUseStar(): Int {
val isMember = containingDeclaration is ClassDescriptor
return if (isMember)
options.nameCountToUseStarImportForMembers
else
options.nameCountToUseStarImport
}
private fun areTargetsEqual(descriptors1: Collection<DeclarationDescriptor>, descriptors2: Collection<DeclarationDescriptor>): Boolean {
return descriptors1.size == descriptors2.size &&
descriptors1.zip(descriptors2).all { (first, second) -> areTargetsEqual(first, second) } //TODO: can have different order?
}
private fun areTargetsEqual(first: DeclarationDescriptor, second: DeclarationDescriptor): Boolean {
if (first == second) return true
val firstFqName = first.importableFqName
val secondFqName = second.importableFqName
return firstFqName == secondFqName ||
(first.isAliasTo(second) && secondFqName != null && isImportedByLowPriorityDefault(secondFqName)) ||
(second.isAliasTo(first) && firstFqName != null && isImportedByLowPriorityDefault(firstFqName))
}
private fun DeclarationDescriptor.isAliasTo(other: DeclarationDescriptor): Boolean =
this is TypeAliasDescriptor && classDescriptor == other ||
this is TypeAliasConstructorDescriptor && underlyingConstructorDescriptor == other
private fun ImportPath.isAllowedByRules(): Boolean = importRules.none { it is ImportRule.DoNotAdd && it.importPath == this }
}

View File

@@ -1,36 +0,0 @@
/*
* Copyright 2010-2019 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.kdoc
import com.intellij.openapi.components.ServiceManager
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.idea.resolve.ResolutionFacade
import org.jetbrains.kotlin.resolve.BindingContext
interface SampleResolutionService {
fun resolveSample(
context: BindingContext,
fromDescriptor: DeclarationDescriptor,
resolutionFacade: ResolutionFacade,
qualifiedName: List<String>
): Collection<DeclarationDescriptor>
companion object {
/**
* It's internal implementation, please use [resolveKDocSampleLink], or [resolveKDocLink]
*/
internal fun resolveSample(
context: BindingContext,
fromDescriptor: DeclarationDescriptor,
resolutionFacade: ResolutionFacade,
qualifiedName: List<String>
): Collection<DeclarationDescriptor> {
val instance = ServiceManager.getService(resolutionFacade.project, SampleResolutionService::class.java)
return instance?.resolveSample(context, fromDescriptor, resolutionFacade, qualifiedName) ?: emptyList()
}
}
}

View File

@@ -1,110 +0,0 @@
/*
* Copyright 2010-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.idea.kdoc
import com.intellij.psi.PsiElement
import com.intellij.psi.util.PsiTreeUtil
import org.jetbrains.kotlin.descriptors.CallableDescriptor
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.descriptors.DeclarationDescriptorWithSource
import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptorIfAny
import org.jetbrains.kotlin.kdoc.parser.KDocKnownTag
import org.jetbrains.kotlin.kdoc.psi.api.KDoc
import org.jetbrains.kotlin.kdoc.psi.impl.KDocTag
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.*
import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils
fun DeclarationDescriptor.findKDoc(
descriptorToPsi: (DeclarationDescriptorWithSource) -> PsiElement? = { DescriptorToSourceUtils.descriptorToDeclaration(it) }
): KDocTag? {
if (this is DeclarationDescriptorWithSource) {
val psiDeclaration = descriptorToPsi(this)?.navigationElement
return (psiDeclaration as? KtElement)?.findKDoc(descriptorToPsi)
}
return null
}
private typealias DescriptorToPsi = (DeclarationDescriptorWithSource) -> PsiElement?
fun KtElement.findKDoc(descriptorToPsi: DescriptorToPsi): KDocTag? {
return this.lookupOwnedKDoc()
?: this.lookupKDocInContainer()
?: this.lookupInheritedKDoc(descriptorToPsi)
}
private fun KtElement.lookupOwnedKDoc(): KDocTag? {
// KDoc for primary constructor is located inside of its class KDoc
val psiDeclaration = when (this) {
is KtPrimaryConstructor -> getContainingClassOrObject()
else -> this
}
if (psiDeclaration is KtDeclaration) {
val kdoc = psiDeclaration.docComment
if (kdoc != null) {
if (this is KtConstructor<*>) {
// ConstructorDescriptor resolves to the same JetDeclaration
val constructorSection = kdoc.findSectionByTag(KDocKnownTag.CONSTRUCTOR)
if (constructorSection != null) {
return constructorSection
}
}
return kdoc.getDefaultSection()
}
}
return null
}
private fun KtElement.lookupKDocInContainer(): KDocTag? {
val subjectName = name
val containingDeclaration =
PsiTreeUtil.findFirstParent(this, true) {
it is KtDeclarationWithBody && it !is KtPrimaryConstructor
|| it is KtClassOrObject
}
val containerKDoc = containingDeclaration?.getChildOfType<KDoc>()
if (containerKDoc == null || subjectName == null) return null
val propertySection = containerKDoc.findSectionByTag(KDocKnownTag.PROPERTY, subjectName)
val paramTag = containerKDoc.findDescendantOfType<KDocTag> { it.knownTag == KDocKnownTag.PARAM && it.getSubjectName() == subjectName }
return when {
this is KtParameter && this.isPropertyParameter() -> propertySection ?: paramTag
this is KtParameter || this is KtTypeParameter -> paramTag
this is KtProperty && containingDeclaration is KtClassOrObject -> propertySection
else -> null
}
}
private fun KtElement.lookupInheritedKDoc(descriptorToPsi: DescriptorToPsi): KDocTag? {
if (this is KtCallableDeclaration) {
val descriptor = this.resolveToDescriptorIfAny() as? CallableDescriptor ?: return null
for (baseDescriptor in descriptor.overriddenDescriptors) {
val baseKDoc = baseDescriptor.original.findKDoc(descriptorToPsi)
if (baseKDoc != null) {
return baseKDoc
}
}
}
return null
}

View File

@@ -1,287 +0,0 @@
/*
* Copyright 2010-2019 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.kdoc
import com.intellij.openapi.components.ServiceManager
import org.jetbrains.kotlin.caches.resolve.KotlinCacheService
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.idea.FrontendInternals
import org.jetbrains.kotlin.idea.resolve.ResolutionFacade
import org.jetbrains.kotlin.idea.util.CallType
import org.jetbrains.kotlin.idea.util.getFileResolutionScope
import org.jetbrains.kotlin.idea.util.substituteExtensionIfCallable
import org.jetbrains.kotlin.incremental.components.LookupLocation
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
import org.jetbrains.kotlin.kdoc.parser.KDocKnownTag
import org.jetbrains.kotlin.kdoc.psi.impl.KDocTag
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.psi.KtPsiFactory
import org.jetbrains.kotlin.psi.KtQualifiedExpression
import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils
import org.jetbrains.kotlin.resolve.FunctionDescriptorUtil
import org.jetbrains.kotlin.resolve.QualifiedExpressionResolver
import org.jetbrains.kotlin.resolve.descriptorUtil.isExtension
import org.jetbrains.kotlin.resolve.descriptorUtil.module
import org.jetbrains.kotlin.resolve.scopes.*
import org.jetbrains.kotlin.resolve.scopes.utils.*
import org.jetbrains.kotlin.resolve.source.PsiSourceElement
import org.jetbrains.kotlin.utils.Printer
import org.jetbrains.kotlin.utils.SmartList
import org.jetbrains.kotlin.utils.addIfNotNull
fun resolveKDocLink(
context: BindingContext,
resolutionFacade: ResolutionFacade,
fromDescriptor: DeclarationDescriptor,
fromSubjectOfTag: KDocTag?,
qualifiedName: List<String>
): Collection<DeclarationDescriptor> = when (fromSubjectOfTag?.knownTag) {
KDocKnownTag.PARAM -> resolveParamLink(fromDescriptor, qualifiedName)
KDocKnownTag.SAMPLE -> resolveKDocSampleLink(context, resolutionFacade, fromDescriptor, qualifiedName)
else -> resolveDefaultKDocLink(context, resolutionFacade, fromDescriptor, qualifiedName)
}
fun getParamDescriptors(fromDescriptor: DeclarationDescriptor): List<DeclarationDescriptor> {
// TODO resolve parameters of functions passed as parameters
when (fromDescriptor) {
is CallableDescriptor -> {
return fromDescriptor.valueParameters + fromDescriptor.typeParameters
}
is ClassifierDescriptor -> {
val typeParams = fromDescriptor.typeConstructor.parameters
if (fromDescriptor is ClassDescriptor) {
val constructorDescriptor = fromDescriptor.unsubstitutedPrimaryConstructor
if (constructorDescriptor != null) {
return typeParams + constructorDescriptor.valueParameters
}
}
return typeParams
}
else -> {
return emptyList()
}
}
}
private fun resolveParamLink(fromDescriptor: DeclarationDescriptor, qualifiedName: List<String>): List<DeclarationDescriptor> {
val name = qualifiedName.singleOrNull() ?: return emptyList()
return getParamDescriptors(fromDescriptor).filter { it.name.asString() == name }
}
fun resolveKDocSampleLink(
context: BindingContext,
resolutionFacade: ResolutionFacade,
fromDescriptor: DeclarationDescriptor,
qualifiedName: List<String>
): Collection<DeclarationDescriptor> {
val resolvedViaService = SampleResolutionService.resolveSample(context, fromDescriptor, resolutionFacade, qualifiedName)
if (resolvedViaService.isNotEmpty()) return resolvedViaService
return resolveDefaultKDocLink(context, resolutionFacade, fromDescriptor, qualifiedName)
}
private fun resolveDefaultKDocLink(
context: BindingContext,
resolutionFacade: ResolutionFacade,
fromDescriptor: DeclarationDescriptor,
qualifiedName: List<String>
): Collection<DeclarationDescriptor> {
val contextScope = getKDocLinkResolutionScope(resolutionFacade, fromDescriptor)
if (qualifiedName.size == 1) {
val shortName = Name.identifier(qualifiedName.single())
val descriptorsByName = SmartList<DeclarationDescriptor>()
descriptorsByName.addIfNotNull(contextScope.findClassifier(shortName, NoLookupLocation.FROM_IDE))
descriptorsByName.addIfNotNull(contextScope.findPackage(shortName))
descriptorsByName.addAll(contextScope.collectFunctions(shortName, NoLookupLocation.FROM_IDE))
descriptorsByName.addAll(contextScope.collectVariables(shortName, NoLookupLocation.FROM_IDE))
if (fromDescriptor is FunctionDescriptor && fromDescriptor.isExtension && shortName.asString() == "this") {
return listOfNotNull(fromDescriptor.extensionReceiverParameter)
}
// Try to find a matching local descriptor (parameter or type parameter) first
val localDescriptors = descriptorsByName.filter { it.containingDeclaration == fromDescriptor }
if (localDescriptors.isNotEmpty()) return localDescriptors
return descriptorsByName
}
val moduleDescriptor = fromDescriptor.module
@OptIn(FrontendInternals::class)
val qualifiedExpressionResolver = resolutionFacade.getFrontendService(moduleDescriptor, QualifiedExpressionResolver::class.java)
val contextElement = DescriptorToSourceUtils.descriptorToDeclaration(fromDescriptor)
val factory = KtPsiFactory(resolutionFacade.project)
// TODO escape identifiers
val codeFragment = factory.createExpressionCodeFragment(qualifiedName.joinToString("."), contextElement)
val qualifiedExpression =
codeFragment.findElementAt(codeFragment.textLength - 1)?.getStrictParentOfType<KtQualifiedExpression>() ?: return emptyList()
val (descriptor, memberName) = qualifiedExpressionResolver.resolveClassOrPackageInQualifiedExpression(
qualifiedExpression,
contextScope,
context
)
if (descriptor == null) return emptyList()
if (memberName != null) {
val memberScope = getKDocLinkMemberScope(descriptor, contextScope)
return memberScope.getContributedFunctions(memberName, NoLookupLocation.FROM_IDE) +
memberScope.getContributedVariables(memberName, NoLookupLocation.FROM_IDE) +
listOfNotNull(memberScope.getContributedClassifier(memberName, NoLookupLocation.FROM_IDE))
}
return listOf(descriptor)
}
private fun getPackageInnerScope(descriptor: PackageFragmentDescriptor): MemberScope {
return descriptor.containingDeclaration.getPackage(descriptor.fqName).memberScope
}
private fun getClassInnerScope(outerScope: LexicalScope, descriptor: ClassDescriptor): LexicalScope {
val headerScope = LexicalScopeImpl(
outerScope, descriptor, false, descriptor.thisAsReceiverParameter,
LexicalScopeKind.SYNTHETIC
) {
descriptor.declaredTypeParameters.forEach { addClassifierDescriptor(it) }
descriptor.constructors.forEach { addFunctionDescriptor(it) }
}
return LexicalChainedScope.create(
headerScope, descriptor, false, null, LexicalScopeKind.SYNTHETIC,
descriptor.defaultType.memberScope,
descriptor.staticScope,
descriptor.companionObjectDescriptor?.defaultType?.memberScope
)
}
fun getKDocLinkResolutionScope(resolutionFacade: ResolutionFacade, contextDescriptor: DeclarationDescriptor): LexicalScope {
return when (contextDescriptor) {
is PackageFragmentDescriptor ->
LexicalScope.Base(getPackageInnerScope(contextDescriptor).memberScopeAsImportingScope(), contextDescriptor)
is PackageViewDescriptor ->
LexicalScope.Base(contextDescriptor.memberScope.memberScopeAsImportingScope(), contextDescriptor)
is ClassDescriptor ->
getClassInnerScope(getOuterScope(contextDescriptor, resolutionFacade), contextDescriptor)
is FunctionDescriptor -> FunctionDescriptorUtil.getFunctionInnerScope(
getOuterScope(contextDescriptor, resolutionFacade),
contextDescriptor, LocalRedeclarationChecker.DO_NOTHING
)
is PropertyDescriptor ->
ScopeUtils.makeScopeForPropertyHeader(getOuterScope(contextDescriptor, resolutionFacade), contextDescriptor)
is DeclarationDescriptorNonRoot ->
getOuterScope(contextDescriptor, resolutionFacade)
else -> throw IllegalArgumentException("Cannot find resolution scope for root $contextDescriptor")
}
}
private fun getOuterScope(descriptor: DeclarationDescriptorWithSource, resolutionFacade: ResolutionFacade): LexicalScope {
val parent = descriptor.containingDeclaration!!
if (parent is PackageFragmentDescriptor) {
val containingFile = (descriptor.source as? PsiSourceElement)?.psi?.containingFile as? KtFile ?: return LexicalScope.Base(
ImportingScope.Empty,
parent
)
val kotlinCacheService = ServiceManager.getService(containingFile.project, KotlinCacheService::class.java)
val facadeToUse = kotlinCacheService?.getResolutionFacade(listOf(containingFile)) ?: resolutionFacade
return facadeToUse.getFileResolutionScope(containingFile)
} else {
return getKDocLinkResolutionScope(resolutionFacade, parent)
}
}
fun getKDocLinkMemberScope(descriptor: DeclarationDescriptor, contextScope: LexicalScope): MemberScope {
return when (descriptor) {
is PackageFragmentDescriptor -> getPackageInnerScope(descriptor)
is PackageViewDescriptor -> descriptor.memberScope
is ClassDescriptor -> {
ChainedMemberScope.create(
"Member scope for KDoc resolve", listOfNotNull(
descriptor.unsubstitutedMemberScope,
descriptor.staticScope,
descriptor.companionObjectDescriptor?.unsubstitutedMemberScope,
ExtensionsScope(descriptor, contextScope)
)
)
}
else -> MemberScope.Empty
}
}
private class ExtensionsScope(
private val receiverClass: ClassDescriptor,
private val contextScope: LexicalScope
) : MemberScope {
private val receiverTypes = listOf(receiverClass.defaultType)
override fun getContributedFunctions(name: Name, location: LookupLocation): Collection<SimpleFunctionDescriptor> {
return contextScope.collectFunctions(name, location).flatMap {
if (it is SimpleFunctionDescriptor && it.isExtension) it.substituteExtensionIfCallable(
receiverTypes,
CallType.DOT
) else emptyList()
}
}
override fun getContributedVariables(name: Name, location: LookupLocation): Collection<PropertyDescriptor> {
return contextScope.collectVariables(name, location).flatMap {
if (it is PropertyDescriptor && it.isExtension) it.substituteExtensionIfCallable(
receiverTypes,
CallType.DOT
) else emptyList()
}
}
override fun getContributedClassifier(name: Name, location: LookupLocation): ClassifierDescriptor? = null
override fun getContributedDescriptors(
kindFilter: DescriptorKindFilter,
nameFilter: (Name) -> Boolean
): Collection<DeclarationDescriptor> {
if (DescriptorKindExclude.Extensions in kindFilter.excludes) return emptyList()
return contextScope.collectDescriptorsFiltered(
kindFilter exclude DescriptorKindExclude.NonExtensions,
nameFilter,
changeNamesForAliased = true
).flatMap {
if (it is CallableDescriptor && it.isExtension) it.substituteExtensionIfCallable(
receiverTypes,
CallType.DOT
) else emptyList()
}
}
override fun getFunctionNames(): Set<Name> =
getContributedDescriptors(kindFilter = DescriptorKindFilter.FUNCTIONS).map { it.name }.toSet()
override fun getVariableNames(): Set<Name> =
getContributedDescriptors(kindFilter = DescriptorKindFilter.VARIABLES).map { it.name }.toSet()
override fun getClassifierNames() = null
override fun printScopeStructure(p: Printer) {
p.println("Extensions for ${receiverClass.name} in:")
contextScope.printStructure(p)
}
}

View File

@@ -1,93 +0,0 @@
/*
* Copyright 2010-2019 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.refactoring.introduce
import com.intellij.openapi.util.Key
import com.intellij.openapi.util.TextRange
import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.idea.analysis.analyzeInContext
import org.jetbrains.kotlin.idea.caches.resolve.getResolutionFacade
import org.jetbrains.kotlin.idea.resolve.getLanguageVersionSettings
import org.jetbrains.kotlin.idea.util.getResolutionScope
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.endOffset
import org.jetbrains.kotlin.psi.psiUtil.nextSiblingOfSameType
import org.jetbrains.kotlin.psi.psiUtil.startOffset
import org.jetbrains.kotlin.resolve.DelegatingBindingTrace
import org.jetbrains.kotlin.resolve.constants.evaluate.ConstantExpressionEvaluator
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.TypeUtils
class ExtractableSubstringInfo(
val startEntry: KtStringTemplateEntry,
val endEntry: KtStringTemplateEntry,
val prefix: String,
val suffix: String,
type: KotlinType? = null
) {
private fun guessLiteralType(literal: String): KotlinType {
val facade = template.getResolutionFacade()
val module = facade.moduleDescriptor
val stringType = module.builtIns.stringType
if (startEntry != endEntry || startEntry !is KtLiteralStringTemplateEntry) return stringType
val expr = KtPsiFactory(startEntry).createExpressionIfPossible(literal) ?: return stringType
val context = facade.analyze(template, BodyResolveMode.PARTIAL)
val scope = template.getResolutionScope(context, facade)
val tempContext = expr.analyzeInContext(scope, template)
val trace = DelegatingBindingTrace(tempContext, "Evaluate '$literal'")
val languageVersionSettings = facade.getLanguageVersionSettings()
val value = ConstantExpressionEvaluator(module, languageVersionSettings, facade.project).evaluateExpression(expr, trace)
if (value == null || value.isError) return stringType
return value.toConstantValue(TypeUtils.NO_EXPECTED_TYPE).getType(module)
}
val template: KtStringTemplateExpression = startEntry.parent as KtStringTemplateExpression
val content = with(entries.map { it.text }.joinToString(separator = "")) { substring(prefix.length, length - suffix.length) }
val type = type ?: guessLiteralType(content)
val contentRange: TextRange
get() = TextRange(startEntry.startOffset + prefix.length, endEntry.endOffset - suffix.length)
val relativeContentRange: TextRange
get() = contentRange.shiftRight(-template.startOffset)
val entries: Sequence<KtStringTemplateEntry>
get() = generateSequence(startEntry) { if (it != endEntry) it.nextSiblingOfSameType() else null }
fun createExpression(): KtExpression {
val quote = template.firstChild.text
val literalValue = if (KotlinBuiltIns.isString(type)) "$quote$content$quote" else content
return KtPsiFactory(startEntry).createExpression(literalValue).apply { extractableSubstringInfo = this@ExtractableSubstringInfo }
}
fun copy(newTemplate: KtStringTemplateExpression): ExtractableSubstringInfo {
val oldEntries = template.entries
val newEntries = newTemplate.entries
val startIndex = oldEntries.indexOf(startEntry)
val endIndex = oldEntries.indexOf(endEntry)
if (startIndex < 0 || startIndex >= newEntries.size || endIndex < 0 || endIndex >= newEntries.size) {
throw AssertionError("Old template($startIndex..$endIndex): ${template.text}, new template: ${newTemplate.text}")
}
return ExtractableSubstringInfo(newEntries[startIndex], newEntries[endIndex], prefix, suffix, type)
}
}
var KtExpression.extractableSubstringInfo: ExtractableSubstringInfo? by UserDataProperty(Key.create("EXTRACTED_SUBSTRING_INFO"))
val KtExpression.substringContextOrThis: KtExpression
get() = extractableSubstringInfo?.template ?: this
val PsiElement.substringContextOrThis: PsiElement
get() = (this as? KtExpression)?.extractableSubstringInfo?.template ?: this

View File

@@ -1,29 +0,0 @@
/*
* 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.util
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.command.CommandProcessor
enum class ActionRunningMode {
RUN_IN_CURRENT_THREAD,
RUN_IN_EDT
}
inline fun <T> ActionRunningMode.runAction(crossinline action: () -> T): T = when (this) {
ActionRunningMode.RUN_IN_CURRENT_THREAD -> {
action()
}
ActionRunningMode.RUN_IN_EDT -> {
var result: T? = null
ApplicationManager.getApplication().invokeAndWait {
CommandProcessor.getInstance().runUndoTransparentAction {
result = ApplicationManager.getApplication().runWriteAction<T> { action() }
}
}
result!!
}
}

View File

@@ -1,108 +0,0 @@
/*
* Copyright 2010-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.idea.util
import org.jetbrains.kotlin.descriptors.annotations.BuiltInAnnotationDescriptor
import org.jetbrains.kotlin.renderer.AnnotationArgumentsRenderingPolicy
import org.jetbrains.kotlin.renderer.ClassifierNamePolicy
import org.jetbrains.kotlin.renderer.DescriptorRenderer
import org.jetbrains.kotlin.renderer.DescriptorRenderer.Companion.FQ_NAMES_IN_TYPES
import org.jetbrains.kotlin.renderer.DescriptorRendererModifier
import org.jetbrains.kotlin.renderer.OverrideRenderingPolicy
import org.jetbrains.kotlin.resolve.calls.inference.isCaptured
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.checker.NewCapturedTypeConstructor
import org.jetbrains.kotlin.types.isDynamic
import org.jetbrains.kotlin.types.typeUtil.builtIns
object IdeDescriptorRenderers {
@JvmField
val APPROXIMATE_FLEXIBLE_TYPES: (KotlinType) -> KotlinType = { it.approximateFlexibleTypes(preferNotNull = false) }
@JvmField
val APPROXIMATE_FLEXIBLE_TYPES_NOT_NULL: (KotlinType) -> KotlinType = { it.approximateFlexibleTypes(preferNotNull = true) }
private fun unwrapAnonymousType(type: KotlinType): KotlinType {
if (type.isDynamic()) return type
if (type.constructor is NewCapturedTypeConstructor) return type
val classifier = type.constructor.declarationDescriptor
if (classifier != null && !classifier.name.isSpecial) return type
type.constructor.supertypes.singleOrNull()?.let { return it }
val builtIns = type.builtIns
return if (type.isMarkedNullable)
builtIns.nullableAnyType
else
builtIns.anyType
}
private val BASE: DescriptorRenderer = DescriptorRenderer.withOptions {
normalizedVisibilities = true
withDefinedIn = false
renderDefaultVisibility = false
overrideRenderingPolicy = OverrideRenderingPolicy.RENDER_OVERRIDE
unitReturnType = false
enhancedTypes = true
modifiers = DescriptorRendererModifier.ALL
renderUnabbreviatedType = false
annotationArgumentsRenderingPolicy = AnnotationArgumentsRenderingPolicy.UNLESS_EMPTY
}
@JvmField
val SOURCE_CODE: DescriptorRenderer = BASE.withOptions {
classifierNamePolicy = ClassifierNamePolicy.SOURCE_CODE_QUALIFIED
typeNormalizer = { APPROXIMATE_FLEXIBLE_TYPES(unwrapAnonymousType(it)) }
}
@JvmField
val FQ_NAMES_IN_TYPES_WITH_NORMALIZER: DescriptorRenderer = FQ_NAMES_IN_TYPES.withOptions {
typeNormalizer = { APPROXIMATE_FLEXIBLE_TYPES(unwrapAnonymousType(it)) }
}
@JvmField
val SOURCE_CODE_TYPES: DescriptorRenderer = BASE.withOptions {
classifierNamePolicy = ClassifierNamePolicy.SOURCE_CODE_QUALIFIED
typeNormalizer = { APPROXIMATE_FLEXIBLE_TYPES(unwrapAnonymousType(it)) }
annotationFilter = { it !is BuiltInAnnotationDescriptor }
parameterNamesInFunctionalTypes = false
}
@JvmField
val SOURCE_CODE_TYPES_WITH_SHORT_NAMES: DescriptorRenderer = BASE.withOptions {
classifierNamePolicy = ClassifierNamePolicy.SHORT
typeNormalizer = { APPROXIMATE_FLEXIBLE_TYPES(unwrapAnonymousType(it)) }
modifiers -= DescriptorRendererModifier.ANNOTATIONS
parameterNamesInFunctionalTypes = false
}
@JvmField
val SOURCE_CODE_NOT_NULL_TYPE_APPROXIMATION: DescriptorRenderer = BASE.withOptions {
classifierNamePolicy = ClassifierNamePolicy.SOURCE_CODE_QUALIFIED
typeNormalizer = { APPROXIMATE_FLEXIBLE_TYPES_NOT_NULL(unwrapAnonymousType(it)) }
presentableUnresolvedTypes = true
informativeErrorType = false
}
@JvmField
val SOURCE_CODE_SHORT_NAMES_NO_ANNOTATIONS: DescriptorRenderer = BASE.withOptions {
classifierNamePolicy = ClassifierNamePolicy.SHORT
typeNormalizer = { APPROXIMATE_FLEXIBLE_TYPES(unwrapAnonymousType(it)) }
modifiers -= DescriptorRendererModifier.ANNOTATIONS
}
}

View File

@@ -1,56 +0,0 @@
/*
* Copyright 2010-2019 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.util
import com.intellij.openapi.components.ServiceManager
import com.intellij.openapi.project.Project
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.resolve.ImportPath
import java.util.*
abstract class ImportInsertHelper {
/*TODO: implementation is not quite correct*/
abstract fun isImportedWithDefault(importPath: ImportPath, contextFile: KtFile): Boolean
abstract fun isImportedWithLowPriorityDefaultImport(importPath: ImportPath, contextFile: KtFile): Boolean
abstract fun mayImportOnShortenReferences(descriptor: DeclarationDescriptor): Boolean
abstract val importSortComparator: Comparator<ImportPath>
abstract fun importDescriptor(
file: KtFile,
descriptor: DeclarationDescriptor,
actionRunningMode: ActionRunningMode = ActionRunningMode.RUN_IN_CURRENT_THREAD,
forceAllUnderImport: Boolean = false
): ImportDescriptorResult
fun importDescriptor(
file: KtFile,
descriptor: DeclarationDescriptor,
forceAllUnderImport: Boolean = false
): ImportDescriptorResult = importDescriptor(
file,
descriptor,
ActionRunningMode.RUN_IN_CURRENT_THREAD,
forceAllUnderImport
)
companion object {
@JvmStatic
fun getInstance(project: Project): ImportInsertHelper =
ServiceManager.getService<ImportInsertHelper>(project, ImportInsertHelper::class.java)
}
}
enum class ImportDescriptorResult {
FAIL,
IMPORT_ADDED,
ALREADY_IMPORTED
}

View File

@@ -1,16 +0,0 @@
/*
* Copyright 2010-2018 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.
*/
@file:JvmName("CidrUtil")
package org.jetbrains.kotlin.idea.util
import com.intellij.util.PlatformUtils
// Currently CIDR IDEs (CLion and AppCode) have no Java support.
// Use this property to bypass Java-specific logic in CIDR.
val isRunningInCidrIde: Boolean by lazy(LazyThreadSafetyMode.PUBLICATION) {
PlatformUtils.isCidr()
}

View File

@@ -1,87 +0,0 @@
/*
* Copyright 2010-2019 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.util.psi.patternMatching
import com.intellij.openapi.util.TextRange
import com.intellij.psi.PsiComment
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiWhiteSpace
import org.jetbrains.kotlin.psi.KtElement
import org.jetbrains.kotlin.psi.KtTreeVisitorVoid
import org.jetbrains.kotlin.psi.psiUtil.siblings
import java.util.*
private val SIGNIFICANT_FILTER = { e: PsiElement -> e !is PsiWhiteSpace && e !is PsiComment && e.textLength > 0 }
interface KotlinPsiRange {
object Empty : KotlinPsiRange {
override val elements: List<PsiElement> get() = Collections.emptyList<PsiElement>()
override fun getTextRange(): TextRange = TextRange.EMPTY_RANGE
}
class ListRange(override val elements: List<PsiElement>) : KotlinPsiRange {
val startElement: PsiElement = elements.first()
val endElement: PsiElement = elements.last()
override fun getTextRange(): TextRange {
val startRange = startElement.textRange
val endRange = endElement.textRange
if (startRange == null || endRange == null) return TextRange.EMPTY_RANGE
return TextRange(startRange.startOffset, endRange.endOffset)
}
}
val elements: List<PsiElement>
fun getTextRange(): TextRange
fun isValid(): Boolean = elements.all { it.isValid }
val empty: Boolean get() = this is Empty
operator fun contains(element: PsiElement): Boolean = getTextRange().contains(element.textRange ?: TextRange.EMPTY_RANGE)
fun match(scope: PsiElement, unifier: KotlinPsiUnifier): List<UnificationResult.Matched> {
val elements = elements.filter(SIGNIFICANT_FILTER)
if (elements.isEmpty()) return Collections.emptyList()
val matches = ArrayList<UnificationResult.Matched>()
scope.accept(
object : KtTreeVisitorVoid() {
override fun visitKtElement(element: KtElement) {
val range = element
.siblings()
.filter(SIGNIFICANT_FILTER)
.take(elements.size)
.toList()
.toRange()
val result = unifier.unify(range, this@KotlinPsiRange)
if (result is UnificationResult.StronglyMatched) {
matches.add(result)
} else {
val matchCountSoFar = matches.size
super.visitKtElement(element)
if (result is UnificationResult.WeaklyMatched && matches.size == matchCountSoFar) {
matches.add(result)
}
}
}
}
)
return matches
}
}
fun List<PsiElement>.toRange(significantOnly: Boolean = true): KotlinPsiRange {
val elements = if (significantOnly) filter(SIGNIFICANT_FILTER) else this
return if (elements.isEmpty()) KotlinPsiRange.Empty else KotlinPsiRange.ListRange(elements)
}
fun PsiElement?.toRange(): KotlinPsiRange = this?.let { KotlinPsiRange.ListRange(Collections.singletonList(it)) } ?: KotlinPsiRange.Empty

View File

@@ -1,623 +0,0 @@
/*
* Copyright 2010-2019 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.resolve.lazy
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiRecursiveElementVisitor
import org.jetbrains.kotlin.KtNodeTypes
import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.*
import org.jetbrains.kotlin.resolve.StatementFilter
import org.jetbrains.kotlin.util.isProbablyNothing
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull
import org.jetbrains.kotlin.utils.addToStdlib.swap
import java.util.*
//TODO: do resolve anonymous object's body
class PartialBodyResolveFilter(
elementsToResolve: Collection<KtElement>,
private val declaration: KtDeclaration,
forCompletion: Boolean
) : StatementFilter() {
private val statementMarks = StatementMarks()
private val globalProbablyNothingCallableNames = ProbablyNothingCallableNames.getInstance(declaration.project)
private val globalProbablyContractedCallableNames = ProbablyContractedCallableNames.getInstance(declaration.project)
private val contextNothingFunctionNames = HashSet<String>()
private val contextNothingVariableNames = HashSet<String>()
override val filter: ((KtExpression) -> Boolean)? = { statementMarks.statementMark(it) != MarkLevel.NONE }
val allStatementsToResolve: Collection<KtExpression>
get() = statementMarks.allMarkedStatements()
init {
elementsToResolve.forEach { assert(declaration.isAncestor(it)) }
assert(!KtPsiUtil.isLocal(declaration)) { "Should never be invoked on local declaration otherwise we may miss some local declarations with type Nothing" }
declaration.forEachDescendantOfType<KtCallableDeclaration> { declaration ->
if (declaration.typeReference.containsProbablyNothing()) {
val name = declaration.name
if (name != null) {
if (declaration is KtNamedFunction) {
contextNothingFunctionNames.add(name)
} else {
contextNothingVariableNames.add(name)
}
}
}
}
elementsToResolve.forEach {
statementMarks.mark(it, if (forCompletion) MarkLevel.NEED_COMPLETION else MarkLevel.NEED_REFERENCE_RESOLVE)
}
declaration.forTopLevelBlocksInside { processBlock(it) }
}
//TODO: do..while is special case
private fun processBlock(block: KtBlockExpression): NameFilter {
if (isValueNeeded(block)) {
block.lastStatement()?.let { statementMarks.mark(it, MarkLevel.NEED_REFERENCE_RESOLVE) }
}
val nameFilter = NameFilter()
val startStatement = statementMarks.lastMarkedStatement(block, MarkLevel.NEED_REFERENCE_RESOLVE) ?: return nameFilter
for (statement in startStatement.siblings(forward = false)) {
if (statement !is KtExpression) continue
if (statement is KtNamedDeclaration) {
val name = statement.getName()
if (name != null && nameFilter(name)) {
statementMarks.mark(statement, MarkLevel.NEED_REFERENCE_RESOLVE)
}
} else if (statement is KtDestructuringDeclaration) {
if (statement.entries.any {
val name = it.name
name != null && nameFilter(name)
}) {
statementMarks.mark(statement, MarkLevel.NEED_REFERENCE_RESOLVE)
}
}
fun updateNameFilter() {
when (statementMarks.statementMark(statement)) {
MarkLevel.NONE, MarkLevel.TAKE -> {
}
MarkLevel.NEED_REFERENCE_RESOLVE -> nameFilter.addUsedNames(statement)
MarkLevel.NEED_COMPLETION -> nameFilter.addAllNames()
}
}
updateNameFilter()
if (!nameFilter.isEmpty) {
val smartCastPlaces = potentialSmartCastPlaces(statement, { it.affectsNames(nameFilter) })
if (!smartCastPlaces.isEmpty()) {
//TODO: do we really need correct resolve for ALL smart cast places?
smartCastPlaces.values
.flatten()
.forEach { statementMarks.mark(it, MarkLevel.NEED_REFERENCE_RESOLVE) }
updateNameFilter()
}
}
val level = statementMarks.statementMark(statement)
if (level > MarkLevel.TAKE) { // otherwise there are no statements inside that need processBlock which only works when reference resolve needed
statement.forTopLevelBlocksInside { nestedBlock ->
val childFilter = processBlock(nestedBlock)
nameFilter.addNamesFromFilter(childFilter)
}
}
}
return nameFilter
}
/**
* Finds places within the given statement that may affect smart-casts after it.
* That is, such places whose containing statements must be left in code to keep the smart-casts.
* Returns map from smart-cast expression names (variable name or qualified variable name) to places.
*/
private fun potentialSmartCastPlaces(
statement: KtExpression,
filter: (SmartCastName) -> Boolean = { true }
): Map<SmartCastName, List<KtExpression>> {
val map = HashMap<SmartCastName, ArrayList<KtExpression>>(0)
fun addPlace(name: SmartCastName, place: KtExpression) {
map.getOrPut(name, { ArrayList(1) }).add(place)
}
fun addPlaces(name: SmartCastName, places: Collection<KtExpression>) {
if (places.isNotEmpty()) {
map.getOrPut(name, { ArrayList(places.size) }).addAll(places)
}
}
fun addIfCanBeSmartCast(expression: KtExpression) {
val name = expression.smartCastExpressionName() ?: return
if (filter(name)) {
addPlace(name, expression)
}
}
statement.accept(object : ControlFlowVisitor() {
override fun visitPostfixExpression(expression: KtPostfixExpression) {
expression.acceptChildren(this)
if (expression.operationToken == KtTokens.EXCLEXCL) {
addIfCanBeSmartCast(expression.baseExpression ?: return)
}
}
override fun visitCallExpression(expression: KtCallExpression) {
super.visitCallExpression(expression)
val nameReference = expression.calleeExpression as? KtNameReferenceExpression ?: return
if (!globalProbablyContractedCallableNames.isProbablyContractedCallableName(nameReference.getReferencedName())) return
val mentionedSmartCastName = expression.findMentionedName(filter)
if (mentionedSmartCastName != null) {
addPlace(mentionedSmartCastName, expression)
}
}
override fun visitBinaryWithTypeRHSExpression(expression: KtBinaryExpressionWithTypeRHS) {
expression.acceptChildren(this)
if (expression.operationReference.getReferencedNameElementType() == KtTokens.AS_KEYWORD) {
addIfCanBeSmartCast(expression.left)
}
}
override fun visitBinaryExpression(expression: KtBinaryExpression) {
expression.acceptChildren(this)
if (expression.operationToken == KtTokens.ELVIS) {
val left = expression.left
val right = expression.right
if (left != null && right != null) {
val smartCastName = left.smartCastExpressionName()
if (smartCastName != null && filter(smartCastName)) {
val exits = collectAlwaysExitPoints(right)
addPlaces(smartCastName, exits)
}
}
}
}
override fun visitIfExpression(expression: KtIfExpression) {
val condition = expression.condition
val thenBranch = expression.then
val elseBranch = expression.`else`
val (thenSmartCastNames, elseSmartCastNames) = possiblySmartCastInCondition(condition)
fun processBranchExits(smartCastNames: Collection<SmartCastName>, branch: KtExpression?) {
if (branch == null) return
val filteredNames = smartCastNames.filter(filter)
if (filteredNames.isNotEmpty()) {
val exits = collectAlwaysExitPoints(branch)
if (exits.isNotEmpty()) {
for (name in filteredNames) {
addPlaces(name, exits)
}
}
}
}
processBranchExits(thenSmartCastNames, elseBranch)
processBranchExits(elseSmartCastNames, thenBranch)
condition?.accept(this)
if (thenBranch != null && elseBranch != null) {
val thenCasts = potentialSmartCastPlaces(thenBranch, filter)
if (thenCasts.isNotEmpty()) {
val elseCasts = potentialSmartCastPlaces(elseBranch) { filter(it) && thenCasts.containsKey(it) }
if (elseCasts.isNotEmpty()) {
for ((name, places) in thenCasts) {
if (elseCasts.containsKey(name)) { // need filtering by cast names in else-branch
addPlaces(name, places)
}
}
for ((name, places) in elseCasts) { // already filtered by cast names in then-branch
addPlaces(name, places)
}
}
}
}
}
override fun visitForExpression(expression: KtForExpression) {
// analyze only the loop-range expression, do not enter the loop body
expression.loopRange?.accept(this)
}
override fun visitWhileExpression(expression: KtWhileExpression) {
val condition = expression.condition
// we need to enter the body only for "while(true)"
if (condition.isTrueConstant()) {
expression.acceptChildren(this)
} else {
condition?.accept(this)
}
}
//TODO: when
})
return map
}
private fun KtExpression.findMentionedName(filter: (SmartCastName) -> Boolean): SmartCastName? {
var foundMentionedName: SmartCastName? = null
val visitor = object : PsiRecursiveElementVisitor() {
override fun visitElement(element: PsiElement) {
if (foundMentionedName != null) return
if (element !is KtSimpleNameExpression) super.visitElement(element)
if (element !is KtExpression) return
element.smartCastExpressionName()?.takeIf(filter)?.let { foundMentionedName = it }
}
}
accept(visitor)
return foundMentionedName
}
/**
* Returns names of expressions that would possibly be smart cast
* in then (first component) and else (second component)
* branches of an if-statement with such condition
*/
private fun possiblySmartCastInCondition(condition: KtExpression?): Pair<Set<SmartCastName>, Set<SmartCastName>> {
val emptyResult = Pair(setOf<SmartCastName>(), setOf<SmartCastName>())
when (condition) {
is KtBinaryExpression -> {
val operation = condition.operationToken
val left = condition.left ?: return emptyResult
val right = condition.right ?: return emptyResult
fun smartCastInEq(): Pair<Set<SmartCastName>, Set<SmartCastName>> = when {
left.isNullLiteral() -> {
Pair(setOf(), right.smartCastExpressionName().singletonOrEmptySet())
}
right.isNullLiteral() -> {
Pair(setOf(), left.smartCastExpressionName().singletonOrEmptySet())
}
else -> {
val leftName = left.smartCastExpressionName()
val rightName = right.smartCastExpressionName()
val names = listOfNotNull(leftName, rightName).toSet()
Pair(names, setOf())
}
}
when (operation) {
KtTokens.EQEQ, KtTokens.EQEQEQ -> return smartCastInEq()
KtTokens.EXCLEQ, KtTokens.EXCLEQEQEQ -> return smartCastInEq().swap()
KtTokens.ANDAND -> {
val casts1 = possiblySmartCastInCondition(left)
val casts2 = possiblySmartCastInCondition(right)
return Pair(casts1.first.union(casts2.first), casts1.second.intersect(casts2.second))
}
KtTokens.OROR -> {
val casts1 = possiblySmartCastInCondition(left)
val casts2 = possiblySmartCastInCondition(right)
return Pair(casts1.first.intersect(casts2.first), casts1.second.union(casts2.second))
}
}
}
is KtIsExpression -> {
val cast = condition.leftHandSide.smartCastExpressionName().singletonOrEmptySet()
return if (condition.isNegated) Pair(setOf(), cast) else Pair(cast, setOf())
}
is KtPrefixExpression -> {
if (condition.operationToken == KtTokens.EXCL) {
val operand = condition.baseExpression ?: return emptyResult
return possiblySmartCastInCondition(operand).swap()
}
}
is KtParenthesizedExpression -> {
val operand = condition.expression ?: return emptyResult
return possiblySmartCastInCondition(operand)
}
}
return emptyResult
}
/**
* If it's possible that the given statement never passes the execution to the next statement (that is, always exits somewhere)
* then this function returns a collection of all places in code that are necessary to be kept to preserve this behaviour.
*/
private fun collectAlwaysExitPoints(statement: KtExpression?): Collection<KtExpression> {
val result = ArrayList<KtExpression>()
statement?.accept(object : ControlFlowVisitor() {
var insideLoopLevel: Int = 0
override fun visitReturnExpression(expression: KtReturnExpression) {
result.add(expression)
}
override fun visitThrowExpression(expression: KtThrowExpression) {
result.add(expression)
}
override fun visitIfExpression(expression: KtIfExpression) {
expression.condition?.accept(this)
val thenBranch = expression.then
val elseBranch = expression.`else`
if (thenBranch != null && elseBranch != null) { // if we have only one branch it makes no sense to search exits in it
val thenExits = collectAlwaysExitPoints(thenBranch)
if (thenExits.isNotEmpty()) {
val elseExits = collectAlwaysExitPoints(elseBranch)
if (elseExits.isNotEmpty()) {
result.addAll(thenExits)
result.addAll(elseExits)
}
}
}
}
override fun visitForExpression(loop: KtForExpression) {
loop.loopRange?.accept(this)
// do not make sense to search exits inside for as not necessary enter it at all
}
override fun visitWhileExpression(loop: KtWhileExpression) {
val condition = loop.condition ?: return
if (condition.isTrueConstant()) {
insideLoopLevel++
loop.body?.accept(this)
insideLoopLevel--
} else {
// do not make sense to search exits inside while-loop as not necessary enter it at all
condition.accept(this)
}
}
override fun visitDoWhileExpression(loop: KtDoWhileExpression) {
loop.condition?.accept(this)
insideLoopLevel++
loop.body?.accept(this)
insideLoopLevel--
}
override fun visitBreakExpression(expression: KtBreakExpression) {
if (insideLoopLevel == 0 || expression.getLabelName() != null) {
result.add(expression)
}
}
override fun visitContinueExpression(expression: KtContinueExpression) {
if (insideLoopLevel == 0 || expression.getLabelName() != null) {
result.add(expression)
}
}
override fun visitCallExpression(expression: KtCallExpression) {
val name = (expression.calleeExpression as? KtSimpleNameExpression)?.getReferencedName()
if (name != null && (name in globalProbablyNothingCallableNames.functionNames() || name in contextNothingFunctionNames)) {
result.add(expression)
}
super.visitCallExpression(expression)
}
override fun visitSimpleNameExpression(expression: KtSimpleNameExpression) {
val name = expression.getReferencedName()
if (name in globalProbablyNothingCallableNames.propertyNames() || name in contextNothingVariableNames) {
result.add(expression)
}
}
override fun visitBinaryExpression(expression: KtBinaryExpression) {
if (expression.operationToken == KtTokens.ELVIS) {
// do not search exits after "?:"
expression.left?.accept(this)
} else {
super.visitBinaryExpression(expression)
}
}
})
return result
}
/**
* Recursively visits code but does not enter constructs that may not affect smart casts/control flow
*/
private abstract class ControlFlowVisitor : KtVisitorVoid() {
override fun visitKtElement(element: KtElement) {
if (element.noControlFlowInside()) return
element.acceptChildren(this)
}
private fun KtElement.noControlFlowInside() = this is KtFunction || this is KtClass || this is KtClassBody
}
private data class SmartCastName(
private val receiverName: SmartCastName?,
private val selectorName: String? /* null means "this" (and receiverName should be null */
) {
init {
if (selectorName == null) {
assert(receiverName == null) { "selectorName is allowed to be null only when receiverName is also null (which means 'this')" }
}
}
override fun toString(): String = if (receiverName != null) "$receiverName.$selectorName" else selectorName ?: "this"
fun affectsNames(nameFilter: (String) -> Boolean): Boolean {
if (selectorName == null) return true
if (!nameFilter(selectorName)) return false
return receiverName == null || receiverName.affectsNames(nameFilter)
}
}
private fun KtExpression.smartCastExpressionName(): SmartCastName? {
return when (this) {
is KtSimpleNameExpression -> SmartCastName(null, this.getReferencedName())
is KtQualifiedExpression -> {
val selector = selectorExpression as? KtSimpleNameExpression ?: return null
val selectorName = selector.getReferencedName()
val receiver = receiverExpression
if (receiver is KtThisExpression) {
return SmartCastName(null, selectorName)
}
val receiverName = receiver.smartCastExpressionName() ?: return null
return SmartCastName(receiverName, selectorName)
}
is KtThisExpression -> SmartCastName(null, null)
else -> null
}
}
//TODO: declarations with special names (e.g. "get")
private class NameFilter : (String) -> Boolean {
private var names: MutableSet<String>? = HashSet()
override fun invoke(name: String) = names == null || name in names!!
val isEmpty: Boolean
get() = names?.isEmpty() ?: false
fun addUsedNames(statement: KtExpression) {
if (names != null) {
statement.forEachDescendantOfType<KtSimpleNameExpression>(canGoInside = { it !is KtBlockExpression }) {
names!!.add(it.getReferencedName())
}
}
}
fun addNamesFromFilter(filter: NameFilter) {
if (names == null) return
if (filter.names == null) {
names = null
} else {
names!!.addAll(filter.names!!)
}
}
fun addAllNames() {
names = null
}
}
private enum class MarkLevel {
NONE,
TAKE,
NEED_REFERENCE_RESOLVE,
NEED_COMPLETION
}
companion object {
fun findStatementToResolve(element: KtElement, declaration: KtDeclaration): KtExpression? {
return element.parentsWithSelf.takeWhile { it != declaration }.firstOrNull { it.isStatement() } as KtExpression?
}
private fun KtElement.forTopLevelBlocksInside(action: (KtBlockExpression) -> Unit) {
forEachDescendantOfType(canGoInside = { it !is KtBlockExpression }, action = action)
}
private fun KtExpression?.isNullLiteral() = this?.node?.elementType == KtNodeTypes.NULL
private fun KtExpression?.isTrueConstant() = this != null && node?.elementType == KtNodeTypes.BOOLEAN_CONSTANT && text == "true"
private fun <T : Any> T?.singletonOrEmptySet(): Set<T> = if (this != null) setOf(this) else setOf()
//TODO: review logic
private fun isValueNeeded(expression: KtExpression): Boolean = when (val parent = expression.parent) {
is KtBlockExpression -> expression == parent.lastStatement() && isValueNeeded(parent)
is KtContainerNode -> { //TODO - not quite correct
val pparent = parent.parent as? KtExpression
pparent != null && isValueNeeded(pparent)
}
is KtDeclarationWithBody -> {
if (expression == parent.bodyExpression)
!parent.hasBlockBody() && !parent.hasDeclaredReturnType()
else
true
}
is KtAnonymousInitializer -> false
else -> true
}
private fun KtBlockExpression.lastStatement(): KtExpression? =
lastChild?.siblings(forward = false)?.firstIsInstanceOrNull<KtExpression>()
private fun PsiElement.isStatement() = this is KtExpression && parent is KtBlockExpression
private fun KtTypeReference?.containsProbablyNothing() =
this?.typeElement?.anyDescendantOfType<KtUserType> { it.isProbablyNothing() } ?: false
}
private inner class StatementMarks {
private val statementMarks = HashMap<KtExpression, MarkLevel>()
private val blockLevels = HashMap<KtBlockExpression, MarkLevel>()
fun mark(element: PsiElement, level: MarkLevel) {
var e = element
while (e != declaration) {
if (e.isStatement()) {
markStatement(e as KtExpression, level)
}
e = e.parent!!
}
}
private fun markStatement(statement: KtExpression, level: MarkLevel) {
val currentLevel = statementMark(statement)
if (currentLevel < level) {
statementMarks[statement] = level
val block = statement.parent as KtBlockExpression
val currentBlockLevel = blockLevels[block] ?: MarkLevel.NONE
if (currentBlockLevel < level) {
blockLevels[block] = level
}
}
}
fun statementMark(statement: KtExpression): MarkLevel = statementMarks[statement] ?: MarkLevel.NONE
fun allMarkedStatements(): Collection<KtExpression> = statementMarks.keys
fun lastMarkedStatement(block: KtBlockExpression, minLevel: MarkLevel): KtExpression? {
val level = blockLevels[block] ?: MarkLevel.NONE
if (level < minLevel) return null // optimization
return block.lastChild.siblings(forward = false)
.filterIsInstance<KtExpression>()
.first { statementMark(it) >= minLevel }
}
}
}

View File

@@ -1,18 +0,0 @@
/*
* Copyright 2010-2018 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.resolve.lazy
import com.intellij.openapi.components.ServiceManager
import com.intellij.openapi.project.Project
interface ProbablyContractedCallableNames {
fun isProbablyContractedCallableName(name: String): Boolean
companion object {
fun getInstance(project: Project): ProbablyContractedCallableNames =
ServiceManager.getService(project, ProbablyContractedCallableNames::class.java)!!
}
}

View File

@@ -1,29 +0,0 @@
/*
* Copyright 2010-2015 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.resolve.lazy
import com.intellij.openapi.components.ServiceManager
import com.intellij.openapi.project.Project
interface ProbablyNothingCallableNames {
fun functionNames(): Collection<String>
fun propertyNames(): Collection<String>
companion object {
fun getInstance(project: Project) = ServiceManager.getService(project, ProbablyNothingCallableNames::class.java)!!
}
}

View File

@@ -1,78 +0,0 @@
/*
* Copyright 2010-2015 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.util
import com.google.common.collect.HashMultimap
import com.google.common.collect.Multimap
import com.intellij.openapi.util.Key
import com.intellij.psi.stubs.PsiFileStub
import com.intellij.psi.stubs.StubElement
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.psi.KtTypeReference
import org.jetbrains.kotlin.psi.KtUserType
fun KtUserType.aliasImportMap(): Multimap<String, String> {
// we need to access containing file via stub because getPsi() may return null when indexing and getContainingFile() will crash
val file = stub?.getContainingFileStub()?.psi ?: return HashMultimap.create()
return (file as KtFile).aliasImportMap()
}
fun KtFile.aliasImportMap(): Multimap<String, String> {
val cached = getUserData(ALIAS_IMPORT_DATA_KEY)
val modificationStamp = modificationStamp
if (cached != null && modificationStamp == cached.fileModificationStamp) {
return cached.map
}
val data = CachedAliasImportData(buildAliasImportMap(), modificationStamp)
putUserData(ALIAS_IMPORT_DATA_KEY, data)
return data.map
}
private fun KtFile.buildAliasImportMap(): Multimap<String, String> {
val map = HashMultimap.create<String, String>()
val importList = importList ?: return map
for (import in importList.imports) {
val aliasName = import.aliasName ?: continue
val name = import.importPath?.fqName?.shortName()?.asString() ?: continue
map.put(aliasName, name)
}
return map
}
private class CachedAliasImportData(val map: Multimap<String, String>, val fileModificationStamp: Long)
private val ALIAS_IMPORT_DATA_KEY = Key<CachedAliasImportData>("ALIAS_IMPORT_MAP_KEY")
fun KtTypeReference?.isProbablyNothing(): Boolean {
val userType = this?.typeElement as? KtUserType ?: return false
return userType.isProbablyNothing()
}
//TODO: support type aliases
fun KtUserType?.isProbablyNothing(): Boolean {
if (this == null) return false
val referencedName = referencedName
return referencedName == "Nothing" || aliasImportMap()[referencedName].contains("Nothing")
}
private fun StubElement<*>.getContainingFileStub(): PsiFileStub<*> {
return if (this is PsiFileStub)
this
else
parentStub.getContainingFileStub()
}

View File

@@ -1,49 +0,0 @@
/*
* Copyright 2010-2019 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.types.substitutions
import org.jetbrains.kotlin.descriptors.CallableDescriptor
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.types.*
import org.jetbrains.kotlin.types.checker.TypeCheckingProcedure
import java.util.*
fun getTypeSubstitution(baseType: KotlinType, derivedType: KotlinType): LinkedHashMap<TypeConstructor, TypeProjection>? {
val substitutedType = TypeCheckingProcedure.findCorrespondingSupertype(derivedType, baseType) ?: return null
val substitution = LinkedHashMap<TypeConstructor, TypeProjection>(substitutedType.arguments.size)
for ((param, arg) in baseType.constructor.parameters.zip(substitutedType.arguments)) {
substitution[param.typeConstructor] = arg
}
return substitution
}
fun getCallableSubstitution(
baseCallable: CallableDescriptor,
derivedCallable: CallableDescriptor
): MutableMap<TypeConstructor, TypeProjection>? {
val baseClass = baseCallable.containingDeclaration as? ClassDescriptor ?: return null
val derivedClass = derivedCallable.containingDeclaration as? ClassDescriptor ?: return null
val substitution = getTypeSubstitution(baseClass.defaultType, derivedClass.defaultType) ?: return null
for ((baseParam, derivedParam) in baseCallable.typeParameters.zip(derivedCallable.typeParameters)) {
substitution[baseParam.typeConstructor] = TypeProjectionImpl(derivedParam.defaultType)
}
return substitution
}
fun getCallableSubstitutor(
baseCallable: CallableDescriptor,
derivedCallable: CallableDescriptor
): TypeSubstitutor? {
return getCallableSubstitution(baseCallable, derivedCallable)?.let { TypeSubstitutor.create(it) }
}
fun getTypeSubstitutor(baseType: KotlinType, derivedType: KotlinType): TypeSubstitutor? {
return getTypeSubstitution(baseType, derivedType)?.let { TypeSubstitutor.create(it) }
}

View File

@@ -1,38 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="JAVA_MODULE" version="4">
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
</content>
<orderEntry type="jdk" jdkName="1.8" jdkType="JavaSDK" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="module" module-name="backend" />
<orderEntry type="module" module-name="frontend" />
<orderEntry type="module" module-name="frontend.java" />
<orderEntry type="module" module-name="light-classes" />
<orderEntry type="module" module-name="eval4j" />
<orderEntry type="module-library">
<library>
<CLASSES>
<root url="jar://$MODULE_DIR$/../../ideaSDK/core/annotations.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
</library>
</orderEntry>
<orderEntry type="library" name="guava" level="project" />
<orderEntry type="module" module-name="js.frontend" />
<orderEntry type="module" module-name="js.serializer" />
<orderEntry type="library" name="trove4j" level="project" />
<orderEntry type="module" module-name="ide-common" exported="" />
<orderEntry type="module" module-name="util" />
<orderEntry type="module" module-name="idea-core" />
<orderEntry type="module" module-name="idea-jps-common" />
<orderEntry type="module" module-name="cli-common" />
<orderEntry type="module" module-name="descriptors" />
<orderEntry type="library" exported="" name="idea-full" level="project" />
<orderEntry type="library" name="uast-java" level="project" />
<orderEntry type="module" module-name="frontend.script" />
</component>
</module>

View File

@@ -1,95 +0,0 @@
found.space=found:\u0020
html.0.has.no.corresponding.expected.declaration.1.html={0} has no corresponding expected declaration{1}
html.0.is.not.abstract.and.does.not.implement.abstract.base.class.member.br.1.html={0} is not abstract and does not implement abstract base class member<br/>{1}
html.0.is.not.abstract.and.does.not.implement.abstract.member.br.1.html={0} is not abstract and does not implement abstract member<br/>{1}
html.0.method.may.be.missing.none.of.the.following.functions.will.be.called.ul.1.ul.html=''{0}'' method may be missing. None of the following functions will be called: <ul>{1}</ul>
html.candidate.resolution.will.be.changed.soon.please.use.fully.qualified.name.to.invoke.the.following.closer.candidate.explicitly.ul.0.ul.html=Candidate resolution will be changed soon, please use fully qualified name to invoke the following closer candidate explicitly: <ul>{0}</ul>
html.expected.0.has.no.actual.declaration.in.module.1.2.html=Expected {0} has no actual declaration in module {1}{2}
html.accidental.override.0.html=Accidental override: {0}
html.method.contains.from.concurrenthashmap.may.have.unexpected.semantics.it.calls.containsvalue.instead.of.containskey.br.use.explicit.form.of.the.call.to.containskey.containsvalue.contains.or.cast.the.value.to.kotlin.collections.map.instead.br.see.https.youtrack.jetbrains.com.issue.kt.18053.for.more.details.html=Method ''contains'' from ConcurrentHashMap may have unexpected semantics: it calls ''containsValue'' instead of ''containsKey''.<br/>Use explicit form of the call to ''containsKey''/''containsValue''/''contains'' or cast the value to kotlin.collections.Map instead.<br/>See https://youtrack.jetbrains.com/issue/KT-18053 for more details
html.javascript.0.html=JavaScript: {0}
html.platform.declaration.clash.0.html=Platform declaration clash: {0}
html.internal.error.occurred.while.analyzing.this.expression.br.table.cellspacing.0.cellpadding.0.tr.td.strong.please.use.the.strong.td.td.img.src.0.td.td.strong.icon.in.the.bottom.right.corner.to.report.this.error.strong.td.tr.table.br.pre.0.pre.html=Internal Error occurred while analyzing this expression <br/><table cellspacing="0" cellpadding="0"><tr><td>(<strong>Please use the "</strong></td><td><img src="{0}"/></td><td><strong>" icon in the bottom-right corner to report this error</strong>):</td></tr></table><br/><pre>{0}</pre>
html.property.delegate.must.have.a.0.method.none.of.the.following.functions.are.suitable.ul.1.ul.html=Property delegate must have a ''{0}'' method. None of the following functions are suitable. <ul>{1}</ul>
html.overload.resolution.ambiguity.on.method.0.all.these.functions.match.ul.1.ul.html=Overload resolution ambiguity on method ''{0}''. All these functions match. <ul>{1}</ul>
html.unresolved.reference.br.none.of.the.following.candidates.is.applicable.because.of.receiver.type.mismatch.ul.0.ul.html=Unresolved reference. <br/> None of the following candidates is applicable because of receiver type mismatch: <ul>{0}</ul>
html.cannot.choose.among.the.following.candidates.without.completing.type.inference.ul.0.ul.html=Cannot choose among the following candidates without completing type inference: <ul>{0}</ul>
html.none.of.the.following.functions.can.be.called.with.the.arguments.supplied.ul.0.ul.html=None of the following functions can be called with the arguments supplied. <ul>{0}</ul>
html.overload.resolution.ambiguity.all.these.functions.match.ul.0.ul.html=Overload resolution ambiguity. All these functions match. <ul>{0}</ul>
html.function.return.type.mismatch.table.tr.td.expected.td.td.1.td.tr.tr.td.found.td.td.2.td.tr.table.html=Function return type mismatch.<table><tr><td>Expected:</td><td>{1}</td></tr><tr><td>Found:</td><td>{2}</td></tr></table>
html.0.must.override.1.br.because.it.inherits.many.implementations.of.it.html={0} must override {1}<br />because it inherits many implementations of it
html.types.of.inherited.var.properties.do.not.match.br.0.br.1.html=Types of inherited var-properties do not match:<br/>{0},<br/>{1}
html.types.of.inherited.properties.are.incompatible.br.0.br.1.html=Types of inherited properties are incompatible:<br/>{0},<br/>{1}
html.actual.class.0.has.no.corresponding.members.for.expected.class.members.1.html=Actual class ''{0}'' has no corresponding members for expected class members:{1}
html.val.property.cannot.override.var.property.br.1.html=Val-property cannot override var-property<br />{1}
html.var.property.type.is.0.which.is.not.a.type.of.overridden.br.1.html=Var-property type is {0}, which is not a type of overridden<br/>{1}
required.space=required:\u0020
type.inference.failed.expected.type.mismatch=Type inference failed. Expected type mismatch:\u0020
html.setter.parameter.type.must.be.equal.to.the.type.of.the.property.table.tr.td.expected.td.td.0.td.tr.tr.td.found.td.td.1.td.tr.table.html=Setter parameter type must be equal to the type of the property.<table><tr><td>Expected:</td><td>{0}</td></tr><tr><td>Found:</td><td>{1}</td></tr></table>
html.property.type.is.0.which.is.not.a.subtype.type.of.overridden.br.1.html=Property type is {0}, which is not a subtype type of overridden<br/>{1}
html.return.types.of.inherited.members.are.incompatible.br.0.br.1.html=Return types of inherited members are incompatible:<br/>{0},<br/>{1}
html.return.type.is.0.which.is.not.a.subtype.of.overridden.br.1.html=Return type is ''{0}'', which is not a subtype of overridden<br/>{1}
html.loop.parameter.type.mismatch.table.tr.td.iterated.values.td.td.0.td.tr.tr.td.parameter.td.td.1.td.tr.table.html=Loop parameter type mismatch.<table><tr><td>Iterated values:</td><td>{0}</td></tr><tr><td>Parameter:</td><td>{1}</td></tr></table>
html.type.argument.is.not.within.its.bounds.table.tr.td.expected.td.td.0.td.tr.tr.td.found.td.td.1.td.tr.table.html=Type argument is not within its bounds.<table><tr><td>Expected:</td><td>{0}</td></tr><tr><td>Found:</td><td>{1}</td></tr></table>
html.method.iterator.is.ambiguous.for.this.expression.ul.0.ul.html=Method ''iterator()'' is ambiguous for this expression.<ul>{0}</ul>
html.getter.return.type.must.be.equal.to.the.type.of.the.property.table.tr.td.expected.td.td.0.td.tr.tr.td.found.td.td.1.td.tr.table.html=Getter return type must be equal to the type of the property.<table><tr><td>Expected:</td><td>{0}</td></tr><tr><td>Found:</td><td>{1}</td></tr></table>
html.type.inference.failed.0.html=Type inference failed: {0}
html.assignment.operators.ambiguity.all.these.functions.match.ul.0.ul.table.html=Assignment operators ambiguity. All these functions match.<ul>{0}</ul></table>
html.type.mismatch.table.tr.td.required.td.td.0.td.tr.tr.td.found.td.td.1.td.tr.table.br.projected.type.2.restricts.use.of.br.3.html=Type mismatch.<table><tr><td>Required:</td><td>{0}</td></tr><tr><td>Found:</td><td>{1}</td></tr></table><br />\nProjected type {2} restricts use of <br />\n{3}\n
html.type.mismatch.table.tr.td.required.td.td.0.td.tr.tr.td.found.td.td.1.td.tr.table.html=Type mismatch.<table><tr><td>Required:</td><td>{0}</td></tr><tr><td>Found:</td><td>{1}</td></tr></table>
intention.suppress.family=Suppress Warnings
intention.suppress.text=Suppress ''{0}'' for {1} {2}
intention.calculating.text=Quick fix is being calculated ...
special.module.for.files.not.under.source.root=<special module for files not under source root>
sdk.0=<sdk {0}>
sources.for.library.0=<sources for library {0}>
library.0=<library {0}>
source.for.script.dependencies=<Source for script dependencies>
script.dependencies=<Script dependencies>
script.0.1=<script {0} {1}>
module.name.0.test={0} (test)
platform.module.0.including.1=<Platform module {0} including {1}>
the.following.declarations.have.the.same.jvm.signature.code.0.1.code.br.ul.2.ul=The following declarations have the same JVM signature (<code>{0}{1}</code>):<br/>\n<ul>\n{2}</ul>
declaration.kind.object=object
declaration.kind.companion.object=companion object
declaration.kind.initializer=initializer
declaration.kind.statement=statement
declaration.kind.file=file
declaration.name.0.of.1={0} of {1}
declaration.kind.secondary.constructor.of=secondary constructor of
declaration.kind.enum.entry=enum entry
declaration.kind.type.parameter=type parameter
declaration.kind.class=class
declaration.kind.interface=interface
declaration.kind.fun=fun
declaration.kind.parameter=parameter
type.parameters.where=where
cannot.be.inferred=cannot be inferred
i.for.i.br.0=<i> for </i><br/>{0}
defined.in=defined in
root.package=root package
automatically.declared.based.on.the.expected.type=Automatically declared based on the expected type
0.smart.cast.to.1={0} smart cast to {1}
unknown.receiver=Unknown receiver
implicit.receiver=Implicit receiver
extension.implicit.receiver=Extension implicit receiver
always.null=Always null
value.captured.in.a.closure=Value captured in a closure
wrapped.into.a.reference.object.to.be.modified.when.captured.in.a.closure=Wrapped into a reference object to be modified when captured in a closure
smart.cast.to.0.for.1.call=Smart cast to {0} (for {1} call)
smart.cast.to.0=Smart cast to {0}
replace.overloaded.operator.with.function.call=Replace overloaded operator with function call
class.initializer=<class initializer>
object.0=object{0}
show.non.public=Show non-public
show.properties=Show properties
klib.metadata.short=Klib Metadata
function.arguments=arguments:\u0020
function.receiver.0=receiver: {0}
kotlin.built.in.declarations=Kotlin built-in declarations
kotlin.javascript.meta.file=Kotlin JavaScript meta file
framework.name.kotlin.sdk=Kotlin SDK

View File

@@ -1,139 +0,0 @@
/*
* Copyright 2010-2018 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.caches.resolve
import com.intellij.openapi.project.Project
import com.intellij.openapi.roots.OrderRootType
import com.intellij.openapi.roots.libraries.Library
import com.intellij.openapi.roots.libraries.PersistentLibraryKind
import com.intellij.openapi.vfs.VirtualFile
import com.intellij.util.PathUtil
import org.jetbrains.kotlin.analyzer.ModuleInfo
import org.jetbrains.kotlin.analyzer.PlatformAnalysisParameters
import org.jetbrains.kotlin.analyzer.ResolverForModuleFactory
import org.jetbrains.kotlin.analyzer.ResolverForProject
import org.jetbrains.kotlin.analyzer.common.CommonAnalysisParameters
import org.jetbrains.kotlin.analyzer.common.CommonResolverForModuleFactory
import org.jetbrains.kotlin.builtins.DefaultBuiltIns
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.config.LanguageVersionSettings
import org.jetbrains.kotlin.context.ProjectContext
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
import org.jetbrains.kotlin.descriptors.PackageFragmentProvider
import org.jetbrains.kotlin.idea.caches.project.IdeaModuleInfo
import org.jetbrains.kotlin.idea.caches.project.LibraryInfo
import org.jetbrains.kotlin.idea.caches.project.SdkInfo
import org.jetbrains.kotlin.idea.caches.resolve.BuiltInsCacheKey
import org.jetbrains.kotlin.idea.framework.CommonLibraryKind
import org.jetbrains.kotlin.idea.klib.AbstractKlibLibraryInfo
import org.jetbrains.kotlin.idea.klib.createKlibPackageFragmentProvider
import org.jetbrains.kotlin.idea.klib.isKlibLibraryRootForPlatform
import org.jetbrains.kotlin.incremental.components.LookupTracker
import org.jetbrains.kotlin.konan.util.KlibMetadataFactories
import org.jetbrains.kotlin.library.metadata.NullFlexibleTypeDeserializer
import org.jetbrains.kotlin.platform.CommonPlatforms
import org.jetbrains.kotlin.platform.TargetPlatform
import org.jetbrains.kotlin.platform.impl.CommonIdePlatformKind
import org.jetbrains.kotlin.resolve.TargetEnvironment
import org.jetbrains.kotlin.serialization.deserialization.MetadataPackageFragment
import org.jetbrains.kotlin.serialization.konan.impl.KlibMetadataModuleDescriptorFactoryImpl
import org.jetbrains.kotlin.storage.StorageManager
class CommonPlatformKindResolution : IdePlatformKindResolution {
override fun isLibraryFileForPlatform(virtualFile: VirtualFile): Boolean {
return virtualFile.extension == MetadataPackageFragment.METADATA_FILE_EXTENSION ||
virtualFile.isKlibLibraryRootForPlatform(CommonPlatforms.defaultCommonPlatform)
}
override val libraryKind: PersistentLibraryKind<*>?
get() = CommonLibraryKind
override val kind get() = CommonIdePlatformKind
override fun getKeyForBuiltIns(moduleInfo: ModuleInfo, sdkInfo: SdkInfo?, stdlibInfo: LibraryInfo?): BuiltInsCacheKey =
BuiltInsCacheKey.DefaultBuiltInsKey
override fun createBuiltIns(
moduleInfo: IdeaModuleInfo,
projectContext: ProjectContext,
resolverForProject: ResolverForProject<IdeaModuleInfo>,
sdkDependency: SdkInfo?,
stdlibDependency: LibraryInfo?,
): KotlinBuiltIns {
return DefaultBuiltIns.Instance
}
override fun createLibraryInfo(project: Project, library: Library): List<LibraryInfo> {
val klibFiles = library.getFiles(OrderRootType.CLASSES).filter {
it.isKlibLibraryRootForPlatform(CommonPlatforms.defaultCommonPlatform)
}
return if (klibFiles.isNotEmpty()) {
klibFiles.mapNotNull {
val path = PathUtil.getLocalPath(it) ?: return@mapNotNull null
CommonKlibLibraryInfo(project, library, path)
}
} else {
// No klib files <=> old metadata-library <=> create usual LibraryInfo
listOf(CommonMetadataLibraryInfo(project, library))
}
}
override fun createKlibPackageFragmentProvider(
moduleInfo: ModuleInfo,
storageManager: StorageManager,
languageVersionSettings: LanguageVersionSettings,
moduleDescriptor: ModuleDescriptor
): PackageFragmentProvider? {
return (moduleInfo as? CommonKlibLibraryInfo)
?.resolvedKotlinLibrary
?.createKlibPackageFragmentProvider(
storageManager = storageManager,
metadataModuleDescriptorFactory = metadataModuleDescriptorFactory,
languageVersionSettings = languageVersionSettings,
moduleDescriptor = moduleDescriptor,
lookupTracker = LookupTracker.DO_NOTHING
)
}
override fun createResolverForModuleFactory(
settings: PlatformAnalysisParameters,
environment: TargetEnvironment,
platform: TargetPlatform
): ResolverForModuleFactory {
return CommonResolverForModuleFactory(
settings as CommonAnalysisParameters,
environment,
platform,
shouldCheckExpectActual = true
)
}
companion object {
private val metadataFactories = KlibMetadataFactories({ DefaultBuiltIns.Instance }, NullFlexibleTypeDeserializer)
private val metadataModuleDescriptorFactory = KlibMetadataModuleDescriptorFactoryImpl(
metadataFactories.DefaultDescriptorFactory,
metadataFactories.DefaultPackageFragmentsFactory,
metadataFactories.flexibleTypeDeserializer,
metadataFactories.platformDependentTypeTransformer
)
}
}
class CommonKlibLibraryInfo(
project: Project,
library: Library,
libraryRoot: String
) : AbstractKlibLibraryInfo(project, library, libraryRoot) {
override val platform: TargetPlatform
get() = CommonPlatforms.defaultCommonPlatform
}
class CommonMetadataLibraryInfo(project: Project, library: Library) : LibraryInfo(project, library) {
override val platform: TargetPlatform
get() = CommonPlatforms.defaultCommonPlatform
}

View File

@@ -1,276 +0,0 @@
/*
* 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.
*/
@file:Suppress("Duplicates")
package org.jetbrains.kotlin.caches.resolve
import com.intellij.psi.search.GlobalSearchScope
import com.intellij.util.containers.addIfNotNull
import org.jetbrains.kotlin.analyzer.*
import org.jetbrains.kotlin.analyzer.common.CommonAnalysisParameters
import org.jetbrains.kotlin.analyzer.common.configureCommonSpecificComponents
import org.jetbrains.kotlin.builtins.jvm.JvmBuiltInsPackageFragmentProvider
import org.jetbrains.kotlin.config.LanguageVersionSettings
import org.jetbrains.kotlin.container.*
import org.jetbrains.kotlin.context.ModuleContext
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
import org.jetbrains.kotlin.descriptors.PackageFragmentProvider
import org.jetbrains.kotlin.descriptors.impl.CompositePackageFragmentProvider
import org.jetbrains.kotlin.descriptors.impl.ModuleDescriptorImpl
import org.jetbrains.kotlin.frontend.di.configureModule
import org.jetbrains.kotlin.frontend.di.configureStandardResolveComponents
import org.jetbrains.kotlin.frontend.java.di.configureJavaSpecificComponents
import org.jetbrains.kotlin.frontend.java.di.initializeJavaSpecificComponents
import org.jetbrains.kotlin.idea.compiler.IdeSealedClassInheritorsProvider
import org.jetbrains.kotlin.idea.configuration.IdeBuiltInsLoadingState
import org.jetbrains.kotlin.idea.project.IdeaEnvironment
import org.jetbrains.kotlin.load.java.lazy.ModuleClassResolver
import org.jetbrains.kotlin.load.java.lazy.ModuleClassResolverImpl
import org.jetbrains.kotlin.load.kotlin.PackagePartProvider
import org.jetbrains.kotlin.load.kotlin.VirtualFileFinderFactory
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.platform.*
import org.jetbrains.kotlin.platform.TargetPlatform
import org.jetbrains.kotlin.platform.js.isJs
import org.jetbrains.kotlin.platform.jvm.JvmPlatform
import org.jetbrains.kotlin.platform.jvm.isJvm
import org.jetbrains.kotlin.platform.konan.NativePlatform
import org.jetbrains.kotlin.platform.konan.NativePlatforms
import org.jetbrains.kotlin.resolve.*
import org.jetbrains.kotlin.resolve.checkers.ExperimentalMarkerDeclarationAnnotationChecker
import org.jetbrains.kotlin.resolve.jvm.JavaDescriptorResolver
import org.jetbrains.kotlin.resolve.jvm.JvmPlatformParameters
import org.jetbrains.kotlin.resolve.lazy.ResolveSession
import org.jetbrains.kotlin.resolve.lazy.declarations.DeclarationProviderFactory
import org.jetbrains.kotlin.resolve.lazy.declarations.DeclarationProviderFactoryService
import org.jetbrains.kotlin.serialization.deserialization.MetadataPackageFragmentProvider
import org.jetbrains.kotlin.serialization.deserialization.MetadataPartProvider
import org.jetbrains.kotlin.storage.StorageManager
class CompositeResolverForModuleFactory(
private val commonAnalysisParameters: CommonAnalysisParameters,
private val jvmAnalysisParameters: JvmPlatformParameters,
private val targetPlatform: TargetPlatform,
private val compilerServices: CompositeAnalyzerServices
) : ResolverForModuleFactory() {
override fun <M : ModuleInfo> createResolverForModule(
moduleDescriptor: ModuleDescriptorImpl,
moduleContext: ModuleContext,
moduleContent: ModuleContent<M>,
resolverForProject: ResolverForProject<M>,
languageVersionSettings: LanguageVersionSettings,
sealedInheritorsProvider: SealedClassInheritorsProvider
): ResolverForModule {
val (moduleInfo, syntheticFiles, moduleContentScope) = moduleContent
val project = moduleContext.project
val declarationProviderFactory = DeclarationProviderFactoryService.createDeclarationProviderFactory(
project, moduleContext.storageManager, syntheticFiles,
moduleContentScope,
moduleInfo
)
val metadataPartProvider = commonAnalysisParameters.metadataPartProviderFactory(moduleContent)
val trace = CodeAnalyzerInitializer.getInstance(project).createTrace()
val moduleClassResolver = ModuleClassResolverImpl { javaClass ->
val referencedClassModule = jvmAnalysisParameters.moduleByJavaClass(javaClass)
// We don't have full control over idea resolve api so we allow for a situation which should not happen in Kotlin.
// For example, type in a java library can reference a class declared in a source root (is valid but rare case)
// Providing a fallback strategy in this case can hide future problems, so we should at least log to be able to diagnose those
@Suppress("UNCHECKED_CAST")
val resolverForReferencedModule = referencedClassModule?.let { resolverForProject.tryGetResolverForModule(it as M) }
val resolverForModule = resolverForReferencedModule?.takeIf {
referencedClassModule.platform.has<JvmPlatform>()
} ?: run {
// in case referenced class lies outside of our resolver, resolve the class as if it is inside our module
// this leads to java class being resolved several times
resolverForProject.resolverForModule(moduleInfo)
}
resolverForModule.componentProvider.get<JavaDescriptorResolver>()
}
val packagePartProvider = jvmAnalysisParameters.packagePartProviderFactory(moduleContent)
val container = createContainerForCompositePlatform(
moduleContext, moduleContentScope, languageVersionSettings, targetPlatform,
compilerServices, trace, declarationProviderFactory, metadataPartProvider,
moduleClassResolver, packagePartProvider
)
val packageFragmentProviders = sequence {
yield(container.get<ResolveSession>().packageFragmentProvider)
yieldAll(getCommonProvidersIfAny(moduleInfo, moduleContext, moduleDescriptor, container)) // todo: module context
yieldAll(getJsProvidersIfAny(moduleInfo, moduleContext, moduleDescriptor, container))
yieldAll(getJvmProvidersIfAny(container))
yieldAll(getNativeProvidersIfAny(moduleInfo, container))
}.toList()
return ResolverForModule(
CompositePackageFragmentProvider(
packageFragmentProviders,
"CompositeProvider@CompositeResolver for $moduleDescriptor"
),
container
)
}
private fun getCommonProvidersIfAny(
moduleInfo: ModuleInfo,
moduleContext: ModuleContext,
moduleDescriptor: ModuleDescriptor,
container: StorageComponentContainer
): List<PackageFragmentProvider> {
if (!targetPlatform.isCommon()) return emptyList()
val metadataProvider = container.get<MetadataPackageFragmentProvider>()
val klibMetadataProvider = CommonPlatforms.defaultCommonPlatform.idePlatformKind.resolution.createKlibPackageFragmentProvider(
moduleInfo,
moduleContext.storageManager,
container.get<LanguageVersionSettings>(),
moduleDescriptor
)
return listOfNotNull(metadataProvider, klibMetadataProvider)
}
@OptIn(ExperimentalStdlibApi::class)
private fun getJvmProvidersIfAny(container: StorageComponentContainer): List<PackageFragmentProvider> =
buildList {
if (targetPlatform.has<JvmPlatform>()) add(container.get<JavaDescriptorResolver>().packageFragmentProvider)
// Use JVM built-ins only for completely-JVM modules
addIfNotNull(container.tryGetService(JvmBuiltInsPackageFragmentProvider::class.java))
}
private fun getNativeProvidersIfAny(moduleInfo: ModuleInfo, container: StorageComponentContainer): List<PackageFragmentProvider> {
if (!targetPlatform.has<NativePlatform>()) return emptyList()
return listOfNotNull(
NativePlatforms.unspecifiedNativePlatform.idePlatformKind.resolution.createKlibPackageFragmentProvider(
moduleInfo,
container.get<StorageManager>(),
container.get<LanguageVersionSettings>(),
container.get<ModuleDescriptor>()
)
)
}
private fun getJsProvidersIfAny(
moduleInfo: ModuleInfo,
moduleContext: ModuleContext,
moduleDescriptor: ModuleDescriptorImpl,
container: StorageComponentContainer
): List<PackageFragmentProvider> {
if (moduleInfo !is LibraryModuleInfo || !moduleInfo.platform.isJs()) return emptyList()
return createPackageFragmentProvider(moduleInfo, container, moduleContext, moduleDescriptor)
}
fun createContainerForCompositePlatform(
moduleContext: ModuleContext,
moduleContentScope: GlobalSearchScope,
languageVersionSettings: LanguageVersionSettings,
targetPlatform: TargetPlatform,
analyzerServices: CompositeAnalyzerServices,
trace: BindingTrace,
declarationProviderFactory: DeclarationProviderFactory,
metadataPartProvider: MetadataPartProvider,
// Guaranteed to be non-null for modules with JVM
moduleClassResolver: ModuleClassResolver?,
packagePartProvider: PackagePartProvider?
): StorageComponentContainer = composeContainer("CompositePlatform") {
// Shared by all PlatformConfigurators
configureDefaultCheckers()
// Specific for each PlatformConfigurator
for (configurator in analyzerServices.services.map { it.platformConfigurator as PlatformConfiguratorBase }) {
configurator.configureExtensionsAndCheckers(this)
}
// Called by all normal containers set-ups
configureModule(moduleContext, targetPlatform, analyzerServices, trace, languageVersionSettings, IdeSealedClassInheritorsProvider)
configureStandardResolveComponents()
useInstance(moduleContentScope)
useInstance(declarationProviderFactory)
// Probably, should be in StandardResolveComponents, but
useInstance(VirtualFileFinderFactory.getInstance(moduleContext.project).create(moduleContentScope))
useInstance(packagePartProvider!!)
// JVM-specific
if (targetPlatform.has<JvmPlatform>()) {
configureJavaSpecificComponents(
moduleContext, moduleClassResolver!!,
languageVersionSettings,
configureJavaClassFinder = null,
javaClassTracker = null,
useBuiltInsProvider = IdeBuiltInsLoadingState.isFromDependenciesForJvm && targetPlatform.isJvm() // use JVM BuiltIns only for completely JVM modules
)
}
// Common-specific
if (targetPlatform.isCommon()) {
configureCommonSpecificComponents()
}
IdeaEnvironment.configure(this)
}.apply {
if (targetPlatform.has<JvmPlatform>()) {
initializeJavaSpecificComponents(trace)
}
}
}
class CompositeAnalyzerServices(val services: List<PlatformDependentAnalyzerServices>) : PlatformDependentAnalyzerServices() {
override val platformConfigurator: PlatformConfigurator = CompositePlatformConfigurator(services.map { it.platformConfigurator })
override fun computePlatformSpecificDefaultImports(storageManager: StorageManager, result: MutableList<ImportPath>) {
val intersectionOfDefaultImports = services.map { service ->
mutableListOf<ImportPath>()
.apply { service.computePlatformSpecificDefaultImports(storageManager, this) }
.toSet()
}.safeIntersect()
result.addAll(intersectionOfDefaultImports)
}
override val defaultLowPriorityImports: List<ImportPath> = services.map { it.defaultLowPriorityImports.toSet() }.safeIntersect()
override val excludedImports: List<FqName> = services.map { it.excludedImports.toSet() }.safeUnion()
private fun <T> List<Set<T>>.safeUnion(): List<T> =
if (isEmpty()) emptyList() else reduce { first, second -> first.union(second) }.toList()
private fun <T> List<Set<T>>.safeIntersect(): List<T> =
if (isEmpty()) emptyList() else reduce { first, second -> first.intersect(second) }.toList()
}
class CompositePlatformConfigurator(private val componentConfigurators: List<PlatformConfigurator>) : PlatformConfigurator {
override val platformSpecificContainer: StorageComponentContainer
get() = composeContainer(this::class.java.simpleName) {
configureDefaultCheckers()
for (configurator in componentConfigurators) {
(configurator as PlatformConfiguratorBase).configureExtensionsAndCheckers(this)
}
}
override fun configureModuleComponents(container: StorageComponentContainer, languageVersionSettings: LanguageVersionSettings) {
componentConfigurators.forEach { it.configureModuleComponents(container, languageVersionSettings) }
}
override fun configureModuleDependentCheckers(container: StorageComponentContainer) {
// We (ab)use the fact that currently, platforms don't use that method, so the only injected compnent will be
// ExperimentalMarkerDeclarationAnnotationChecker.
// Unfortunately, it is declared in base class, so repeating call to 'configureModuleDependentCheckers' will lead
// to multiple registrrations.
container.useImpl<ExperimentalMarkerDeclarationAnnotationChecker>()
}
}

View File

@@ -1,99 +0,0 @@
/*
* Copyright 2010-2017 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.caches.resolve
import com.intellij.openapi.project.Project
import com.intellij.openapi.roots.libraries.Library
import com.intellij.openapi.roots.libraries.PersistentLibraryKind
import com.intellij.openapi.vfs.VirtualFile
import org.jetbrains.kotlin.analyzer.ModuleInfo
import org.jetbrains.kotlin.analyzer.PlatformAnalysisParameters
import org.jetbrains.kotlin.analyzer.ResolverForModuleFactory
import org.jetbrains.kotlin.analyzer.ResolverForProject
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.config.LanguageVersionSettings
import org.jetbrains.kotlin.context.ProjectContext
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
import org.jetbrains.kotlin.descriptors.PackageFragmentProvider
import org.jetbrains.kotlin.extensions.ApplicationExtensionDescriptor
import org.jetbrains.kotlin.idea.caches.project.IdeaModuleInfo
import org.jetbrains.kotlin.idea.caches.project.LibraryInfo
import org.jetbrains.kotlin.idea.caches.project.SdkInfo
import org.jetbrains.kotlin.idea.caches.resolve.BuiltInsCacheKey
import org.jetbrains.kotlin.platform.IdePlatformKind
import org.jetbrains.kotlin.platform.TargetPlatform
import org.jetbrains.kotlin.resolve.TargetEnvironment
import org.jetbrains.kotlin.storage.StorageManager
interface IdePlatformKindResolution {
val kind: IdePlatformKind<*>
fun getKeyForBuiltIns(moduleInfo: ModuleInfo, sdkInfo: SdkInfo?, stdlibInfo: LibraryInfo?): BuiltInsCacheKey
fun createBuiltIns(
moduleInfo: IdeaModuleInfo,
projectContext: ProjectContext,
resolverForProject: ResolverForProject<IdeaModuleInfo>,
sdkDependency: SdkInfo?,
stdlibDependency: LibraryInfo?
): KotlinBuiltIns
fun createResolverForModuleFactory(
settings: PlatformAnalysisParameters,
environment: TargetEnvironment,
platform: TargetPlatform
): ResolverForModuleFactory
fun isLibraryFileForPlatform(virtualFile: VirtualFile): Boolean
fun createKlibPackageFragmentProvider(
moduleInfo: ModuleInfo,
storageManager: StorageManager,
languageVersionSettings: LanguageVersionSettings,
moduleDescriptor: ModuleDescriptor
): PackageFragmentProvider? = null
val libraryKind: PersistentLibraryKind<*>?
fun createLibraryInfo(project: Project, library: Library): List<LibraryInfo>
companion object : ApplicationExtensionDescriptor<IdePlatformKindResolution>(
"org.jetbrains.kotlin.idePlatformKindResolution", IdePlatformKindResolution::class.java
) {
private val CACHED_RESOLUTION_SUPPORT by lazy {
val allPlatformKinds = IdePlatformKind.ALL_KINDS
val groupedResolution = getInstances().groupBy { it.kind }.mapValues { it.value.single() }
for (kind in allPlatformKinds) {
if (kind !in groupedResolution) {
throw IllegalStateException(
"Resolution support for the platform '$kind' is missing. " +
"Implement 'IdePlatformKindResolution' for it."
)
}
}
groupedResolution
}
fun getResolution(kind: IdePlatformKind<*>): IdePlatformKindResolution {
return CACHED_RESOLUTION_SUPPORT[kind] ?: error("Unknown platform $this")
}
}
}
val IdePlatformKind<*>.resolution: IdePlatformKindResolution
get() = IdePlatformKindResolution.getResolution(this)

View File

@@ -1,127 +0,0 @@
/*
* 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.caches.resolve
import com.intellij.openapi.project.Project
import com.intellij.openapi.roots.OrderRootType
import com.intellij.openapi.roots.libraries.Library
import com.intellij.openapi.roots.libraries.PersistentLibraryKind
import com.intellij.openapi.vfs.VirtualFile
import com.intellij.util.PathUtil
import org.jetbrains.kotlin.analyzer.ModuleInfo
import org.jetbrains.kotlin.analyzer.PlatformAnalysisParameters
import org.jetbrains.kotlin.analyzer.ResolverForModuleFactory
import org.jetbrains.kotlin.analyzer.ResolverForProject
import org.jetbrains.kotlin.builtins.DefaultBuiltIns
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.config.LanguageVersionSettings
import org.jetbrains.kotlin.context.ProjectContext
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
import org.jetbrains.kotlin.descriptors.PackageFragmentProvider
import org.jetbrains.kotlin.idea.caches.project.IdeaModuleInfo
import org.jetbrains.kotlin.idea.caches.project.LibraryInfo
import org.jetbrains.kotlin.idea.caches.project.SdkInfo
import org.jetbrains.kotlin.idea.caches.resolve.BuiltInsCacheKey
import org.jetbrains.kotlin.idea.framework.JSLibraryKind
import org.jetbrains.kotlin.idea.klib.AbstractKlibLibraryInfo
import org.jetbrains.kotlin.idea.klib.createKlibPackageFragmentProvider
import org.jetbrains.kotlin.idea.klib.isKlibLibraryRootForPlatform
import org.jetbrains.kotlin.incremental.components.LookupTracker
import org.jetbrains.kotlin.konan.util.KlibMetadataFactories
import org.jetbrains.kotlin.platform.TargetPlatform
import org.jetbrains.kotlin.platform.impl.JsIdePlatformKind
import org.jetbrains.kotlin.platform.js.JsPlatforms
import org.jetbrains.kotlin.resolve.TargetEnvironment
import org.jetbrains.kotlin.serialization.js.DynamicTypeDeserializer
import org.jetbrains.kotlin.serialization.konan.impl.KlibMetadataModuleDescriptorFactoryImpl
import org.jetbrains.kotlin.storage.StorageManager
class JsPlatformKindResolution : IdePlatformKindResolution {
override fun isLibraryFileForPlatform(virtualFile: VirtualFile): Boolean {
return virtualFile.extension == "js"
|| virtualFile.extension == "kjsm"
|| virtualFile.isKlibLibraryRootForPlatform(JsPlatforms.defaultJsPlatform)
}
override val libraryKind: PersistentLibraryKind<*>?
get() = JSLibraryKind
override val kind get() = JsIdePlatformKind
override fun getKeyForBuiltIns(moduleInfo: ModuleInfo, sdkInfo: SdkInfo?, stdlibInfo: LibraryInfo?): BuiltInsCacheKey {
return BuiltInsCacheKey.DefaultBuiltInsKey
}
override fun createBuiltIns(
moduleInfo: IdeaModuleInfo,
projectContext: ProjectContext,
resolverForProject: ResolverForProject<IdeaModuleInfo>,
sdkDependency: SdkInfo?,
stdlibDependency: LibraryInfo?,
): KotlinBuiltIns {
return DefaultBuiltIns.Instance
}
override fun createResolverForModuleFactory(
settings: PlatformAnalysisParameters,
environment: TargetEnvironment,
platform: TargetPlatform
): ResolverForModuleFactory = JsResolverForModuleFactory(environment)
override fun createLibraryInfo(project: Project, library: Library): List<LibraryInfo> {
val klibFiles = library.getFiles(OrderRootType.CLASSES).filter {
it.isKlibLibraryRootForPlatform(JsPlatforms.defaultJsPlatform)
}
return if (klibFiles.isNotEmpty()) {
klibFiles.mapNotNull {
// TODO report error?
val path = PathUtil.getLocalPath(it) ?: return@mapNotNull null
JsKlibLibraryInfo(project, library, path)
}
} else {
listOf(JsMetadataLibraryInfo(project, library))
}
}
override fun createKlibPackageFragmentProvider(
moduleInfo: ModuleInfo,
storageManager: StorageManager,
languageVersionSettings: LanguageVersionSettings,
moduleDescriptor: ModuleDescriptor
): PackageFragmentProvider? {
return (moduleInfo as? JsKlibLibraryInfo)
?.resolvedKotlinLibrary
?.createKlibPackageFragmentProvider(
storageManager = storageManager,
metadataModuleDescriptorFactory = metadataModuleDescriptorFactory,
languageVersionSettings = languageVersionSettings,
moduleDescriptor = moduleDescriptor,
lookupTracker = LookupTracker.DO_NOTHING
)
}
companion object {
private val metadataFactories = KlibMetadataFactories({ DefaultBuiltIns.Instance }, DynamicTypeDeserializer)
private val metadataModuleDescriptorFactory = KlibMetadataModuleDescriptorFactoryImpl(
metadataFactories.DefaultDescriptorFactory,
metadataFactories.DefaultPackageFragmentsFactory,
metadataFactories.flexibleTypeDeserializer,
metadataFactories.platformDependentTypeTransformer
)
}
}
class JsKlibLibraryInfo(project: Project, library: Library, libraryRoot: String) : AbstractKlibLibraryInfo(project, library, libraryRoot) {
override val platform: TargetPlatform
get() = JsPlatforms.defaultJsPlatform
}
class JsMetadataLibraryInfo(project: Project, library: Library) : LibraryInfo(project, library) {
override val platform: TargetPlatform
get() = JsPlatforms.defaultJsPlatform
}

View File

@@ -1,117 +0,0 @@
/*
* 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.caches.resolve
import com.intellij.openapi.diagnostic.Logger
import org.jetbrains.kotlin.analyzer.*
import org.jetbrains.kotlin.config.LanguageVersionSettings
import org.jetbrains.kotlin.container.StorageComponentContainer
import org.jetbrains.kotlin.container.get
import org.jetbrains.kotlin.context.ModuleContext
import org.jetbrains.kotlin.descriptors.PackageFragmentProvider
import org.jetbrains.kotlin.descriptors.impl.CompositePackageFragmentProvider
import org.jetbrains.kotlin.descriptors.impl.ModuleDescriptorImpl
import org.jetbrains.kotlin.frontend.di.createContainerForLazyResolve
import org.jetbrains.kotlin.incremental.components.LookupTracker
import org.jetbrains.kotlin.js.resolve.JsPlatformAnalyzerServices
import org.jetbrains.kotlin.platform.idePlatformKind
import org.jetbrains.kotlin.platform.js.JsPlatforms
import org.jetbrains.kotlin.resolve.BindingTraceContext
import org.jetbrains.kotlin.resolve.SealedClassInheritorsProvider
import org.jetbrains.kotlin.resolve.TargetEnvironment
import org.jetbrains.kotlin.resolve.lazy.ResolveSession
import org.jetbrains.kotlin.resolve.lazy.declarations.DeclarationProviderFactoryService
import org.jetbrains.kotlin.serialization.js.KotlinJavascriptSerializationUtil
import org.jetbrains.kotlin.serialization.js.createKotlinJavascriptPackageFragmentProvider
import org.jetbrains.kotlin.utils.KotlinJavascriptMetadataUtils
import java.io.File
private val LOG = Logger.getInstance(JsResolverForModuleFactory::class.java)
class JsResolverForModuleFactory(
private val targetEnvironment: TargetEnvironment
) : ResolverForModuleFactory() {
override fun <M : ModuleInfo> createResolverForModule(
moduleDescriptor: ModuleDescriptorImpl,
moduleContext: ModuleContext,
moduleContent: ModuleContent<M>,
resolverForProject: ResolverForProject<M>,
languageVersionSettings: LanguageVersionSettings,
sealedInheritorsProvider: SealedClassInheritorsProvider
): ResolverForModule {
val (moduleInfo, syntheticFiles, moduleContentScope) = moduleContent
val project = moduleContext.project
val declarationProviderFactory = DeclarationProviderFactoryService.createDeclarationProviderFactory(
project,
moduleContext.storageManager,
syntheticFiles,
moduleContentScope,
moduleInfo
)
val container = createContainerForLazyResolve(
moduleContext,
declarationProviderFactory,
BindingTraceContext(/* allowSliceRewrite = */ true),
moduleDescriptor.platform!!,
JsPlatformAnalyzerServices,
targetEnvironment,
languageVersionSettings
)
var packageFragmentProvider = container.get<ResolveSession>().packageFragmentProvider
val libraryProviders = createPackageFragmentProvider(moduleInfo, container, moduleContext, moduleDescriptor)
if (libraryProviders.isNotEmpty()) {
packageFragmentProvider = CompositePackageFragmentProvider(
listOf(packageFragmentProvider) + libraryProviders,
"CompositeProvider@JsResolver for $moduleDescriptor"
)
}
return ResolverForModule(packageFragmentProvider, container)
}
}
internal fun <M : ModuleInfo> createPackageFragmentProvider(
moduleInfo: M,
container: StorageComponentContainer,
moduleContext: ModuleContext,
moduleDescriptor: ModuleDescriptorImpl
): List<PackageFragmentProvider> = when (moduleInfo) {
is JsKlibLibraryInfo -> {
listOfNotNull(
JsPlatforms.defaultJsPlatform.idePlatformKind.resolution.createKlibPackageFragmentProvider(
moduleInfo,
moduleContext.storageManager,
container.get<LanguageVersionSettings>(),
moduleDescriptor
)
)
}
is LibraryModuleInfo -> {
moduleInfo.getLibraryRoots()
.flatMap {
if (File(it).exists()) {
KotlinJavascriptMetadataUtils.loadMetadata(it)
} else {
// TODO can/should we warn a user about a problem in a library root? If so how?
LOG.error("Library $it not found")
emptyList()
}
}
.filter { it.version.isCompatible() }
.map { metadata ->
val (header, packageFragmentProtos) =
KotlinJavascriptSerializationUtil.readModuleAsProto(metadata.body, metadata.version)
createKotlinJavascriptPackageFragmentProvider(
moduleContext.storageManager, moduleDescriptor, header, packageFragmentProtos, metadata.version,
container.get(), LookupTracker.DO_NOTHING
)
}
}
else -> emptyList()
}

View File

@@ -1,138 +0,0 @@
/*
* Copyright 2010-2018 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.caches.resolve
import com.intellij.openapi.diagnostic.Logger
import com.intellij.openapi.project.Project
import com.intellij.openapi.projectRoots.Sdk
import com.intellij.openapi.roots.libraries.Library
import com.intellij.openapi.roots.libraries.PersistentLibraryKind
import com.intellij.openapi.vfs.VirtualFile
import org.jetbrains.kotlin.analyzer.ModuleInfo
import org.jetbrains.kotlin.analyzer.PlatformAnalysisParameters
import org.jetbrains.kotlin.analyzer.ResolverForModuleFactory
import org.jetbrains.kotlin.analyzer.ResolverForProject
import org.jetbrains.kotlin.builtins.DefaultBuiltIns
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.builtins.jvm.JvmBuiltIns
import org.jetbrains.kotlin.context.ProjectContext
import org.jetbrains.kotlin.descriptors.impl.ModuleDescriptorImpl
import org.jetbrains.kotlin.idea.caches.project.IdeaModuleInfo
import org.jetbrains.kotlin.idea.caches.project.LibraryInfo
import org.jetbrains.kotlin.idea.caches.project.SdkInfo
import org.jetbrains.kotlin.idea.caches.resolve.BuiltInsCacheKey
import org.jetbrains.kotlin.idea.configuration.IdeBuiltInsLoadingState
import org.jetbrains.kotlin.idea.caches.resolve.supportsAdditionalBuiltInsMembers
import org.jetbrains.kotlin.platform.TargetPlatform
import org.jetbrains.kotlin.platform.impl.JvmIdePlatformKind
import org.jetbrains.kotlin.platform.jvm.JvmPlatforms
import org.jetbrains.kotlin.resolve.TargetEnvironment
import org.jetbrains.kotlin.resolve.jvm.JvmPlatformParameters
import org.jetbrains.kotlin.resolve.jvm.JvmResolverForModuleFactory
private val LOG = Logger.getInstance(JvmPlatformKindResolution::class.java)
class JvmPlatformKindResolution : IdePlatformKindResolution {
override fun isLibraryFileForPlatform(virtualFile: VirtualFile): Boolean {
return false // TODO: No library kind for JVM
}
override fun createResolverForModuleFactory(
settings: PlatformAnalysisParameters,
environment: TargetEnvironment,
platform: TargetPlatform
): ResolverForModuleFactory {
return JvmResolverForModuleFactory(settings as JvmPlatformParameters, environment, platform)
}
override val libraryKind: PersistentLibraryKind<*>?
get() = null
override fun createLibraryInfo(project: Project, library: Library): List<LibraryInfo> =
listOf(JvmLibraryInfo(project, library))
override val kind get() = JvmIdePlatformKind
override fun getKeyForBuiltIns(moduleInfo: ModuleInfo, sdkInfo: SdkInfo?, stdlibInfo: LibraryInfo?): BuiltInsCacheKey {
if (IdeBuiltInsLoadingState.isFromClassLoader && stdlibInfo != null) {
LOG.error("Standard library ${stdlibInfo.displayedName} provided for built-ins, but loading from dependencies is disabled")
}
return if (sdkInfo != null)
CacheKeyByBuiltInsDependencies(sdkInfo.sdk, stdlibInfo)
else BuiltInsCacheKey.DefaultBuiltInsKey
}
override fun createBuiltIns(
moduleInfo: IdeaModuleInfo,
projectContext: ProjectContext,
resolverForProject: ResolverForProject<IdeaModuleInfo>,
sdkDependency: SdkInfo?,
stdlibDependency: LibraryInfo?,
): KotlinBuiltIns {
return when {
sdkDependency == null -> DefaultBuiltIns.Instance
stdlibDependency == null || moduleInfo is SdkInfo ->
createBuiltInsFromClassLoader(moduleInfo, projectContext, resolverForProject, sdkDependency)
else -> createBuiltinsFromModuleDependencies(moduleInfo, projectContext, resolverForProject, sdkDependency, stdlibDependency)
}
}
private fun createBuiltInsFromClassLoader(
moduleInfo: IdeaModuleInfo,
projectContext: ProjectContext,
resolverForProject: ResolverForProject<IdeaModuleInfo>,
sdkDependency: SdkInfo,
): JvmBuiltIns {
return JvmBuiltIns(projectContext.storageManager, JvmBuiltIns.Kind.FROM_CLASS_LOADER).apply {
setPostponedSettingsComputation {
// SDK should be present, otherwise we wouldn't have created JvmBuiltIns in createBuiltIns
val sdkDescriptor = resolverForProject.descriptorForModule(sdkDependency)
val isAdditionalBuiltInsFeaturesSupported =
moduleInfo.supportsAdditionalBuiltInsMembers(projectContext.project)
JvmBuiltIns.Settings(sdkDescriptor, isAdditionalBuiltInsFeaturesSupported)
}
}
}
private fun createBuiltinsFromModuleDependencies(
moduleInfo: IdeaModuleInfo,
projectContext: ProjectContext,
resolverForProject: ResolverForProject<IdeaModuleInfo>,
sdkDependency: SdkInfo,
stdlibDependency: LibraryInfo,
): JvmBuiltIns {
if (IdeBuiltInsLoadingState.isFromClassLoader) {
LOG.error("Incorrect attempt to create built-ins from module dependencies")
}
return JvmBuiltIns(projectContext.storageManager, JvmBuiltIns.Kind.FROM_DEPENDENCIES).apply {
setPostponedBuiltinsModuleComputation {
val stdlibDescriptor = resolverForProject.descriptorForModule(stdlibDependency)
@Suppress("unchecked_cast")
stdlibDescriptor as ModuleDescriptorImpl
}
setPostponedSettingsComputation {
val sdkDescriptor = resolverForProject.descriptorForModule(sdkDependency)
val isAdditionalBuiltInsFeaturesSupported =
moduleInfo.supportsAdditionalBuiltInsMembers(projectContext.project)
JvmBuiltIns.Settings(sdkDescriptor, isAdditionalBuiltInsFeaturesSupported)
}
}
}
data class CacheKeyByBuiltInsDependencies(val sdk: Sdk, val stdlib: LibraryInfo?) : BuiltInsCacheKey
}
class JvmLibraryInfo(project: Project, library: Library) : LibraryInfo(project, library) {
override val platform: TargetPlatform
get() = JvmPlatforms.defaultJvmPlatform
}

View File

@@ -1,155 +0,0 @@
/*
* Copyright 2010-2015 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.idea;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.util.Iconable;
import com.intellij.psi.PsiElement;
import com.intellij.ui.RowIcon;
import com.intellij.util.PlatformIcons;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.descriptors.*;
import org.jetbrains.kotlin.descriptors.impl.LocalVariableDescriptor;
import org.jetbrains.kotlin.psi.KtElement;
import javax.swing.*;
public final class KotlinDescriptorIconProvider {
private static final Logger LOG = Logger.getInstance("#org.jetbrains.kotlin.idea.KotlinDescriptorIconProvider");
private KotlinDescriptorIconProvider() {
}
@Nullable
public static Icon getIcon(@NotNull DeclarationDescriptor descriptor, @Nullable PsiElement declaration, @Iconable.IconFlags int flags) {
if (declaration != null && !(declaration instanceof KtElement)) {
return declaration.getIcon(flags);
}
Icon result = getBaseIcon(descriptor);
if ((flags & Iconable.ICON_FLAG_VISIBILITY) > 0) {
RowIcon rowIcon = new RowIcon(2);
rowIcon.setIcon(result, 0);
rowIcon.setIcon(getVisibilityIcon(descriptor), 1);
result = rowIcon;
}
return result;
}
private static Icon getVisibilityIcon(@NotNull DeclarationDescriptor descriptor) {
if (descriptor instanceof DeclarationDescriptorWithVisibility) {
DeclarationDescriptorWithVisibility descriptorWithVisibility = (DeclarationDescriptorWithVisibility) descriptor;
DescriptorVisibility visibility = descriptorWithVisibility.getVisibility().normalize();
if (visibility == DescriptorVisibilities.PUBLIC) {
return PlatformIcons.PUBLIC_ICON;
}
if (visibility == DescriptorVisibilities.PROTECTED) {
return PlatformIcons.PROTECTED_ICON;
}
if (DescriptorVisibilities.isPrivate(visibility)) {
return PlatformIcons.PRIVATE_ICON;
}
if (visibility == DescriptorVisibilities.INTERNAL) {
return PlatformIcons.PACKAGE_LOCAL_ICON;
}
}
return null;
}
private static Modality getModalitySafe(@NotNull MemberDescriptor descriptor) {
try {
return descriptor.getModality();
}
catch (InvalidModuleException ex) {
return Modality.FINAL;
}
}
private static Icon getBaseIcon(@NotNull DeclarationDescriptor descriptor) {
if (descriptor instanceof PackageFragmentDescriptor || descriptor instanceof PackageViewDescriptor) {
return PlatformIcons.PACKAGE_ICON;
}
if (descriptor instanceof FunctionDescriptor) {
FunctionDescriptor functionDescriptor = (FunctionDescriptor) descriptor;
if (functionDescriptor.getExtensionReceiverParameter() != null) {
return Modality.ABSTRACT == getModalitySafe(functionDescriptor)
? KotlinIcons.ABSTRACT_EXTENSION_FUNCTION
: KotlinIcons.EXTENSION_FUNCTION;
}
if (descriptor.getContainingDeclaration() instanceof ClassDescriptor) {
return Modality.ABSTRACT == getModalitySafe(functionDescriptor)
? PlatformIcons.ABSTRACT_METHOD_ICON
: PlatformIcons.METHOD_ICON;
}
else {
return KotlinIcons.FUNCTION;
}
}
if (descriptor instanceof ClassDescriptor) {
ClassDescriptor classDescriptor = (ClassDescriptor) descriptor;
switch (classDescriptor.getKind()) {
case INTERFACE:
return KotlinIcons.INTERFACE;
case ENUM_CLASS:
return KotlinIcons.ENUM;
case ENUM_ENTRY:
return KotlinIcons.ENUM;
case ANNOTATION_CLASS:
return KotlinIcons.ANNOTATION;
case OBJECT:
return KotlinIcons.OBJECT;
case CLASS:
return Modality.ABSTRACT == getModalitySafe(classDescriptor) ?
KotlinIcons.ABSTRACT_CLASS :
KotlinIcons.CLASS;
default:
LOG.warn("No icon for descriptor: " + descriptor);
return null;
}
}
if (descriptor instanceof ValueParameterDescriptor) {
return KotlinIcons.PARAMETER;
}
if (descriptor instanceof LocalVariableDescriptor) {
return ((VariableDescriptor) descriptor).isVar() ? KotlinIcons.VAR : KotlinIcons.VAL;
}
if (descriptor instanceof PropertyDescriptor) {
return ((VariableDescriptor) descriptor).isVar() ? KotlinIcons.FIELD_VAR : KotlinIcons.FIELD_VAL;
}
if (descriptor instanceof TypeParameterDescriptor) {
return PlatformIcons.CLASS_ICON;
}
if (descriptor instanceof TypeAliasDescriptor) {
return KotlinIcons.TYPE_ALIAS;
}
LOG.warn("No icon for descriptor: " + descriptor);
return null;
}
}

View File

@@ -1,14 +0,0 @@
/*
* Copyright 2010-2019 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
import org.jetbrains.kotlin.idea.util.hasMatchingExpected
import org.jetbrains.kotlin.psi.KtDeclaration
import org.jetbrains.kotlin.psi.psiUtil.hasActualModifier
class KotlinIconProvider : KotlinIconProviderBase() {
override fun KtDeclaration.isMatchingExpected() = hasActualModifier() && hasMatchingExpected()
}

View File

@@ -1,24 +0,0 @@
/*
* 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
import org.jetbrains.annotations.NonNls
import org.jetbrains.annotations.PropertyKey
import org.jetbrains.kotlin.util.AbstractKotlinBundle
@NonNls
private const val BUNDLE = "messages.KotlinIdeaAnalysisBundle"
object KotlinIdeaAnalysisBundle : AbstractKotlinBundle(BUNDLE) {
@JvmStatic
fun message(@NonNls @PropertyKey(resourceBundle = BUNDLE) key: String, vararg params: Any): String = getMessage(key, *params)
@JvmStatic
fun htmlMessage(@NonNls @PropertyKey(resourceBundle = BUNDLE) key: String, vararg params: Any): String =
getMessage(key, *params).withHtml()
@JvmStatic
fun lazyMessage(@PropertyKey(resourceBundle = BUNDLE) key: String, vararg params: Any): () -> String = { getMessage(key, *params) }
}

View File

@@ -1,71 +0,0 @@
/*
* Copyright 2010-2019 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
import com.intellij.ide.plugins.IdeaPluginDescriptor
import com.intellij.ide.plugins.PluginManagerCore
import com.intellij.openapi.extensions.PluginId
import java.io.File
object KotlinPluginUtil {
private class KotlinPluginInfo(val id: PluginId, val version: String, val path: File) {
constructor(plugin: IdeaPluginDescriptor) : this(plugin.pluginId, plugin.version, plugin.path)
}
private val KNOWN_KOTLIN_PLUGIN_IDS = listOf(
"org.jetbrains.kotlin",
"org.jetbrains.kotlin.native.clion",
"org.jetbrains.kotlin.native.appcode"
)
private val KOTLIN_PLUGIN_INFO: KotlinPluginInfo by lazy {
val plugin = PluginManagerCore.getPlugins().firstOrNull { it.pluginId.idString in KNOWN_KOTLIN_PLUGIN_IDS }
?: error("Kotlin plugin not found: " + PluginManagerCore.getPlugins().contentToString())
KotlinPluginInfo(plugin)
}
val KOTLIN_PLUGIN_ID: PluginId
get() = KOTLIN_PLUGIN_INFO.id
private const val PATCHED_PLUGIN_VERSION_KEY = "kotlin.plugin.version"
private val PATCHED_PLUGIN_VERSION: String? = System.getProperty(PATCHED_PLUGIN_VERSION_KEY, null)
@JvmStatic
fun getPluginVersion(): String {
if (PATCHED_PLUGIN_VERSION != null) {
assert(isPatched())
return PATCHED_PLUGIN_VERSION
}
@Suppress("DEPRECATION")
return getPluginVersionFromIdea()
}
@Deprecated("This method returns original plugin version. Please use getPluginVersion() instead.")
@JvmStatic
fun getPluginVersionFromIdea(): String {
return KOTLIN_PLUGIN_INFO.version
}
@JvmStatic
fun isPatched(): Boolean {
return PATCHED_PLUGIN_VERSION != null
}
@JvmStatic
fun isSnapshotVersion(): Boolean {
return "@snapshot@" == getPluginVersion()
}
fun isDevVersion(): Boolean {
return getPluginVersion().contains("-dev-")
}
fun getPluginPath(): File {
return KOTLIN_PLUGIN_INFO.path
}
}

View File

@@ -1,27 +0,0 @@
/*
* 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.idea.api.fe10.impl
import org.jetbrains.kotlin.idea.api.ResolveAllowanceService
class ResolveAllowanceServiceFE10Impl: ResolveAllowanceService() {
override fun switchOnAllowingResolveInEdtInCurrentThread(): SwitchResult {
return SwitchResult.ALREADY_SWITCHED
}
override fun isResolveOnEdtInCurrentThreadAllowed(): Boolean = true
override fun switchOffAllowingResolveInEdtInCurrentThread() {}
override fun switchOnForbidResolveInCurrentThread(actionName: String): SwitchResult {
return SwitchResult.ALREADY_SWITCHED
}
override fun switchOffForbidResolveInCurrentThread() {
}
override fun getResolveInCurrentThreadForbiddenReason(): String? = null
}

View File

@@ -1,41 +0,0 @@
/*
* Copyright 2010-2015 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.idea.caches
import com.intellij.openapi.vfs.VirtualFile
import java.io.DataInput
import java.io.DataOutput
data class CachedAttributeData<out T>(val value: T, val timeStamp: Long)
interface FileAttributeService {
fun register(id: String, version: Int, fixedSize: Boolean = true) {}
fun <T : Enum<T>> writeEnumAttribute(id: String, file: VirtualFile, value: T): CachedAttributeData<T> =
CachedAttributeData(value, timeStamp = file.timeStamp)
fun <T : Enum<T>> readEnumAttribute(id: String, file: VirtualFile, klass: Class<T>): CachedAttributeData<T>? = null
fun writeBooleanAttribute(id: String, file: VirtualFile, value: Boolean): CachedAttributeData<Boolean> =
CachedAttributeData(value, timeStamp = file.timeStamp)
fun readBooleanAttribute(id: String, file: VirtualFile): CachedAttributeData<Boolean>? = null
fun <T> write(file: VirtualFile, id: String, value: T, writeValueFun: (DataOutput, T) -> Unit): CachedAttributeData<T>
fun <T> read(file: VirtualFile, id: String, readValueFun: (DataInput) -> T): CachedAttributeData<T>?
}

View File

@@ -1,140 +0,0 @@
/*
* Copyright 2010-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.idea.caches
import com.intellij.ide.highlighter.JavaClassFileType
import com.intellij.openapi.components.ServiceManager
import com.intellij.openapi.components.service
import com.intellij.openapi.util.Key
import com.intellij.openapi.vfs.VirtualFile
import com.intellij.openapi.vfs.VirtualFileWithId
import com.intellij.reference.SoftReference
import org.jetbrains.kotlin.load.kotlin.KotlinBinaryClassCache
import org.jetbrains.kotlin.load.kotlin.KotlinJvmBinaryClass
import org.jetbrains.kotlin.load.kotlin.header.KotlinClassHeader
import org.jetbrains.kotlin.metadata.jvm.deserialization.JvmMetadataVersion
import org.jetbrains.kotlin.name.ClassId
class IDEKotlinBinaryClassCache {
class KotlinBinaryClassHeaderData(
val classId: ClassId,
val kind: KotlinClassHeader.Kind,
val metadataVersion: JvmMetadataVersion,
val partNamesIfMultifileFacade: List<String>,
val packageName: String?
)
data class KotlinBinaryData(val isKotlinBinary: Boolean, val timestamp: Long, val headerData: KotlinBinaryClassHeaderData?)
/**
* Checks if this file is a compiled Kotlin class file (not necessarily ABI-compatible with the current plugin)
*/
fun isKotlinJvmCompiledFile(file: VirtualFile, fileContent: ByteArray? = null): Boolean {
if (file.extension != JavaClassFileType.INSTANCE!!.defaultExtension) {
return false
}
val cached = getKotlinBinaryFromCache(file)
if (cached != null) {
return cached.isKotlinBinary
}
return getKotlinBinaryClass(file, fileContent) != null
}
fun getKotlinBinaryClass(file: VirtualFile, fileContent: ByteArray? = null): KotlinJvmBinaryClass? {
val cached = getKotlinBinaryFromCache(file)
if (cached != null && !cached.isKotlinBinary) {
return null
}
val kotlinBinaryClass = KotlinBinaryClassCache.getKotlinBinaryClassOrClassFileContent(file, fileContent)?.toKotlinJvmBinaryClass()
val isKotlinBinaryClass = kotlinBinaryClass != null
if (file is VirtualFileWithId) {
attributeService.writeBooleanAttribute(KOTLIN_IS_COMPILED_FILE_ATTRIBUTE, file, isKotlinBinaryClass)
}
if (isKotlinBinaryClass) {
val headerInfo = createHeaderInfo(kotlinBinaryClass)
file.putUserData(KOTLIN_BINARY_DATA_KEY, SoftReference(KotlinBinaryData(isKotlinBinaryClass, file.timeStamp, headerInfo)))
}
return kotlinBinaryClass
}
fun getKotlinBinaryClassHeaderData(file: VirtualFile, fileContent: ByteArray? = null): KotlinBinaryClassHeaderData? {
val cached = getKotlinBinaryFromCache(file)
if (cached != null) {
if (!cached.isKotlinBinary) {
return null
}
if (cached.headerData != null) {
return cached.headerData
}
}
val kotlinBinaryClass = getKotlinBinaryClass(file, fileContent)
return createHeaderInfo(kotlinBinaryClass)
}
private val attributeService = ServiceManager.getService(FileAttributeService::class.java)
private fun createHeaderInfo(kotlinBinaryClass: KotlinJvmBinaryClass?): KotlinBinaryClassHeaderData? {
val classId = kotlinBinaryClass?.classId ?: return null
return kotlinBinaryClass.classHeader.toLightHeader(classId)
}
private fun KotlinClassHeader.toLightHeader(classId: ClassId) =
KotlinBinaryClassHeaderData(
classId, kind, metadataVersion, multifilePartNames, packageName
)
private val KOTLIN_IS_COMPILED_FILE_ATTRIBUTE: String = "kotlin-is-binary-compiled".apply {
ServiceManager.getService(FileAttributeService::class.java)?.register(this, 2)
}
private val KOTLIN_BINARY_DATA_KEY = Key.create<SoftReference<KotlinBinaryData>>(KOTLIN_IS_COMPILED_FILE_ATTRIBUTE)
private fun getKotlinBinaryFromCache(file: VirtualFile): KotlinBinaryData? {
val userData = file.getUserData(KOTLIN_BINARY_DATA_KEY)?.get()
if (userData != null && userData.timestamp == file.timeStamp) {
return userData
}
val isKotlinBinaryAttribute = if (file is VirtualFileWithId)
attributeService.readBooleanAttribute(KOTLIN_IS_COMPILED_FILE_ATTRIBUTE, file)
else
null
if (isKotlinBinaryAttribute != null) {
val isKotlinBinary = isKotlinBinaryAttribute.value
val kotlinBinaryData = KotlinBinaryData(isKotlinBinary, file.timeStamp, null)
if (isKotlinBinary) {
file.putUserData(KOTLIN_BINARY_DATA_KEY, SoftReference(kotlinBinaryData))
}
return kotlinBinaryData
}
return null
}
companion object {
fun getInstance(): IDEKotlinBinaryClassCache = service()
}
}

View File

@@ -1,348 +0,0 @@
/*
* Copyright 2010-2019 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.caches
import com.intellij.openapi.diagnostic.Logger
import com.intellij.openapi.module.Module
import com.intellij.openapi.project.Project
import com.intellij.openapi.vfs.VirtualFile
import com.intellij.psi.PsiClass
import com.intellij.psi.PsiField
import com.intellij.psi.PsiMethod
import com.intellij.psi.search.GlobalSearchScope
import com.intellij.psi.search.PsiShortNamesCache
import com.intellij.psi.stubs.StubIndex
import com.intellij.util.ArrayUtil
import com.intellij.util.Processor
import com.intellij.util.Processors
import com.intellij.util.containers.ContainerUtil
import com.intellij.util.indexing.IdFilter
import gnu.trove.THashSet
import org.jetbrains.kotlin.asJava.LightClassUtil
import org.jetbrains.kotlin.asJava.defaultImplsChild
import org.jetbrains.kotlin.asJava.finder.JavaElementFinder
import org.jetbrains.kotlin.asJava.getAccessorLightMethods
import org.jetbrains.kotlin.fileClasses.javaFileFacadeFqName
import org.jetbrains.kotlin.idea.decompiler.builtIns.KotlinBuiltInFileType
import org.jetbrains.kotlin.idea.stubindex.*
import org.jetbrains.kotlin.load.java.JvmAbi
import org.jetbrains.kotlin.load.java.getPropertyNamesCandidatesByAccessorName
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.psi.KtClassOrObject
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.psi.KtNamedDeclaration
import org.jetbrains.kotlin.psi.KtNamedFunction
class KotlinShortNamesCache(private val project: Project) : PsiShortNamesCache() {
companion object {
private val LOG = Logger.getInstance(KotlinShortNamesCache::class.java)
}
//hacky way to avoid searches for Kotlin classes, when looking for Java (from Kotlin)
val disableSearch: ThreadLocal<Boolean> = object : ThreadLocal<Boolean>() {
override fun initialValue(): Boolean = false
}
//region Classes
override fun processAllClassNames(processor: Processor<in String>): Boolean {
if (disableSearch.get()) return true
return KotlinClassShortNameIndex.getInstance().processAllKeys(project, processor) &&
KotlinFileFacadeShortNameIndex.INSTANCE.processAllKeys(project, processor)
}
override fun processAllClassNames(processor: Processor<in String>, scope: GlobalSearchScope, filter: IdFilter?): Boolean {
if (disableSearch.get()) return true
return processAllClassNames(processor)
}
/**
* Return kotlin class names from project sources which should be visible from java.
*/
override fun getAllClassNames(): Array<String> {
if (disableSearch.get()) return ArrayUtil.EMPTY_STRING_ARRAY
return withArrayProcessor(ArrayUtil.EMPTY_STRING_ARRAY) { processor ->
processAllClassNames(processor)
}
}
override fun processClassesWithName(
name: String,
processor: Processor<in PsiClass>,
scope: GlobalSearchScope,
filter: IdFilter?
): Boolean {
if (disableSearch.get()) return true
val effectiveScope = kotlinDeclarationsVisibleFromJavaScope(scope)
val fqNameProcessor = Processor<FqName> { fqName: FqName? ->
if (fqName == null) return@Processor true
val isInterfaceDefaultImpl = name == JvmAbi.DEFAULT_IMPLS_CLASS_NAME && fqName.shortName().asString() != name
if (fqName.shortName().asString() != name && !isInterfaceDefaultImpl) {
LOG.error(
"A declaration obtained from index has non-matching name:" +
"\nin index: $name" +
"\ndeclared: ${fqName.shortName()}($fqName)"
)
return@Processor true
}
val fqNameToSearch = if (isInterfaceDefaultImpl) fqName.defaultImplsChild() else fqName
val psiClass = JavaElementFinder.getInstance(project).findClass(fqNameToSearch.asString(), effectiveScope)
?: return@Processor true
return@Processor processor.process(psiClass)
}
val allKtClassOrObjectsProcessed = StubIndex.getInstance().processElements(
KotlinClassShortNameIndex.getInstance().key,
name,
project,
effectiveScope,
filter,
KtClassOrObject::class.java
) { ktClassOrObject ->
fqNameProcessor.process(ktClassOrObject.fqName)
}
if (!allKtClassOrObjectsProcessed) {
return false
}
return StubIndex.getInstance().processElements(
KotlinFileFacadeShortNameIndex.getInstance().key,
name,
project,
effectiveScope,
filter,
KtFile::class.java
) { ktFile ->
fqNameProcessor.process(ktFile.javaFileFacadeFqName)
}
}
/**
* Return class names form kotlin sources in given scope which should be visible as Java classes.
*/
override fun getClassesByName(name: String, scope: GlobalSearchScope): Array<PsiClass> {
if (disableSearch.get()) return PsiClass.EMPTY_ARRAY
return withArrayProcessor(PsiClass.EMPTY_ARRAY) { processor ->
processClassesWithName(name, processor, scope, null)
}
}
private fun kotlinDeclarationsVisibleFromJavaScope(scope: GlobalSearchScope): GlobalSearchScope {
val noBuiltInsScope: GlobalSearchScope = object : GlobalSearchScope(project) {
override fun isSearchInModuleContent(aModule: Module) = true
override fun compare(file1: VirtualFile, file2: VirtualFile) = 0
override fun isSearchInLibraries() = true
override fun contains(file: VirtualFile) = file.fileType != KotlinBuiltInFileType
}
return KotlinSourceFilterScope.sourceAndClassFiles(scope, project).intersectWith(noBuiltInsScope)
}
//endregion
//region Methods
override fun processAllMethodNames(
processor: Processor<in String>,
scope: GlobalSearchScope,
filter: IdFilter?
): Boolean {
if (disableSearch.get()) return true
return processAllMethodNames(processor)
}
override fun getAllMethodNames(): Array<String> {
if (disableSearch.get()) ArrayUtil.EMPTY_STRING_ARRAY
return withArrayProcessor(ArrayUtil.EMPTY_STRING_ARRAY) { processor ->
processAllMethodNames(processor)
}
}
private fun processAllMethodNames(processor: Processor<in String>): Boolean {
if (disableSearch.get()) return true
if (!KotlinFunctionShortNameIndex.getInstance().processAllKeys(project, processor)) {
return false
}
return KotlinPropertyShortNameIndex.getInstance().processAllKeys(project) { name ->
return@processAllKeys processor.process(JvmAbi.setterName(name)) && processor.process(JvmAbi.getterName(name))
}
}
override fun processMethodsWithName(
name: String,
processor: Processor<in PsiMethod>,
scope: GlobalSearchScope,
filter: IdFilter?
): Boolean {
if (disableSearch.get()) return true
val allFunctionsProcessed = StubIndex.getInstance().processElements(
KotlinFunctionShortNameIndex.getInstance().key,
name,
project,
scope,
filter,
KtNamedFunction::class.java
) { ktNamedFunction ->
val methods = LightClassUtil.getLightClassMethodsByName(ktNamedFunction, name)
return@processElements methods.all { method ->
processor.process(method)
}
}
if (!allFunctionsProcessed) {
return false
}
for (propertyName in getPropertyNamesCandidatesByAccessorName(Name.identifier(name))) {
val allProcessed = StubIndex.getInstance().processElements(
KotlinPropertyShortNameIndex.getInstance().key,
propertyName.asString(),
project,
scope,
filter,
KtNamedDeclaration::class.java
) { ktNamedDeclaration ->
val methods: Sequence<PsiMethod> = ktNamedDeclaration.getAccessorLightMethods()
.asSequence()
.filter { it.name == name }
return@processElements methods.all { method ->
processor.process(method)
}
}
if (!allProcessed) {
return false
}
}
return true
}
override fun getMethodsByName(name: String, scope: GlobalSearchScope): Array<PsiMethod> {
if (disableSearch.get()) return PsiMethod.EMPTY_ARRAY
return withArrayProcessor(PsiMethod.EMPTY_ARRAY) { processor ->
processMethodsWithName(name, processor, scope, null)
}
}
override fun getMethodsByNameIfNotMoreThan(name: String, scope: GlobalSearchScope, maxCount: Int): Array<PsiMethod> {
if (disableSearch.get()) return PsiMethod.EMPTY_ARRAY
require(maxCount >= 0)
return withArrayProcessor(PsiMethod.EMPTY_ARRAY) { processor ->
processMethodsWithName(
name,
{ psiMethod ->
processor.size != maxCount && processor.process(psiMethod)
},
scope,
null
)
}
}
override fun processMethodsWithName(
name: String,
scope: GlobalSearchScope,
processor: Processor<in PsiMethod>
): Boolean {
if (disableSearch.get()) return true
return ContainerUtil.process(getMethodsByName(name, scope), processor)
}
//endregion
//region Fields
override fun processAllFieldNames(processor: Processor<in String>, scope: GlobalSearchScope, filter: IdFilter?): Boolean {
if (disableSearch.get()) return true
return processAllFieldNames(processor)
}
override fun getAllFieldNames(): Array<String> {
if (disableSearch.get()) return ArrayUtil.EMPTY_STRING_ARRAY
return withArrayProcessor(ArrayUtil.EMPTY_STRING_ARRAY) { processor ->
processAllFieldNames(processor)
}
}
private fun processAllFieldNames(processor: Processor<in String>): Boolean {
if (disableSearch.get()) return true
return KotlinPropertyShortNameIndex.getInstance().processAllKeys(project, processor)
}
override fun processFieldsWithName(
name: String,
processor: Processor<in PsiField>,
scope: GlobalSearchScope,
filter: IdFilter?
): Boolean {
if (disableSearch.get()) return true
return StubIndex.getInstance().processElements(
KotlinPropertyShortNameIndex.getInstance().key,
name,
project,
scope,
filter,
KtNamedDeclaration::class.java
) { ktNamedDeclaration ->
val field = LightClassUtil.getLightClassBackingField(ktNamedDeclaration)
?: return@processElements true
return@processElements processor.process(field)
}
}
override fun getFieldsByName(name: String, scope: GlobalSearchScope): Array<PsiField> {
if (disableSearch.get()) return PsiField.EMPTY_ARRAY
return withArrayProcessor(PsiField.EMPTY_ARRAY) { processor ->
processFieldsWithName(name, processor, scope, null)
}
}
override fun getFieldsByNameIfNotMoreThan(name: String, scope: GlobalSearchScope, maxCount: Int): Array<PsiField> {
if (disableSearch.get()) return PsiField.EMPTY_ARRAY
require(maxCount >= 0)
return withArrayProcessor(PsiField.EMPTY_ARRAY) { processor ->
processFieldsWithName(
name,
{ psiField ->
processor.size != maxCount && processor.process(psiField)
},
scope,
null
)
}
}
//endregion
private inline fun <T> withArrayProcessor(
result: Array<T>,
process: (CancelableArrayCollectProcessor<T>) -> Unit
): Array<T> {
return CancelableArrayCollectProcessor<T>().also { processor ->
process(processor)
}.toArray(result)
}
private class CancelableArrayCollectProcessor<T> : Processor<T> {
val troveSet = THashSet<T>()
private val processor = Processors.cancelableCollectProcessor<T>(troveSet)
override fun process(value: T): Boolean {
return processor.process(value)
}
val size: Int get() = troveSet.size
fun toArray(a: Array<T>): Array<T> = troveSet.toArray(a)
}
}

View File

@@ -1,418 +0,0 @@
/*
* Copyright 2000-2019 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.caches
import com.intellij.ProjectTopics
import com.intellij.openapi.Disposable
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.components.ServiceManager
import com.intellij.openapi.diagnostic.Logger
import com.intellij.openapi.module.Module
import com.intellij.openapi.progress.ProcessCanceledException
import com.intellij.openapi.project.Project
import com.intellij.openapi.project.rootManager
import com.intellij.openapi.roots.ModuleRootEvent
import com.intellij.openapi.roots.ModuleRootListener
import com.intellij.openapi.startup.StartupActivity
import com.intellij.openapi.util.Key
import com.intellij.openapi.vfs.VfsUtilCore
import com.intellij.openapi.vfs.VirtualFile
import com.intellij.openapi.vfs.VirtualFileManager
import com.intellij.openapi.vfs.newvfs.BulkFileListener
import com.intellij.openapi.vfs.newvfs.events.*
import com.intellij.psi.PsiFile
import com.intellij.psi.PsiManager
import com.intellij.psi.impl.PsiManagerEx
import com.intellij.psi.impl.PsiTreeChangeEventImpl
import com.intellij.psi.impl.PsiTreeChangePreprocessor
import com.intellij.psi.search.GlobalSearchScope
import com.intellij.util.containers.ContainerUtil
import org.jetbrains.kotlin.idea.KotlinFileType
import org.jetbrains.kotlin.idea.caches.PerModulePackageCacheService.Companion.DEBUG_LOG_ENABLE_PerModulePackageCache
import org.jetbrains.kotlin.idea.caches.PerModulePackageCacheService.Companion.FULL_DROP_THRESHOLD
import org.jetbrains.kotlin.idea.caches.project.ModuleSourceInfo
import org.jetbrains.kotlin.idea.caches.project.getModuleInfoByVirtualFile
import org.jetbrains.kotlin.idea.caches.project.getNullableModuleInfo
import org.jetbrains.kotlin.idea.stubindex.PackageIndexUtil
import org.jetbrains.kotlin.idea.util.getSourceRoot
import org.jetbrains.kotlin.idea.util.sourceRoot
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.psi.KtPackageDirective
import org.jetbrains.kotlin.psi.NotNullableUserDataProperty
import org.jetbrains.kotlin.psi.psiUtil.getChildrenOfType
import org.jetbrains.kotlin.psi.psiUtil.getParentOfType
import java.util.concurrent.ConcurrentHashMap
import java.util.concurrent.ConcurrentMap
class KotlinPackageContentModificationListener : StartupActivity {
companion object {
val LOG = Logger.getInstance(this::class.java)
}
override fun runActivity(project: Project) {
val connection = project.messageBus.connect(project)
connection.subscribe(VirtualFileManager.VFS_CHANGES, object : BulkFileListener {
override fun before(events: MutableList<out VFileEvent>) = onEvents(events, false)
override fun after(events: List<VFileEvent>) = onEvents(events, true)
private fun isRelevant(event: VFileEvent): Boolean = when (event) {
is VFilePropertyChangeEvent -> false
is VFileCreateEvent -> true
is VFileMoveEvent -> true
is VFileDeleteEvent -> true
is VFileContentChangeEvent -> true
is VFileCopyEvent -> true
else -> {
LOG.warn("Unknown vfs event: ${event.javaClass}")
false
}
}
fun onEvents(events: List<VFileEvent>, isAfter: Boolean) {
val service = PerModulePackageCacheService.getInstance(project)
val fileManager = PsiManagerEx.getInstanceEx(project).fileManager
if (events.size >= FULL_DROP_THRESHOLD) {
service.onTooComplexChange()
} else {
events.asSequence()
.filter(::isRelevant)
.filter {
(it.isValid || it !is VFileCreateEvent) && it.file != null
}
.filter {
val vFile = it.file!!
vFile.isDirectory || vFile.fileType == KotlinFileType.INSTANCE
}
.filter {
// It expected that content change events will be duplicated with more precise PSI events and processed
// in KotlinPackageStatementPsiTreeChangePreprocessor, but events might have been missing if PSI view provider
// is absent.
if (it is VFileContentChangeEvent) {
isAfter && fileManager.findCachedViewProvider(it.file) == null
} else {
true
}
}
.filter {
when (val origin = it.requestor) {
is Project -> origin == project
is PsiManager -> origin.project == project
else -> true
}
}
.forEach { event -> service.notifyPackageChange(event) }
}
}
})
connection.subscribe(ProjectTopics.PROJECT_ROOTS, object : ModuleRootListener {
override fun rootsChanged(event: ModuleRootEvent) {
PerModulePackageCacheService.getInstance(project).onTooComplexChange()
}
})
}
}
class KotlinPackageStatementPsiTreeChangePreprocessor(private val project: Project) : PsiTreeChangePreprocessor {
override fun treeChanged(event: PsiTreeChangeEventImpl) {
val eFile = event.file ?: event.child as? PsiFile
if (eFile == null) {
LOG.debugIfEnabled(project, true) { "Got PsiEvent: $event without file" }
}
val file = eFile as? KtFile ?: return
when (event.code) {
PsiTreeChangeEventImpl.PsiEventType.CHILD_ADDED,
PsiTreeChangeEventImpl.PsiEventType.CHILD_MOVED,
PsiTreeChangeEventImpl.PsiEventType.CHILD_REPLACED,
PsiTreeChangeEventImpl.PsiEventType.CHILD_REMOVED -> {
val child = event.child ?: run {
LOG.debugIfEnabled(project, true) { "Got PsiEvent: $event without child" }
return
}
if (child.getParentOfType<KtPackageDirective>(false) != null)
ServiceManager.getService(project, PerModulePackageCacheService::class.java).notifyPackageChange(file)
}
PsiTreeChangeEventImpl.PsiEventType.CHILDREN_CHANGED -> {
val parent = event.parent ?: run {
LOG.debugIfEnabled(project, true) { "Got PsiEvent: $event without parent" }
return
}
val childrenOfType = parent.getChildrenOfType<KtPackageDirective>()
if (
(!event.isGenericChange && (childrenOfType.any() || parent is KtPackageDirective)) ||
(childrenOfType.any { it.name.isEmpty() } && parent is KtFile)
) {
ServiceManager.getService(project, PerModulePackageCacheService::class.java).notifyPackageChange(file)
}
}
else -> {
}
}
}
companion object {
val LOG = Logger.getInstance(this::class.java)
}
}
private typealias ImplicitPackageData = MutableMap<FqName, MutableList<VirtualFile>>
class ImplicitPackagePrefixCache(private val project: Project) {
private val implicitPackageCache = ConcurrentHashMap<VirtualFile, ImplicitPackageData>()
fun getPrefix(sourceRoot: VirtualFile): FqName {
val implicitPackageMap = implicitPackageCache.getOrPut(sourceRoot) { analyzeImplicitPackagePrefixes(sourceRoot) }
return implicitPackageMap.keys.singleOrNull() ?: FqName.ROOT
}
internal fun clear() {
implicitPackageCache.clear()
}
private fun analyzeImplicitPackagePrefixes(sourceRoot: VirtualFile): MutableMap<FqName, MutableList<VirtualFile>> {
val result = mutableMapOf<FqName, MutableList<VirtualFile>>()
val ktFiles = sourceRoot.children.filter { it.fileType == KotlinFileType.INSTANCE }
for (ktFile in ktFiles) {
result.addFile(ktFile)
}
return result
}
private fun ImplicitPackageData.addFile(ktFile: VirtualFile) {
synchronized(this) {
val psiFile = PsiManager.getInstance(project).findFile(ktFile) as? KtFile ?: return
addPsiFile(psiFile, ktFile)
}
}
private fun ImplicitPackageData.addPsiFile(
psiFile: KtFile,
ktFile: VirtualFile
) = getOrPut(psiFile.packageFqName) { mutableListOf() }.add(ktFile)
private fun ImplicitPackageData.removeFile(file: VirtualFile) {
synchronized(this) {
for ((key, value) in this) {
if (value.remove(file)) {
if (value.isEmpty()) remove(key)
break
}
}
}
}
private fun ImplicitPackageData.updateFile(file: KtFile) {
synchronized(this) {
removeFile(file.virtualFile)
addPsiFile(file, file.virtualFile)
}
}
internal fun update(event: VFileEvent) {
when (event) {
is VFileCreateEvent -> checkNewFileInSourceRoot(event.file)
is VFileDeleteEvent -> checkDeletedFileInSourceRoot(event.file)
is VFileCopyEvent -> {
val newParent = event.newParent
if (newParent.isValid) {
checkNewFileInSourceRoot(newParent.findChild(event.newChildName))
}
}
is VFileMoveEvent -> {
checkNewFileInSourceRoot(event.file)
if (event.oldParent.getSourceRoot(project) == event.oldParent) {
implicitPackageCache[event.oldParent]?.removeFile(event.file)
}
}
}
}
private fun checkNewFileInSourceRoot(file: VirtualFile?) {
if (file == null) return
if (file.getSourceRoot(project) == file.parent) {
implicitPackageCache[file.parent]?.addFile(file)
}
}
private fun checkDeletedFileInSourceRoot(file: VirtualFile?) {
val directory = file?.parent
if (directory == null || !directory.isValid) return
if (directory.getSourceRoot(project) == directory) {
implicitPackageCache[directory]?.removeFile(file)
}
}
internal fun update(ktFile: KtFile) {
val parent = ktFile.virtualFile?.parent ?: return
if (ktFile.sourceRoot == parent) {
implicitPackageCache[parent]?.updateFile(ktFile)
}
}
}
class PerModulePackageCacheService(private val project: Project) : Disposable {
/*
* Actually an WeakMap<Module, SoftMap<ModuleSourceInfo, SoftMap<FqName, Boolean>>>
*/
private val cache = ContainerUtil.createConcurrentWeakMap<Module, ConcurrentMap<ModuleSourceInfo, ConcurrentMap<FqName, Boolean>>>()
private val implicitPackagePrefixCache = ImplicitPackagePrefixCache(project)
private val pendingVFileChanges: MutableSet<VFileEvent> = mutableSetOf()
private val pendingKtFileChanges: MutableSet<KtFile> = mutableSetOf()
private val projectScope = GlobalSearchScope.projectScope(project)
internal fun onTooComplexChange() {
clear()
}
private fun clear() {
synchronized(this) {
pendingVFileChanges.clear()
pendingKtFileChanges.clear()
cache.clear()
implicitPackagePrefixCache.clear()
}
}
internal fun notifyPackageChange(file: VFileEvent): Unit = synchronized(this) {
pendingVFileChanges += file
}
internal fun notifyPackageChange(file: KtFile): Unit = synchronized(this) {
pendingKtFileChanges += file
}
private fun invalidateCacheForModuleSourceInfo(moduleSourceInfo: ModuleSourceInfo) {
LOG.debugIfEnabled(project) { "Invalidated cache for $moduleSourceInfo" }
val perSourceInfoData = cache[moduleSourceInfo.module] ?: return
val dataForSourceInfo = perSourceInfoData[moduleSourceInfo] ?: return
dataForSourceInfo.clear()
}
private fun checkPendingChanges() = synchronized(this) {
if (pendingVFileChanges.size + pendingKtFileChanges.size >= FULL_DROP_THRESHOLD) {
onTooComplexChange()
} else {
pendingVFileChanges.processPending { event ->
val vfile = event.file ?: return@processPending
// When VirtualFile !isValid (deleted for example), it impossible to use getModuleInfoByVirtualFile
// For directory we must check both is it in some sourceRoot, and is it contains some sourceRoot
if (vfile.isDirectory || !vfile.isValid) {
for ((module, data) in cache) {
val sourceRootUrls = module.rootManager.sourceRootUrls
if (sourceRootUrls.any { url ->
vfile.containedInOrContains(url)
}) {
LOG.debugIfEnabled(project) { "Invalidated cache for $module" }
data.clear()
}
}
} else {
val infoByVirtualFile = getModuleInfoByVirtualFile(project, vfile)
if (infoByVirtualFile == null || infoByVirtualFile !is ModuleSourceInfo) {
LOG.debugIfEnabled(project) { "Skip $vfile as it has mismatched ModuleInfo=$infoByVirtualFile" }
}
(infoByVirtualFile as? ModuleSourceInfo)?.let {
invalidateCacheForModuleSourceInfo(it)
}
}
implicitPackagePrefixCache.update(event)
}
pendingKtFileChanges.processPending { file ->
if (file.virtualFile != null && file.virtualFile !in projectScope) {
LOG.debugIfEnabled(project) {
"Skip $file without vFile, or not in scope: ${file.virtualFile?.let { it !in projectScope }}"
}
return@processPending
}
val nullableModuleInfo = file.getNullableModuleInfo()
(nullableModuleInfo as? ModuleSourceInfo)?.let { invalidateCacheForModuleSourceInfo(it) }
if (nullableModuleInfo == null || nullableModuleInfo !is ModuleSourceInfo) {
LOG.debugIfEnabled(project) { "Skip $file as it has mismatched ModuleInfo=$nullableModuleInfo" }
}
implicitPackagePrefixCache.update(file)
}
}
}
private inline fun <T> MutableCollection<T>.processPending(crossinline body: (T) -> Unit) {
this.removeIf { value ->
try {
body(value)
} catch (pce: ProcessCanceledException) {
throw pce
} catch (exc: Exception) {
// Log and proceed. Otherwise pending object processing won't be cleared and exception will be thrown forever.
LOG.error(exc)
}
return@removeIf true
}
}
private fun VirtualFile.containedInOrContains(root: String) =
(VfsUtilCore.isEqualOrAncestor(url, root)
|| isDirectory && VfsUtilCore.isEqualOrAncestor(root, url))
fun packageExists(packageFqName: FqName, moduleInfo: ModuleSourceInfo): Boolean {
val module = moduleInfo.module
checkPendingChanges()
val perSourceInfoCache = cache.getOrPut(module) {
ContainerUtil.createConcurrentSoftMap()
}
val cacheForCurrentModuleInfo = perSourceInfoCache.getOrPut(moduleInfo) {
ContainerUtil.createConcurrentSoftMap()
}
return cacheForCurrentModuleInfo.getOrPut(packageFqName) {
val packageExists = PackageIndexUtil.packageExists(packageFqName, moduleInfo.contentScope(), project)
LOG.debugIfEnabled(project) { "Computed cache value for $packageFqName in $moduleInfo is $packageExists" }
packageExists
}
}
fun getImplicitPackagePrefix(sourceRoot: VirtualFile): FqName {
checkPendingChanges()
return implicitPackagePrefixCache.getPrefix(sourceRoot)
}
override fun dispose() {
clear()
}
companion object {
const val FULL_DROP_THRESHOLD = 1000
private val LOG = Logger.getInstance(this::class.java)
fun getInstance(project: Project): PerModulePackageCacheService =
ServiceManager.getService(project, PerModulePackageCacheService::class.java)
var Project.DEBUG_LOG_ENABLE_PerModulePackageCache: Boolean
by NotNullableUserDataProperty<Project, Boolean>(Key.create("debug.PerModulePackageCache"), false)
}
}
private fun Logger.debugIfEnabled(project: Project, withCurrentTrace: Boolean = false, message: () -> String) {
if (ApplicationManager.getApplication().isUnitTestMode && project.DEBUG_LOG_ENABLE_PerModulePackageCache) {
val msg = message()
if (withCurrentTrace) {
val e = Exception().apply { fillInStackTrace() }
this.debug(msg, e)
} else {
this.debug(msg)
}
}
}

View File

@@ -1,68 +0,0 @@
/*
* Copyright 2010-2017 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.idea.caches.lightClasses
import com.intellij.ide.highlighter.JavaClassFileType
import com.intellij.openapi.components.ServiceManager
import com.intellij.openapi.diagnostic.Logger
import com.intellij.openapi.project.Project
import com.intellij.openapi.vfs.VirtualFile
import com.intellij.psi.impl.compiled.ClsFileImpl
import com.intellij.psi.impl.java.stubs.PsiJavaFileStub
import com.intellij.psi.impl.java.stubs.impl.PsiJavaFileStubImpl
import com.intellij.util.cls.ClsFormatException
import com.intellij.util.containers.ContainerUtil
import java.io.IOException
class ClsJavaStubByVirtualFileCache {
private class CachedJavaStub(val modificationStamp: Long, val javaFileStub: PsiJavaFileStubImpl)
private val cache = ContainerUtil.createConcurrentWeakKeySoftValueMap<VirtualFile, CachedJavaStub>()
fun get(classFile: VirtualFile): PsiJavaFileStubImpl? {
val cached = cache.get(classFile)
val fileModificationStamp = classFile.modificationStamp
if (cached != null && cached.modificationStamp == fileModificationStamp) {
return cached.javaFileStub
}
val stub = createStub(classFile) as PsiJavaFileStubImpl? ?: return null
cache.put(classFile, CachedJavaStub(fileModificationStamp, stub))
return stub
}
private fun createStub(file: VirtualFile): PsiJavaFileStub? {
if (file.fileType !== JavaClassFileType.INSTANCE) return null
try {
return ClsFileImpl.buildFileStub(file, file.contentsToByteArray(false))
} catch (e: ClsFormatException) {
LOG.error("Failed to build java cls class for " + file.canonicalPath!!, e)
} catch (e: IOException) {
LOG.error("Failed to build java cls class for " + file.canonicalPath!!, e)
}
return null
}
companion object {
private val LOG = Logger.getInstance(ClsJavaStubByVirtualFileCache::class.java)
fun getInstance(project: Project): ClsJavaStubByVirtualFileCache {
return ServiceManager.getService(project, ClsJavaStubByVirtualFileCache::class.java)
}
}
}

View File

@@ -1,474 +0,0 @@
/*
* Copyright 2010-2019 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.caches.lightClasses
import com.intellij.openapi.diagnostic.Logger
import com.intellij.openapi.project.Project
import com.intellij.psi.search.EverythingGlobalScope
import com.intellij.psi.search.GlobalSearchScope
import com.intellij.psi.stubs.StubIndex
import org.jetbrains.kotlin.asJava.builder.LightClassConstructionContext
import org.jetbrains.kotlin.config.JvmTarget
import org.jetbrains.kotlin.config.LanguageVersionSettings
import org.jetbrains.kotlin.container.get
import org.jetbrains.kotlin.container.useImpl
import org.jetbrains.kotlin.container.useInstance
import org.jetbrains.kotlin.context.ModuleContext
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
import org.jetbrains.kotlin.descriptors.impl.CompositePackageFragmentProvider
import org.jetbrains.kotlin.descriptors.impl.ModuleDescriptorImpl
import org.jetbrains.kotlin.frontend.di.configureModule
import org.jetbrains.kotlin.idea.FrontendInternals
import org.jetbrains.kotlin.idea.caches.lightClasses.IDELightClassConstructionContext.Mode.EXACT
import org.jetbrains.kotlin.idea.caches.lightClasses.IDELightClassConstructionContext.Mode.LIGHT
import org.jetbrains.kotlin.idea.caches.lightClasses.annotations.KOTLINX_SERIALIZABLE_FQ_NAME
import org.jetbrains.kotlin.idea.caches.lightClasses.annotations.KOTLINX_SERIALIZER_FQ_NAME
import org.jetbrains.kotlin.idea.caches.project.getModuleInfo
import org.jetbrains.kotlin.idea.caches.resolve.getResolutionFacade
import org.jetbrains.kotlin.idea.compiler.IDELanguageSettingsProvider
import org.jetbrains.kotlin.idea.project.IdeaEnvironment
import org.jetbrains.kotlin.idea.project.ResolveElementCache
import org.jetbrains.kotlin.idea.project.languageVersionSettings
import org.jetbrains.kotlin.idea.resolve.ResolutionFacade
import org.jetbrains.kotlin.idea.stubindex.KotlinOverridableInternalMembersShortNameIndex
import org.jetbrains.kotlin.incremental.components.ExpectActualTracker
import org.jetbrains.kotlin.incremental.components.LookupTracker
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.platform.jvm.JvmPlatforms
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.getElementTextWithContext
import org.jetbrains.kotlin.resolve.*
import org.jetbrains.kotlin.resolve.calls.CallResolver
import org.jetbrains.kotlin.resolve.calls.context.BasicCallResolutionContext
import org.jetbrains.kotlin.resolve.calls.context.CheckArgumentTypesMode
import org.jetbrains.kotlin.resolve.calls.context.ContextDependency
import org.jetbrains.kotlin.resolve.calls.results.OverloadResolutionResults
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowInfoFactory
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowValueFactory
import org.jetbrains.kotlin.resolve.calls.util.CallMaker
import org.jetbrains.kotlin.resolve.constants.evaluate.ConstantExpressionEvaluator
import org.jetbrains.kotlin.resolve.jvm.annotations.JVM_SYNTHETIC_ANNOTATION_FQ_NAME
import org.jetbrains.kotlin.resolve.jvm.platform.JvmPlatformAnalyzerServices
import org.jetbrains.kotlin.resolve.lazy.FileScopeProviderImpl
import org.jetbrains.kotlin.resolve.lazy.ForceResolveUtil
import org.jetbrains.kotlin.resolve.lazy.ResolveSession
import org.jetbrains.kotlin.resolve.lazy.declarations.FileBasedDeclarationProviderFactory
import org.jetbrains.kotlin.resolve.scopes.LexicalScope
import org.jetbrains.kotlin.storage.LockBasedStorageManager
import org.jetbrains.kotlin.storage.StorageManager
import org.jetbrains.kotlin.types.ErrorUtils
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.TypeUtils
import org.jetbrains.kotlin.types.WrappedTypeFactory
import org.jetbrains.kotlin.utils.sure
class IDELightClassConstructionContext(
bindingContext: BindingContext,
module: ModuleDescriptor,
languageVersionSettings: LanguageVersionSettings,
jvmTarget: JvmTarget,
val mode: Mode
) : LightClassConstructionContext(bindingContext, module, languageVersionSettings, jvmTarget) {
enum class Mode {
LIGHT,
EXACT
}
override fun toString() = "${this.javaClass.simpleName}:$mode"
}
internal object IDELightClassContexts {
private val LOG = Logger.getInstance(this::class.java)
fun contextForNonLocalClassOrObject(classOrObject: KtClassOrObject): LightClassConstructionContext {
val resolutionFacade = classOrObject.getResolutionFacade()
val bindingContext = if (classOrObject is KtClass && classOrObject.isAnnotation()) {
// need to make sure default values for parameters are resolved
// because java resolve depends on whether there is a default value for an annotation attribute
@OptIn(FrontendInternals::class)
resolutionFacade.getFrontendService(ResolveElementCache::class.java)
.resolvePrimaryConstructorParametersDefaultValues(classOrObject)
} else {
resolutionFacade.analyze(classOrObject)
}
val classDescriptor = bindingContext.get(BindingContext.CLASS, classOrObject).sure {
"Class descriptor was not found for ${classOrObject.getElementTextWithContext()}"
}
ForceResolveUtil.forceResolveAllContents(classDescriptor)
return IDELightClassConstructionContext(
bindingContext,
resolutionFacade.moduleDescriptor,
classOrObject.languageVersionSettings,
resolutionFacade.jvmTarget,
EXACT
)
}
fun contextForLocalClassOrObject(classOrObject: KtClassOrObject): LightClassConstructionContext {
val resolutionFacade = classOrObject.getResolutionFacade()
val bindingContext = resolutionFacade.analyze(classOrObject)
val descriptor = bindingContext.get(BindingContext.CLASS, classOrObject)
if (descriptor == null) {
LOG.warn("No class descriptor in context for class: " + classOrObject.getElementTextWithContext())
return IDELightClassConstructionContext(
bindingContext,
resolutionFacade.moduleDescriptor,
classOrObject.languageVersionSettings,
resolutionFacade.jvmTarget,
EXACT
)
}
ForceResolveUtil.forceResolveAllContents(descriptor)
return IDELightClassConstructionContext(
bindingContext,
resolutionFacade.moduleDescriptor,
classOrObject.languageVersionSettings,
resolutionFacade.jvmTarget,
EXACT
)
}
fun contextForFacade(files: List<KtFile>): LightClassConstructionContext {
val resolutionFacade = files.first().getResolutionFacade()
@OptIn(FrontendInternals::class)
val resolveSession = resolutionFacade.getFrontendService(ResolveSession::class.java)
forceResolvePackageDeclarations(files, resolveSession)
return IDELightClassConstructionContext(
resolveSession.bindingContext,
resolveSession.moduleDescriptor,
files.first().languageVersionSettings,
resolutionFacade.jvmTarget,
EXACT
)
}
fun contextForScript(script: KtScript): LightClassConstructionContext {
val resolutionFacade = script.getResolutionFacade()
val bindingContext = resolutionFacade.analyze(script)
val descriptor = bindingContext[BindingContext.SCRIPT, script]
if (descriptor == null) {
LOG.warn("No script descriptor in context for script: " + script.getElementTextWithContext())
return IDELightClassConstructionContext(
bindingContext,
resolutionFacade.moduleDescriptor,
script.languageVersionSettings,
resolutionFacade.jvmTarget,
EXACT
)
}
ForceResolveUtil.forceResolveAllContents(descriptor)
return IDELightClassConstructionContext(
bindingContext, resolutionFacade.moduleDescriptor, script.languageVersionSettings, resolutionFacade.jvmTarget,
EXACT
)
}
fun lightContextForClassOrObject(classOrObject: KtClassOrObject): LightClassConstructionContext? {
if (!isDummyResolveApplicable(classOrObject)) return null
val resolutionFacade = classOrObject.getResolutionFacade()
val resolveSession = setupAdHocResolve(
classOrObject.project,
resolutionFacade.moduleDescriptor,
listOf(classOrObject.containingKtFile)
)
val descriptor = resolveSession.resolveToDescriptor(classOrObject) as? ClassDescriptor ?: return null
if (!isDummyResolveApplicableByDescriptor(descriptor)) return null
ForceResolveUtil.forceResolveAllContents(descriptor)
return IDELightClassConstructionContext(
resolveSession.bindingContext,
resolveSession.moduleDescriptor,
classOrObject.languageVersionSettings,
resolutionFacade.jvmTarget,
LIGHT
)
}
fun lightContextForFacade(files: List<KtFile>): LightClassConstructionContext {
val representativeFile = files.first()
val resolutionFacade = representativeFile.getResolutionFacade()
val resolveSession = setupAdHocResolve(representativeFile.project, resolutionFacade.moduleDescriptor, files)
forceResolvePackageDeclarations(files, resolveSession)
return IDELightClassConstructionContext(
resolveSession.bindingContext,
resolveSession.moduleDescriptor,
files.first().languageVersionSettings,
resolutionFacade.jvmTarget,
LIGHT
)
}
@OptIn(FrontendInternals::class)
private val ResolutionFacade.jvmTarget: JvmTarget
get() = getFrontendService(JvmTarget::class.java)
private fun isDummyResolveApplicable(classOrObject: KtClassOrObject): Boolean {
if (classOrObject.hasModifier(KtTokens.INLINE_KEYWORD)) return false
if (classOrObject.hasLightClassMatchingErrors) return false
if (hasDelegatedSupertypes(classOrObject)) return false
if (isDataClassWithGeneratedMembersOverridden(classOrObject)) return false
if (isDataClassWhichExtendsOtherClass(classOrObject)) return false
if (hasMembersOverridingInternalMembers(classOrObject)) return false
if (hasSerializationLikeAnnotations(classOrObject)) return false
if (hasJvmSyntheticMembers(classOrObject)) return false
return classOrObject.declarations.filterIsInstance<KtClassOrObject>().all { isDummyResolveApplicable(it) }
}
private fun hasSerializationLikeAnnotations(classOrObject: KtClassOrObject) =
classOrObject.annotationEntries.any { isSerializableOrSerializerShortName(it.shortName) }
private fun isDummyResolveApplicableByDescriptor(classDescriptor: ClassDescriptor): Boolean {
if (classDescriptor.annotations.any { isSerializableOrSerializerFqName(it.fqName) }) return false
return classDescriptor
.unsubstitutedInnerClassesScope
.getContributedDescriptors()
.filterIsInstance<ClassDescriptor>()
.all(::isDummyResolveApplicableByDescriptor)
}
private fun isSerializableOrSerializerShortName(shortName: Name?) =
shortName == KOTLINX_SERIALIZABLE_FQ_NAME.shortName() || shortName == KOTLINX_SERIALIZER_FQ_NAME.shortName()
private fun isSerializableOrSerializerFqName(fqName: FqName?) =
fqName == KOTLINX_SERIALIZABLE_FQ_NAME || fqName == KOTLINX_SERIALIZER_FQ_NAME
private fun hasJvmSyntheticMembers(classOrObject: KtClassOrObject) =
classOrObject.declarations.filterIsInstance<KtFunction>().any { isJvmSynthetic(it) }
private fun isJvmSynthetic(fn: KtFunction) =
fn.annotationEntries.any { it.shortName == JVM_SYNTHETIC_ANNOTATION_FQ_NAME.shortName() }
private fun hasDelegatedSupertypes(classOrObject: KtClassOrObject) =
classOrObject.superTypeListEntries.any { it is KtDelegatedSuperTypeEntry }
private fun isDataClassWithGeneratedMembersOverridden(classOrObject: KtClassOrObject): Boolean {
return classOrObject.hasModifier(KtTokens.DATA_KEYWORD) &&
classOrObject.declarations.filterIsInstance<KtFunction>().any {
isGeneratedForDataClass(it.nameAsSafeName)
}
}
private fun isGeneratedForDataClass(name: Name): Boolean {
return name == FunctionsFromAny.EQUALS_METHOD_NAME ||
// known failure is related to equals override, checking for other methods 'just in case'
name == DataClassDescriptorResolver.COPY_METHOD_NAME ||
name == FunctionsFromAny.HASH_CODE_METHOD_NAME ||
name == FunctionsFromAny.TO_STRING_METHOD_NAME ||
DataClassDescriptorResolver.isComponentLike(name)
}
private fun isDataClassWhichExtendsOtherClass(classOrObject: KtClassOrObject): Boolean {
return classOrObject.hasModifier(KtTokens.DATA_KEYWORD) &&
classOrObject.superTypeListEntries.isNotEmpty()
}
private fun hasMembersOverridingInternalMembers(classOrObject: KtClassOrObject): Boolean {
return classOrObject.declarations.filterIsInstance<KtCallableDeclaration>().any {
possiblyOverridesInternalMember(it)
}
}
private fun possiblyOverridesInternalMember(declaration: KtCallableDeclaration): Boolean {
if (!declaration.hasModifier(KtTokens.OVERRIDE_KEYWORD)) return false
return declaration.name?.let { anyInternalMembersWithThisName(it, declaration.project) } ?: false
}
private fun anyInternalMembersWithThisName(name: String, project: Project): Boolean {
var result = false
StubIndex.getInstance().processElements(
KotlinOverridableInternalMembersShortNameIndex.Instance.key, name, project,
EverythingGlobalScope(project), KtCallableDeclaration::class.java
) {
result = true
false // stop processing at first matching result
}
return result
}
fun forceResolvePackageDeclarations(files: Collection<KtFile>, session: ResolveSession) {
for (file in files) {
if (file.isScript()) continue
val packageFqName = file.packageFqName
// make sure we create a package descriptor
val packageDescriptor = session.moduleDescriptor.getPackage(packageFqName)
if (packageDescriptor.isEmpty()) {
LOG.warn("No descriptor found for package " + packageFqName + " in file " + file.name + "\n" + file.text)
session.forceResolveAll()
continue
}
for (declaration in file.declarations) {
when (declaration) {
is KtFunction -> {
val name = declaration.nameAsSafeName
val functions = packageDescriptor.memberScope.getContributedFunctions(name, NoLookupLocation.FROM_IDE)
for (descriptor in functions) {
ForceResolveUtil.forceResolveAllContents(descriptor)
}
}
is KtProperty -> {
val name = declaration.nameAsSafeName
val properties = packageDescriptor.memberScope.getContributedVariables(name, NoLookupLocation.FROM_IDE)
for (descriptor in properties) {
ForceResolveUtil.forceResolveAllContents(descriptor)
}
}
is KtClassOrObject, is KtTypeAlias, is KtDestructuringDeclaration -> {
// Do nothing: we are not interested in classes or type aliases,
// and all destructuring declarations are erroneous at top level
}
else -> LOG.error("Unsupported declaration kind: " + declaration + " in file " + file.name + "\n" + file.text)
}
}
ForceResolveUtil.forceResolveAllContents(session.getFileAnnotations(file))
}
}
private fun setupAdHocResolve(project: Project, realWorldModule: ModuleDescriptor, files: List<KtFile>): ResolveSession {
val trace = BindingTraceContext()
val sm = LockBasedStorageManager.NO_LOCKS
val moduleDescriptor =
ModuleDescriptorImpl(realWorldModule.name, sm, realWorldModule.builtIns, stableName = realWorldModule.stableName)
moduleDescriptor.setDependencies(moduleDescriptor, moduleDescriptor.builtIns.builtInsModule)
val moduleInfo = files.first().getModuleInfo()
val container = createContainer("LightClassStub", JvmPlatformAnalyzerServices) {
val jvmTarget = IDELanguageSettingsProvider.getTargetPlatform(moduleInfo, project) as? JvmTarget ?: JvmTarget.DEFAULT
configureModule(
ModuleContext(moduleDescriptor, project, "ad hoc resolve"), JvmPlatforms.jvmPlatformByTargetVersion(jvmTarget),
JvmPlatformAnalyzerServices, trace,
IDELanguageSettingsProvider.getLanguageVersionSettings(moduleInfo, project)
)
useInstance(GlobalSearchScope.EMPTY_SCOPE)
useInstance(LookupTracker.DO_NOTHING)
useInstance(ExpectActualTracker.DoNothing)
useImpl<FileScopeProviderImpl>()
useInstance(FileBasedDeclarationProviderFactory(sm, files))
useInstance(CodegenAffectingAnnotations(realWorldModule))
useImpl<AdHocAnnotationResolver>()
useInstance(object : WrappedTypeFactory(sm) {
override fun createDeferredType(trace: BindingTrace, computation: () -> KotlinType) = errorType()
override fun createRecursionIntolerantDeferredType(trace: BindingTrace, computation: () -> KotlinType) = errorType()
private fun errorType() = ErrorUtils.createErrorType("Error type in ad hoc resolve for lighter classes")
})
IdeaEnvironment.configure(this)
useImpl<ResolveSession>()
}
val resolveSession = container.get<ResolveSession>()
moduleDescriptor.initialize(
CompositePackageFragmentProvider(
listOf(resolveSession.packageFragmentProvider),
"CompositeProvider@IDELightClassContexts for $moduleDescriptor"
)
)
return resolveSession
}
class CodegenAffectingAnnotations(private val realModule: ModuleDescriptor) {
fun get(name: String): ClassDescriptor? {
val annotationFqName = annotationsThatAffectCodegen.firstOrNull { it.shortName().asString() == name } ?: return null
return realModule.getPackage(annotationFqName.parent()).memberScope
.getContributedClassifier(annotationFqName.shortName(), NoLookupLocation.FROM_IDE) as? ClassDescriptor
}
// see JvmPlatformAnnotations.kt, JvmFlagAnnotations.kt, also PsiModifier.MODIFIERS
private val annotationsThatAffectCodegen = listOf(
"JvmField", "JvmOverloads", "JvmName", "JvmStatic",
"Synchronized", "Transient", "Volatile", "Strictfp"
).map { FqName("kotlin.jvm").child(Name.identifier(it)) } +
FqName("kotlin.PublishedApi") +
FqName("kotlin.Deprecated") +
FqName("kotlin.internal.InlineOnly") +
FqName("kotlinx.android.parcel.Parcelize") +
KOTLINX_SERIALIZABLE_FQ_NAME +
KOTLINX_SERIALIZER_FQ_NAME
}
class AdHocAnnotationResolver(
private val codegenAffectingAnnotations: CodegenAffectingAnnotations,
private val callResolver: CallResolver,
private val languageVersionSettings: LanguageVersionSettings,
private val dataFlowValueFactory: DataFlowValueFactory, constantExpressionEvaluator: ConstantExpressionEvaluator,
storageManager: StorageManager
) : AnnotationResolverImpl(callResolver, constantExpressionEvaluator, storageManager) {
override fun resolveAnnotationType(scope: LexicalScope, entryElement: KtAnnotationEntry, trace: BindingTrace): KotlinType {
return annotationClassByEntry(entryElement)?.defaultType ?: super.resolveAnnotationType(scope, entryElement, trace)
}
private fun annotationClassByEntry(entryElement: KtAnnotationEntry): ClassDescriptor? {
val annotationTypeReferencePsi = entryElement.calleeExpression?.constructorReferenceExpression ?: return null
val referencedName = annotationTypeReferencePsi.getReferencedName()
return codegenAffectingAnnotations.get(referencedName)
}
override fun resolveAnnotationCall(
annotationEntry: KtAnnotationEntry,
scope: LexicalScope,
trace: BindingTrace
): OverloadResolutionResults<FunctionDescriptor> {
val annotationConstructor = annotationClassByEntry(annotationEntry)?.constructors?.singleOrNull()
?: return super.resolveAnnotationCall(annotationEntry, scope, trace)
@Suppress("UNCHECKED_CAST")
return callResolver.resolveConstructorCall(
BasicCallResolutionContext.create(
trace, scope, CallMaker.makeCall(null, null, annotationEntry), TypeUtils.NO_EXPECTED_TYPE,
DataFlowInfoFactory.EMPTY, ContextDependency.INDEPENDENT, CheckArgumentTypesMode.CHECK_VALUE_ARGUMENTS,
true, languageVersionSettings,
dataFlowValueFactory
),
annotationEntry.calleeExpression!!.constructorReferenceExpression!!,
annotationConstructor.returnType
) as OverloadResolutionResults<FunctionDescriptor>
}
}
}

View File

@@ -1,72 +0,0 @@
/*
* Copyright 2010-2017 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.idea.caches.lightClasses
import com.intellij.psi.CommonClassNames
import com.intellij.psi.PsiClass
import org.jetbrains.kotlin.asJava.ImpreciseResolveResult
import org.jetbrains.kotlin.asJava.ImpreciseResolveResult.*
import org.jetbrains.kotlin.asJava.classes.KtLightClass
import org.jetbrains.kotlin.asJava.classes.LightClassInheritanceHelper
import org.jetbrains.kotlin.asJava.classes.defaultJavaAncestorQualifiedName
import org.jetbrains.kotlin.idea.caches.resolve.util.isInDumbMode
import org.jetbrains.kotlin.idea.search.PsiBasedClassResolver
import org.jetbrains.kotlin.psi.KtSimpleNameExpression
import org.jetbrains.kotlin.psi.KtSuperTypeCallEntry
import org.jetbrains.kotlin.psi.KtSuperTypeListEntry
class IdeLightClassInheritanceHelper : LightClassInheritanceHelper {
override fun isInheritor(
lightClass: KtLightClass,
baseClass: PsiClass,
checkDeep: Boolean
): ImpreciseResolveResult {
if (baseClass.project.isInDumbMode()) return NO_MATCH
if (lightClass.manager.areElementsEquivalent(baseClass, lightClass)) return NO_MATCH
val classOrObject = lightClass.kotlinOrigin ?: return UNSURE
if (checkDeep && baseClass.qualifiedName == CommonClassNames.JAVA_LANG_OBJECT) {
return MATCH
}
val entries = classOrObject.superTypeListEntries
val hasSuperClass = entries.any { it is KtSuperTypeCallEntry }
if (baseClass.qualifiedName == classOrObject.defaultJavaAncestorQualifiedName() && (!hasSuperClass || checkDeep)) {
return MATCH
}
val amongEntries = isAmongEntries(baseClass, entries)
return when {
!checkDeep -> amongEntries
amongEntries == MATCH -> MATCH
else -> UNSURE
}
}
private fun isAmongEntries(baseClass: PsiClass, entries: List<KtSuperTypeListEntry>): ImpreciseResolveResult {
val psiBasedResolver = PsiBasedClassResolver.getInstance(baseClass)
entries@ for (entry in entries) {
val reference: KtSimpleNameExpression = entry.typeAsUserType?.referenceExpression ?: continue@entries
when (psiBasedResolver.canBeTargetReference(reference)) {
MATCH -> return MATCH
NO_MATCH -> continue@entries
UNSURE -> return UNSURE
}
}
return NO_MATCH
}
}

View File

@@ -1,190 +0,0 @@
/*
* Copyright 2000-2019 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.caches.lightClasses
import com.intellij.openapi.diagnostic.Logger
import com.intellij.openapi.util.Key
import com.intellij.psi.PsiClass
import com.intellij.psi.PsiMember
import com.intellij.psi.PsiMethod
import com.intellij.psi.impl.java.stubs.PsiJavaFileStub
import org.jetbrains.kotlin.asJava.LightClassBuilder
import org.jetbrains.kotlin.asJava.builder.*
import org.jetbrains.kotlin.asJava.classes.KtLightClass
import org.jetbrains.kotlin.asJava.classes.lazyPub
import org.jetbrains.kotlin.asJava.elements.KtLightField
import org.jetbrains.kotlin.asJava.elements.KtLightFieldImpl
import org.jetbrains.kotlin.asJava.elements.KtLightMethod
import org.jetbrains.kotlin.asJava.elements.KtLightMethodImpl
import org.jetbrains.kotlin.psi.KtClassOrObject
import org.jetbrains.kotlin.psi.NotNullableUserDataProperty
import org.jetbrains.kotlin.psi.debugText.getDebugText
import org.jetbrains.kotlin.resolve.diagnostics.Diagnostics
import org.jetbrains.kotlin.storage.StorageManager
typealias ExactLightClassContextProvider = () -> LightClassConstructionContext
typealias DummyLightClassContextProvider = (() -> LightClassConstructionContext?)?
typealias DiagnosticsHolderProvider = () -> LazyLightClassDataHolder.DiagnosticsHolder
sealed class LazyLightClassDataHolder(
private val builder: LightClassBuilder,
private val exactContextProvider: ExactLightClassContextProvider,
dummyContextProvider: DummyLightClassContextProvider,
private val diagnosticsHolderProvider: DiagnosticsHolderProvider
) : LightClassDataHolder {
class DiagnosticsHolder(storageManager: StorageManager) {
private val cache = storageManager.createCacheWithNotNullValues<LazyLightClassDataHolder, Diagnostics>()
fun getOrCompute(lazyLightClassDataHolder: LazyLightClassDataHolder, diagnostics: () -> Diagnostics) =
cache.computeIfAbsent(lazyLightClassDataHolder, diagnostics)
}
private val _builderExactContextProvider: LightClassBuilderResult by lazyPub { builder(exactContextProvider()) }
private val exactResultLazyValue = lazyPub { _builderExactContextProvider.stub }
private val lazyInexactStub by lazyPub {
dummyContextProvider?.let { provider -> provider()?.let { context -> builder.invoke(context).stub } }
}
private val inexactStub: PsiJavaFileStub?
get() = if (exactResultLazyValue.isInitialized()) null else lazyInexactStub
override val javaFileStub by exactResultLazyValue
override val extraDiagnostics: Diagnostics
get() = diagnosticsHolderProvider().getOrCompute(this) {
// Force lazy diagnostics computation because otherwise a lot of memory is retained by computation.
// NB: Laziness here is not crucial anyway since somebody already has requested diagnostics and we hope one will use them
_builderExactContextProvider.diagnostics.takeUnless { it.isEmpty() } ?: Diagnostics.EMPTY
}
// for facade or defaultImpls
override fun findData(findDelegate: (PsiJavaFileStub) -> PsiClass): LightClassData =
LazyLightClassData { stub ->
findDelegate(stub)
}
class ForClass(
builder: LightClassBuilder,
exactContextProvider: ExactLightClassContextProvider,
dummyContextProvider: DummyLightClassContextProvider,
diagnosticsHolderProvider: DiagnosticsHolderProvider
) : LazyLightClassDataHolder(builder, exactContextProvider, dummyContextProvider, diagnosticsHolderProvider),
LightClassDataHolder.ForClass {
override fun findDataForClassOrObject(classOrObject: KtClassOrObject): LightClassData =
LazyLightClassData { stub ->
stub.findDelegate(classOrObject)
}
}
class ForFacade(
builder: LightClassBuilder,
exactContextProvider: ExactLightClassContextProvider,
dummyContextProvider: DummyLightClassContextProvider,
diagnosticsHolderProvider: DiagnosticsHolderProvider
) : LazyLightClassDataHolder(builder, exactContextProvider, dummyContextProvider, diagnosticsHolderProvider),
LightClassDataHolder.ForFacade
class ForScript(
builder: LightClassBuilder,
exactContextProvider: ExactLightClassContextProvider,
dummyContextProvider: DummyLightClassContextProvider,
diagnosticsHolderProvider: DiagnosticsHolderProvider
) : LazyLightClassDataHolder(builder, exactContextProvider, dummyContextProvider, diagnosticsHolderProvider),
LightClassDataHolder.ForScript
private inner class LazyLightClassData(
findDelegate: (PsiJavaFileStub) -> PsiClass
) : LightClassData {
override val clsDelegate: PsiClass by lazyPub { findDelegate(javaFileStub) }
private val dummyDelegate: PsiClass? by lazyPub { inexactStub?.let(findDelegate) }
override fun getOwnFields(containingClass: KtLightClass): List<KtLightField> {
if (dummyDelegate == null) return KtLightFieldImpl.fromClsFields(clsDelegate, containingClass)
return dummyDelegate!!.fields.map { dummyField ->
val fieldOrigin = KtLightFieldImpl.getOrigin(dummyField)
val fieldName = dummyField.name
KtLightFieldImpl.lazy(dummyField, fieldOrigin, containingClass) {
val findFieldByName = clsDelegate.findFieldByName(fieldName, false)
findFieldByName.checkMatches(dummyField, containingClass) ?:
// fallback in case of non-matched (like synthetic) fields
//
// it costs some performance and has to happen in rare and odd cases
KtLightFieldImpl.create(
KtLightFieldImpl.getOrigin(dummyField), dummyField, containingClass
)
}
}
}
override fun getOwnMethods(containingClass: KtLightClass): List<KtLightMethod> {
if (dummyDelegate == null) return KtLightMethodImpl.fromClsMethods(clsDelegate, containingClass)
return dummyDelegate!!.methods.map { dummyMethod ->
val methodOrigin = KtLightMethodImpl.getOrigin(dummyMethod)
KtLightMethodImpl.lazy(dummyMethod, containingClass, methodOrigin) {
val dummyIndex = dummyMethod.memberIndex!!
val byMemberIndex: (PsiMethod) -> Boolean = { it.memberIndex == dummyIndex }
/* Searching all methods may be necessary in some cases where we failed to rollback optimization:
Overriding internal member that was final
Resulting light member is not consistent in this case, so this should happen only for erroneous code
*/
val findMethodsByName = clsDelegate.findMethodsByName(dummyMethod.name, false)
val candidateDelegateMethod = findMethodsByName.firstOrNull(byMemberIndex)
?: clsDelegate.methods.firstOrNull(byMemberIndex)
candidateDelegateMethod.checkMatches(dummyMethod, containingClass) ?:
// fallback if unable to find method for a dummy method (e.g. synthetic methods marked explicit or implicit) are
// not visible as own methods.
//
// it costs some performance and has to happen in rare and odd cases
KtLightMethodImpl.create(dummyMethod, KtLightMethodImpl.getOrigin(dummyMethod), containingClass)
}
}
}
}
private fun <T : PsiMember> T?.checkMatches(dummyMember: T, containingClass: KtLightClass): T? {
if (this == null) {
logMismatch("Couldn't match ${dummyMember.debugName}", containingClass)
return null
}
val parameterCountMatches = (this as? PsiMethod)?.parameterList?.parametersCount ?: 0 ==
(dummyMember as? PsiMethod)?.parameterList?.parametersCount ?: 0
if (this.memberIndex != dummyMember.memberIndex || !parameterCountMatches) {
logMismatch("Wrongly matched ${dummyMember.debugName} to ${this.debugName}", containingClass)
return null
}
return this
}
companion object {
private val LOG = Logger.getInstance(LazyLightClassDataHolder::class.java)
private fun logMismatch(message: String, containingClass: KtLightClass) {
containingClass.kotlinOrigin?.hasLightClassMatchingErrors = true
LOG.warn("$message, class.kt: ${(containingClass.kotlinOrigin)?.getDebugText()}")
}
}
}
private val PsiMember.debugName
get() = "${this::class.java
.simpleName}:${this.name} ${this.memberIndex}" + if (this is PsiMethod) " (with ${parameterList.parametersCount} parameters)" else ""
var KtClassOrObject.hasLightClassMatchingErrors: Boolean by NotNullableUserDataProperty(Key.create("LIGHT_CLASS_MATCHING_ERRORS"), false)

View File

@@ -1,286 +0,0 @@
/*
* 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.caches.lightClasses
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiField
import com.intellij.psi.PsiMember
import com.intellij.psi.PsiMethod
import com.intellij.psi.impl.compiled.SignatureParsing
import com.intellij.util.cls.ClsFormatException
import org.jetbrains.kotlin.asJava.builder.LightMemberOrigin
import org.jetbrains.kotlin.asJava.classes.lazyPub
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.idea.decompiler.classFile.KtClsFile
import org.jetbrains.kotlin.idea.decompiler.textBuilder.DecompiledTextIndexer
import org.jetbrains.kotlin.load.java.JvmAbi
import org.jetbrains.kotlin.load.kotlin.MemberSignature
import org.jetbrains.kotlin.metadata.deserialization.getExtensionOrNull
import org.jetbrains.kotlin.metadata.jvm.JvmProtoBuf
import org.jetbrains.kotlin.metadata.jvm.deserialization.JvmProtoBufUtil
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.psi.KtClassOrObject
import org.jetbrains.kotlin.psi.KtDeclaration
import org.jetbrains.kotlin.psi.KtDeclarationContainer
import org.jetbrains.kotlin.resolve.calls.components.hasDefaultValue
import org.jetbrains.kotlin.resolve.descriptorUtil.classId
import org.jetbrains.kotlin.resolve.descriptorUtil.isExtension
import org.jetbrains.kotlin.resolve.jvm.JvmClassName
import org.jetbrains.kotlin.resolve.jvm.annotations.findJvmOverloadsAnnotation
import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOriginKind
import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedClassConstructorDescriptor
import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedPropertyDescriptor
import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedSimpleFunctionDescriptor
import org.jetbrains.kotlin.type.MapPsiToAsmDesc
import java.text.CharacterIterator
import java.text.StringCharacterIterator
interface LightMemberOriginForCompiledElement<T : PsiMember> : LightMemberOrigin {
val member: T
override val originKind: JvmDeclarationOriginKind
get() = JvmDeclarationOriginKind.OTHER
override fun isEquivalentTo(other: PsiElement?): Boolean {
return when (other) {
is KtDeclaration -> originalElement?.isEquivalentTo(other) ?: false
is PsiMember -> member.isEquivalentTo(other)
else -> false
}
}
override fun isValid(): Boolean = member.isValid
}
data class LightMemberOriginForCompiledField(val psiField: PsiField, val file: KtClsFile) : LightMemberOriginForCompiledElement<PsiField> {
override val member: PsiField
get() = psiField
override fun copy(): LightMemberOrigin {
return LightMemberOriginForCompiledField(psiField.copy() as PsiField, file)
}
override fun isEquivalentTo(other: LightMemberOrigin?): Boolean {
if (other !is LightMemberOriginForCompiledField) return false
return psiField.isEquivalentTo(other.psiField)
}
override val originalElement: KtDeclaration? by lazyPub {
val desc = MapPsiToAsmDesc.typeDesc(psiField.type)
val signature = MemberSignature.fromFieldNameAndDesc(psiField.name, desc)
findDeclarationInCompiledFile(file, psiField, signature)
}
}
data class LightMemberOriginForCompiledMethod(val psiMethod: PsiMethod, val file: KtClsFile) :
LightMemberOriginForCompiledElement<PsiMethod> {
override val member: PsiMethod
get() = psiMethod
override fun isEquivalentTo(other: LightMemberOrigin?): Boolean {
if (other !is LightMemberOriginForCompiledMethod) return false
return psiMethod.isEquivalentTo(other.psiMethod)
}
override fun copy(): LightMemberOrigin {
return LightMemberOriginForCompiledMethod(psiMethod.copy() as PsiMethod, file)
}
override val originalElement: KtDeclaration? by lazyPub {
val desc = MapPsiToAsmDesc.methodDesc(psiMethod)
val name = if (psiMethod.isConstructor) "<init>" else psiMethod.name
val signature = MemberSignature.fromMethodNameAndDesc(name, desc)
findDeclarationInCompiledFile(file, psiMethod, signature)
}
}
internal fun findDeclarationInCompiledFile(file: KtClsFile, member: PsiMember, signature: MemberSignature): KtDeclaration? {
val relativeClassName = member.relativeClassName()
val key = ClassNameAndSignature(relativeClassName, signature)
val memberName = member.name
if (memberName != null && !file.isContentsLoaded && file.hasDeclarationWithKey(ByJvmSignatureIndexer, key)) {
val container: KtDeclarationContainer? = if (relativeClassName.isEmpty())
file
else {
val topClassOrObject = file.declarations.singleOrNull() as? KtClassOrObject
relativeClassName.fold(topClassOrObject) { classOrObject, name ->
classOrObject?.declarations?.singleOrNull { it.name == name.asString() } as? KtClassOrObject
}
}
val declaration = container?.declarations?.singleOrNull {
it.name == memberName
}
if (declaration != null) {
return declaration
}
}
return file.getDeclaration(ByJvmSignatureIndexer, key)
}
// this is convenient data structure for this purpose and is not supposed to be used outside this file
// every member is represented by its jvm signature and relative class name (which is easy to obtain from descriptors or cls psi)
// relative class name is a path containing inner/nested class names from top level class to the class containing this member (excluding top level class name)
// Examples: for top level function or function in a top level class relativeClassName is empty
// For: class TopLevel { class A { class B { fun f() } } }
// relativeClassName for function 'f' will be [A, B]
private data class ClassNameAndSignature(val relativeClassName: List<Name>, val memberSignature: MemberSignature)
private fun PsiMember.relativeClassName(): List<Name> {
return generateSequence(this.containingClass) { it.containingClass }.toList().dropLast(1).reversed().map { Name.identifier(it.name!!) }
}
private fun ClassDescriptor.relativeClassName(): List<Name> {
return classId!!.relativeClassName.pathSegments().drop(1)
}
private fun excludeParametersFromDescriptor(descriptor: String, omittedParameters: List<Int>): String? {
fun tryParseParametersAndReturnType(): Pair<List<String>, String>? {
val iterator = StringCharacterIterator(descriptor)
fun parseTypeString(): String? {
val begin = iterator.index
try {
SignatureParsing.parseTypeString(iterator) { it }
} catch (e: ClsFormatException) {
return null
}
val end = iterator.index
return descriptor.substring(begin, end)
}
if (iterator.current() != '(') return null
iterator.next()
if (iterator.current() == ')') {
iterator.next()
val returnType = parseTypeString() ?: return null
return emptyList<String>() to returnType
}
val parameterTypes = mutableListOf<String>()
while (iterator.current() != ')' && iterator.current() != CharacterIterator.DONE) {
parameterTypes += parseTypeString() ?: return null
}
if (iterator.current() != ')') return null
iterator.next()
val returnType = parseTypeString() ?: return null
return parameterTypes to returnType
}
val (parameterTypes, returnType) = tryParseParametersAndReturnType() ?: return null
val parametersList = parameterTypes
.filterIndexed { index, _ -> index !in omittedParameters }
.joinToString("")
return "($parametersList)$returnType"
}
private fun ClassDescriptor.desc(): String = "L" + JvmClassName.byClassId(classId!!).internalName + ";"
private object ByJvmSignatureIndexer : DecompiledTextIndexer<ClassNameAndSignature> {
override fun indexDescriptor(descriptor: DeclarationDescriptor): Collection<ClassNameAndSignature> {
val signatures = arrayListOf<ClassNameAndSignature>()
fun save(id: List<Name>, signature: MemberSignature) {
signatures.add(ClassNameAndSignature(id, signature))
}
fun ClassDescriptor.apply() {
when (kind) {
ClassKind.ENUM_ENTRY -> {
val enumClass = containingDeclaration as ClassDescriptor
val signature = MemberSignature.fromFieldNameAndDesc(name.asString(), enumClass.desc())
save(enumClass.relativeClassName(), signature)
}
ClassKind.OBJECT -> {
val instanceFieldSignature = MemberSignature.fromFieldNameAndDesc(JvmAbi.INSTANCE_FIELD, desc())
save(relativeClassName(), instanceFieldSignature)
if (isCompanionObject) {
val signature = MemberSignature.fromFieldNameAndDesc(name.asString(), desc())
save((containingDeclaration as? ClassDescriptor)?.relativeClassName().orEmpty(), signature)
}
}
else -> {
}
}
}
fun DeserializedClassConstructorDescriptor.apply() {
JvmProtoBufUtil.getJvmConstructorSignature(proto, nameResolver, typeTable)?.let {
val id = (containingDeclaration as? ClassDescriptor)?.relativeClassName().orEmpty()
val signature = MemberSignature.fromJvmMemberSignature(it)
save(id, signature)
}
}
fun DeserializedSimpleFunctionDescriptor.apply() {
JvmProtoBufUtil.getJvmMethodSignature(proto, nameResolver, typeTable)?.let {
val id = (containingDeclaration as? ClassDescriptor)?.relativeClassName().orEmpty()
val signature = MemberSignature.fromJvmMemberSignature(it)
save(id, signature)
if (findJvmOverloadsAnnotation() == null) return
val extensionShift = if (isExtension) 1 else 0
val omittedList = mutableListOf<Int>()
valueParameters.asReversed().forEach { parameter ->
if (parameter.hasDefaultValue()) {
omittedList.add(parameter.index + extensionShift)
val newDescriptor = excludeParametersFromDescriptor(it.desc, omittedList)
if (newDescriptor != null) {
val overloadedSignature = MemberSignature.fromMethodNameAndDesc(it.name, newDescriptor)
save(id, overloadedSignature)
}
}
}
}
}
fun DeserializedPropertyDescriptor.apply() {
val className = (containingDeclaration as? ClassDescriptor)?.relativeClassName().orEmpty()
val signature = proto.getExtensionOrNull(JvmProtoBuf.propertySignature)
if (signature != null) {
val fieldSignature = JvmProtoBufUtil.getJvmFieldSignature(proto, nameResolver, typeTable)
if (fieldSignature != null) {
save(className, MemberSignature.fromJvmMemberSignature(fieldSignature))
}
if (signature.hasGetter()) {
save(className, MemberSignature.fromMethod(nameResolver, signature.getter))
}
if (signature.hasSetter()) {
save(className, MemberSignature.fromMethod(nameResolver, signature.setter))
}
}
}
when (descriptor) {
is ClassDescriptor -> descriptor.apply()
is DeserializedClassConstructorDescriptor -> descriptor.apply()
is DeserializedSimpleFunctionDescriptor -> descriptor.apply()
is DeserializedPropertyDescriptor -> descriptor.apply()
}
return signatures
}
}
// expose with different type
val BySignatureIndexer: DecompiledTextIndexer<*> = ByJvmSignatureIndexer

View File

@@ -1,11 +0,0 @@
/*
* Copyright 2010-2018 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.caches.lightClasses.annotations
import org.jetbrains.kotlin.name.FqName
val KOTLINX_SERIALIZABLE_FQ_NAME = FqName("kotlinx.serialization.Serializable")
val KOTLINX_SERIALIZER_FQ_NAME = FqName("kotlinx.serialization.Serializer")

View File

@@ -1,234 +0,0 @@
/*
* 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.caches.lightClasses
import com.intellij.openapi.util.Pair
import com.intellij.psi.*
import com.intellij.psi.impl.PsiClassImplUtil
import com.intellij.psi.impl.PsiImplUtil
import com.intellij.psi.impl.PsiSuperMethodImplUtil
import com.intellij.psi.javadoc.PsiDocComment
import com.intellij.psi.scope.PsiScopeProcessor
import com.intellij.psi.util.PsiUtil
import org.jetbrains.annotations.NonNls
import org.jetbrains.kotlin.analyzer.KotlinModificationTrackerService
import org.jetbrains.kotlin.asJava.classes.KotlinClassInnerStuffCache
import org.jetbrains.kotlin.asJava.classes.LightClassesLazyCreator
import org.jetbrains.kotlin.asJava.classes.lazyPub
import org.jetbrains.kotlin.idea.caches.lightClasses.decompiledDeclarations.KtLightEnumEntryForDecompiledDeclaration
import org.jetbrains.kotlin.idea.caches.lightClasses.decompiledDeclarations.KtLightFieldForDecompiledDeclaration
import org.jetbrains.kotlin.idea.caches.lightClasses.decompiledDeclarations.KtLightMethodForDecompiledDeclaration
import org.jetbrains.kotlin.idea.decompiler.classFile.KtClsFile
import org.jetbrains.kotlin.load.java.structure.LightClassOriginKind
import org.jetbrains.kotlin.psi.KtClassOrObject
open class KtLightClassForDecompiledDeclaration(
override val clsDelegate: PsiClass,
private val clsParent: PsiElement,
private val file: KtClsFile,
kotlinOrigin: KtClassOrObject?
) : KtLightClassForDecompiledDeclarationBase(clsDelegate, clsParent, kotlinOrigin) {
private val myInnersCache = KotlinClassInnerStuffCache(
myClass = this,
externalDependencies = listOf(KotlinModificationTrackerService.getInstance(manager.project).outOfBlockModificationTracker),
lazyCreator = LightClassesLazyCreator(project)
)
override fun getOwnMethods(): MutableList<PsiMethod> = _methods
override fun getOwnFields(): MutableList<PsiField> = _fields
override fun getOwnInnerClasses(): MutableList<PsiClass> = _innerClasses
override fun getFields() = myInnersCache.fields
override fun getMethods() = myInnersCache.methods
override fun getConstructors() = myInnersCache.constructors
override fun getInnerClasses() = myInnersCache.innerClasses
override fun findFieldByName(name: String, checkBases: Boolean) = myInnersCache.findFieldByName(name, checkBases)
override fun findMethodsByName(name: String, checkBases: Boolean) = myInnersCache.findMethodsByName(name, checkBases)
override fun findInnerClassByName(name: String, checkBases: Boolean) = myInnersCache.findInnerClassByName(name, checkBases)
override fun hasModifierProperty(name: String): Boolean =
clsDelegate.hasModifierProperty(name)
override fun findMethodBySignature(patternMethod: PsiMethod?, checkBases: Boolean): PsiMethod? =
patternMethod?.let { PsiClassImplUtil.findMethodBySignature(this, it, checkBases) }
override fun findMethodsBySignature(patternMethod: PsiMethod?, checkBases: Boolean): Array<PsiMethod?> =
patternMethod?.let { PsiClassImplUtil.findMethodsBySignature(this, it, checkBases) } ?: emptyArray()
override fun findMethodsAndTheirSubstitutorsByName(@NonNls name: String?, checkBases: Boolean): List<Pair<PsiMethod, PsiSubstitutor>> =
PsiClassImplUtil.findMethodsAndTheirSubstitutorsByName(this, name, checkBases)
override fun getImplementsList(): PsiReferenceList? = clsDelegate.implementsList
override fun getRBrace(): PsiElement? = null
override fun getLBrace(): PsiElement? = null
override fun getInitializers(): Array<PsiClassInitializer> = clsDelegate.initializers
override fun getContainingClass(): PsiClass? = parent as? PsiClass
override fun isInheritorDeep(baseClass: PsiClass?, classToByPass: PsiClass?): Boolean = clsDelegate.isInheritorDeep(baseClass, classToByPass)
override fun getAllMethodsAndTheirSubstitutors(): List<Pair<PsiMethod?, PsiSubstitutor?>?> =
PsiClassImplUtil.getAllWithSubstitutorsByMap<PsiMethod>(this, PsiClassImplUtil.MemberType.METHOD)
override fun isInterface(): Boolean = clsDelegate.isInterface
override fun getTypeParameters(): Array<PsiTypeParameter> =
clsDelegate.typeParameters
override fun isInheritor(baseClass: PsiClass, checkDeep: Boolean): Boolean =
clsDelegate.isInheritor(baseClass, checkDeep)
override fun processDeclarations(
processor: PsiScopeProcessor,
state: ResolveState,
lastParent: PsiElement?,
place: PsiElement
): Boolean {
if (isEnum) {
if (!KotlinClassInnerStuffCache.processDeclarationsInEnum(processor, state, myInnersCache)) return false
}
return PsiClassImplUtil.processDeclarationsInClass(
this, processor, state, null,
lastParent, place, PsiUtil.getLanguageLevel(place), false
)
}
override fun isEnum(): Boolean = clsDelegate.isEnum
override fun getExtendsListTypes(): Array<PsiClassType?> =
PsiClassImplUtil.getExtendsListTypes(this)
override fun getTypeParameterList(): PsiTypeParameterList? = clsDelegate.typeParameterList
override fun isAnnotationType(): Boolean = clsDelegate.isAnnotationType
override fun getNameIdentifier(): PsiIdentifier? = clsDelegate.nameIdentifier
override fun getInterfaces(): Array<PsiClass> =
PsiClassImplUtil.getInterfaces(this)
override fun getSuperClass(): PsiClass? =
PsiClassImplUtil.getSuperClass(this)
override fun getSupers(): Array<PsiClass> =
PsiClassImplUtil.getSupers(this)
override fun getSuperTypes(): Array<PsiClassType> =
PsiClassImplUtil.getSuperTypes(this)
override fun getVisibleSignatures(): MutableCollection<HierarchicalMethodSignature> =
PsiSuperMethodImplUtil.getVisibleSignatures(this)
override fun getQualifiedName(): String? = clsDelegate.qualifiedName
override fun getImplementsListTypes(): Array<PsiClassType?> =
PsiClassImplUtil.getImplementsListTypes(this)
override fun isDeprecated(): Boolean = clsDelegate.isDeprecated
override fun setName(name: String): PsiElement = clsDelegate.setName(name)
override fun hasTypeParameters(): Boolean =
PsiImplUtil.hasTypeParameters(this)
override fun getExtendsList(): PsiReferenceList? = clsDelegate.extendsList
override fun getDocComment(): PsiDocComment? = clsDelegate.docComment
override fun getModifierList(): PsiModifierList? = clsDelegate.modifierList
override fun getScope(): PsiElement = clsDelegate.scope
override fun getAllInnerClasses(): Array<PsiClass> = PsiClassImplUtil.getAllInnerClasses(this)
override fun getAllMethods(): Array<PsiMethod> = PsiClassImplUtil.getAllMethods(this)
override fun getAllFields(): Array<PsiField> = PsiClassImplUtil.getAllFields(this)
private val _methods: MutableList<PsiMethod> by lazyPub {
mutableListOf<PsiMethod>().also {
clsDelegate.methods.mapTo(it) { psiMethod ->
KtLightMethodForDecompiledDeclaration(
funDelegate = psiMethod,
funParent = this,
lightMemberOrigin = LightMemberOriginForCompiledMethod(psiMethod, file)
)
}
}
}
private val _fields: MutableList<PsiField> by lazyPub {
mutableListOf<PsiField>().also {
clsDelegate.fields.mapTo(it) { psiField ->
if (psiField !is PsiEnumConstant) {
KtLightFieldForDecompiledDeclaration(
fldDelegate = psiField,
fldParent = this,
lightMemberOrigin = LightMemberOriginForCompiledField(psiField, file)
)
} else {
KtLightEnumEntryForDecompiledDeclaration(
fldDelegate = psiField,
fldParent = this,
lightMemberOrigin = LightMemberOriginForCompiledField(psiField, file),
file = file
)
}
}
}
}
private val _innerClasses: MutableList<PsiClass> by lazyPub {
mutableListOf<PsiClass>().also {
clsDelegate.innerClasses.mapTo(it) { psiClass ->
val innerDeclaration = kotlinOrigin
?.declarations
?.filterIsInstance<KtClassOrObject>()
?.firstOrNull { cls -> cls.name == clsDelegate.name }
KtLightClassForDecompiledDeclaration(
clsDelegate = psiClass,
clsParent = this,
file = file,
kotlinOrigin = innerDeclaration,
)
}
}
}
override val originKind: LightClassOriginKind = LightClassOriginKind.BINARY
override fun getNavigationElement() = kotlinOrigin?.navigationElement ?: file
override fun equals(other: Any?): Boolean =
other is KtLightClassForDecompiledDeclaration &&
qualifiedName == other.qualifiedName &&
kotlinOrigin?.fqName == other.kotlinOrigin?.fqName
override fun hashCode(): Int = qualifiedName?.hashCode() ?: kotlinOrigin?.fqName?.hashCode() ?: 0
override fun copy(): PsiElement = this
override fun clone(): Any = this
override fun toString(): String = "${this.javaClass.simpleName} of $parent"
override fun getName(): String? = clsDelegate.name
override fun isValid(): Boolean = file.isValid && clsDelegate.isValid && (kotlinOrigin?.isValid != false)
}

View File

@@ -1,39 +0,0 @@
/*
* 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.caches.lightClasses.decompiledDeclarations
import com.intellij.psi.*
import org.jetbrains.kotlin.idea.caches.lightClasses.KtLightClassForDecompiledDeclaration
import org.jetbrains.kotlin.idea.decompiler.classFile.KtClsFile
import org.jetbrains.kotlin.psi.KtClassOrObject
internal class KtLightEnumClassForDecompiledDeclaration(
private val psiConstantInitializer: PsiEnumConstantInitializer,
private val enumConstant: KtLightEnumEntryForDecompiledDeclaration,
clsParent: KtLightClassForDecompiledDeclaration,
file: KtClsFile,
kotlinOrigin: KtClassOrObject?
) :
KtLightClassForDecompiledDeclaration(
clsDelegate = psiConstantInitializer,
clsParent = clsParent,
file = file,
kotlinOrigin = kotlinOrigin
), PsiEnumConstantInitializer {
override fun getBaseClassType(): PsiClassType = psiConstantInitializer.baseClassType
override fun getArgumentList(): PsiExpressionList? = psiConstantInitializer.argumentList
override fun getEnumConstant(): PsiEnumConstant = enumConstant
override fun getBaseClassReference(): PsiJavaCodeReferenceElement = psiConstantInitializer.baseClassReference
override fun isInQualifiedNew(): Boolean = psiConstantInitializer.isInQualifiedNew
override fun equals(other: Any?): Boolean = other is KtLightEnumClassForDecompiledDeclaration && super.equals(other)
override fun hashCode(): Int = super.hashCode()
}

View File

@@ -1,47 +0,0 @@
/*
* 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.caches.lightClasses.decompiledDeclarations
import com.intellij.psi.*
import org.jetbrains.kotlin.asJava.classes.lazyPub
import org.jetbrains.kotlin.idea.caches.lightClasses.KtLightClassForDecompiledDeclaration
import org.jetbrains.kotlin.idea.caches.lightClasses.LightMemberOriginForCompiledField
import org.jetbrains.kotlin.idea.decompiler.classFile.KtClsFile
internal class KtLightEnumEntryForDecompiledDeclaration(
private val fldDelegate: PsiEnumConstant,
fldParent: KtLightClassForDecompiledDeclaration,
lightMemberOrigin: LightMemberOriginForCompiledField,
file: KtClsFile,
) : KtLightFieldForDecompiledDeclaration(
fldDelegate,
fldParent,
lightMemberOrigin
), PsiEnumConstant {
private val _initializingClass: PsiEnumConstantInitializer? by lazyPub {
fldDelegate.initializingClass?.let {
KtLightEnumClassForDecompiledDeclaration(
psiConstantInitializer = it,
enumConstant = this,
clsParent = fldParent,
file = file,
kotlinOrigin = null
)
}
}
override fun getArgumentList(): PsiExpressionList? = fldDelegate.argumentList
override fun resolveConstructor(): PsiMethod? = fldDelegate.resolveConstructor()
override fun resolveMethod(): PsiMethod? = fldDelegate.resolveMethod()
override fun resolveMethodGenerics(): JavaResolveResult = fldDelegate.resolveMethodGenerics()
override fun getInitializingClass(): PsiEnumConstantInitializer? = _initializingClass
override fun getOrCreateInitializingClass(): PsiEnumConstantInitializer =
_initializingClass ?: error("cannot create initializing class in light enum constant")
override fun equals(other: Any?): Boolean = other is KtLightEnumEntryForDecompiledDeclaration && super.equals(other)
override fun hashCode(): Int = super.hashCode()
}

View File

@@ -1,76 +0,0 @@
/*
* 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.caches.lightClasses.decompiledDeclarations
import com.intellij.psi.*
import com.intellij.psi.impl.PsiVariableEx
import com.intellij.psi.javadoc.PsiDocComment
import org.jetbrains.kotlin.asJava.classes.KtLightClass
import org.jetbrains.kotlin.asJava.elements.KtLightElementBase
import org.jetbrains.kotlin.asJava.elements.KtLightFieldForSourceDeclarationSupport
import org.jetbrains.kotlin.asJava.elements.KtLightMember
import org.jetbrains.kotlin.idea.caches.lightClasses.LightMemberOriginForCompiledField
import org.jetbrains.kotlin.psi.KtDeclaration
open class KtLightFieldForDecompiledDeclaration(
private val fldDelegate: PsiField,
private val fldParent: KtLightClass,
override val lightMemberOrigin: LightMemberOriginForCompiledField
) : KtLightElementBase(fldParent), PsiField, KtLightFieldForSourceDeclarationSupport, KtLightMember<PsiField>, PsiVariableEx {
override val kotlinOrigin: KtDeclaration? get() = lightMemberOrigin.originalElement
override fun hasModifierProperty(name: String): Boolean = fldDelegate.hasModifierProperty(name)
override fun setInitializer(initializer: PsiExpression?) {
fldDelegate.initializer = initializer
}
override fun getContainingClass(): KtLightClass = fldParent
override fun normalizeDeclaration() = fldDelegate.normalizeDeclaration()
override fun getNameIdentifier(): PsiIdentifier = fldDelegate.nameIdentifier
override fun getName(): String = fldDelegate.name
override fun getInitializer(): PsiExpression? = fldDelegate.initializer
override fun getDocComment(): PsiDocComment? = fldDelegate.docComment
override fun getTypeElement(): PsiTypeElement? = fldDelegate.typeElement
override fun getModifierList(): PsiModifierList? = fldDelegate.modifierList
override fun hasInitializer(): Boolean = fldDelegate.hasInitializer()
override fun getType(): PsiType = fldDelegate.type
override fun isDeprecated(): Boolean = fldDelegate.isDeprecated
override fun setName(name: String): PsiElement = fldDelegate.setName(name)
override fun computeConstantValue(): Any? = fldDelegate.computeConstantValue()
override fun computeConstantValue(visitedVars: MutableSet<PsiVariable>?): Any? = (fldDelegate as? PsiVariableEx)?.computeConstantValue(visitedVars)
override fun equals(other: Any?): Boolean = other is KtLightFieldForDecompiledDeclaration &&
name == other.name &&
fldParent == other.fldParent &&
fldDelegate == other.fldDelegate
override fun hashCode(): Int = name.hashCode()
override fun copy(): PsiElement = this
override fun clone(): Any = this
override fun toString(): String = "${this.javaClass.simpleName} of $fldParent"
override val clsDelegate: PsiField = fldDelegate
override fun isValid(): Boolean = parent.isValid
}

View File

@@ -1,105 +0,0 @@
/*
* 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.caches.lightClasses.decompiledDeclarations
import com.intellij.psi.*
import com.intellij.psi.impl.PsiSuperMethodImplUtil
import com.intellij.psi.javadoc.PsiDocComment
import com.intellij.psi.util.MethodSignature
import com.intellij.psi.util.MethodSignatureBackedByPsiMethod
import org.jetbrains.kotlin.asJava.checkIsMangled
import org.jetbrains.kotlin.asJava.classes.KtLightClass
import org.jetbrains.kotlin.asJava.elements.KtLightElementBase
import org.jetbrains.kotlin.asJava.elements.KtLightMember
import org.jetbrains.kotlin.asJava.elements.KtLightMethod
import org.jetbrains.kotlin.idea.caches.lightClasses.LightMemberOriginForCompiledMethod
import org.jetbrains.kotlin.psi.KtDeclaration
class KtLightMethodForDecompiledDeclaration(
private val funDelegate: PsiMethod,
private val funParent: KtLightClass,
override val lightMemberOrigin: LightMemberOriginForCompiledMethod,
) : KtLightElementBase(funParent), PsiMethod, KtLightMethod, KtLightMember<PsiMethod> {
override val kotlinOrigin: KtDeclaration? get() = lightMemberOrigin.originalElement
override val isMangled: Boolean get() = checkIsMangled()
override fun hasModifierProperty(name: String): Boolean = funDelegate.hasModifierProperty(name)
override fun getReturnTypeElement(): PsiTypeElement? = funDelegate.returnTypeElement
override fun getContainingClass(): KtLightClass = funParent
override fun getTypeParameters(): Array<PsiTypeParameter> = funDelegate.typeParameters
override fun getThrowsList(): PsiReferenceList = funDelegate.throwsList
override fun getReturnType(): PsiType? = funDelegate.returnType
override fun hasTypeParameters(): Boolean = funDelegate.hasTypeParameters()
override fun getTypeParameterList(): PsiTypeParameterList? = funDelegate.typeParameterList
override fun isVarArgs(): Boolean = funDelegate.isVarArgs
override fun isConstructor(): Boolean = funDelegate.isConstructor
override fun getNameIdentifier(): PsiIdentifier? = funDelegate.nameIdentifier
override fun getName(): String = funDelegate.name
override fun getDocComment(): PsiDocComment? = funDelegate.docComment
override fun getModifierList(): PsiModifierList = funDelegate.modifierList
override fun getBody(): PsiCodeBlock? = null
override fun getDefaultValue(): PsiAnnotationMemberValue? = (funDelegate as? PsiAnnotationMethod)?.defaultValue
override fun isDeprecated(): Boolean = funDelegate.isDeprecated
override fun setName(name: String): PsiElement = funDelegate.setName(name)
override fun getParameterList(): PsiParameterList = funDelegate.parameterList
override fun getHierarchicalMethodSignature() = PsiSuperMethodImplUtil.getHierarchicalMethodSignature(this)
override fun findSuperMethodSignaturesIncludingStatic(checkAccess: Boolean): List<MethodSignatureBackedByPsiMethod> =
PsiSuperMethodImplUtil.findSuperMethodSignaturesIncludingStatic(this, checkAccess)
override fun findDeepestSuperMethod() = PsiSuperMethodImplUtil.findDeepestSuperMethod(this)
override fun findDeepestSuperMethods(): Array<out PsiMethod> = PsiSuperMethodImplUtil.findDeepestSuperMethods(this)
override fun findSuperMethods(): Array<out PsiMethod> = PsiSuperMethodImplUtil.findSuperMethods(this)
override fun findSuperMethods(checkAccess: Boolean): Array<out PsiMethod> =
PsiSuperMethodImplUtil.findSuperMethods(this, checkAccess)
override fun findSuperMethods(parentClass: PsiClass?): Array<out PsiMethod> =
PsiSuperMethodImplUtil.findSuperMethods(this, parentClass)
override fun getSignature(substitutor: PsiSubstitutor): MethodSignature =
MethodSignatureBackedByPsiMethod.create(this, substitutor)
override fun equals(other: Any?): Boolean = other is KtLightMethodForDecompiledDeclaration &&
name == other.name &&
funParent == other.funParent &&
funDelegate == other.funDelegate
override fun hashCode(): Int = name.hashCode()
override fun copy(): PsiElement = this
override fun clone(): Any = this
override fun toString(): String = "${this.javaClass.simpleName} of $funParent"
override val clsDelegate: PsiMethod = funDelegate
override fun isValid(): Boolean = parent.isValid
}

View File

@@ -1,422 +0,0 @@
/*
* Copyright 2010-2017 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.idea.caches.lightClasses
import com.intellij.openapi.util.Key
import com.intellij.psi.*
import com.intellij.psi.impl.InheritanceImplUtil
import com.intellij.psi.impl.PsiClassImplUtil
import com.intellij.psi.impl.PsiSubstitutorImpl.createSubstitutor
import com.intellij.psi.impl.PsiSuperMethodImplUtil
import com.intellij.psi.impl.light.*
import com.intellij.psi.impl.source.ClassInnerStuffCache
import com.intellij.psi.impl.source.PsiExtensibleClass
import com.intellij.psi.impl.source.PsiImmediateClassType
import com.intellij.psi.util.MethodSignatureBackedByPsiMethod
import com.siyeh.ig.psiutils.TypeUtils
import org.jetbrains.kotlin.asJava.classes.cannotModify
import org.jetbrains.kotlin.asJava.classes.lazyPub
import org.jetbrains.kotlin.asJava.elements.KtLightElementBase
import org.jetbrains.kotlin.builtins.DefaultBuiltIns
import org.jetbrains.kotlin.builtins.jvm.JavaToKotlinClassMap
import org.jetbrains.kotlin.idea.KotlinLanguage
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
import org.jetbrains.kotlin.load.java.BuiltinSpecialProperties
import org.jetbrains.kotlin.load.java.JvmAbi
import org.jetbrains.kotlin.load.java.SpecialGenericSignatures
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.psi.UserDataProperty
private val readOnlyQualifiedNamesToJavaClass = JavaToKotlinClassMap.mutabilityMappings.associateBy { (_, readOnly, _) ->
readOnly.asSingleFqName()
}
private val mutableQualifiedNamesToJavaClass = JavaToKotlinClassMap.mutabilityMappings.associateBy { (_, _, mutable) ->
mutable.asSingleFqName()
}
private val membersWithSpecializedSignature: Set<String> =
SpecialGenericSignatures.ERASED_VALUE_PARAMETERS_SIGNATURES.mapTo(LinkedHashSet()) {
val fqNameString = it.substringBefore('(').replace('/', '.')
FqName(fqNameString).shortName().asString()
}
private val javaGetterNameToKotlinGetterName: Map<String, String> =
BuiltinSpecialProperties.PROPERTY_FQ_NAME_TO_JVM_GETTER_NAME_MAP.map { (propertyFqName, javaGetterShortName) ->
Pair(javaGetterShortName.asString(), JvmAbi.getterName(propertyFqName.shortName().asString()))
}.toMap()
fun platformMutabilityWrapper(fqName: FqName, findJavaClass: (String) -> PsiClass?): PsiClass? {
readOnlyQualifiedNamesToJavaClass[fqName]?.let { (javaClass, kotlinReadOnly) ->
val javaBaseClass = findJavaClass(javaClass.asSingleFqName().asString()) ?: return null
return getOrCreateWrapper(javaBaseClass, kotlinReadOnly.asSingleFqName(), isMutable = false)
}
mutableQualifiedNamesToJavaClass[fqName]?.let { (javaClass, _, kotlinMutable) ->
val javaBaseClass = findJavaClass(javaClass.asSingleFqName().asString()) ?: return null
return getOrCreateWrapper(javaBaseClass, kotlinMutable.asSingleFqName(), isMutable = true)
}
return null
}
private fun getOrCreateWrapper(javaBaseClass: PsiClass, kotlinFqName: FqName, isMutable: Boolean): KtLightMutabilityPlatformWrapper {
val userDataStorage = if (isMutable) javaBaseClass::mutableWrapper else javaBaseClass::readOnlyWrapper
return userDataStorage.get() ?: KtLightMutabilityPlatformWrapper(
javaBaseClass,
kotlinFqName,
isMutable
).also { userDataStorage.set(it) }
}
private var PsiClass.readOnlyWrapper: KtLightMutabilityPlatformWrapper? by UserDataProperty(Key.create("READ_ONLY_WRAPPER"))
private var PsiClass.mutableWrapper: KtLightMutabilityPlatformWrapper? by UserDataProperty(Key.create("MUTABLE_WRAPPER"))
class KtLightMutabilityPlatformWrapper(
private val javaBaseClass: PsiClass,
private val kotlinInterfaceFqName: FqName,
private val isMutable: Boolean
) : KtAbstractContainerWrapper(kotlinInterfaceFqName, javaBaseClass), PsiClass {
private val _methods by lazyPub { calcMethods() }
private fun calcMethods() = javaBaseClass.methods.flatMap { methodWrappers(it) }
override fun getOwnMethods() = _methods
private fun methodWrappers(method: PsiMethod): List<PsiMethod> {
val methodName = method.name
javaGetterNameToKotlinGetterName.get(methodName)?.let { kotlinName ->
val finalBridgeForJava = method.finalBridge()
val abstractKotlinGetter = method.wrap(name = kotlinName)
return listOf(finalBridgeForJava, abstractKotlinGetter)
}
if (!method.isInKotlinInterface()) {
// compiler generates stub override
return listOf(method.openBridge())
}
return methodsWithSpecializedSignature(method)
}
private fun methodsWithSpecializedSignature(method: PsiMethod): List<PsiMethod> {
val methodName = method.name
if (methodName !in membersWithSpecializedSignature) return emptyList()
if (javaBaseClass.qualifiedName == CommonClassNames.JAVA_UTIL_MAP) {
val abstractKotlinVariantWithGeneric = javaUtilMapMethodWithSpecialSignature(method) ?: return emptyList()
val finalBridgeWithObject = method.finalBridge()
return listOf(finalBridgeWithObject, abstractKotlinVariantWithGeneric)
}
if (methodName in SpecialGenericSignatures.ERASED_COLLECTION_PARAMETER_NAMES) {
return emptyList()
}
if (methodName == "remove" && method.parameterList.parameters.singleOrNull()?.type == PsiType.INT) {
// remove(int) -> abstract removeAt(int), final bridge remove(int)
return listOf(method.finalBridge(), createRemoveAt(method))
}
val finalBridgeWithObject = method.finalBridge()
val abstractKotlinVariantWithGeneric = method.wrap(substituteObjectWith = singleTypeParameterAsType())
return listOf(finalBridgeWithObject, abstractKotlinVariantWithGeneric)
}
private fun singleTypeParameterAsType() = typeParameters.single().asType()
private fun createRemoveAt(baseMethod: PsiMethod): PsiMethod {
return baseMethod.wrap(
name = "removeAt",
signature = MethodSignature(
parameterTypes = listOf(PsiType.INT),
returnType = singleTypeParameterAsType()
)
)
}
private fun PsiMethod.finalBridge() = wrap(makeFinal = true, hasImplementation = true)
private fun PsiMethod.openBridge() = wrap(makeFinal = false, hasImplementation = true)
private fun PsiMethod.wrap(
makeFinal: Boolean = false,
hasImplementation: Boolean = false,
name: String = this.name,
substituteObjectWith: PsiType? = null,
signature: MethodSignature? = null
) = KtLightMethodWrapper(
this@KtLightMutabilityPlatformWrapper, this@wrap,
isFinal = makeFinal,
name = name,
substituteObjectWith = substituteObjectWith,
providedSignature = signature,
hasImplementation = hasImplementation
)
private fun javaUtilMapMethodWithSpecialSignature(method: PsiMethod): KtLightMethodWrapper? {
val k = typeParameters[0].asType()
val v = typeParameters[1].asType()
val signature = when (method.name) {
"get" -> MethodSignature(
parameterTypes = listOf(k),
returnType = v
)
"getOrDefault" -> MethodSignature(
parameterTypes = listOf(k, v),
returnType = v
)
"containsKey" -> MethodSignature(
parameterTypes = listOf(k),
returnType = PsiType.BOOLEAN
)
"containsValue" -> MethodSignature(
parameterTypes = listOf(v),
returnType = PsiType.BOOLEAN
)
"remove" ->
when (method.parameterList.parametersCount) {
1 -> MethodSignature(
parameterTypes = listOf(k),
returnType = v
)
2 -> MethodSignature(
parameterTypes = listOf(k, v),
returnType = PsiType.BOOLEAN
)
else -> null
}
else -> null
} ?: return null
return method.wrap(signature = signature)
}
private fun PsiMethod.isInKotlinInterface(): Boolean {
if (javaBaseClass.qualifiedName == CommonClassNames.JAVA_UTIL_MAP_ENTRY) {
when (name) {
"getValue", "getKey" -> return true
}
}
val kotlinInterface = DefaultBuiltIns.Instance.getBuiltInClassByFqName(kotlinInterfaceFqName)
val scope = kotlinInterface.unsubstitutedMemberScope
val methodName = Name.identifier(name)
return scope.getContributedFunctions(methodName, NoLookupLocation.FROM_IDE).isNotEmpty()
|| scope.getContributedVariables(methodName, NoLookupLocation.FROM_IDE).isNotEmpty()
}
override fun getContainingFile() = javaBaseClass.containingFile
}
private data class MethodSignature(val parameterTypes: List<PsiType>, val returnType: PsiType)
private class KtLightMethodWrapper(
private val containingClass: KtAbstractContainerWrapper,
private val baseMethod: PsiMethod,
private val name: String,
private val isFinal: Boolean,
private val hasImplementation: Boolean,
private val substituteObjectWith: PsiType?,
private val providedSignature: MethodSignature?
) : PsiMethod, KtLightElementBase(containingClass) {
init {
if (!hasImplementation && isFinal) {
error("Can't be final without an implementation")
}
}
private fun substituteType(psiType: PsiType): PsiType {
val substituted = containingClass.substitutor.substitute(psiType)
return if (TypeUtils.isJavaLangObject(substituted) && substituteObjectWith != null) {
substituteObjectWith
} else {
substituted
}
}
override fun getPresentation() = baseMethod.presentation
override val kotlinOrigin get() = null
override fun hasModifierProperty(name: String) =
when (name) {
PsiModifier.DEFAULT -> hasImplementation
PsiModifier.ABSTRACT -> !hasImplementation
PsiModifier.FINAL -> isFinal
else -> baseMethod.hasModifierProperty(name)
}
override fun getParameterList(): PsiParameterList {
return LightParameterListBuilder(manager, KotlinLanguage.INSTANCE).apply {
baseMethod.parameterList.parameters.forEachIndexed { index, paramFromJava ->
val type = providedSignature?.parameterTypes?.get(index) ?: substituteType(paramFromJava.type)
addParameter(
LightParameter(
paramFromJava.name ?: "p$index", type,
this@KtLightMethodWrapper, KotlinLanguage.INSTANCE, paramFromJava.isVarArgs
)
)
}
}
}
override fun getName() = name
override fun getReturnType() = providedSignature?.returnType ?: baseMethod.returnType?.let { substituteType(it) }
override fun getTypeParameters() = baseMethod.typeParameters
override fun getTypeParameterList() = baseMethod.typeParameterList
override fun findSuperMethods(checkAccess: Boolean) = PsiSuperMethodImplUtil.findSuperMethods(this, checkAccess)
override fun findSuperMethods(parentClass: PsiClass) = PsiSuperMethodImplUtil.findSuperMethods(this, parentClass)
override fun findSuperMethods() = PsiSuperMethodImplUtil.findSuperMethods(this)
override fun findSuperMethodSignaturesIncludingStatic(checkAccess: Boolean) =
PsiSuperMethodImplUtil.findSuperMethodSignaturesIncludingStatic(this, checkAccess)
@Suppress("OverridingDeprecatedMember")
override fun findDeepestSuperMethod() = PsiSuperMethodImplUtil.findDeepestSuperMethod(this)
override fun findDeepestSuperMethods() = PsiSuperMethodImplUtil.findDeepestSuperMethods(this)
override fun getHierarchicalMethodSignature() = PsiSuperMethodImplUtil.getHierarchicalMethodSignature(this)
override fun getSignature(substitutor: PsiSubstitutor) = MethodSignatureBackedByPsiMethod.create(this, substitutor)
override fun getReturnTypeElement(): PsiTypeElement? = null
override fun getContainingClass() = containingClass
override fun getThrowsList() = baseMethod.throwsList
override fun hasTypeParameters() = baseMethod.hasTypeParameters()
override fun isVarArgs() = baseMethod.isVarArgs
override fun isConstructor() = false
private val identifier by lazyPub { LightIdentifier(manager, name) }
override fun getNameIdentifier() = identifier
override fun getDocComment() = baseMethod.docComment
override fun getModifierList() = baseMethod.modifierList
override fun getBody() = null
override fun isDeprecated() = baseMethod.isDeprecated
override fun setName(name: String) = cannotModify()
override fun toString(): String {
return "$javaClass:$name${parameterList.parameters.map { it.type }.joinToString(prefix = "(", postfix = ")", separator = ", ")}"
}
}
abstract class KtAbstractContainerWrapper(internal val fqName: FqName, private val superInterface: PsiClass) :
LightElement(superInterface.manager, KotlinLanguage.INSTANCE), PsiExtensibleClass {
private val memberCache = ClassInnerStuffCache(this)
private val superClassTypeParametersToMyTypeParameters: Map<PsiTypeParameter, PsiTypeParameter> = superInterface.typeParameters
.mapIndexed { index, supersParameter ->
supersParameter to LightTypeParameterBuilder(supersParameter.name ?: "T$index", this, index)
}
.toMap()
internal val substitutor = createSubstitutor(superClassTypeParametersToMyTypeParameters.mapValues {
it.value.asType()
})
override fun getSupers() = arrayOf(superInterface)
override fun getQualifiedName() = fqName.asString()
override fun toString() = "$javaClass:$name"
override fun hasModifierProperty(name: String) = name == PsiModifier.PUBLIC || name == PsiModifier.ABSTRACT
private val _typeParameterList by lazyPub {
LightTypeParameterListBuilder(manager, KotlinLanguage.INSTANCE).apply {
superClassTypeParametersToMyTypeParameters.values.forEach { addParameter(it) }
}
}
override fun getTypeParameterList() = _typeParameterList
private val identifier by lazyPub { LightIdentifier(manager, name) }
override fun getNameIdentifier() = identifier
override fun getName() = fqName.shortName().asString()
private val _implementsList by lazyPub {
LightReferenceListBuilder(manager, PsiReferenceList.Role.IMPLEMENTS_LIST).apply {
addReference(superInterface)
}
}
override fun getImplementsList() = _implementsList
override fun getSuperTypes() = arrayOf(PsiImmediateClassType(superInterface, substitutor))
override fun getMethods() = memberCache.methods
override fun getTypeParameters() = superClassTypeParametersToMyTypeParameters.values.toTypedArray()
override fun getInterfaces() = arrayOf(superInterface)
override fun getInitializers() = PsiClassInitializer.EMPTY_ARRAY
override fun getContainingClass() = null
override fun getFields() = PsiField.EMPTY_ARRAY
override fun isInterface() = true
override fun isInheritor(baseClass: PsiClass, checkDeep: Boolean) = InheritanceImplUtil.isInheritor(this, baseClass, checkDeep)
override fun getOwnInnerClasses() = emptyList<PsiClass>()
override fun getSuperClass() = null
override fun findInnerClassByName(name: String?, checkBases: Boolean) = null
override fun getExtendsListTypes() = PsiClassType.EMPTY_ARRAY
override fun isInheritorDeep(baseClass: PsiClass, classToByPass: PsiClass?) =
InheritanceImplUtil.isInheritorDeep(this, baseClass, classToByPass)
override fun isAnnotationType() = false
override fun findMethodsAndTheirSubstitutorsByName(name: String?, checkBases: Boolean) =
PsiClassImplUtil.findMethodsAndTheirSubstitutorsByName(this, name, checkBases)
override fun getInnerClasses() = PsiClass.EMPTY_ARRAY
override fun findMethodBySignature(patternMethod: PsiMethod, checkBases: Boolean) =
PsiClassImplUtil.findMethodBySignature(this, patternMethod, checkBases)
override fun findFieldByName(name: String?, checkBases: Boolean) = null
override fun getAllFields() = PsiClassImplUtil.getAllFields(this)
override fun getAllInnerClasses() = PsiClassImplUtil.getAllInnerClasses(this)
override fun findMethodsByName(name: String?, checkBases: Boolean) = memberCache.findMethodsByName(name, checkBases)
override fun getAllMethods() = PsiClassImplUtil.getAllMethods(this)
override fun getOwnFields() = emptyList<PsiField>()
override fun getAllMethodsAndTheirSubstitutors() =
PsiClassImplUtil.getAllWithSubstitutorsByMap<PsiMethod>(this, PsiClassImplUtil.MemberType.METHOD)
override fun hasTypeParameters() = true
override fun getRBrace() = null
override fun getLBrace() = null
override fun getVisibleSignatures() = PsiSuperMethodImplUtil.getVisibleSignatures(this)
override fun getExtendsList() = null
override fun getDocComment() = null
override fun isEnum() = false
private val _modifierList by lazyPub { LightModifierList(manager, KotlinLanguage.INSTANCE, PsiModifier.PUBLIC) }
override fun getModifierList() = _modifierList
override fun getScope() = superInterface.scope
override fun getImplementsListTypes() = superTypes
override fun getConstructors() = PsiMethod.EMPTY_ARRAY
override fun isDeprecated() = false
override fun setName(name: String) = cannotModify()
override fun findMethodsBySignature(patternMethod: PsiMethod, checkBases: Boolean) =
PsiClassImplUtil.findMethodsBySignature(this, patternMethod, checkBases)
}
private fun PsiTypeParameter.asType() = PsiImmediateClassType(this, PsiSubstitutor.EMPTY)

View File

@@ -1,635 +0,0 @@
/*
* 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.caches.project
import com.intellij.openapi.diagnostic.Logger
import com.intellij.openapi.module.Module
import com.intellij.openapi.module.impl.scopes.LibraryScopeBase
import com.intellij.openapi.project.Project
import com.intellij.openapi.projectRoots.JavaSdk
import com.intellij.openapi.projectRoots.Sdk
import com.intellij.openapi.roots.*
import com.intellij.openapi.roots.impl.libraries.LibraryEx
import com.intellij.openapi.roots.libraries.Library
import com.intellij.openapi.util.ModificationTracker
import com.intellij.openapi.vfs.VirtualFile
import com.intellij.openapi.vfs.newvfs.NewVirtualFile
import com.intellij.openapi.vfs.newvfs.NewVirtualFileSystem
import com.intellij.psi.search.GlobalSearchScope
import com.intellij.util.PathUtil
import com.intellij.util.SmartList
import com.intellij.util.containers.ContainerUtil
import org.jetbrains.jps.model.java.JavaSourceRootType
import org.jetbrains.jps.model.module.JpsModuleSourceRootType
import org.jetbrains.kotlin.analyzer.*
import org.jetbrains.kotlin.caches.project.cacheByClassInvalidatingOnRootModifications
import org.jetbrains.kotlin.caches.project.cacheInvalidatingOnRootModifications
import org.jetbrains.kotlin.caches.resolve.resolution
import org.jetbrains.kotlin.config.SourceKotlinRootType
import org.jetbrains.kotlin.config.TestSourceKotlinRootType
import org.jetbrains.kotlin.descriptors.ModuleCapability
import org.jetbrains.kotlin.idea.KotlinIdeaAnalysisBundle
import org.jetbrains.kotlin.idea.caches.resolve.util.enlargedSearchScope
import org.jetbrains.kotlin.idea.caches.trackers.KotlinModuleOutOfCodeBlockModificationTracker
import org.jetbrains.kotlin.idea.configuration.BuildSystemType
import org.jetbrains.kotlin.idea.configuration.getBuildSystemType
import org.jetbrains.kotlin.idea.core.isInTestSourceContentKotlinAware
import org.jetbrains.kotlin.idea.framework.KotlinSdkType
import org.jetbrains.kotlin.idea.framework.effectiveKind
import org.jetbrains.kotlin.idea.framework.platform
import org.jetbrains.kotlin.idea.klib.AbstractKlibLibraryInfo
import org.jetbrains.kotlin.idea.project.*
import org.jetbrains.kotlin.idea.stubindex.KotlinSourceFilterScope
import org.jetbrains.kotlin.idea.util.isInSourceContentWithoutInjected
import org.jetbrains.kotlin.idea.util.rootManager
import org.jetbrains.kotlin.konan.library.KONAN_STDLIB_NAME
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.platform.*
import org.jetbrains.kotlin.platform.compat.toOldPlatform
import org.jetbrains.kotlin.platform.js.isJs
import org.jetbrains.kotlin.platform.jvm.JvmPlatforms
import org.jetbrains.kotlin.platform.jvm.isJvm
import org.jetbrains.kotlin.platform.konan.NativePlatform
import org.jetbrains.kotlin.platform.konan.NativePlatforms
import org.jetbrains.kotlin.platform.konan.isNative
import org.jetbrains.kotlin.resolve.PlatformDependentAnalyzerServices
import org.jetbrains.kotlin.resolve.jvm.TopPackageNamesProvider
import org.jetbrains.kotlin.resolve.jvm.platform.JvmPlatformAnalyzerServices
import org.jetbrains.kotlin.types.typeUtil.closure
import org.jetbrains.kotlin.utils.addIfNotNull
import java.util.*
internal val LOG = Logger.getInstance(IdeaModuleInfo::class.java)
@Suppress("DEPRECATION_ERROR")
interface IdeaModuleInfo : org.jetbrains.kotlin.idea.caches.resolve.IdeaModuleInfo {
fun contentScope(): GlobalSearchScope
val moduleOrigin: ModuleOrigin
val project: Project?
override val capabilities: Map<ModuleCapability<*>, Any?>
get() = super.capabilities + mapOf(OriginCapability to moduleOrigin)
override fun dependencies(): List<IdeaModuleInfo>
}
private fun orderEntryToModuleInfo(project: Project, orderEntry: OrderEntry, forProduction: Boolean): List<IdeaModuleInfo> {
fun Module.toInfos() = correspondingModuleInfos().filter { !forProduction || it is ModuleProductionSourceInfo }
if (!orderEntry.isValid) return emptyList()
return when (orderEntry) {
is ModuleSourceOrderEntry -> {
orderEntry.getOwnerModule().toInfos()
}
is ModuleOrderEntry -> {
val module = orderEntry.module ?: return emptyList()
if (forProduction && orderEntry.isProductionOnTestDependency) {
listOfNotNull(module.testSourceInfo())
} else {
module.toInfos()
}
}
is LibraryOrderEntry -> {
val library = orderEntry.library ?: return listOf()
createLibraryInfo(project, library)
}
is JdkOrderEntry -> {
val sdk = orderEntry.jdk ?: return listOf()
listOfNotNull(SdkInfo(project, sdk))
}
else -> {
throw IllegalStateException("Unexpected order entry $orderEntry")
}
}
}
private val Project.libraryInfoCache: MutableMap<Library, List<LibraryInfo>>
get() = cacheInvalidatingOnRootModifications { ContainerUtil.createConcurrentWeakMap() }
fun createLibraryInfo(project: Project, library: Library): List<LibraryInfo> =
project.libraryInfoCache.getOrPut(library) {
val approximatePlatform = if (library is LibraryEx && !library.isDisposed) {
// for Native returns 'unspecifiedNativePlatform', thus "approximate"
library.effectiveKind(project).platform
} else {
DefaultIdeTargetPlatformKindProvider.defaultPlatform
}
approximatePlatform.idePlatformKind.resolution.createLibraryInfo(project, library)
}
private fun OrderEntry.acceptAsDependency(forProduction: Boolean): Boolean {
return this !is ExportableOrderEntry
|| !forProduction
// this is needed for Maven/Gradle projects with "production-on-test" dependency
|| this is ModuleOrderEntry && isProductionOnTestDependency
|| scope.isForProductionCompile
}
private fun ideaModelDependencies(
module: Module,
forProduction: Boolean,
platform: TargetPlatform
): List<IdeaModuleInfo> {
// Use StringBuilder so that all lines are written into the log atomically (otherwise
// logs of call to ideaModelDependencies for several different modules interleave, leading
// to unreadable mess)
val debugString: StringBuilder? = if (LOG.isDebugEnabled) StringBuilder() else null
debugString?.appendLine("Building idea model dependencies for module $module, platform=${platform}, forProduction=$forProduction")
//NOTE: lib dependencies can be processed several times during recursive traversal
val result = LinkedHashSet<IdeaModuleInfo>()
val dependencyEnumerator = ModuleRootManager.getInstance(module).orderEntries().compileOnly().recursively().exportedOnly()
if (forProduction && module.getBuildSystemType() == BuildSystemType.JPS) {
dependencyEnumerator.productionOnly()
}
debugString?.append(" IDEA dependencies: [")
dependencyEnumerator.forEach { orderEntry ->
debugString?.append("${orderEntry.presentableName} ")
if (orderEntry.acceptAsDependency(forProduction)) {
result.addAll(orderEntryToModuleInfo(module.project, orderEntry!!, forProduction))
debugString?.append("OK; ")
} else {
debugString?.append("SKIP; ")
}
true
}
debugString?.appendLine("]")
// Some dependencies prohibited (e.g. common can not depend on a platform)
val correctedResult = result.filterNot { it is LibraryInfo && !platform.canDependOn(it, module.isHMPPEnabled) }
debugString?.appendLine(" Corrected result: ${correctedResult.joinToString(prefix = "[", postfix = "]", separator = ";") { it.displayedName }}")
LOG.debug(debugString?.toString())
return correctedResult
}
internal fun TargetPlatform.canDependOn(other: IdeaModuleInfo, isHmppEnabled: Boolean): Boolean {
if (isHmppEnabled) {
// HACK: allow depending on stdlib even if platforms do not match
if (isNative() && other is AbstractKlibLibraryInfo && other.libraryRoot.endsWith(KONAN_STDLIB_NAME)) return true
val platformsWhichAreNotContainedInOther = this.componentPlatforms - other.platform.componentPlatforms
if (platformsWhichAreNotContainedInOther.isEmpty()) return true
// unspecifiedNativePlatform is effectively a wildcard for NativePlatform
return platformsWhichAreNotContainedInOther.all { it is NativePlatform } &&
NativePlatforms.unspecifiedNativePlatform.componentPlatforms.single() in other.platform.componentPlatforms
} else {
return this.isJvm() && other.platform.isJvm() ||
this.isJs() && other.platform.isJs() ||
this.isNative() && other.platform.isNative() ||
this.isCommon() && other.platform.isCommon()
}
}
interface ModuleSourceInfo : IdeaModuleInfo, TrackableModuleInfo, ModuleSourceInfoBase {
val module: Module
override val expectedBy: List<ModuleSourceInfo>
override val displayedName get() = module.name
override val moduleOrigin: ModuleOrigin
get() = ModuleOrigin.MODULE
override val project: Project
get() = module.project
override val platform: TargetPlatform
get() = TargetPlatformDetector.getPlatform(module)
@Suppress("DEPRECATION_ERROR")
@Deprecated(
message = "This accessor is deprecated and will be removed soon, use API from 'org.jetbrains.kotlin.platform.*' packages instead",
replaceWith = ReplaceWith("platform"),
level = DeprecationLevel.ERROR
)
fun getPlatform(): org.jetbrains.kotlin.resolve.TargetPlatform = platform.toOldPlatform()
override val analyzerServices: PlatformDependentAnalyzerServices
get() = platform.findAnalyzerServices(module.project)
override fun createModificationTracker(): ModificationTracker =
KotlinModuleOutOfCodeBlockModificationTracker(module)
}
sealed class ModuleSourceInfoWithExpectedBy(private val forProduction: Boolean) : ModuleSourceInfo {
override val expectedBy: List<ModuleSourceInfo>
get() {
val expectedByModules = module.implementedModules
return expectedByModules.mapNotNull { if (forProduction) it.productionSourceInfo() else it.testSourceInfo() }
}
override fun dependencies(): List<IdeaModuleInfo> = module.cacheByClassInvalidatingOnRootModifications(this::class.java) {
ideaModelDependencies(module, forProduction, platform)
}
}
data class ModuleProductionSourceInfo internal constructor(
override val module: Module
) : ModuleSourceInfoWithExpectedBy(forProduction = true) {
override val name = Name.special("<production sources for module ${module.name}>")
override val stableName: Name by lazy { module.getStableName() }
override fun contentScope(): GlobalSearchScope {
return enlargedSearchScope(ModuleProductionSourceScope(module), module, isTestScope = false)
}
}
//TODO: (module refactoring) do not create ModuleTestSourceInfo when there are no test roots for module
@Suppress("DEPRECATION_ERROR")
data class ModuleTestSourceInfo internal constructor(override val module: Module) :
ModuleSourceInfoWithExpectedBy(forProduction = false), org.jetbrains.kotlin.idea.caches.resolve.ModuleTestSourceInfo {
override val name = Name.special("<test sources for module ${module.name}>")
override val displayedName get() = KotlinIdeaAnalysisBundle.message("module.name.0.test", module.name)
override val stableName: Name by lazy { module.getStableName() }
override fun contentScope(): GlobalSearchScope = enlargedSearchScope(ModuleTestSourceScope(module), module, isTestScope = true)
override fun modulesWhoseInternalsAreVisible(): Collection<ModuleInfo> =
module.cacheByClassInvalidatingOnRootModifications(KeyForModulesWhoseInternalsAreVisible::class.java) {
val list = SmartList<ModuleInfo>()
list.addIfNotNull(module.productionSourceInfo())
TestModuleProperties.getInstance(module).productionModule?.let {
list.addIfNotNull(it.productionSourceInfo())
}
list.addAll(list.closure { it.expectedBy })
list.toHashSet()
}
private object KeyForModulesWhoseInternalsAreVisible
}
fun Module.productionSourceInfo(): ModuleProductionSourceInfo? = if (hasProductionRoots()) ModuleProductionSourceInfo(this) else null
fun Module.testSourceInfo(): ModuleTestSourceInfo? = if (hasTestRoots()) ModuleTestSourceInfo(this) else null
internal fun Module.correspondingModuleInfos(): List<ModuleSourceInfo> = listOf(testSourceInfo(), productionSourceInfo()).filterNotNull()
private fun Module.hasProductionRoots() =
hasRootsOfType(JavaSourceRootType.SOURCE) || hasRootsOfType(SourceKotlinRootType) || (isNewMPPModule && sourceType == SourceType.PRODUCTION)
private fun Module.hasTestRoots() =
hasRootsOfType(JavaSourceRootType.TEST_SOURCE) || hasRootsOfType(TestSourceKotlinRootType) || (isNewMPPModule && sourceType == SourceType.TEST)
private fun Module.hasRootsOfType(sourceRootType: JpsModuleSourceRootType<*>): Boolean =
rootManager.contentEntries.any { it.getSourceFolders(sourceRootType).isNotEmpty() }
abstract class ModuleSourceScope(val module: Module) : GlobalSearchScope(module.project) {
override fun compare(file1: VirtualFile, file2: VirtualFile) = 0
override fun isSearchInModuleContent(aModule: Module) = aModule == module
override fun isSearchInLibraries() = false
}
@Suppress("EqualsOrHashCode") // DelegatingGlobalSearchScope requires to provide calcHashCode()
class ModuleProductionSourceScope(module: Module) : ModuleSourceScope(module) {
val moduleFileIndex = ModuleRootManager.getInstance(module).fileIndex
override fun equals(other: Any?): Boolean {
if (this === other) return true
return (other is ModuleProductionSourceScope && module == other.module)
}
override fun calcHashCode(): Int = 31 * module.hashCode()
override fun contains(file: VirtualFile) =
moduleFileIndex.isInSourceContentWithoutInjected(file) && !moduleFileIndex.isInTestSourceContentKotlinAware(file)
override fun toString() = "ModuleProductionSourceScope($module)"
}
@Suppress("EqualsOrHashCode") // DelegatingGlobalSearchScope requires to provide calcHashCode()
private class ModuleTestSourceScope(module: Module) : ModuleSourceScope(module) {
val moduleFileIndex = ModuleRootManager.getInstance(module).fileIndex
override fun equals(other: Any?): Boolean {
if (this === other) return true
return (other is ModuleTestSourceScope && module == other.module)
}
override fun calcHashCode(): Int = 37 * module.hashCode()
override fun contains(file: VirtualFile) = moduleFileIndex.isInTestSourceContentKotlinAware(file)
override fun toString() = "ModuleTestSourceScope($module)"
}
abstract class LibraryInfo(override val project: Project, val library: Library) :
IdeaModuleInfo, LibraryModuleInfo, BinaryModuleInfo, TrackableModuleInfo {
override val moduleOrigin: ModuleOrigin
get() = ModuleOrigin.LIBRARY
override val name: Name = Name.special("<library ${library.name}>")
override val displayedName: String
get() = KotlinIdeaAnalysisBundle.message("library.0", library.name.toString())
override fun contentScope(): GlobalSearchScope = LibraryWithoutSourceScope(project, library)
override fun dependencies(): List<IdeaModuleInfo> {
val result = LinkedHashSet<IdeaModuleInfo>()
result.add(this)
val (libraries, sdks) = LibraryDependenciesCache.getInstance(project).getLibrariesAndSdksUsedWith(this)
result.addAll(sdks)
result.addAll(libraries)
return result.toList()
}
abstract override val platform: TargetPlatform // must override
override val analyzerServices: PlatformDependentAnalyzerServices
get() = platform.findAnalyzerServices(project)
override val sourcesModuleInfo: SourceForBinaryModuleInfo
get() = LibrarySourceInfo(project, library, this)
override fun getLibraryRoots(): Collection<String> =
library.getFiles(OrderRootType.CLASSES).mapNotNull(PathUtil::getLocalPath)
override fun createModificationTracker(): ModificationTracker {
if (!project.libraryToSourceAnalysisEnabled)
return ModificationTracker.NEVER_CHANGED
return ResolutionAnchorAwareLibraryModificationTracker(this)
}
override fun toString() = "${this::class.simpleName}(libraryName=${library.name}, libraryRoots=${getLibraryRoots()})"
override fun equals(other: Any?): Boolean {
if (this === other) return true
return (other is LibraryInfo && library == other.library)
}
private val lazyHashCode: Int by lazy {
43 * library.hashCode()
}
override fun hashCode(): Int = lazyHashCode
}
data class LibrarySourceInfo(override val project: Project, val library: Library, override val binariesModuleInfo: BinaryModuleInfo) :
IdeaModuleInfo, SourceForBinaryModuleInfo {
override val name: Name = Name.special("<sources for library ${library.name}>")
override val displayedName: String
get() = KotlinIdeaAnalysisBundle.message("sources.for.library.0", library.name.toString())
override fun sourceScope(): GlobalSearchScope = KotlinSourceFilterScope.librarySources(
LibrarySourceScope(
project,
library
), project
)
override fun modulesWhoseInternalsAreVisible(): Collection<ModuleInfo> {
return createLibraryInfo(project, library)
}
override val platform: TargetPlatform
get() = binariesModuleInfo.platform
override val analyzerServices: PlatformDependentAnalyzerServices
get() = binariesModuleInfo.analyzerServices
override fun toString() = "LibrarySourceInfo(libraryName=${library.name})"
}
//TODO: (module refactoring) there should be separate SdkSourceInfo but there are no kotlin source in existing sdks for now :)
data class SdkInfo(override val project: Project, val sdk: Sdk) : IdeaModuleInfo, SdkInfoBase {
override val moduleOrigin: ModuleOrigin
get() = ModuleOrigin.LIBRARY
override val name: Name = Name.special("<sdk ${sdk.name}>")
override val displayedName: String
get() = KotlinIdeaAnalysisBundle.message("sdk.0", sdk.name)
override fun contentScope(): GlobalSearchScope = SdkScope(project, sdk)
override fun dependencies(): List<IdeaModuleInfo> = listOf(this)
override val platform: TargetPlatform
get() = when {
sdk.sdkType is KotlinSdkType -> CommonPlatforms.defaultCommonPlatform
else -> JvmPlatforms.unspecifiedJvmPlatform // TODO(dsavvinov): provide proper target version
}
override val analyzerServices: PlatformDependentAnalyzerServices
get() = JvmPlatformAnalyzerServices
override val capabilities: Map<ModuleCapability<*>, Any?>
get() = when (this.sdk.sdkType) {
is JavaSdk -> super<IdeaModuleInfo>.capabilities + mapOf(JDK_CAPABILITY to true)
else -> super<IdeaModuleInfo>.capabilities
}
}
object NotUnderContentRootModuleInfo : IdeaModuleInfo {
override val moduleOrigin: ModuleOrigin
get() = ModuleOrigin.OTHER
override val name: Name = Name.special("<special module for files not under source root>")
override val displayedName: String
get() = KotlinIdeaAnalysisBundle.message("special.module.for.files.not.under.source.root")
override val project: Project?
get() = null
override fun contentScope() = GlobalSearchScope.EMPTY_SCOPE
//TODO: (module refactoring) dependency on runtime can be of use here
override fun dependencies(): List<IdeaModuleInfo> = listOf(this)
override val platform: TargetPlatform
get() = DefaultIdeTargetPlatformKindProvider.defaultPlatform
override val analyzerServices: PlatformDependentAnalyzerServices
get() = platform.single().findAnalyzerServices()
}
internal open class PoweredLibraryScopeBase(project: Project, classes: Array<VirtualFile>, sources: Array<VirtualFile>) :
LibraryScopeBase(project, classes, sources), TopPackageNamesProvider {
private val entriesVirtualFileSystems: Set<NewVirtualFileSystem>? = run {
val fileSystems = mutableSetOf<NewVirtualFileSystem>()
for (file in classes + sources) {
val newVirtualFile = file as? NewVirtualFile ?: return@run null
fileSystems.add(newVirtualFile.fileSystem)
}
fileSystems
}
override val topPackageNames: Set<String> by lazy {
(classes + sources)
.flatMap { it.children.toList() }
.filter(VirtualFile::isDirectory)
.map(VirtualFile::getName)
.toSet() + "" // empty package is always present
}
override fun contains(file: VirtualFile): Boolean {
((file as? NewVirtualFile)?.fileSystem)?.let {
if (entriesVirtualFileSystems != null && !entriesVirtualFileSystems.contains(it)) {
return false
}
}
return super.contains(file)
}
}
@Suppress("EqualsOrHashCode") // DelegatingGlobalSearchScope requires to provide calcHashCode()
private class LibraryWithoutSourceScope(project: Project, private val library: Library) :
PoweredLibraryScopeBase(project, library.getFiles(OrderRootType.CLASSES), arrayOf()) {
override fun getFileRoot(file: VirtualFile): VirtualFile? = myIndex.getClassRootForFile(file)
override fun equals(other: Any?) = other is LibraryWithoutSourceScope && library == other.library
override fun calcHashCode(): Int = library.hashCode()
override fun toString() = "LibraryWithoutSourceScope($library)"
}
@Suppress("EqualsOrHashCode") // DelegatingGlobalSearchScope requires to provide calcHashCode()
private class LibrarySourceScope(project: Project, private val library: Library) :
PoweredLibraryScopeBase(project, arrayOf(), library.getFiles(OrderRootType.SOURCES)) {
override fun getFileRoot(file: VirtualFile): VirtualFile? = myIndex.getSourceRootForFile(file)
override fun equals(other: Any?) = other is LibrarySourceScope && library == other.library
override fun calcHashCode(): Int = library.hashCode()
override fun toString() = "LibrarySourceScope($library)"
}
//TODO: (module refactoring) android sdk has modified scope
@Suppress("EqualsOrHashCode") // DelegatingGlobalSearchScope requires to provide calcHashCode()
private class SdkScope(project: Project, val sdk: Sdk) :
PoweredLibraryScopeBase(project, sdk.rootProvider.getFiles(OrderRootType.CLASSES), arrayOf()) {
override fun equals(other: Any?) = other is SdkScope && sdk == other.sdk
override fun calcHashCode(): Int = sdk.hashCode()
override fun toString() = "SdkScope($sdk)"
}
fun IdeaModuleInfo.isLibraryClasses() = this is SdkInfo || this is LibraryInfo
val OriginCapability = ModuleCapability<ModuleOrigin>("MODULE_ORIGIN")
enum class ModuleOrigin {
MODULE,
LIBRARY,
OTHER
}
interface BinaryModuleInfo : IdeaModuleInfo {
val sourcesModuleInfo: SourceForBinaryModuleInfo?
fun binariesScope(): GlobalSearchScope {
val contentScope = contentScope()
if (contentScope === GlobalSearchScope.EMPTY_SCOPE) {
return contentScope
}
val project = contentScope.project
?: error("Project is empty for scope $contentScope (${contentScope.javaClass.name})")
return KotlinSourceFilterScope.libraryClassFiles(contentScope, project)
}
}
interface SourceForBinaryModuleInfo : IdeaModuleInfo {
val binariesModuleInfo: BinaryModuleInfo
fun sourceScope(): GlobalSearchScope
// module infos for library source do not have contents in the following sense:
// we can not provide a collection of files that is supposed to be analyzed in IDE independently
//
// as of now each source file is analyzed separately and depends on corresponding binaries
// see KotlinCacheServiceImpl#createFacadeForSyntheticFiles
override fun contentScope(): GlobalSearchScope = GlobalSearchScope.EMPTY_SCOPE
override fun dependencies() = listOf(this) + binariesModuleInfo.dependencies()
override val moduleOrigin: ModuleOrigin
get() = ModuleOrigin.OTHER
}
data class PlatformModuleInfo(
override val platformModule: ModuleSourceInfo,
private val commonModules: List<ModuleSourceInfo> // NOTE: usually contains a single element for current implementation
) : IdeaModuleInfo, CombinedModuleInfo, TrackableModuleInfo {
override val capabilities: Map<ModuleCapability<*>, Any?>
get() = platformModule.capabilities
override fun contentScope() = GlobalSearchScope.union(containedModules.map { it.contentScope() }.toTypedArray())
override val containedModules: List<ModuleSourceInfo> = listOf(platformModule) + commonModules
override val project: Project
get() = platformModule.module.project
override val platform: TargetPlatform
get() = platformModule.platform
override val moduleOrigin: ModuleOrigin
get() = platformModule.moduleOrigin
override val analyzerServices: PlatformDependentAnalyzerServices
get() = platform.findAnalyzerServices(platformModule.module.project)
override fun dependencies() = platformModule.dependencies()
override val expectedBy: List<ModuleInfo>
get() = platformModule.expectedBy
override fun modulesWhoseInternalsAreVisible() = containedModules.flatMap { it.modulesWhoseInternalsAreVisible() }
override val name: Name = Name.special("<Platform module ${platformModule.name} including ${commonModules.map { it.name }}>")
override val displayedName: String
get() = KotlinIdeaAnalysisBundle.message(
"platform.module.0.including.1",
platformModule.displayedName,
commonModules.map { it.displayedName }
)
override fun createModificationTracker() = platformModule.createModificationTracker()
}
fun IdeaModuleInfo.projectSourceModules(): List<ModuleSourceInfo>? =
(this as? ModuleSourceInfo)?.let(::listOf) ?: (this as? PlatformModuleInfo)?.containedModules
enum class SourceType {
PRODUCTION,
TEST
}
internal val ModuleSourceInfo.sourceType get() = if (this is ModuleTestSourceInfo) SourceType.TEST else SourceType.PRODUCTION

View File

@@ -1,143 +0,0 @@
/*
* 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.caches.project
import com.intellij.openapi.components.ServiceManager
import com.intellij.openapi.progress.ProgressManager
import com.intellij.openapi.project.Project
import com.intellij.openapi.roots.OrderRootType
import com.intellij.openapi.vfs.VirtualFile
import com.intellij.psi.search.DelegatingGlobalSearchScope
import com.intellij.psi.search.GlobalSearchScope
import com.intellij.util.indexing.FileBasedIndex
import org.jetbrains.kotlin.caches.project.cacheInvalidatingOnRootModifications
import org.jetbrains.kotlin.idea.configuration.IdeBuiltInsLoadingState
import org.jetbrains.kotlin.idea.util.application.runReadAction
import org.jetbrains.kotlin.idea.vfilefinder.KotlinStdlibIndex
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
import java.util.*
import java.util.concurrent.ConcurrentHashMap
// TODO(kirpichenkov): works only for JVM (see KT-44552)
interface KotlinStdlibCache {
fun isStdlib(libraryInfo: LibraryInfo): Boolean
fun isStdlibDependency(libraryInfo: LibraryInfo): Boolean
fun findStdlibInModuleDependencies(module: IdeaModuleInfo): LibraryInfo?
companion object {
fun getInstance(project: Project): KotlinStdlibCache =
if (IdeBuiltInsLoadingState.isFromClassLoader) {
Disabled
} else {
ServiceManager.getService(project, KotlinStdlibCache::class.java)
?: error("Failed to load service ${KotlinStdlibCache::class.java.name}")
}
val Disabled = object : KotlinStdlibCache {
override fun isStdlib(libraryInfo: LibraryInfo) = false
override fun isStdlibDependency(libraryInfo: LibraryInfo) = false
override fun findStdlibInModuleDependencies(module: IdeaModuleInfo): LibraryInfo? = null
}
}
}
class KotlinStdlibCacheImpl(val project: Project) : KotlinStdlibCache {
@JvmInline
private value class StdlibDependency(val libraryInfo: LibraryInfo?)
private val isStdlibCache: MutableMap<LibraryInfo, Boolean>
get() = project.cacheInvalidatingOnRootModifications {
ConcurrentHashMap<LibraryInfo, Boolean>()
}
private val isStdlibDependencyCache: MutableMap<LibraryInfo, Boolean>
get() = project.cacheInvalidatingOnRootModifications {
ConcurrentHashMap<LibraryInfo, Boolean>()
}
private val moduleStdlibDependencyCache: MutableMap<IdeaModuleInfo, StdlibDependency>
get() = project.cacheInvalidatingOnRootModifications {
ConcurrentHashMap<IdeaModuleInfo, StdlibDependency>()
}
private class LibraryScope(
project: Project,
private val directories: Set<VirtualFile>
) : DelegatingGlobalSearchScope(GlobalSearchScope.allScope(project)) {
private val fileSystems = directories.mapTo(hashSetOf(), VirtualFile::getFileSystem)
override fun contains(file: VirtualFile): Boolean =
file.fileSystem in fileSystems && generateSequence(file, VirtualFile::getParent).any { it in directories }
override fun toString() = "All files under: $directories"
}
private fun libraryScopeContainsIndexedFilesForNames(libraryInfo: LibraryInfo, names: Collection<FqName>) = runReadAction {
names.any { name ->
FileBasedIndex.getInstance().getContainingFiles(
KotlinStdlibIndex.KEY,
name,
LibraryScope(project, libraryInfo.library.rootProvider.getFiles(OrderRootType.CLASSES).toSet())
).isNotEmpty()
}
}
private fun libraryScopeContainsIndexedFilesForName(libraryInfo: LibraryInfo, name: FqName) =
libraryScopeContainsIndexedFilesForNames(libraryInfo, listOf(name))
override fun isStdlib(libraryInfo: LibraryInfo): Boolean {
return isStdlibCache.getOrPut(libraryInfo) {
libraryScopeContainsIndexedFilesForName(libraryInfo, KotlinStdlibIndex.KOTLIN_STDLIB_NAME)
}
}
override fun isStdlibDependency(libraryInfo: LibraryInfo): Boolean {
return isStdlibDependencyCache.getOrPut(libraryInfo) {
libraryScopeContainsIndexedFilesForNames(libraryInfo, KotlinStdlibIndex.STANDARD_LIBRARY_DEPENDENCY_NAMES)
}
}
override fun findStdlibInModuleDependencies(module: IdeaModuleInfo): LibraryInfo? {
val stdlibDependency = moduleStdlibDependencyCache.getOrPut(module) {
fun IdeaModuleInfo.asStdLibInfo() = this.safeAs<LibraryInfo>()?.takeIf { isStdlib(it) }
val stdLib: LibraryInfo? = module.asStdLibInfo() ?: run {
val checkedLibraryInfo = mutableSetOf<IdeaModuleInfo>()
val stack = ArrayDeque<IdeaModuleInfo>()
stack.add(module)
// bfs
while (stack.isNotEmpty()) {
ProgressManager.checkCanceled()
val poll = stack.poll()
if (!checkedLibraryInfo.add(poll)) continue
stack += poll.dependencies().also { dependencies ->
dependencies
.firstOrNull { it is LibraryInfo && isStdlib(it) }
?.let { return@run it as LibraryInfo }
}
}
null
}
StdlibDependency(stdLib)
}
return stdlibDependency.libraryInfo
}
}
fun LibraryInfo.isCoreKotlinLibrary(project: Project): Boolean =
isKotlinStdlib(project) || isKotlinStdlibDependency(project)
fun LibraryInfo.isKotlinStdlib(project: Project): Boolean =
KotlinStdlibCache.getInstance(project).isStdlib(this)
fun LibraryInfo.isKotlinStdlibDependency(project: Project): Boolean =
KotlinStdlibCache.getInstance(project).isStdlibDependency(this)

View File

@@ -1,172 +0,0 @@
/*
* Copyright 2000-2018 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.caches.project
import com.intellij.openapi.components.ServiceManager
import com.intellij.openapi.module.Module
import com.intellij.openapi.module.ModuleManager
import com.intellij.openapi.progress.ProgressManager
import com.intellij.openapi.project.Project
import com.intellij.openapi.roots.*
import com.intellij.openapi.roots.impl.libraries.LibraryEx
import com.intellij.openapi.roots.libraries.Library
import com.intellij.openapi.util.Condition
import com.intellij.psi.util.CachedValueProvider
import com.intellij.psi.util.CachedValuesManager
import com.intellij.util.containers.ContainerUtil
import com.intellij.util.containers.MultiMap
import org.jetbrains.kotlin.idea.configuration.IdeBuiltInsLoadingState
import org.jetbrains.kotlin.idea.core.util.CachedValue
import org.jetbrains.kotlin.idea.core.util.getValue
import org.jetbrains.kotlin.idea.project.isHMPPEnabled
import org.jetbrains.kotlin.platform.TargetPlatform
import org.jetbrains.kotlin.platform.isCommon
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
import java.util.*
internal typealias LibrariesAndSdks = Pair<List<LibraryInfo>, List<SdkInfo>>
interface LibraryDependenciesCache {
companion object {
fun getInstance(project: Project) = ServiceManager.getService(project, LibraryDependenciesCache::class.java)!!
}
fun getLibrariesAndSdksUsedWith(libraryInfo: LibraryInfo): LibrariesAndSdks
}
class LibraryDependenciesCacheImpl(private val project: Project) : LibraryDependenciesCache {
private val cache by CachedValue(project) {
CachedValueProvider.Result(
ContainerUtil.createConcurrentWeakMap<LibraryInfo, LibrariesAndSdks>(),
ProjectRootManager.getInstance(project)
)
}
private val moduleDependenciesCache by CachedValue(project) {
CachedValueProvider.Result(
ContainerUtil.createConcurrentWeakMap<Module, LibrariesAndSdks>(),
ProjectRootManager.getInstance(project)
)
}
override fun getLibrariesAndSdksUsedWith(libraryInfo: LibraryInfo): LibrariesAndSdks =
cache.getOrPut(libraryInfo) { computeLibrariesAndSdksUsedWith(libraryInfo) }
private fun computeLibrariesAndSdksUsedIn(module: Module): LibrariesAndSdks {
val libraries = LinkedHashSet<LibraryInfo>()
val sdks = LinkedHashSet<SdkInfo>()
val processedModules = HashSet<Module>()
val condition = Condition<OrderEntry> { orderEntry ->
orderEntry.safeAs<ModuleOrderEntry>()?.let {
it.module?.run { this !in processedModules } ?: false
} ?: true
}
ModuleRootManager.getInstance(module).orderEntries().recursively().satisfying(condition).process(object : RootPolicy<Unit>() {
override fun visitModuleSourceOrderEntry(moduleSourceOrderEntry: ModuleSourceOrderEntry, value: Unit) {
processedModules.add(moduleSourceOrderEntry.ownerModule)
}
override fun visitLibraryOrderEntry(libraryOrderEntry: LibraryOrderEntry, value: Unit) {
libraryOrderEntry.library.safeAs<LibraryEx>()?.takeIf { !it.isDisposed }?.let {
libraries += createLibraryInfo(project, it)
}
}
override fun visitJdkOrderEntry(jdkOrderEntry: JdkOrderEntry, value: Unit) {
jdkOrderEntry.jdk?.let { jdk ->
sdks += SdkInfo(project, jdk)
}
}
}, Unit)
return Pair(libraries.toList(), sdks.toList())
}
//NOTE: used LibraryRuntimeClasspathScope as reference
private fun computeLibrariesAndSdksUsedWith(libraryInfo: LibraryInfo): LibrariesAndSdks {
val libraries = LinkedHashSet<LibraryInfo>()
val sdks = LinkedHashSet<SdkInfo>()
val platform = libraryInfo.platform
for (module in getLibraryUsageIndex().getModulesLibraryIsUsedIn(libraryInfo)) {
ProgressManager.checkCanceled()
val (moduleLibraries, moduleSdks) = moduleDependenciesCache.getOrPut(module) {
computeLibrariesAndSdksUsedIn(module)
}
moduleLibraries.filter { compatiblePlatforms(platform, it.platform) }.forEach { libraries.add(it) }
moduleSdks.filter { compatiblePlatforms(platform, it.platform) }.forEach { sdks.add(it) }
}
val filteredLibraries = filterForBuiltins(libraryInfo, libraries)
return Pair(filteredLibraries.toList(), sdks.toList())
}
/**
* @return true if it's OK to add a dependency from a library with platform [from] to a library with platform [to]
*/
private fun compatiblePlatforms(from: TargetPlatform, to: TargetPlatform): Boolean {
return from === to || to.containsAll(from) || to.isCommon()
}
private fun getLibraryUsageIndex(): LibraryUsageIndex {
return CachedValuesManager.getManager(project).getCachedValue(project) {
CachedValueProvider.Result(LibraryUsageIndex(), ProjectRootModificationTracker.getInstance(project))
}!!
}
/*
* When built-ins are created from module dependencies (as opposed to loading them from classloader)
* we must resolve Kotlin standard library containing some of the built-ins declarations in the same
* resolver for project as JDK. This comes from the following requirements:
* - JvmBuiltins need JDK and standard library descriptors -> resolver for project should be able to
* resolve them
* - Builtins are created in BuiltinsCache -> module descriptors should be resolved under lock of the
* SDK resolver to prevent deadlocks
* This means we have to maintain dependencies of the standard library manually or effectively drop
* resolver for SDK otherwise. Libraries depend on superset of their actual dependencies because of
* the inability to get real dependencies from IDEA model. So moving stdlib with all dependencies
* down is a questionable option.
*/
private fun filterForBuiltins(libraryInfo: LibraryInfo, dependencyLibraries: Set<LibraryInfo>): Set<LibraryInfo> {
return if (!IdeBuiltInsLoadingState.isFromClassLoader && libraryInfo.isCoreKotlinLibrary(project)) {
dependencyLibraries.filterTo(mutableSetOf()) { it.isCoreKotlinLibrary(project) }
} else {
dependencyLibraries
}
}
private inner class LibraryUsageIndex {
private val modulesLibraryIsUsedIn: MultiMap<Library, Module> = MultiMap.createSet()
init {
for (module in ModuleManager.getInstance(project).modules) {
for (entry in ModuleRootManager.getInstance(module).orderEntries) {
if (entry is LibraryOrderEntry) {
val library = entry.library
if (library != null) {
modulesLibraryIsUsedIn.putValue(library, module)
}
}
}
}
}
fun getModulesLibraryIsUsedIn(libraryInfo: LibraryInfo) = sequence<Module> {
val ideaModelInfosCache = getIdeaModelInfosCache(project)
for (module in modulesLibraryIsUsedIn[libraryInfo.library]) {
val mappedModuleInfos = ideaModelInfosCache.getModuleInfosForModule(module)
if (mappedModuleInfos.any { it.platform.canDependOn(libraryInfo, module.isHMPPEnabled) }) {
yield(module)
}
}
}
}
}

View File

@@ -1,102 +0,0 @@
/*
* Copyright 2000-2018 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.caches.project
import com.intellij.ide.highlighter.ArchiveFileType
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.fileTypes.FileTypeEvent
import com.intellij.openapi.fileTypes.FileTypeListener
import com.intellij.openapi.fileTypes.FileTypeManager
import com.intellij.openapi.project.DumbService
import com.intellij.openapi.project.Project
import com.intellij.openapi.roots.ProjectFileIndex
import com.intellij.openapi.util.SimpleModificationTracker
import com.intellij.openapi.vfs.JarFileSystem
import com.intellij.openapi.vfs.VirtualFile
import com.intellij.openapi.vfs.VirtualFileManager
import com.intellij.openapi.vfs.newvfs.BulkFileListener
import com.intellij.openapi.vfs.newvfs.events.VFileCopyEvent
import com.intellij.openapi.vfs.newvfs.events.VFileCreateEvent
import com.intellij.openapi.vfs.newvfs.events.VFileEvent
import com.intellij.openapi.vfs.newvfs.events.VFileMoveEvent
import org.jetbrains.kotlin.idea.util.application.getServiceSafe
class LibraryModificationTracker(project: Project) : SimpleModificationTracker() {
companion object {
@JvmStatic
fun getInstance(project: Project): LibraryModificationTracker = project.getServiceSafe()
}
init {
val connection = project.messageBus.connect(project)
connection.subscribe(VirtualFileManager.VFS_CHANGES, object : BulkFileListener {
override fun after(events: List<VFileEvent>) {
events.filter(::isRelevantEvent).let { createEvents ->
if (createEvents.isNotEmpty()) {
ApplicationManager.getApplication().invokeLater {
if (!project.isDisposed) {
processBulk(createEvents) {
projectFileIndex.isInLibraryClasses(it) || isLibraryArchiveRoot(it)
}
}
}
}
}
}
override fun before(events: List<VFileEvent>) {
processBulk(events) {
projectFileIndex.isInLibraryClasses(it)
}
}
})
connection.subscribe(DumbService.DUMB_MODE, object : DumbService.DumbModeListener {
override fun enteredDumbMode() {
incModificationCount()
}
override fun exitDumbMode() {
incModificationCount()
}
})
connection.subscribe(FileTypeManager.TOPIC, object : FileTypeListener {
override fun beforeFileTypesChanged(event: FileTypeEvent) {
incModificationCount()
}
override fun fileTypesChanged(event: FileTypeEvent) {
incModificationCount()
}
})
}
private val projectFileIndex = ProjectFileIndex.SERVICE.getInstance(project)
private inline fun processBulk(events: List<VFileEvent>, check: (VirtualFile) -> Boolean) {
events.forEach { event ->
if (event.isValid) {
val file = event.file
if (file != null && check(file)) {
incModificationCount()
return
}
}
}
}
// if library points to a jar, the jar does not pass isInLibraryClasses check, so we have to perform additional check for this case
private fun isLibraryArchiveRoot(virtualFile: VirtualFile): Boolean {
if (virtualFile.fileType != ArchiveFileType.INSTANCE) return false
val archiveRoot = JarFileSystem.getInstance().getRootByLocal(virtualFile) ?: return false
return projectFileIndex.isInLibraryClasses(archiveRoot)
}
}
private fun isRelevantEvent(vFileEvent: VFileEvent) =
vFileEvent is VFileCreateEvent || vFileEvent is VFileMoveEvent || vFileEvent is VFileCopyEvent

View File

@@ -1,77 +0,0 @@
/*
* Copyright 2000-2018 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.caches.project
import com.intellij.openapi.module.Module
import com.intellij.openapi.module.ModuleManager
import com.intellij.openapi.project.Project
import com.intellij.openapi.roots.ModuleOrderEntry
import com.intellij.openapi.roots.ModuleRootManager
import com.intellij.openapi.roots.ProjectRootModificationTracker
import com.intellij.psi.util.CachedValueProvider
import com.intellij.psi.util.CachedValuesManager
import com.intellij.util.containers.MultiMap
import com.intellij.util.containers.Queue
import gnu.trove.THashSet
import java.util.*
//NOTE: this is an approximation that may contain more module infos then the exact solution
fun ModuleSourceInfo.getDependentModules(): Set<ModuleSourceInfo> {
val dependents = getDependents(module)
return when (sourceType) {
SourceType.TEST -> dependents.mapNotNullTo(HashSet<ModuleSourceInfo>(), Module::testSourceInfo)
SourceType.PRODUCTION -> dependents.flatMapTo(HashSet<ModuleSourceInfo>()) { it.correspondingModuleInfos() }
}
}
//NOTE: getDependents adapted from com.intellij.openapi.module.impl.scopes.ModuleWithDependentsScope#buildDependents()
private fun getDependents(module: Module): Set<Module> {
val result = THashSet<Module>()
result.add(module)
val processedExporting = THashSet<Module>()
val index = getModuleIndex(module.project)
val walkingQueue = Queue<Module>(10)
walkingQueue.addLast(module)
while (!walkingQueue.isEmpty) {
val current = walkingQueue.pullFirst()
processedExporting.add(current!!)
result.addAll(index.plainUsages[current])
for (dependent in index.exportingUsages[current]) {
result.add(dependent)
if (processedExporting.add(dependent)) {
walkingQueue.addLast(dependent)
}
}
}
return result
}
private class ModuleIndex {
val plainUsages = MultiMap.create<Module, Module>()
val exportingUsages = MultiMap.create<Module, Module>()
}
private fun getModuleIndex(project: Project): ModuleIndex {
return CachedValuesManager.getManager(project).getCachedValue(project) {
val index = ModuleIndex()
for (module in ModuleManager.getInstance(project).modules) {
for (orderEntry in ModuleRootManager.getInstance(module).orderEntries) {
if (orderEntry is ModuleOrderEntry) {
val referenced = orderEntry.module
if (referenced != null) {
val map = if (orderEntry.isExported) index.exportingUsages else index.plainUsages
map.putValue(referenced, module)
}
}
}
}
CachedValueProvider.Result(index, ProjectRootModificationTracker.getInstance(project))
}!!
}

View File

@@ -1,21 +0,0 @@
/*
* 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.caches.project
import com.intellij.openapi.util.ModificationTracker
import org.jetbrains.kotlin.idea.caches.resolve.ResolutionAnchorCacheService
import org.jetbrains.kotlin.idea.caches.trackers.getLatestModificationCount
class ResolutionAnchorAwareLibraryModificationTracker(
private val libraryInfo: LibraryInfo,
) : ModificationTracker {
override fun getModificationCount(): Long {
val anchorDependencyModules = ResolutionAnchorCacheService.getInstance(libraryInfo.project)
.getDependencyResolutionAnchors(libraryInfo)
.map { it.module }
return getLatestModificationCount(anchorDependencyModules)
}
}

View File

@@ -1,49 +0,0 @@
/*
* 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.caches.project
import com.intellij.openapi.module.Module
import com.intellij.openapi.project.Project
import com.intellij.openapi.roots.ModuleRootManager
import com.intellij.util.CommonProcessors
import org.jetbrains.kotlin.idea.caches.resolve.ResolutionAnchorCacheService
import org.jetbrains.kotlin.idea.caches.trackers.ModuleDependencyProviderExtension
import org.jetbrains.kotlin.idea.project.libraryToSourceAnalysisEnabled
import org.jetbrains.kotlin.progress.ProgressIndicatorAndCompilationCanceledStatus.checkCanceled
class ResolutionAnchorModuleDependencyProviderExtension(private val project: Project) : ModuleDependencyProviderExtension {
/**
* Consider modules M1, M2, M3, library L1 resolving via Resolution anchor M2, other libraries L2, L3 with the following dependencies:
* M2 depends on M1
* L1 depends on anchor M2
* L2 depends on L1
* L3 depends on L2
* M3 depends on L3
* Then modification of M1 should lead to complete invalidation of all modules and libraries in this example.
*
* Updates for libraries aren't managed here, corresponding ModificationTracker is responsible for that.
* This extension provides missing dependencies from source-dependent library dependencies only to source modules.
*/
override fun getAdditionalDependencyModules(module: Module): Collection<Module> {
if (!project.libraryToSourceAnalysisEnabled) return emptySet()
val resolutionAnchorDependencies = HashSet<ModuleSourceInfo>()
ModuleRootManager.getInstance(module).orderEntries().recursively().forEachLibrary { library ->
getIdeaModelInfosCache(project).getLibraryInfosForLibrary(library).flatMapTo(resolutionAnchorDependencies) { libraryInfo ->
checkCanceled()
ResolutionAnchorCacheService.getInstance(project).getDependencyResolutionAnchors(libraryInfo)
}
true
}
val additionalModules = HashSet<Module>()
for (anchorModule in resolutionAnchorDependencies) {
ModuleRootManager.getInstance(anchorModule.module).orderEntries().recursively()
.forEachModule(CommonProcessors.CollectProcessor(additionalModules))
}
return additionalModules
}
}

View File

@@ -1,152 +0,0 @@
/*
* 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.caches.project
import com.intellij.openapi.project.Project
import com.intellij.openapi.projectRoots.Sdk
import com.intellij.openapi.vfs.VirtualFile
import com.intellij.psi.search.GlobalSearchScope
import org.jetbrains.kotlin.idea.KotlinIdeaAnalysisBundle
import org.jetbrains.kotlin.idea.core.script.ScriptConfigurationManager
import org.jetbrains.kotlin.idea.core.script.dependencies.ScriptAdditionalIdeaDependenciesProvider
import org.jetbrains.kotlin.idea.project.TargetPlatformDetector
import org.jetbrains.kotlin.idea.stubindex.KotlinSourceFilterScope
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.platform.TargetPlatform
import org.jetbrains.kotlin.platform.jvm.JvmPlatforms
import org.jetbrains.kotlin.resolve.PlatformDependentAnalyzerServices
import org.jetbrains.kotlin.resolve.jvm.platform.JvmPlatformAnalyzerServices
import org.jetbrains.kotlin.scripting.definitions.ScriptDefinition
data class ScriptModuleInfo(
override val project: Project,
val scriptFile: VirtualFile,
val scriptDefinition: ScriptDefinition
) : IdeaModuleInfo {
override val moduleOrigin: ModuleOrigin
get() = ModuleOrigin.OTHER
override val name: Name = Name.special("<script ${scriptFile.name} ${scriptDefinition.name}>")
override val displayedName: String
get() = KotlinIdeaAnalysisBundle.message("script.0.1", scriptFile.presentableName, scriptDefinition.name)
override fun contentScope() = GlobalSearchScope.fileScope(project, scriptFile)
override fun dependencies(): List<IdeaModuleInfo> {
return arrayListOf<IdeaModuleInfo>(this).apply {
val scriptDependentModules = ScriptAdditionalIdeaDependenciesProvider.getRelatedModules(scriptFile, project)
scriptDependentModules.forEach {
addAll(it.correspondingModuleInfos())
}
val scriptDependentLibraries = ScriptAdditionalIdeaDependenciesProvider.getRelatedLibraries(scriptFile, project)
scriptDependentLibraries.forEach {
addAll(createLibraryInfo(project, it))
}
val dependenciesInfo = ScriptDependenciesInfo.ForFile(project, scriptFile, scriptDefinition)
add(dependenciesInfo)
dependenciesInfo.sdk?.let { add(SdkInfo(project, it)) }
}
}
override val platform: TargetPlatform
get() = TargetPlatformDetector.getPlatform4Script(project, scriptFile, scriptDefinition)
override val analyzerServices: PlatformDependentAnalyzerServices
get() = JvmPlatformAnalyzerServices
}
sealed class ScriptDependenciesInfo(override val project: Project) : IdeaModuleInfo, BinaryModuleInfo {
abstract val sdk: Sdk?
override val name = Name.special("<Script dependencies>")
override val displayedName: String
get() = KotlinIdeaAnalysisBundle.message("script.dependencies")
override fun dependencies(): List<IdeaModuleInfo> = listOfNotNull(this, sdk?.let { SdkInfo(project, it) })
// NOTE: intentionally not taking corresponding script info into account
// otherwise there is no way to implement getModuleInfo
override fun hashCode() = project.hashCode()
override fun equals(other: Any?): Boolean = other is ScriptDependenciesInfo && this.project == other.project
override val moduleOrigin: ModuleOrigin
get() = ModuleOrigin.LIBRARY
override val sourcesModuleInfo: SourceForBinaryModuleInfo?
get() = ScriptDependenciesSourceInfo.ForProject(project)
override val platform: TargetPlatform
get() = JvmPlatforms.unspecifiedJvmPlatform // TODO(dsavvinov): choose proper TargetVersion
override val analyzerServices: PlatformDependentAnalyzerServices
get() = JvmPlatformAnalyzerServices
class ForFile(
project: Project,
val scriptFile: VirtualFile,
val scriptDefinition: ScriptDefinition
) : ScriptDependenciesInfo(project) {
override val sdk: Sdk?
get() {
return ScriptConfigurationManager.getInstance(project).getScriptSdk(scriptFile)
}
override fun contentScope(): GlobalSearchScope {
// TODO: this is not very efficient because KotlinSourceFilterScope already checks if the files are in scripts classpath
return KotlinSourceFilterScope.libraryClassFiles(
ScriptConfigurationManager.getInstance(project).getScriptDependenciesClassFilesScope(scriptFile), project
)
}
}
// we do not know which scripts these dependencies are
class ForProject(project: Project) : ScriptDependenciesInfo(project) {
override val sdk: Sdk?
get() {
return ScriptConfigurationManager.getInstance(project).getFirstScriptsSdk()
}
override fun contentScope(): GlobalSearchScope {
return KotlinSourceFilterScope.libraryClassFiles(
ScriptConfigurationManager.getInstance(project).getAllScriptsDependenciesClassFilesScope(), project
)
}
}
}
sealed class ScriptDependenciesSourceInfo(override val project: Project) : IdeaModuleInfo, SourceForBinaryModuleInfo {
override val name = Name.special("<Source for script dependencies>")
override val displayedName: String
get() = KotlinIdeaAnalysisBundle.message("source.for.script.dependencies")
override val binariesModuleInfo: ScriptDependenciesInfo
get() = ScriptDependenciesInfo.ForProject(project)
// include project sources because script dependencies sources may contain roots from project
// the main example is buildSrc for *.gradle.kts files
override fun sourceScope(): GlobalSearchScope = KotlinSourceFilterScope.projectAndLibrariesSources(
ScriptConfigurationManager.getInstance(project).getAllScriptDependenciesSourcesScope(), project
)
override fun hashCode() = project.hashCode()
override fun equals(other: Any?): Boolean = other is ScriptDependenciesSourceInfo && this.project == other.project
override val platform: TargetPlatform
get() = JvmPlatforms.unspecifiedJvmPlatform // TODO(dsavvinov): choose proper TargetVersion
override val analyzerServices: PlatformDependentAnalyzerServices
get() = JvmPlatformAnalyzerServices
class ForProject(project: Project) : ScriptDependenciesSourceInfo(project)
}

View File

@@ -1,141 +0,0 @@
/*
* 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.caches.project
import com.intellij.openapi.components.ServiceManager
import com.intellij.openapi.progress.ProgressManager
import com.intellij.openapi.project.Project
import com.jetbrains.rd.util.concurrentMapOf
import org.jetbrains.kotlin.analyzer.ModuleInfo
import org.jetbrains.kotlin.caches.project.cacheInvalidatingOnRootModifications
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
import java.util.ArrayDeque
/**
* Maintains and caches mapping ModuleInfo -> SdkInfo *form its dependencies*
* (note that this SDK might be different from Project SDK)
*
* Cache is needed because one and the same library might (and usually does)
* participate as dependency in several modules, so if someone queries a lot
* of modules for their SDKs (ex.: determine built-ins for each module in a
* project), we end up inspecting one and the same dependency multiple times.
*
* With projects with abnormally high amount of dependencies this might lead
* to performance issues.
*/
interface SdkInfoCache {
fun findOrGetCachedSdk(moduleInfo: ModuleInfo): SdkInfo?
companion object {
fun getInstance(project: Project): SdkInfoCache =
ServiceManager.getService(project, SdkInfoCache::class.java)
}
}
class SdkInfoCacheImpl(private val project: Project) : SdkInfoCache {
@JvmInline
value class SdkDependency(val sdk: SdkInfo?)
private val cache: MutableMap<ModuleInfo, SdkDependency>
get() = project.cacheInvalidatingOnRootModifications { concurrentMapOf() }
override fun findOrGetCachedSdk(moduleInfo: ModuleInfo): SdkInfo? {
// get an operate on the fixed instance of a cache to avoid case of roots modification in the middle of lookup
val instance = cache
if (!instance.containsKey(moduleInfo)) {
findSdk(instance, moduleInfo)
}
return instance[moduleInfo]?.sdk
}
private fun findSdk(cache: MutableMap<ModuleInfo, SdkDependency>, moduleInfo: ModuleInfo) {
moduleInfo.safeAs<SdkDependency>()?.let {
cache[moduleInfo] = it
return
}
val libraryDependenciesCache = LibraryDependenciesCache.getInstance(this.project)
val visitedModuleInfos = mutableSetOf<ModuleInfo>()
// graphs is a stack of paths is used to implement DFS without recursion
// it depends on a number of libs, that could be > 10k for a huge monorepos
val graphs = ArrayDeque<List<ModuleInfo>>().also {
// initial graph item
it.add(listOf(moduleInfo))
}
val (path, sdkInfo) = run {
while (graphs.isNotEmpty()) {
ProgressManager.checkCanceled()
// graph of DFS from the root i.e from `moduleInfo`
// note: traverse of graph goes in a depth over the most left edge:
// - poll() retrieves and removes the head of the queue
// - push(element) inserts the element at the front of the deque
val graph = graphs.poll()
val last = graph.last()
// the result could be immediately returned when cache already has it
val cached = cache[last]
if (cached != null) {
cached.sdk?.let { return@run graph to cached }
continue
}
if (!visitedModuleInfos.add(last)) continue
val dependencies = run deps@{
if (last is LibraryInfo) {
// use a special case for LibraryInfo to reuse values from a library dependencies cache
val (libraries, sdks) = libraryDependenciesCache.getLibrariesAndSdksUsedWith(last)
sdks.firstOrNull()?.let {
return@run graph to SdkDependency(it)
}
libraries
} else {
last.dependencies()
.also { dependencies ->
dependencies.firstIsInstanceOrNull<SdkInfo>()?.let {
return@run graph to SdkDependency(it)
}
}
}
}
dependencies.forEach { dependency ->
val sdkDependency = cache[dependency]
if (sdkDependency != null) {
sdkDependency.sdk?.let {
// sdk is found when some dependency is already resolved
return@run (graph + dependency) to sdkDependency
}
} else {
// otherwise add a new graph of (existed graph + dependency) as candidates for DFS lookup
if (!visitedModuleInfos.contains(dependency)) {
graphs.push(graph + dependency)
}
}
}
}
return@run null to noSdkDependency
}
// when sdk is found: mark all graph elements could be resolved to the same sdk
path?.let {
it.forEach { info -> cache[info] = sdkInfo }
visitedModuleInfos.removeAll(it)
}
// mark all visited modules (apart of found path) as dead ends
visitedModuleInfos.forEach { info -> cache[info] = noSdkDependency }
}
companion object {
private val noSdkDependency = SdkDependency(null)
}
}

View File

@@ -1,332 +0,0 @@
/*
* Copyright 2010-2019 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.caches.project
import com.intellij.ide.scratch.ScratchFileService
import com.intellij.ide.scratch.ScratchRootType
import com.intellij.openapi.module.ModuleManager
import com.intellij.openapi.project.Project
import com.intellij.openapi.roots.*
import com.intellij.openapi.util.Key
import com.intellij.openapi.vfs.VirtualFile
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiFile
import org.jetbrains.kotlin.analyzer.ModuleInfo
import org.jetbrains.kotlin.asJava.classes.KtLightClassForFacade
import org.jetbrains.kotlin.asJava.elements.KtLightElement
import org.jetbrains.kotlin.idea.caches.lightClasses.KtLightClassForDecompiledDeclaration
import org.jetbrains.kotlin.idea.core.isInTestSourceContentKotlinAware
import org.jetbrains.kotlin.idea.core.script.ScriptConfigurationManager
import org.jetbrains.kotlin.idea.core.script.scriptRelatedModuleName
import org.jetbrains.kotlin.idea.highlighter.OutsidersPsiFileSupportUtils
import org.jetbrains.kotlin.idea.util.ProjectRootsUtil
import org.jetbrains.kotlin.idea.util.isInSourceContentWithoutInjected
import org.jetbrains.kotlin.idea.util.isKotlinBinary
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.getParentOfType
import org.jetbrains.kotlin.scripting.definitions.findScriptDefinition
import org.jetbrains.kotlin.scripting.definitions.runReadAction
import org.jetbrains.kotlin.utils.addIfNotNull
import org.jetbrains.kotlin.utils.sure
import org.jetbrains.kotlin.utils.yieldIfNotNull
var PsiFile.forcedModuleInfo: ModuleInfo? by UserDataProperty(Key.create("FORCED_MODULE_INFO"))
fun PsiElement.getModuleInfo(): IdeaModuleInfo = this.collectInfos(ModuleInfoCollector.NotNullTakeFirst)
fun PsiElement.getNullableModuleInfo(): IdeaModuleInfo? = this.collectInfos(ModuleInfoCollector.NullableTakeFirst)
fun PsiElement.getModuleInfos(): Sequence<IdeaModuleInfo> = this.collectInfos(ModuleInfoCollector.ToSequence)
fun ModuleInfo.findSdkAcrossDependencies(): SdkInfo? {
val project = (this as? IdeaModuleInfo)?.project ?: return null
return SdkInfoCache.getInstance(project).findOrGetCachedSdk(this)
}
fun IdeaModuleInfo.findJvmStdlibAcrossDependencies(): LibraryInfo? {
val project = project ?: return null
return KotlinStdlibCache.getInstance(project).findStdlibInModuleDependencies(this)
}
fun getModuleInfoByVirtualFile(project: Project, virtualFile: VirtualFile): IdeaModuleInfo? =
collectInfosByVirtualFile(
project, virtualFile,
treatAsLibrarySource = false,
onOccurrence = { return@getModuleInfoByVirtualFile it }
)
fun getBinaryLibrariesModuleInfos(project: Project, virtualFile: VirtualFile) =
collectModuleInfosByType<BinaryModuleInfo>(
project,
virtualFile
)
fun getLibrarySourcesModuleInfos(project: Project, virtualFile: VirtualFile) =
collectModuleInfosByType<LibrarySourceInfo>(
project,
virtualFile
)
fun getScriptRelatedModuleInfo(project: Project, virtualFile: VirtualFile): ModuleSourceInfo? {
val moduleRelatedModuleInfo = getModuleRelatedModuleInfo(project, virtualFile)
if (moduleRelatedModuleInfo != null) {
return moduleRelatedModuleInfo
}
return if (ScratchFileService.getInstance().getRootType(virtualFile) is ScratchRootType) {
val scratchModule = virtualFile.scriptRelatedModuleName?.let { ModuleManager.getInstance(project).findModuleByName(it) }
scratchModule?.testSourceInfo() ?: scratchModule?.productionSourceInfo()
} else null
}
private typealias VirtualFileProcessor<T> = (Project, VirtualFile, Boolean) -> T
private sealed class ModuleInfoCollector<out T>(
val onResult: (IdeaModuleInfo?) -> T,
val onFailure: (String) -> T,
val virtualFileProcessor: VirtualFileProcessor<T>
) {
object NotNullTakeFirst : ModuleInfoCollector<IdeaModuleInfo>(
onResult = { it ?: NotUnderContentRootModuleInfo },
onFailure = { reason ->
LOG.error("Could not find correct module information.\nReason: $reason")
NotUnderContentRootModuleInfo
},
virtualFileProcessor = processor@{ project, virtualFile, isLibrarySource ->
collectInfosByVirtualFile(
project,
virtualFile,
isLibrarySource
) {
return@processor it ?: NotUnderContentRootModuleInfo
}
}
)
object NullableTakeFirst : ModuleInfoCollector<IdeaModuleInfo?>(
onResult = { it },
onFailure = { reason ->
LOG.warn("Could not find correct module information.\nReason: $reason")
null
},
virtualFileProcessor = processor@{ project, virtualFile, isLibrarySource ->
collectInfosByVirtualFile(
project,
virtualFile,
isLibrarySource
) { return@processor it }
}
)
object ToSequence : ModuleInfoCollector<Sequence<IdeaModuleInfo>>(
onResult = { result -> result?.let { sequenceOf(it) } ?: emptySequence() },
onFailure = { reason ->
LOG.warn("Could not find correct module information.\nReason: $reason")
emptySequence()
},
virtualFileProcessor = { project, virtualFile, isLibrarySource ->
sequence {
collectInfosByVirtualFile(
project,
virtualFile,
isLibrarySource
) { yieldIfNotNull(it) }
}
}
)
}
private fun <T> PsiElement.collectInfos(c: ModuleInfoCollector<T>): T {
(containingFile?.forcedModuleInfo as? IdeaModuleInfo)?.let {
return c.onResult(it)
}
if (this is KtLightElement<*, *>) {
return this.processLightElement(c)
}
collectModuleInfoByUserData(this).firstOrNull()?.let {
return c.onResult(it)
}
val containingFile =
containingFile ?: return c.onFailure("Analyzing element of type ${this::class.java} with no containing file\nText:\n$text")
val containingKtFile = (this as? KtElement)?.containingFile as? KtFile
containingKtFile?.analysisContext?.let {
return it.collectInfos(c)
}
containingKtFile?.doNotAnalyze?.let {
return c.onFailure("Should not analyze element: $text in file ${containingKtFile.name}\n$it")
}
val explicitModuleInfo = containingKtFile?.forcedModuleInfo ?: (containingKtFile?.originalFile as? KtFile)?.forcedModuleInfo
if (explicitModuleInfo is IdeaModuleInfo) {
return c.onResult(explicitModuleInfo)
}
if (containingKtFile is KtCodeFragment) {
val context = containingKtFile.getContext()
?: return c.onFailure("Analyzing code fragment of type ${containingKtFile::class.java} with no context element\nText:\n${containingKtFile.getText()}")
return context.collectInfos(c)
}
val virtualFile = containingFile.originalFile.virtualFile
?: return c.onFailure("Analyzing element of type ${this::class.java} in non-physical file $containingFile of type ${containingFile::class.java}\nText:\n$text")
val isScript = runReadAction { containingKtFile?.isScript() == true }
if (isScript) {
getModuleRelatedModuleInfo(project, virtualFile)?.let {
return c.onResult(it)
}
val script = runReadAction { containingKtFile?.script }
script?.let {
containingKtFile?.findScriptDefinition()?.let {
return c.onResult(ScriptModuleInfo(project, virtualFile, it))
}
}
}
return c.virtualFileProcessor(
project,
virtualFile,
(containingFile as? KtFile)?.isCompiled ?: false
)
}
private fun <T> KtLightElement<*, *>.processLightElement(c: ModuleInfoCollector<T>): T {
val decompiledClass = this.getParentOfType<KtLightClassForDecompiledDeclaration>(strict = false)
if (decompiledClass != null) {
return c.virtualFileProcessor(
project,
containingFile.virtualFile.sure { "Decompiled class should be build from physical file" },
false
)
}
val element = kotlinOrigin ?: when (this) {
is KtLightClassForFacade -> this.files.first()
else -> return c.onFailure("Light element without origin is referenced by resolve:\n$this\n${this.clsDelegate.text}")
}
return element.collectInfos(c)
}
private inline fun <T> collectInfosByVirtualFile(
project: Project,
virtualFile: VirtualFile,
treatAsLibrarySource: Boolean,
onOccurrence: (IdeaModuleInfo?) -> T
): T {
collectModuleInfoByUserData(project, virtualFile).map(onOccurrence)
val moduleRelatedModuleInfo = getModuleRelatedModuleInfo(project, virtualFile)
if (moduleRelatedModuleInfo != null) {
onOccurrence(moduleRelatedModuleInfo)
}
val projectFileIndex = ProjectFileIndex.SERVICE.getInstance(project)
projectFileIndex.getOrderEntriesForFile(virtualFile).forEach {
it.toIdeaModuleInfo(project, virtualFile, treatAsLibrarySource).map(onOccurrence)
}
val isBinary = virtualFile.fileType.isKotlinBinary()
val scriptConfigurationManager = ScriptConfigurationManager.getInstance(project)
if (isBinary && virtualFile in scriptConfigurationManager.getAllScriptsDependenciesClassFilesScope()) {
if (treatAsLibrarySource) {
onOccurrence(ScriptDependenciesSourceInfo.ForProject(project))
} else {
onOccurrence(ScriptDependenciesInfo.ForProject(project))
}
}
if (!isBinary && virtualFile in scriptConfigurationManager.getAllScriptDependenciesSourcesScope()) {
onOccurrence(ScriptDependenciesSourceInfo.ForProject(project))
}
return onOccurrence(null)
}
private fun getModuleRelatedModuleInfo(project: Project, virtualFile: VirtualFile): ModuleSourceInfo? {
val projectFileIndex = ProjectFileIndex.SERVICE.getInstance(project)
val module = projectFileIndex.getModuleForFile(virtualFile)
if (module != null && !module.isDisposed) {
val moduleFileIndex = ModuleRootManager.getInstance(module).fileIndex
if (moduleFileIndex.isInTestSourceContentKotlinAware(virtualFile)) {
return module.testSourceInfo()
} else if (moduleFileIndex.isInSourceContentWithoutInjected(virtualFile)) {
return module.productionSourceInfo()
}
}
val fileOrigin = OutsidersPsiFileSupportUtils.getOutsiderFileOrigin(project, virtualFile)
if (fileOrigin != null) {
return getModuleRelatedModuleInfo(project, fileOrigin)
}
return null
}
private inline fun <reified T : IdeaModuleInfo> collectModuleInfosByType(project: Project, virtualFile: VirtualFile): Collection<T> {
val result = linkedSetOf<T>()
collectInfosByVirtualFile(project, virtualFile, treatAsLibrarySource = false) {
result.addIfNotNull(it as? T)
}
return result
}
private fun OrderEntry.toIdeaModuleInfo(
project: Project,
virtualFile: VirtualFile,
treatAsLibrarySource: Boolean = false
): List<IdeaModuleInfo> {
if (this is ModuleOrderEntry) return emptyList()
if (!isValid) return emptyList()
when (this) {
is LibraryOrderEntry -> {
val library = library ?: return emptyList()
if (ProjectRootsUtil.isLibraryClassFile(project, virtualFile) && !treatAsLibrarySource) {
return createLibraryInfo(project, library)
} else if (ProjectRootsUtil.isLibraryFile(project, virtualFile) || treatAsLibrarySource) {
return createLibraryInfo(project, library).map { it.sourcesModuleInfo }
}
}
is JdkOrderEntry -> {
return listOf(SdkInfo(project, jdk ?: return emptyList()))
}
else -> return emptyList()
}
return emptyList()
}
/**
* @see [org.jetbrains.kotlin.types.typeUtil.closure].
*/
fun <T> Collection<T>.lazyClosure(f: (T) -> Collection<T>): Sequence<T> = sequence<T> {
if (size == 0) return@sequence
var sizeBeforeIteration = 0
yieldAll(this@lazyClosure)
var yieldedCount = size
var elementsToCheck = this@lazyClosure
while (yieldedCount > sizeBeforeIteration) {
val toAdd = hashSetOf<T>()
elementsToCheck.forEach {
val neighbours = f(it)
yieldAll(neighbours)
yieldedCount += neighbours.size
toAdd.addAll(neighbours)
}
elementsToCheck = toAdd
sizeBeforeIteration = yieldedCount
}
}

View File

@@ -1,117 +0,0 @@
/*
* Copyright 2010-2018 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.caches.project
import com.intellij.openapi.module.Module
import com.intellij.openapi.module.ModuleManager
import com.intellij.openapi.project.Project
import com.intellij.openapi.projectRoots.Sdk
import com.intellij.openapi.roots.JdkOrderEntry
import com.intellij.openapi.roots.LibraryOrderEntry
import com.intellij.openapi.roots.ModuleRootManager
import com.intellij.openapi.roots.libraries.Library
import com.intellij.util.containers.MultiMap
import org.jetbrains.kotlin.caches.project.cacheInvalidatingOnRootModifications
import org.jetbrains.kotlin.idea.util.getProjectJdkTableSafe
import org.jetbrains.kotlin.platform.TargetPlatform
import org.jetbrains.kotlin.platform.isCommon
import org.jetbrains.kotlin.progress.ProgressIndicatorAndCompilationCanceledStatus.checkCanceled
import org.jetbrains.kotlin.types.typeUtil.closure
import java.util.concurrent.ConcurrentHashMap
/** null-platform means that we should get all modules */
fun getModuleInfosFromIdeaModel(project: Project, platform: TargetPlatform? = null): List<IdeaModuleInfo> {
val ideaModelInfosCache = getIdeaModelInfosCache(project)
return if (platform != null)
ideaModelInfosCache.forPlatform(platform)
else
ideaModelInfosCache.allModules()
}
fun getIdeaModelInfosCache(project: Project): IdeaModelInfosCache = project.cacheInvalidatingOnRootModifications {
collectModuleInfosFromIdeaModel(project)
}
class IdeaModelInfosCache(
private val moduleSourceInfosByModules: MultiMap<Module, ModuleSourceInfo>,
private val libraryInfosByLibraries: MultiMap<Library, LibraryInfo>,
private val sdkInfosBySdks: Map<Sdk, SdkInfo>,
) {
private val resultByPlatform = ConcurrentHashMap<TargetPlatform, List<IdeaModuleInfo>>()
private val moduleSourceInfos = moduleSourceInfosByModules.values().toList()
private val libraryInfos = libraryInfosByLibraries.values().toList()
private val sdkInfos = sdkInfosBySdks.values.toList()
fun forPlatform(platform: TargetPlatform): List<IdeaModuleInfo> {
return resultByPlatform.getOrPut(platform) {
mergePlatformModules(moduleSourceInfos, platform) + libraryInfos + sdkInfos
}
}
fun allModules(): List<IdeaModuleInfo> = moduleSourceInfos + libraryInfos + sdkInfos
fun getModuleInfosForModule(module: Module): Collection<ModuleSourceInfo> = moduleSourceInfosByModules[module]
fun getLibraryInfosForLibrary(library: Library): Collection<LibraryInfo> = libraryInfosByLibraries[library]
fun getSdkInfoForSdk(sdk: Sdk): SdkInfo? = sdkInfosBySdks[sdk]
}
private fun collectModuleInfosFromIdeaModel(
project: Project
): IdeaModelInfosCache {
val ideaModules = ModuleManager.getInstance(project).modules.toList()
//TODO: (module refactoring) include libraries that are not among dependencies of any module
val ideaLibraries = ideaModules.flatMap {
ModuleRootManager.getInstance(it).orderEntries.filterIsInstance<LibraryOrderEntry>().map {
it.library
}
}.filterNotNull().toSet()
val sdksFromModulesDependencies = ideaModules.flatMap {
ModuleRootManager.getInstance(it).orderEntries.filterIsInstance<JdkOrderEntry>().map {
it.jdk
}
}
return IdeaModelInfosCache(
moduleSourceInfosByModules = MultiMap.create<Module, ModuleSourceInfo>().also { moduleInfosByModules ->
for (module in ideaModules) {
checkCanceled()
moduleInfosByModules.putValues(module, module.correspondingModuleInfos())
}
},
libraryInfosByLibraries = MultiMap.create<Library, LibraryInfo>().also { libraryInfosByLibraries ->
for (library in ideaLibraries) {
checkCanceled()
libraryInfosByLibraries.putValues(library, createLibraryInfo(project, library))
}
},
sdkInfosBySdks = (sdksFromModulesDependencies + getAllProjectSdks()).filterNotNull().toSet().associateWith { SdkInfo(project, it) }
)
}
private fun mergePlatformModules(
allModules: List<ModuleSourceInfo>,
platform: TargetPlatform
): List<IdeaModuleInfo> {
if (platform.isCommon()) return allModules
val platformModules =
allModules.flatMap { module ->
if (module.platform == platform && module.expectedBy.isNotEmpty())
listOf(module to module.expectedBy)
else emptyList()
}.map { (module, expectedBys) ->
PlatformModuleInfo(module, expectedBys.closure(preserveOrder = true) { it.expectedBy }.toList())
}
val rest = allModules - platformModules.flatMap { it.containedModules }
return rest + platformModules
}
internal fun getAllProjectSdks(): Array<Sdk> = getProjectJdkTableSafe().allJdks

View File

@@ -1,200 +0,0 @@
/*
* Copyright 2000-2019 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.caches.project
import com.intellij.facet.FacetManager
import com.intellij.facet.FacetTypeRegistry
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.externalSystem.ExternalSystemModulePropertyManager
import com.intellij.openapi.externalSystem.service.project.IdeModelsProviderImpl
import com.intellij.openapi.module.Module
import com.intellij.openapi.module.ModuleManager
import com.intellij.openapi.project.Project
import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.analyzer.ModuleInfo
import org.jetbrains.kotlin.caches.project.cacheInvalidatingOnRootModifications
import org.jetbrains.kotlin.caches.resolve.KotlinCacheService
import org.jetbrains.kotlin.config.KotlinFacetSettings
import org.jetbrains.kotlin.config.KotlinMultiplatformVersion
import org.jetbrains.kotlin.config.isHmpp
import org.jetbrains.kotlin.config.isNewMPP
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
import org.jetbrains.kotlin.idea.caches.project.SourceType.PRODUCTION
import org.jetbrains.kotlin.idea.caches.project.SourceType.TEST
import org.jetbrains.kotlin.idea.core.isAndroidModule
import org.jetbrains.kotlin.idea.facet.KotlinFacet
import org.jetbrains.kotlin.idea.facet.KotlinFacetType
import org.jetbrains.kotlin.idea.facet.KotlinFacetType.Companion.ID
import org.jetbrains.kotlin.idea.project.platform
import org.jetbrains.kotlin.idea.util.rootManager
import org.jetbrains.kotlin.platform.TargetPlatform
import org.jetbrains.kotlin.platform.isCommon
val Module.isNewMPPModule: Boolean
get() = facetSettings?.mppVersion.isNewMPP ||
facetSettings?.mppVersion.isHmpp // TODO: review clients, correct them to use precise checks for MPP version
val Module.externalProjectId: String
get() = facetSettings?.externalProjectId ?: ""
val Module.sourceType: SourceType?
get() = facetSettings?.isTestModule?.let { isTest -> if (isTest) TEST else PRODUCTION }
val Module.isMPPModule: Boolean
get() = facetSettings?.isMPPModule ?: false
val Module.isTestModule: Boolean
get() = facetSettings?.isTestModule ?: false
val KotlinFacetSettings.isMPPModule: Boolean
get() = this.mppVersion != null
private val Module.facetSettings get() = KotlinFacet.get(this)?.configuration?.settings
val Module.implementingModules: List<Module>
get() = cacheInvalidatingOnRootModifications {
fun Module.implementingModulesM2(moduleManager: ModuleManager): List<Module> {
return moduleManager.getModuleDependentModules(this).filter {
it.isNewMPPModule && it.externalProjectId == externalProjectId
}
}
val moduleManager = ModuleManager.getInstance(project)
when (facetSettings?.mppVersion) {
null -> emptyList()
KotlinMultiplatformVersion.M3 -> {
val thisModuleStableName = stableModuleName
val result = mutableSetOf<Module>()
moduleManager.modules.filterTo(result) { it.facetSettings?.dependsOnModuleNames?.contains(thisModuleStableName) == true }
// HACK: we do not import proper dependsOn for android source-sets in M3,
// so add all Android modules that M2-implemention would've added,
// to at least not make things worse.
// See KT-33809 for details
implementingModulesM2(moduleManager).forEach { if (it !in result && it.isAndroidModule()) result += it }
result.toList()
}
KotlinMultiplatformVersion.M2 -> implementingModulesM2(moduleManager)
KotlinMultiplatformVersion.M1 -> moduleManager.modules.filter { name in it.findOldFashionedImplementedModuleNames() }
}
}
private val Module.stableModuleName: String
get() = ExternalSystemModulePropertyManager.getInstance(this).getLinkedProjectId()
?: name.also {
if (!ApplicationManager.getApplication().isUnitTestMode) LOG.error("Don't have a LinkedProjectId for module $this for HMPP!")
}
private val Project.modulesByLinkedKey: Map<String, Module>
get() = cacheInvalidatingOnRootModifications {
val moduleManager = ModuleManager.getInstance(this)
moduleManager.modules.associateBy { it.stableModuleName }
}
val Module.implementedModules: List<Module>
get() = cacheInvalidatingOnRootModifications {
fun Module.implementedModulesM2(): List<Module> {
return rootManager.dependencies.filter {
// TODO: remove additional android check
it.isNewMPPModule &&
it.platform.isCommon() &&
it.externalProjectId == externalProjectId &&
(isAndroidModule() || it.isTestModule == isTestModule)
}
}
val facetSettings = facetSettings
when (facetSettings?.mppVersion) {
null -> emptyList()
KotlinMultiplatformVersion.M3 -> {
facetSettings.dependsOnModuleNames
.mapNotNull { project.modulesByLinkedKey[it] }
// HACK: we do not import proper dependsOn for android source-sets in M3, so fallback to M2-impl
// to at least not make things worse.
// See KT-33809 for details
.plus(if (isAndroidModule()) implementedModulesM2() else emptyList())
.distinct()
}
KotlinMultiplatformVersion.M2 -> {
implementedModulesM2()
}
KotlinMultiplatformVersion.M1 -> {
val modelsProvider = IdeModelsProviderImpl(project)
findOldFashionedImplementedModuleNames().mapNotNull { modelsProvider.findIdeModule(it) }
}
}
}
private fun Module.findOldFashionedImplementedModuleNames(): List<String> {
val facet = FacetManager.getInstance(this).findFacet(
KotlinFacetType.TYPE_ID,
FacetTypeRegistry.getInstance().findFacetType(ID)!!.defaultFacetName
)
return facet?.configuration?.settings?.implementedModuleNames ?: emptyList()
}
val ModuleDescriptor.implementingDescriptors: List<ModuleDescriptor>
get() {
val moduleInfo = getCapability(ModuleInfo.Capability)
if (moduleInfo is PlatformModuleInfo) {
return listOf(this)
}
val moduleSourceInfo = moduleInfo as? ModuleSourceInfo ?: return emptyList()
val implementingModuleInfos = moduleSourceInfo.module.implementingModules.mapNotNull { it.toInfo(moduleSourceInfo.sourceType) }
return implementingModuleInfos.mapNotNull { it.toDescriptor() }
}
fun Module.toInfo(type: SourceType): ModuleSourceInfo? = when (type) {
PRODUCTION -> productionSourceInfo()
TEST -> testSourceInfo()
}
/**
* This function returns immediate parents in dependsOn graph
*/
val ModuleDescriptor.implementedDescriptors: List<ModuleDescriptor>
get() {
val moduleInfo = getCapability(ModuleInfo.Capability)
if (moduleInfo is PlatformModuleInfo) return listOf(this)
val moduleSourceInfo = moduleInfo as? ModuleSourceInfo ?: return emptyList()
return moduleSourceInfo.expectedBy.mapNotNull { it.toDescriptor() }
}
fun Module.toDescriptor() = (productionSourceInfo() ?: testSourceInfo())?.toDescriptor()
fun ModuleSourceInfo.toDescriptor() = KotlinCacheService.getInstance(module.project)
.getResolutionFacadeByModuleInfo(this, platform)?.moduleDescriptor
fun PsiElement.getPlatformModuleInfo(desiredPlatform: TargetPlatform): PlatformModuleInfo? {
assert(!desiredPlatform.isCommon()) { "Platform module cannot have Common platform" }
val moduleInfo = getNullableModuleInfo() as? ModuleSourceInfo ?: return null
val platform = moduleInfo.platform
return when {
platform.isCommon() -> {
val correspondingImplementingModule = moduleInfo.module.implementingModules.map { it.toInfo(moduleInfo.sourceType) }
.firstOrNull { it?.platform == desiredPlatform } ?: return null
PlatformModuleInfo(correspondingImplementingModule, correspondingImplementingModule.expectedBy)
}
platform == desiredPlatform -> {
val expectedBy = moduleInfo.expectedBy.takeIf { it.isNotEmpty() } ?: return null
PlatformModuleInfo(moduleInfo, expectedBy)
}
else -> null
}
}

View File

@@ -1,90 +0,0 @@
/*
* Copyright 2010-2018 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.
*/
/*
* Copyright 2010-2018 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.caches.project
import com.intellij.openapi.module.Module
import com.intellij.openapi.module.ModuleUtilCore
import com.intellij.openapi.project.Project
import com.intellij.openapi.util.Key
import com.intellij.openapi.vfs.VirtualFile
import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.idea.LIBRARY_KEY
import org.jetbrains.kotlin.idea.MODULE_ROOT_TYPE_KEY
import org.jetbrains.kotlin.idea.SDK_KEY
import org.jetbrains.kotlin.idea.caches.project.UserDataModuleContainer.ForPsiElement
import org.jetbrains.kotlin.idea.caches.project.UserDataModuleContainer.ForVirtualFile
import org.jetbrains.kotlin.idea.core.getSourceType
import org.jetbrains.kotlin.utils.addIfNotNull
// This file declares non-exported API for overriding module info with user-data
private sealed class UserDataModuleContainer {
abstract fun <T> getUserData(key: Key<T>): T?
abstract fun getModule(): Module?
data class ForVirtualFile(val virtualFile: VirtualFile, val project: Project) : UserDataModuleContainer() {
override fun <T> getUserData(key: Key<T>): T? = virtualFile.getUserData(key)
override fun getModule(): Module? = ModuleUtilCore.findModuleForFile(virtualFile, project)
}
data class ForPsiElement(val psiElement: PsiElement) : UserDataModuleContainer() {
override fun <T> getUserData(key: Key<T>): T? {
return psiElement.getUserData(key)
?: psiElement.containingFile?.getUserData(key)
?: psiElement.containingFile?.originalFile?.virtualFile?.getUserData(key)
}
override fun getModule(): Module? = ModuleUtilCore.findModuleForPsiElement(psiElement)
}
}
private fun collectModuleInfoByUserData(
project: Project,
container: UserDataModuleContainer
): List<IdeaModuleInfo> {
fun forModule(): ModuleSourceInfo? {
val rootType = container.getUserData(MODULE_ROOT_TYPE_KEY) ?: return null
val module = container.getModule() ?: return null
return when (rootType.getSourceType()) {
null -> null
SourceType.PRODUCTION -> module.productionSourceInfo()
SourceType.TEST -> module.testSourceInfo()
}
}
val result = mutableListOf<IdeaModuleInfo>()
result.addIfNotNull(forModule())
val library = container.getUserData(LIBRARY_KEY)
if (library != null) {
result.addAll(createLibraryInfo(project, library))
}
val sdk = container.getUserData(SDK_KEY)
if (sdk != null) {
result.add(SdkInfo(project, sdk))
}
return result
}
fun collectModuleInfoByUserData(
project: Project,
virtualFile: VirtualFile
): List<IdeaModuleInfo> = collectModuleInfoByUserData(project, ForVirtualFile(virtualFile, project))
fun collectModuleInfoByUserData(
psiElement: PsiElement
): List<IdeaModuleInfo> = collectModuleInfoByUserData(psiElement.project, ForPsiElement(psiElement))

View File

@@ -1,213 +0,0 @@
/*
* Copyright 2010-2015 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.idea.caches.resolve
import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.idea.analysis.analyzeInContext
import org.jetbrains.kotlin.idea.caches.resolve.util.analyzeControlFlow
import org.jetbrains.kotlin.idea.project.ResolveElementCache
import org.jetbrains.kotlin.idea.util.getResolutionScope
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.codeFragmentUtil.suppressDiagnosticsInDebugMode
import org.jetbrains.kotlin.psi.psiUtil.getParentOfType
import org.jetbrains.kotlin.psi.psiUtil.getParentOfTypes3
import org.jetbrains.kotlin.resolve.*
import org.jetbrains.kotlin.resolve.bindingContextUtil.getDataFlowInfoAfter
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowInfo
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
import org.jetbrains.kotlin.resolve.lazy.ResolveSession
import org.jetbrains.kotlin.resolve.scopes.*
import org.jetbrains.kotlin.resolve.scopes.utils.addImportingScopes
import javax.inject.Inject
class CodeFragmentAnalyzer(
private val resolveSession: ResolveSession,
private val qualifierResolver: QualifiedExpressionResolver,
private val typeResolver: TypeResolver
) {
@set:Inject // component dependency cycle
lateinit var resolveElementCache: ResolveElementCache
fun analyzeCodeFragment(codeFragment: KtCodeFragment, bodyResolveMode: BodyResolveMode): BindingTrace {
val contextAnalysisResult = analyzeCodeFragmentContext(codeFragment, bodyResolveMode)
return doAnalyzeCodeFragment(codeFragment, contextAnalysisResult)
}
private fun doAnalyzeCodeFragment(codeFragment: KtCodeFragment, contextInfo: ContextInfo): BindingTrace {
val (bindingContext, scope, dataFlowInfo) = contextInfo
val bindingTrace = DelegatingBindingTrace(bindingContext, "For code fragment analysis")
when (val contentElement = codeFragment.getContentElement()) {
is KtExpression -> {
contentElement.analyzeInContext(scope, trace = bindingTrace, dataFlowInfo = dataFlowInfo)
analyzeControlFlow(resolveSession, contentElement, bindingTrace)
}
is KtTypeReference -> {
val context = TypeResolutionContext(
scope, bindingTrace,
true, true, codeFragment.suppressDiagnosticsInDebugMode()
).noBareTypes()
typeResolver.resolvePossiblyBareType(context, contentElement)
}
}
return bindingTrace
}
private data class ContextInfo(val bindingContext: BindingContext, val scope: LexicalScope, val dataFlowInfo: DataFlowInfo)
private fun analyzeCodeFragmentContext(codeFragment: KtCodeFragment, bodyResolveMode: BodyResolveMode): ContextInfo {
fun resolutionFactory(element: KtElement): BindingContext {
return resolveElementCache.resolveToElements(listOf(element), bodyResolveMode)
}
val context = refineContextElement(codeFragment.context)
val info = getContextInfo(context, ::resolutionFactory)
return info.copy(scope = enrichScopeWithImports(info.scope, codeFragment))
}
private tailrec fun getContextInfo(context: PsiElement?, resolutionFactory: (KtElement) -> BindingContext): ContextInfo {
var bindingContext: BindingContext = BindingContext.EMPTY
var dataFlowInfo: DataFlowInfo = DataFlowInfo.EMPTY
var scope: LexicalScope? = null
when (context) {
is KtPrimaryConstructor -> {
val containingClass = context.getContainingClassOrObject()
val resolutionResult = getClassDescriptor(containingClass, resolutionFactory)
if (resolutionResult != null) {
bindingContext = resolutionResult.bindingContext
scope = resolutionResult.descriptor.scopeForInitializerResolution
}
}
is KtSecondaryConstructor -> {
val expression = (context.bodyExpression ?: context.getDelegationCall().calleeExpression) as? KtExpression
if (expression != null) {
bindingContext = resolutionFactory(expression)
scope = bindingContext[BindingContext.LEXICAL_SCOPE, expression]
}
}
is KtClassOrObject -> {
val resolutionResult = getClassDescriptor(context, resolutionFactory)
if (resolutionResult != null) {
bindingContext = resolutionResult.bindingContext
scope = resolutionResult.descriptor.scopeForMemberDeclarationResolution
}
}
is KtFunction -> {
val bindingContextForFunction = resolutionFactory(context)
val functionDescriptor = bindingContextForFunction[BindingContext.FUNCTION, context]
if (functionDescriptor != null) {
bindingContext = bindingContextForFunction
@Suppress("NON_TAIL_RECURSIVE_CALL")
val outerScope = getContextInfo(context.getParentOfType<KtDeclaration>(true), resolutionFactory).scope
val localRedeclarationChecker = LocalRedeclarationChecker.DO_NOTHING
scope = FunctionDescriptorUtil.getFunctionInnerScope(outerScope, functionDescriptor, localRedeclarationChecker)
}
}
is KtFile -> {
bindingContext = resolveSession.bindingContext
scope = resolveSession.fileScopeProvider.getFileResolutionScope(context)
}
is KtElement -> {
bindingContext = resolutionFactory(context)
scope = context.getResolutionScope(bindingContext)
dataFlowInfo = bindingContext.getDataFlowInfoAfter(context)
}
}
if (scope == null) {
val parentDeclaration = context?.getParentOfTypes3<KtDeclaration, KtFile, KtExpression>()
if (parentDeclaration != null) {
return getContextInfo(parentDeclaration, resolutionFactory)
}
}
return ContextInfo(bindingContext, scope ?: createEmptyScope(resolveSession.moduleDescriptor), dataFlowInfo)
}
private data class ClassResolutionResult(val bindingContext: BindingContext, val descriptor: ClassDescriptorWithResolutionScopes)
private fun getClassDescriptor(
classOrObject: KtClassOrObject,
resolutionFactory: (KtElement) -> BindingContext
): ClassResolutionResult? {
val bindingContext: BindingContext
val classDescriptor: ClassDescriptor?
if (!KtPsiUtil.isLocal(classOrObject)) {
bindingContext = resolveSession.bindingContext
classDescriptor = resolveSession.getClassDescriptor(classOrObject, NoLookupLocation.FROM_IDE)
} else {
bindingContext = resolutionFactory(classOrObject)
classDescriptor = bindingContext[BindingContext.DECLARATION_TO_DESCRIPTOR, classOrObject] as ClassDescriptor?
}
return (classDescriptor as? ClassDescriptorWithResolutionScopes)?.let { ClassResolutionResult(bindingContext, it) }
}
private fun refineContextElement(context: PsiElement?): KtElement? {
return when (context) {
is KtParameter -> context.getParentOfType<KtFunction>(true)?.let { it }
is KtProperty -> context.delegateExpressionOrInitializer
is KtConstructor<*> -> context
is KtFunctionLiteral -> context.bodyExpression?.statements?.lastOrNull()
is KtDeclarationWithBody -> context.bodyExpression
is KtBlockExpression -> context.statements.lastOrNull()
else -> null
} ?: context as? KtElement
}
private fun enrichScopeWithImports(scope: LexicalScope, codeFragment: KtCodeFragment): LexicalScope {
val additionalImportingScopes = mutableListOf<ImportingScope>()
val externalDescriptors = codeFragment.externalDescriptors ?: emptyList()
if (externalDescriptors.isNotEmpty()) {
additionalImportingScopes += ExplicitImportsScope(externalDescriptors)
}
val importList = codeFragment.importsAsImportList()
if (importList != null && importList.imports.isNotEmpty()) {
additionalImportingScopes += createImportScopes(importList)
}
if (additionalImportingScopes.isNotEmpty()) {
return scope.addImportingScopes(additionalImportingScopes)
}
return scope
}
private fun createImportScopes(importList: KtImportList): List<ImportingScope> {
return importList.imports.mapNotNull {
qualifierResolver.processImportReference(
it, resolveSession.moduleDescriptor, resolveSession.trace,
excludedImportNames = emptyList(), packageFragmentForVisibilityCheck = null
)
}
}
private fun createEmptyScope(moduleDescriptor: ModuleDescriptor): LexicalScope {
return LexicalScope.Base(ImportingScope.Empty, moduleDescriptor)
}
}

View File

@@ -1,356 +0,0 @@
/*
* Copyright 2000-2018 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.caches.resolve
import com.intellij.openapi.project.Project
import com.intellij.openapi.vfs.VirtualFile
import com.intellij.psi.*
import com.intellij.psi.impl.compiled.ClsClassImpl
import com.intellij.psi.impl.compiled.ClsFileImpl
import com.intellij.psi.search.GlobalSearchScope
import com.intellij.psi.util.PsiTreeUtil
import com.intellij.util.SmartList
import org.jetbrains.kotlin.asJava.KotlinAsJavaSupport
import org.jetbrains.kotlin.asJava.builder.ClsWrapperStubPsiFactory
import org.jetbrains.kotlin.asJava.classes.*
import org.jetbrains.kotlin.config.JvmAnalysisFlags
import org.jetbrains.kotlin.fileClasses.javaFileFacadeFqName
import org.jetbrains.kotlin.idea.caches.lightClasses.ClsJavaStubByVirtualFileCache
import org.jetbrains.kotlin.idea.caches.lightClasses.KtLightClassForDecompiledDeclaration
import org.jetbrains.kotlin.idea.caches.lightClasses.platformMutabilityWrapper
import org.jetbrains.kotlin.idea.caches.project.*
import org.jetbrains.kotlin.idea.caches.project.IdeaModuleInfo
import org.jetbrains.kotlin.idea.decompiler.classFile.KtClsFile
import org.jetbrains.kotlin.idea.decompiler.navigation.SourceNavigationHelper
import org.jetbrains.kotlin.idea.project.getLanguageVersionSettings
import org.jetbrains.kotlin.idea.project.platform
import org.jetbrains.kotlin.idea.stubindex.*
import org.jetbrains.kotlin.idea.util.ProjectRootsUtil
import org.jetbrains.kotlin.idea.util.application.runReadAction
import org.jetbrains.kotlin.idea.util.runReadActionInSmartMode
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.platform.jvm.JvmPlatforms
import org.jetbrains.kotlin.platform.jvm.isJvm
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.resolve.scopes.MemberScope
import org.jetbrains.kotlin.utils.sure
open class IDEKotlinAsJavaSupport(private val project: Project) : KotlinAsJavaSupport() {
private val psiManager: PsiManager = PsiManager.getInstance(project)
private val languageVersionSettings = project.getLanguageVersionSettings()
protected open fun createLightClassForSourceDeclaration(classOrObject: KtClassOrObject): KtLightClass? =
KtLightClassForSourceDeclaration.create(classOrObject, languageVersionSettings.getFlag(JvmAnalysisFlags.jvmDefaultMode))
protected open fun createLightClassForScript(script: KtScript): KtLightClass? =
KtLightClassForScript.create(script)
protected open fun createLightClassForFacade(
manager: PsiManager,
facadeClassFqName: FqName,
searchScope: GlobalSearchScope
): KtLightClass? = KtLightClassForFacadeImpl.createForFacade(psiManager, facadeClassFqName, searchScope)
override fun getFacadeNames(packageFqName: FqName, scope: GlobalSearchScope): Collection<String> {
val facadeFilesInPackage = project.runReadActionInSmartMode {
KotlinFileFacadeClassByPackageIndex.getInstance().get(packageFqName.asString(), project, scope)
}
return facadeFilesInPackage.map { it.javaFileFacadeFqName.shortName().asString() }.toSet()
}
override fun getFacadeClassesInPackage(packageFqName: FqName, scope: GlobalSearchScope): Collection<PsiClass> {
val facadeFilesInPackage = runReadAction {
KotlinFileFacadeClassByPackageIndex.getInstance()
.get(packageFqName.asString(), project, scope).platformSourcesFirst()
}
val groupedByFqNameAndModuleInfo = facadeFilesInPackage.groupBy {
Pair(it.javaFileFacadeFqName, it.getModuleInfoPreferringJvmPlatform())
}
return groupedByFqNameAndModuleInfo.flatMap {
val (key, files) = it
val (fqName, moduleInfo) = key
createLightClassForFileFacade(fqName, files, moduleInfo)
}
}
override fun findClassOrObjectDeclarations(fqName: FqName, searchScope: GlobalSearchScope): Collection<KtClassOrObject> {
return project.runReadActionInSmartMode {
KotlinFullClassNameIndex.getInstance().get(
fqName.asString(),
project,
KotlinSourceFilterScope.sourceAndClassFiles(searchScope, project)
)
}
}
override fun findFilesForPackage(fqName: FqName, searchScope: GlobalSearchScope): Collection<KtFile> {
return project.runReadActionInSmartMode {
PackageIndexUtil.findFilesWithExactPackage(
fqName,
KotlinSourceFilterScope.sourceAndClassFiles(
searchScope,
project
),
project
)
}
}
override fun findClassOrObjectDeclarationsInPackage(
packageFqName: FqName,
searchScope: GlobalSearchScope
): Collection<KtClassOrObject> {
return KotlinTopLevelClassByPackageIndex.getInstance().get(
packageFqName.asString(), project,
KotlinSourceFilterScope.sourceAndClassFiles(searchScope, project)
)
}
override fun packageExists(fqName: FqName, scope: GlobalSearchScope): Boolean {
return PackageIndexUtil.packageExists(
fqName,
KotlinSourceFilterScope.sourceAndClassFiles(
scope,
project
),
project
)
}
override fun getSubPackages(fqn: FqName, scope: GlobalSearchScope): Collection<FqName> {
return PackageIndexUtil.getSubPackageFqNames(
fqn,
KotlinSourceFilterScope.sourceAndClassFiles(
scope,
project
),
project,
MemberScope.ALL_NAME_FILTER
)
}
private val recursiveGuard = ThreadLocal<Boolean>()
private inline fun <T> guardedRun(body: () -> T): T? {
if (recursiveGuard.get() == true) return null
return try {
recursiveGuard.set(true)
body()
} finally {
recursiveGuard.set(false)
}
}
override fun getLightClass(classOrObject: KtClassOrObject): KtLightClass? {
if (!classOrObject.isValid) {
return null
}
val virtualFile = classOrObject.containingFile.virtualFile
if (virtualFile != null) {
when {
ProjectRootsUtil.isProjectSourceFile(project, virtualFile) ->
return createLightClassForSourceDeclaration(classOrObject)
ProjectRootsUtil.isLibraryClassFile(project, virtualFile) ->
return getLightClassForDecompiledClassOrObject(classOrObject)
ProjectRootsUtil.isLibrarySourceFile(project, virtualFile) ->
return guardedRun {
SourceNavigationHelper.getOriginalClass(classOrObject) as? KtLightClass
}
}
}
if ((classOrObject.containingFile as? KtFile)?.analysisContext != null ||
classOrObject.containingFile.originalFile.virtualFile != null
) {
// explicit request to create light class from dummy.kt
return createLightClassForSourceDeclaration(classOrObject)
}
return null
}
override fun getLightClassForScript(script: KtScript): KtLightClass? {
if (!script.isValid) {
return null
}
return createLightClassForScript(script)
}
override fun getFacadeClasses(facadeFqName: FqName, scope: GlobalSearchScope): Collection<PsiClass> {
val filesByModule = findFilesForFacade(facadeFqName, scope).groupBy(PsiElement::getModuleInfoPreferringJvmPlatform)
return filesByModule.flatMap {
createLightClassForFileFacade(facadeFqName, it.value, it.key)
}
}
override fun getScriptClasses(scriptFqName: FqName, scope: GlobalSearchScope): Collection<PsiClass> {
return KotlinScriptFqnIndex.instance.get(scriptFqName.asString(), project, scope).mapNotNull {
getLightClassForScript(it)
}
}
override fun getKotlinInternalClasses(fqName: FqName, scope: GlobalSearchScope): Collection<PsiClass> {
if (fqName.isRoot) return emptyList()
val packageParts = findPackageParts(fqName, scope)
val platformWrapper = findPlatformWrapper(fqName, scope)
return if (platformWrapper != null) packageParts + platformWrapper else packageParts
}
private fun findPackageParts(fqName: FqName, scope: GlobalSearchScope): List<KtLightClassForDecompiledDeclaration> {
val facadeKtFiles = StaticFacadeIndexUtil.getMultifileClassForPart(fqName, scope, project)
val partShortName = fqName.shortName().asString()
val partClassFileShortName = "$partShortName.class"
return facadeKtFiles.mapNotNull { facadeKtFile ->
if (facadeKtFile is KtClsFile) {
val partClassFile = facadeKtFile.virtualFile.parent.findChild(partClassFileShortName) ?: return@mapNotNull null
val javaClsClass = createClsJavaClassFromVirtualFile(facadeKtFile, partClassFile, null) ?: return@mapNotNull null
KtLightClassForDecompiledDeclaration(javaClsClass, javaClsClass.parent, facadeKtFile, null)
} else {
// TODO should we build light classes for parts from source?
null
}
}
}
private fun findPlatformWrapper(fqName: FqName, scope: GlobalSearchScope): PsiClass? {
return platformMutabilityWrapper(fqName) {
JavaPsiFacade.getInstance(
project
).findClass(it, scope)
}
}
fun createLightClassForFileFacade(
facadeFqName: FqName,
facadeFiles: List<KtFile>,
moduleInfo: IdeaModuleInfo
): List<PsiClass> = SmartList<PsiClass>().apply {
tryCreateFacadesForSourceFiles(moduleInfo, facadeFqName)?.let { sourcesFacade ->
add(sourcesFacade)
}
facadeFiles.filterIsInstance<KtClsFile>().mapNotNullTo(this) {
createLightClassForDecompiledKotlinFile(it)
}
}
private fun tryCreateFacadesForSourceFiles(moduleInfo: IdeaModuleInfo, facadeFqName: FqName): PsiClass? {
if (moduleInfo !is ModuleSourceInfo && moduleInfo !is PlatformModuleInfo) return null
return createLightClassForFacade(psiManager, facadeFqName, moduleInfo.contentScope())
}
override fun findFilesForFacade(facadeFqName: FqName, scope: GlobalSearchScope): Collection<KtFile> {
return runReadAction {
KotlinFileFacadeFqNameIndex.INSTANCE.get(facadeFqName.asString(), project, scope).platformSourcesFirst()
}
}
override fun getFakeLightClass(classOrObject: KtClassOrObject): KtFakeLightClass =
KtDescriptorBasedFakeLightClass(classOrObject)
// NOTE: this is a hacky solution to the following problem:
// when building this light class resolver will be built by the first file in the list
// (we could assume that files are in the same module before)
// thus we need to ensure that resolver will be built by the file from platform part of the module
// (resolver built by a file from the common part will have no knowledge of the platform part)
// the actual of order of files that resolver receives is controlled by *findFilesForFacade* method
private fun Collection<KtFile>.platformSourcesFirst() = sortedByDescending { it.platform.isJvm() }
private fun getLightClassForDecompiledClassOrObject(decompiledClassOrObject: KtClassOrObject): KtLightClassForDecompiledDeclaration? {
if (decompiledClassOrObject is KtEnumEntry) {
return null
}
val containingKtFile = decompiledClassOrObject.containingFile as? KtClsFile ?: return null
val rootLightClassForDecompiledFile = createLightClassForDecompiledKotlinFile(containingKtFile) ?: return null
return findCorrespondingLightClass(decompiledClassOrObject, rootLightClassForDecompiledFile)
}
private fun findCorrespondingLightClass(
decompiledClassOrObject: KtClassOrObject,
rootLightClassForDecompiledFile: KtLightClassForDecompiledDeclaration
): KtLightClassForDecompiledDeclaration? {
val relativeFqName = getClassRelativeName(decompiledClassOrObject) ?: return null
val iterator = relativeFqName.pathSegments().iterator()
val base = iterator.next()
// In case class files have been obfuscated (i.e., SomeClass belongs to a.class file), just ignore them
if (rootLightClassForDecompiledFile.name != base.asString()) return null
var current: KtLightClassForDecompiledDeclaration = rootLightClassForDecompiledFile
while (iterator.hasNext()) {
val name = iterator.next()
val innerClass = current.findInnerClassByName(name.asString(), false).sure {
"Could not find corresponding inner/nested class " + relativeFqName + " in class " + decompiledClassOrObject.fqName + "\n" +
"File: " + decompiledClassOrObject.containingKtFile.virtualFile.name
}
current = innerClass as KtLightClassForDecompiledDeclaration
}
return current
}
private fun getClassRelativeName(decompiledClassOrObject: KtClassOrObject): FqName? {
val name = decompiledClassOrObject.nameAsName ?: return null
val parent = PsiTreeUtil.getParentOfType(
decompiledClassOrObject,
KtClassOrObject::class.java,
true
)
if (parent == null) {
assert(decompiledClassOrObject.isTopLevel())
return FqName.topLevel(name)
}
return getClassRelativeName(parent)?.child(name)
}
private fun createLightClassForDecompiledKotlinFile(file: KtClsFile): KtLightClassForDecompiledDeclaration? {
val virtualFile = file.virtualFile ?: return null
val classOrObject = file.declarations.filterIsInstance<KtClassOrObject>().singleOrNull()
val javaClsClass = createClsJavaClassFromVirtualFile(
file, virtualFile,
correspondingClassOrObject = classOrObject
) ?: return null
return KtLightClassForDecompiledDeclaration(javaClsClass, javaClsClass.parent, file, classOrObject)
}
private fun createClsJavaClassFromVirtualFile(
mirrorFile: KtFile,
classFile: VirtualFile,
correspondingClassOrObject: KtClassOrObject?
): ClsClassImpl? {
val javaFileStub = ClsJavaStubByVirtualFileCache.getInstance(project).get(classFile) ?: return null
javaFileStub.psiFactory = ClsWrapperStubPsiFactory.INSTANCE
val manager = PsiManager.getInstance(mirrorFile.project)
val fakeFile = object : ClsFileImpl(ClassFileViewProvider(manager, classFile)) {
override fun getNavigationElement(): PsiElement {
if (correspondingClassOrObject != null) {
return correspondingClassOrObject.navigationElement.containingFile
}
return super.getNavigationElement()
}
override fun getStub() = javaFileStub
override fun getMirror() = mirrorFile
override fun isPhysical() = false
}
javaFileStub.psi = fakeFile
return fakeFile.classes.single() as ClsClassImpl
}
}
internal fun PsiElement.getModuleInfoPreferringJvmPlatform(): IdeaModuleInfo {
return getPlatformModuleInfo(JvmPlatforms.unspecifiedJvmPlatform) ?: getModuleInfo()
}

View File

@@ -1,160 +0,0 @@
/*
* Copyright 2010-2019 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.caches.resolve
import com.intellij.openapi.module.ModuleUtilCore
import com.intellij.openapi.project.Project
import com.intellij.openapi.util.registry.Registry
import com.intellij.psi.search.GlobalSearchScope
import com.intellij.psi.util.CachedValueProvider
import com.intellij.psi.util.CachedValuesManager
import com.intellij.psi.util.PsiModificationTracker
import com.intellij.util.containers.ConcurrentFactoryMap
import org.jetbrains.kotlin.asJava.LightClassBuilder
import org.jetbrains.kotlin.asJava.LightClassGenerationSupport
import org.jetbrains.kotlin.asJava.builder.InvalidLightClassDataHolder
import org.jetbrains.kotlin.asJava.builder.LightClassDataHolder
import org.jetbrains.kotlin.asJava.classes.*
import org.jetbrains.kotlin.asJava.finder.JavaElementFinder
import org.jetbrains.kotlin.codegen.ClassBuilderMode
import org.jetbrains.kotlin.codegen.JvmCodegenUtil
import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper
import org.jetbrains.kotlin.config.JvmTarget
import org.jetbrains.kotlin.config.LanguageFeature
import org.jetbrains.kotlin.config.LanguageVersionSettings
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor
import org.jetbrains.kotlin.idea.FrontendInternals
import org.jetbrains.kotlin.idea.caches.lightClasses.IDELightClassContexts
import org.jetbrains.kotlin.idea.caches.lightClasses.LazyLightClassDataHolder
import org.jetbrains.kotlin.idea.project.languageVersionSettings
import org.jetbrains.kotlin.idea.project.platform
import org.jetbrains.kotlin.idea.resolve.frontendService
import org.jetbrains.kotlin.idea.stubindex.KotlinTypeAliasShortNameIndex
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.platform.jvm.JdkPlatform
import org.jetbrains.kotlin.platform.subplatformsOfType
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.deprecation.DeprecationResolver
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
import org.jetbrains.kotlin.resolve.lazy.NoDescriptorForDeclarationException
import org.jetbrains.kotlin.types.KotlinType
import java.util.concurrent.ConcurrentMap
class IDELightClassGenerationSupport(project: Project) : LightClassGenerationSupport() {
private class KtUltraLightSupportImpl(private val element: KtElement) : KtUltraLightSupport {
private val module = ModuleUtilCore.findModuleForPsiElement(element)
override val languageVersionSettings: LanguageVersionSettings
get() = module?.languageVersionSettings ?: KotlinTypeMapper.LANGUAGE_VERSION_SETTINGS_DEFAULT
override val isReleasedCoroutine
get() = languageVersionSettings.supportsFeature(LanguageFeature.ReleaseCoroutines) ?: true
private val resolutionFacade get() = element.getResolutionFacade()
override val moduleDescriptor get() = resolutionFacade.moduleDescriptor
override val moduleName: String by lazyPub {
JvmCodegenUtil.getModuleName(moduleDescriptor)
}
override fun possiblyHasAlias(file: KtFile, shortName: Name): Boolean =
allAliases(file)[shortName.asString()] == true
private fun allAliases(file: KtFile): ConcurrentMap<String, Boolean> = CachedValuesManager.getCachedValue(file) {
val importAliases = file.importDirectives.mapNotNull { it.aliasName }.toSet()
val map = ConcurrentFactoryMap.createMap<String, Boolean> { s ->
s in importAliases || KotlinTypeAliasShortNameIndex.getInstance().get(s, file.project, file.resolveScope).isNotEmpty()
}
CachedValueProvider.Result.create<ConcurrentMap<String, Boolean>>(map, PsiModificationTracker.MODIFICATION_COUNT)
}
@OptIn(FrontendInternals::class)
override val deprecationResolver: DeprecationResolver
get() = resolutionFacade.getFrontendService(DeprecationResolver::class.java)
override val typeMapper: KotlinTypeMapper by lazyPub {
KotlinTypeMapper(
BindingContext.EMPTY, ClassBuilderMode.LIGHT_CLASSES,
moduleName, languageVersionSettings,
useOldInlineClassesManglingScheme = false,
jvmTarget = module?.platform?.subplatformsOfType<JdkPlatform>()?.firstOrNull()?.targetVersion ?: JvmTarget.DEFAULT,
typePreprocessor = KotlinType::cleanFromAnonymousTypes,
namePreprocessor = ::tryGetPredefinedName
)
}
}
override fun getUltraLightClassSupport(element: KtElement): KtUltraLightSupport = KtUltraLightSupportImpl(element)
override val useUltraLightClasses: Boolean
get() =
!KtUltraLightSupport.forceUsingOldLightClasses && Registry.`is`("kotlin.use.ultra.light.classes", true)
private val scopeFileComparator = JavaElementFinder.byClasspathComparator(GlobalSearchScope.allScope(project))
override fun createDataHolderForClass(classOrObject: KtClassOrObject, builder: LightClassBuilder): LightClassDataHolder.ForClass {
return when {
classOrObject.shouldNotBeVisibleAsLightClass() -> InvalidLightClassDataHolder
classOrObject.isLocal -> LazyLightClassDataHolder.ForClass(
builder,
exactContextProvider = { IDELightClassContexts.contextForLocalClassOrObject(classOrObject) },
dummyContextProvider = null,
diagnosticsHolderProvider = { classOrObject.getDiagnosticsHolder() }
)
else -> LazyLightClassDataHolder.ForClass(
builder,
exactContextProvider = { IDELightClassContexts.contextForNonLocalClassOrObject(classOrObject) },
dummyContextProvider = { IDELightClassContexts.lightContextForClassOrObject(classOrObject) },
diagnosticsHolderProvider = { classOrObject.getDiagnosticsHolder() }
)
}
}
override fun createDataHolderForFacade(files: Collection<KtFile>, builder: LightClassBuilder): LightClassDataHolder.ForFacade {
assert(!files.isEmpty()) { "No files in facade" }
val sortedFiles = files.sortedWith(scopeFileComparator)
return LazyLightClassDataHolder.ForFacade(
builder,
exactContextProvider = { IDELightClassContexts.contextForFacade(sortedFiles) },
dummyContextProvider = { IDELightClassContexts.lightContextForFacade(sortedFiles) },
diagnosticsHolderProvider = { files.first().getDiagnosticsHolder() }
)
}
override fun createDataHolderForScript(script: KtScript, builder: LightClassBuilder): LightClassDataHolder.ForScript {
return LazyLightClassDataHolder.ForScript(
builder,
exactContextProvider = { IDELightClassContexts.contextForScript(script) },
dummyContextProvider = { null },
diagnosticsHolderProvider = { script.getDiagnosticsHolder() }
)
}
@OptIn(FrontendInternals::class)
private fun KtElement.getDiagnosticsHolder() =
getResolutionFacade().frontendService<LazyLightClassDataHolder.DiagnosticsHolder>()
override fun resolveToDescriptor(declaration: KtDeclaration): DeclarationDescriptor? {
return try {
declaration.resolveToDescriptorIfAny(BodyResolveMode.FULL)
} catch (e: NoDescriptorForDeclarationException) {
null
}
}
override fun analyze(element: KtElement) = element.analyze(BodyResolveMode.PARTIAL)
override fun analyzeAnnotation(element: KtAnnotationEntry): AnnotationDescriptor? = element.resolveToDescriptorIfAny()
override fun analyzeWithContent(element: KtClassOrObject) = element.analyzeWithContent()
}

View File

@@ -1,48 +0,0 @@
/*
* Copyright 2010-2015 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.idea.caches.resolve
import com.intellij.psi.search.GlobalSearchScope
import com.intellij.util.indexing.FileBasedIndex
import org.jetbrains.kotlin.idea.vfilefinder.KotlinJvmModuleAnnotationsIndex
import org.jetbrains.kotlin.idea.vfilefinder.KotlinModuleMappingIndex
import org.jetbrains.kotlin.load.kotlin.PackagePartProvider
import org.jetbrains.kotlin.metadata.jvm.deserialization.PackageParts
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.serialization.deserialization.ClassData
import org.jetbrains.kotlin.serialization.deserialization.MetadataPartProvider
class IDEPackagePartProvider(val scope: GlobalSearchScope) : PackagePartProvider, MetadataPartProvider {
override fun findPackageParts(packageFqName: String): List<String> =
getPackageParts(packageFqName).flatMap(PackageParts::parts).distinct()
override fun findMetadataPackageParts(packageFqName: String): List<String> =
getPackageParts(packageFqName).flatMap(PackageParts::metadataParts).distinct()
private fun getPackageParts(packageFqName: String): MutableList<PackageParts> =
FileBasedIndex.getInstance().getValues(KotlinModuleMappingIndex.KEY, packageFqName, scope)
// Note that in case of several modules with the same name, we return all annotations on all of them, which is probably incorrect
override fun getAnnotationsOnBinaryModule(moduleName: String): List<ClassId> =
FileBasedIndex.getInstance().getValues(KotlinJvmModuleAnnotationsIndex.KEY, moduleName, scope).flatten()
// Optional annotations are not needed in IDE because they can only be used in common module sources, and they are loaded via the
// standard common module resolution there. (In the CLI compiler the situation is different because we compile common+platform
// sources together, _without_ common dependencies.)
override fun getAllOptionalAnnotationClasses(): List<ClassData> =
emptyList()
}

View File

@@ -1,213 +0,0 @@
/*
* Copyright 2010-2019 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.caches.resolve
import com.intellij.openapi.components.ServiceManager
import com.intellij.openapi.util.ModificationTracker
import org.jetbrains.kotlin.analyzer.*
import org.jetbrains.kotlin.analyzer.common.CommonAnalysisParameters
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.caches.resolve.*
import org.jetbrains.kotlin.context.ProjectContext
import org.jetbrains.kotlin.context.withModule
import org.jetbrains.kotlin.descriptors.ModuleCapability
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
import org.jetbrains.kotlin.descriptors.impl.ModuleDescriptorImpl
import org.jetbrains.kotlin.idea.caches.project.*
import org.jetbrains.kotlin.idea.caches.project.IdeaModuleInfo
import org.jetbrains.kotlin.idea.caches.project.getNullableModuleInfo
import org.jetbrains.kotlin.idea.compiler.IDELanguageSettingsProvider
import org.jetbrains.kotlin.idea.configuration.IdeBuiltInsLoadingState
import org.jetbrains.kotlin.idea.project.IdeaEnvironment
import org.jetbrains.kotlin.idea.compiler.IdeSealedClassInheritorsProvider
import org.jetbrains.kotlin.idea.project.findAnalyzerServices
import org.jetbrains.kotlin.idea.project.useCompositeAnalysis
import org.jetbrains.kotlin.load.java.structure.JavaClass
import org.jetbrains.kotlin.load.java.structure.impl.JavaClassImpl
import org.jetbrains.kotlin.platform.idePlatformKind
import org.jetbrains.kotlin.platform.isCommon
import org.jetbrains.kotlin.platform.jvm.JvmPlatforms
import org.jetbrains.kotlin.platform.jvm.isJvm
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.resolve.RESOLUTION_ANCHOR_PROVIDER_CAPABILITY
import org.jetbrains.kotlin.resolve.ResolutionAnchorProvider
import org.jetbrains.kotlin.resolve.jvm.JvmPlatformParameters
class IdeaResolverForProject(
debugName: String,
projectContext: ProjectContext,
modules: Collection<IdeaModuleInfo>,
private val syntheticFilesByModule: Map<IdeaModuleInfo, Collection<KtFile>>,
delegateResolver: ResolverForProject<IdeaModuleInfo>,
fallbackModificationTracker: ModificationTracker? = null,
private val settings: PlatformAnalysisSettings
) : AbstractResolverForProject<IdeaModuleInfo>(
debugName,
projectContext,
modules,
fallbackModificationTracker,
delegateResolver,
ServiceManager.getService(projectContext.project, IdePackageOracleFactory::class.java)
) {
companion object {
val PLATFORM_ANALYSIS_SETTINGS = ModuleCapability<PlatformAnalysisSettings>("PlatformAnalysisSettings")
}
private val resolutionAnchorProvider = ServiceManager.getService(projectContext.project, ResolutionAnchorProvider::class.java)
private val constantSdkDependencyIfAny: SdkInfo? =
if (settings is PlatformAnalysisSettingsImpl) settings.sdk?.let { SdkInfo(projectContext.project, it) } else null
private val builtInsCache: BuiltInsCache =
(delegateResolver as? IdeaResolverForProject)?.builtInsCache ?: BuiltInsCache(projectContext, this)
override fun getAdditionalCapabilities(): Map<ModuleCapability<*>, Any?> {
return super.getAdditionalCapabilities() +
(PLATFORM_ANALYSIS_SETTINGS to settings) +
(RESOLUTION_ANCHOR_PROVIDER_CAPABILITY to resolutionAnchorProvider)
}
override fun sdkDependency(module: IdeaModuleInfo): SdkInfo? {
if (projectContext.project.useCompositeAnalysis) {
require(constantSdkDependencyIfAny == null) { "Shouldn't pass SDK dependency manually for composite analysis mode" }
}
return constantSdkDependencyIfAny ?: module.findSdkAcrossDependencies()
}
override fun modulesContent(module: IdeaModuleInfo): ModuleContent<IdeaModuleInfo> =
ModuleContent(module, syntheticFilesByModule[module] ?: emptyList(), module.contentScope())
override fun builtInsForModule(module: IdeaModuleInfo): KotlinBuiltIns = builtInsCache.getOrCreateIfNeeded(module)
override fun createResolverForModule(descriptor: ModuleDescriptor, moduleInfo: IdeaModuleInfo): ResolverForModule {
val moduleContent = ModuleContent(moduleInfo, syntheticFilesByModule[moduleInfo] ?: listOf(), moduleInfo.contentScope())
val languageVersionSettings =
IDELanguageSettingsProvider.getLanguageVersionSettings(moduleInfo, projectContext.project)
val resolverForModuleFactory = getResolverForModuleFactory(moduleInfo)
return resolverForModuleFactory.createResolverForModule(
descriptor as ModuleDescriptorImpl,
projectContext.withModule(descriptor),
moduleContent,
this,
languageVersionSettings,
IdeSealedClassInheritorsProvider,
)
}
private fun getResolverForModuleFactory(moduleInfo: IdeaModuleInfo): ResolverForModuleFactory {
val platform = moduleInfo.platform
val jvmPlatformParameters = JvmPlatformParameters(
packagePartProviderFactory = { IDEPackagePartProvider(it.moduleContentScope) },
moduleByJavaClass = { javaClass: JavaClass ->
val psiClass = (javaClass as JavaClassImpl).psi
psiClass.getPlatformModuleInfo(JvmPlatforms.unspecifiedJvmPlatform)?.platformModule ?: psiClass.getNullableModuleInfo()
},
resolverForReferencedModule = { targetModuleInfo, referencingModuleInfo ->
require(targetModuleInfo is IdeaModuleInfo && referencingModuleInfo is IdeaModuleInfo) {
"Unexpected modules passed through JvmPlatformParameters to IDE resolver ($targetModuleInfo, $referencingModuleInfo)"
}
tryGetResolverForModuleWithResolutionAnchorFallback(targetModuleInfo, referencingModuleInfo)
},
useBuiltinsProviderForModule = {
IdeBuiltInsLoadingState.isFromDependenciesForJvm && it is LibraryInfo && it.isKotlinStdlib(projectContext.project)
}
)
val commonPlatformParameters = CommonAnalysisParameters(
metadataPartProviderFactory = { IDEPackagePartProvider(it.moduleContentScope) }
)
return if (!projectContext.project.useCompositeAnalysis) {
val parameters = when {
platform.isJvm() -> jvmPlatformParameters
platform.isCommon() -> commonPlatformParameters
else -> PlatformAnalysisParameters.Empty
}
platform.idePlatformKind.resolution.createResolverForModuleFactory(parameters, IdeaEnvironment, platform)
} else {
CompositeResolverForModuleFactory(
commonPlatformParameters,
jvmPlatformParameters,
platform,
CompositeAnalyzerServices(platform.componentPlatforms.map { it.findAnalyzerServices() })
)
}
}
// Important: ProjectContext must be from SDK to be sure that we won't run into deadlocks
class BuiltInsCache(private val projectContextFromSdkResolver: ProjectContext, private val resolverForSdk: IdeaResolverForProject) {
private val cache = mutableMapOf<BuiltInsCacheKey, KotlinBuiltIns>()
fun getOrCreateIfNeeded(module: IdeaModuleInfo): KotlinBuiltIns = projectContextFromSdkResolver.storageManager.compute {
val sdk = resolverForSdk.sdkDependency(module)
val stdlib = findStdlibForModulesBuiltins(module)
val key = module.platform.idePlatformKind.resolution.getKeyForBuiltIns(module, sdk, stdlib)
val cachedBuiltIns = cache[key]
if (cachedBuiltIns != null) return@compute cachedBuiltIns
module.platform.idePlatformKind.resolution
.createBuiltIns(module, projectContextFromSdkResolver, resolverForSdk, sdk, stdlib)
.also {
// TODO: MemoizedFunction should be used here instead, but for proper we also need a module (for LV settings) that is not contained in the key
cache[key] = it
}
}
private fun findStdlibForModulesBuiltins(module: IdeaModuleInfo): LibraryInfo? {
return when (IdeBuiltInsLoadingState.state) {
IdeBuiltInsLoadingState.IdeBuiltInsLoading.FROM_CLASSLOADER -> null
IdeBuiltInsLoadingState.IdeBuiltInsLoading.FROM_DEPENDENCIES_JVM -> {
if (module.platform.isJvm()) {
module.findJvmStdlibAcrossDependencies()
} else {
null
}
}
}
}
}
private fun tryGetResolverForModuleWithResolutionAnchorFallback(
targetModuleInfo: IdeaModuleInfo,
referencingModuleInfo: IdeaModuleInfo,
): ResolverForModule? {
tryGetResolverForModule(targetModuleInfo)?.let { return it }
return getResolverForProjectUsingResolutionAnchor(targetModuleInfo, referencingModuleInfo)
}
private fun getResolverForProjectUsingResolutionAnchor(
targetModuleInfo: IdeaModuleInfo,
referencingModuleInfo: IdeaModuleInfo
): ResolverForModule? {
val moduleDescriptorOfReferencingModule = descriptorByModule[referencingModuleInfo]?.moduleDescriptor
?: error("$referencingModuleInfo is not contained in this resolver, which means incorrect use of anchor-aware search")
val anchorModuleInfo = resolutionAnchorProvider.getResolutionAnchor(moduleDescriptorOfReferencingModule)?.moduleInfo ?: return null
val resolverForProjectFromAnchorModule = KotlinCacheService.getInstance(projectContext.project)
.getResolutionFacadeByModuleInfo(anchorModuleInfo, anchorModuleInfo.platform)
?.getResolverForProject()
?: return null
require(resolverForProjectFromAnchorModule is IdeaResolverForProject) {
"Resolution via anchor modules is expected to be used only from IDE resolvers"
}
return resolverForProjectFromAnchorModule.tryGetResolverForModule(targetModuleInfo)
}
}
interface BuiltInsCacheKey {
object DefaultBuiltInsKey : BuiltInsCacheKey
}

View File

@@ -1,512 +0,0 @@
/*
* Copyright 2010-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.idea.caches.resolve
import com.intellij.openapi.diagnostic.ControlFlowException
import com.intellij.openapi.diagnostic.Logger
import com.intellij.openapi.project.Project
import com.intellij.openapi.projectRoots.Sdk
import com.intellij.openapi.roots.ProjectRootModificationTracker
import com.intellij.openapi.util.ModificationTracker
import com.intellij.psi.PsiCodeFragment
import com.intellij.psi.PsiFile
import com.intellij.psi.util.CachedValue
import com.intellij.psi.util.CachedValueProvider
import com.intellij.psi.util.CachedValuesManager
import com.intellij.psi.util.PsiModificationTracker
import com.intellij.util.containers.SLRUCache
import org.jetbrains.kotlin.analyzer.ModuleInfo
import org.jetbrains.kotlin.analyzer.ResolverForProject.Companion.resolverForLibrariesName
import org.jetbrains.kotlin.analyzer.ResolverForProject.Companion.resolverForModulesName
import org.jetbrains.kotlin.analyzer.ResolverForProject.Companion.resolverForScriptDependenciesName
import org.jetbrains.kotlin.analyzer.ResolverForProject.Companion.resolverForSdkName
import org.jetbrains.kotlin.analyzer.ResolverForProject.Companion.resolverForSpecialInfoName
import org.jetbrains.kotlin.builtins.StandardNames
import org.jetbrains.kotlin.caches.resolve.KotlinCacheService
import org.jetbrains.kotlin.caches.resolve.PlatformAnalysisSettings
import org.jetbrains.kotlin.config.LanguageFeature
import org.jetbrains.kotlin.context.GlobalContext
import org.jetbrains.kotlin.context.GlobalContextImpl
import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor
import org.jetbrains.kotlin.idea.caches.project.*
import org.jetbrains.kotlin.idea.caches.project.IdeaModuleInfo
import org.jetbrains.kotlin.idea.caches.resolve.util.GlobalFacadeModuleFilters
import org.jetbrains.kotlin.idea.caches.resolve.util.contextWithCompositeExceptionTracker
import org.jetbrains.kotlin.idea.caches.trackers.outOfBlockModificationCount
import org.jetbrains.kotlin.idea.compiler.IDELanguageSettingsProvider
import org.jetbrains.kotlin.idea.core.script.ScriptDependenciesModificationTracker
import org.jetbrains.kotlin.idea.core.script.dependencies.ScriptAdditionalIdeaDependenciesProvider
import org.jetbrains.kotlin.idea.project.TargetPlatformDetector
import org.jetbrains.kotlin.idea.project.useCompositeAnalysis
import org.jetbrains.kotlin.idea.resolve.ResolutionFacade
import org.jetbrains.kotlin.idea.util.ProjectRootsUtil
import org.jetbrains.kotlin.platform.TargetPlatform
import org.jetbrains.kotlin.platform.jvm.JvmPlatforms
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.contains
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.diagnostics.KotlinSuppressCache
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
import org.jetbrains.kotlin.utils.KotlinExceptionWithAttachments
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull
import org.jetbrains.kotlin.utils.addToStdlib.sumByLong
internal val LOG = Logger.getInstance(KotlinCacheService::class.java)
data class PlatformAnalysisSettingsImpl(
val platform: TargetPlatform,
val sdk: Sdk?,
val isAdditionalBuiltInFeaturesSupported: Boolean,
) : PlatformAnalysisSettings
object CompositeAnalysisSettings : PlatformAnalysisSettings
fun createPlatformAnalysisSettings(
project: Project,
platform: TargetPlatform,
sdk: Sdk?,
isAdditionalBuiltInFeaturesSupported: Boolean
) = if (project.useCompositeAnalysis)
CompositeAnalysisSettings
else
PlatformAnalysisSettingsImpl(platform, sdk, isAdditionalBuiltInFeaturesSupported)
class KotlinCacheServiceImpl(val project: Project) : KotlinCacheService {
override fun getResolutionFacade(elements: List<KtElement>): ResolutionFacade {
val files = getFilesForElements(elements)
val platform = TargetPlatformDetector.getPlatform(files.first())
return getFacadeToAnalyzeFiles(files, platform)
}
override fun getResolutionFacade(elements: List<KtElement>, platform: TargetPlatform): ResolutionFacade {
val files = getFilesForElements(elements)
return getFacadeToAnalyzeFiles(files, platform)
}
private fun getFilesForElements(elements: List<KtElement>): List<KtFile> {
return elements.map {
try {
// in theory `containingKtFile` is `@NotNull` but in practice EA-114080
@Suppress("USELESS_ELVIS")
it.containingKtFile ?: throw IllegalStateException("containingKtFile was null for $it of ${it.javaClass}")
} catch (e: Exception) {
if (e is ControlFlowException) throw e
throw KotlinExceptionWithAttachments("Couldn't get containingKtFile for ktElement", e)
.withAttachment("element.kt", it.text)
}
}
}
override fun getSuppressionCache(): KotlinSuppressCache = kotlinSuppressCache.value
private val globalFacadesPerPlatformAndSdk: SLRUCache<PlatformAnalysisSettings, GlobalFacade> =
object : SLRUCache<PlatformAnalysisSettings, GlobalFacade>(2 * 3 * 2, 2 * 3 * 2) {
override fun createValue(settings: PlatformAnalysisSettings): GlobalFacade {
return GlobalFacade(settings)
}
}
private val facadeForScriptDependenciesForProject = createFacadeForScriptDependencies(ScriptDependenciesInfo.ForProject(project))
private fun createFacadeForScriptDependencies(
dependenciesModuleInfo: ScriptDependenciesInfo
): ProjectResolutionFacade {
val sdk = dependenciesModuleInfo.sdk
val platform = JvmPlatforms.defaultJvmPlatform // TODO: Js scripts?
val settings = createPlatformAnalysisSettings(project, platform, sdk, true)
val dependenciesForScriptDependencies = listOf(
LibraryModificationTracker.getInstance(project),
ProjectRootModificationTracker.getInstance(project),
ScriptDependenciesModificationTracker.getInstance(project)
)
val scriptFile = (dependenciesModuleInfo as? ScriptDependenciesInfo.ForFile)?.scriptFile
val relatedModules = scriptFile?.let { ScriptAdditionalIdeaDependenciesProvider.getRelatedModules(it, project) }
val globalFacade =
if (relatedModules?.isNotEmpty() == true) {
facadeForModules(settings)
} else {
getOrBuildGlobalFacade(settings).facadeForSdk
}
val globalContext = globalFacade.globalContext.contextWithCompositeExceptionTracker(project, "facadeForScriptDependencies")
return ProjectResolutionFacade(
"facadeForScriptDependencies",
resolverForScriptDependenciesName,
project, globalContext, settings,
reuseDataFrom = globalFacade,
allModules = dependenciesModuleInfo.dependencies(),
//TODO: provide correct trackers
dependencies = dependenciesForScriptDependencies,
moduleFilter = { it == dependenciesModuleInfo },
invalidateOnOOCB = true
)
}
private inner class GlobalFacade(settings: PlatformAnalysisSettings) {
private val sdkContext = GlobalContext(resolverForSdkName)
private val moduleFilters = GlobalFacadeModuleFilters(project)
val facadeForSdk = ProjectResolutionFacade(
"facadeForSdk", "$resolverForSdkName with settings=$settings",
project, sdkContext, settings,
moduleFilter = moduleFilters::sdkFacadeFilter,
dependencies = listOf(
LibraryModificationTracker.getInstance(project),
ProjectRootModificationTracker.getInstance(project)
),
invalidateOnOOCB = false,
reuseDataFrom = null
)
private val librariesContext = sdkContext.contextWithCompositeExceptionTracker(project, resolverForLibrariesName)
val facadeForLibraries = ProjectResolutionFacade(
"facadeForLibraries", "$resolverForLibrariesName with settings=$settings",
project, librariesContext, settings,
reuseDataFrom = facadeForSdk,
moduleFilter = moduleFilters::libraryFacadeFilter,
invalidateOnOOCB = false,
dependencies = listOf(
LibraryModificationTracker.getInstance(project),
ProjectRootModificationTracker.getInstance(project)
)
)
private val modulesContext = librariesContext.contextWithCompositeExceptionTracker(project, resolverForModulesName)
val facadeForModules = ProjectResolutionFacade(
"facadeForModules", "$resolverForModulesName with settings=$settings",
project, modulesContext, settings,
reuseDataFrom = facadeForLibraries,
moduleFilter = moduleFilters::moduleFacadeFilter,
dependencies = listOf(
LibraryModificationTracker.getInstance(project),
ProjectRootModificationTracker.getInstance(project)
),
invalidateOnOOCB = true
)
}
private fun IdeaModuleInfo.platformSettings(targetPlatform: TargetPlatform) = createPlatformAnalysisSettings(
this@KotlinCacheServiceImpl.project, targetPlatform, sdk,
supportsAdditionalBuiltInsMembers(this@KotlinCacheServiceImpl.project)
)
private fun facadeForModules(settings: PlatformAnalysisSettings) =
getOrBuildGlobalFacade(settings).facadeForModules
private fun librariesFacade(settings: PlatformAnalysisSettings) =
getOrBuildGlobalFacade(settings).facadeForLibraries
@Synchronized
private fun getOrBuildGlobalFacade(settings: PlatformAnalysisSettings) =
globalFacadesPerPlatformAndSdk[settings]
private fun createFacadeForFilesWithSpecialModuleInfo(files: Set<KtFile>): ProjectResolutionFacade {
// we assume that all files come from the same module
val targetPlatform = files.map { TargetPlatformDetector.getPlatform(it) }.toSet().single()
val specialModuleInfo = files.map(KtFile::getModuleInfo).toSet().single()
val settings = specialModuleInfo.platformSettings(specialModuleInfo.platform ?: targetPlatform)
// Dummy files created e.g. by J2K do not receive events.
val dependencyTrackerForSyntheticFileCache = if (files.all { it.originalFile != it }) {
ModificationTracker { files.sumByLong { it.outOfBlockModificationCount } }
} else ModificationTracker { files.sumByLong { it.modificationStamp } }
val resolverDebugName =
"$resolverForSpecialInfoName $specialModuleInfo for files ${files.joinToString { it.name }} for platform $targetPlatform"
fun makeProjectResolutionFacade(
debugName: String,
globalContext: GlobalContextImpl,
reuseDataFrom: ProjectResolutionFacade? = null,
moduleFilter: (IdeaModuleInfo) -> Boolean = { true },
allModules: Collection<IdeaModuleInfo>? = null
): ProjectResolutionFacade {
return ProjectResolutionFacade(
debugName,
resolverDebugName,
project,
globalContext,
settings,
syntheticFiles = files,
reuseDataFrom = reuseDataFrom,
moduleFilter = moduleFilter,
dependencies = listOf(dependencyTrackerForSyntheticFileCache),
invalidateOnOOCB = true,
allModules = allModules
)
}
return when {
specialModuleInfo is ModuleSourceInfo -> {
val dependentModules = specialModuleInfo.getDependentModules()
val modulesFacade = facadeForModules(settings)
val globalContext =
modulesFacade.globalContext.contextWithCompositeExceptionTracker(
project,
"facadeForSpecialModuleInfo (ModuleSourceInfo)"
)
makeProjectResolutionFacade(
"facadeForSpecialModuleInfo (ModuleSourceInfo)",
globalContext,
reuseDataFrom = modulesFacade,
moduleFilter = { it in dependentModules }
)
}
specialModuleInfo is ScriptModuleInfo -> {
val facadeForScriptDependencies = createFacadeForScriptDependencies(
ScriptDependenciesInfo.ForFile(project, specialModuleInfo.scriptFile, specialModuleInfo.scriptDefinition)
)
val globalContext = facadeForScriptDependencies.globalContext.contextWithCompositeExceptionTracker(
project,
"facadeForSpecialModuleInfo (ScriptModuleInfo)"
)
makeProjectResolutionFacade(
"facadeForSpecialModuleInfo (ScriptModuleInfo)",
globalContext,
reuseDataFrom = facadeForScriptDependencies,
allModules = specialModuleInfo.dependencies(),
moduleFilter = { it == specialModuleInfo }
)
}
specialModuleInfo is ScriptDependenciesInfo -> facadeForScriptDependenciesForProject
specialModuleInfo is ScriptDependenciesSourceInfo -> {
val globalContext =
facadeForScriptDependenciesForProject.globalContext.contextWithCompositeExceptionTracker(
project,
"facadeForSpecialModuleInfo (ScriptDependenciesSourceInfo)"
)
makeProjectResolutionFacade(
"facadeForSpecialModuleInfo (ScriptDependenciesSourceInfo)",
globalContext,
reuseDataFrom = facadeForScriptDependenciesForProject,
allModules = specialModuleInfo.dependencies(),
moduleFilter = { it == specialModuleInfo }
)
}
specialModuleInfo is LibrarySourceInfo || specialModuleInfo === NotUnderContentRootModuleInfo -> {
val librariesFacade = librariesFacade(settings)
val debugName = "facadeForSpecialModuleInfo (LibrarySourceInfo or NotUnderContentRootModuleInfo)"
val globalContext = librariesFacade.globalContext.contextWithCompositeExceptionTracker(project, debugName)
makeProjectResolutionFacade(
debugName,
globalContext,
reuseDataFrom = librariesFacade,
moduleFilter = { it == specialModuleInfo }
)
}
specialModuleInfo.isLibraryClasses() -> {
//NOTE: this code should not be called for sdk or library classes
// currently the only known scenario is when we cannot determine that file is a library source
// (file under both classes and sources root)
LOG.warn("Creating cache with synthetic files ($files) in classes of library $specialModuleInfo")
val globalContext = GlobalContext("facadeForSpecialModuleInfo for file under both classes and root")
makeProjectResolutionFacade(
"facadeForSpecialModuleInfo for file under both classes and root",
globalContext
)
}
else -> throw IllegalStateException("Unknown IdeaModuleInfo ${specialModuleInfo::class.java}")
}
}
private val kotlinSuppressCache: CachedValue<KotlinSuppressCache> = CachedValuesManager.getManager(project).createCachedValue(
{
CachedValueProvider.Result<KotlinSuppressCache>(
object : KotlinSuppressCache() {
override fun getSuppressionAnnotations(annotated: KtAnnotated): List<AnnotationDescriptor> {
if (annotated.annotationEntries.none {
it.calleeExpression?.text?.endsWith(SUPPRESS_ANNOTATION_SHORT_NAME) == true
}
) {
// Avoid running resolve heuristics
// TODO: Check aliases in imports
return emptyList()
}
val context =
when (annotated) {
is KtFile -> {
annotated.fileAnnotationList?.analyze(BodyResolveMode.PARTIAL)
?: return emptyList()
}
is KtModifierListOwner -> {
annotated.modifierList?.analyze(BodyResolveMode.PARTIAL)
?: return emptyList()
}
else ->
annotated.analyze(BodyResolveMode.PARTIAL)
}
val annotatedDescriptor = context.get(BindingContext.DECLARATION_TO_DESCRIPTOR, annotated)
if (annotatedDescriptor != null) {
return annotatedDescriptor.annotations.toList()
}
return annotated.annotationEntries.mapNotNull {
context.get(
BindingContext.ANNOTATION,
it
)
}
}
},
LibraryModificationTracker.getInstance(project),
PsiModificationTracker.MODIFICATION_COUNT
)
},
false
)
private val specialFilesCacheProvider = CachedValueProvider {
// NOTE: computations inside createFacadeForFilesWithSpecialModuleInfo depend on project root structure
// so we additionally drop the whole slru cache on change
CachedValueProvider.Result(
object : SLRUCache<Set<KtFile>, ProjectResolutionFacade>(2, 3) {
override fun createValue(files: Set<KtFile>) = createFacadeForFilesWithSpecialModuleInfo(files)
},
LibraryModificationTracker.getInstance(project),
ProjectRootModificationTracker.getInstance(project)
)
}
private fun getFacadeForSpecialFiles(files: Set<KtFile>): ProjectResolutionFacade {
val cachedValue: SLRUCache<Set<KtFile>, ProjectResolutionFacade> =
CachedValuesManager.getManager(project).getCachedValue(project, specialFilesCacheProvider)
// In Upsource, we create multiple instances of KotlinCacheService, which all access the same CachedValue instance (UP-8046)
// This is so because class name of provider is used as a key when fetching cached value, see CachedValueManager.getKeyForClass.
// To avoid race conditions, we can't use any local lock to access the cached value contents.
return synchronized(cachedValue) {
cachedValue.get(files)
}
}
private val scriptsCacheProvider = CachedValueProvider {
CachedValueProvider.Result(
object : SLRUCache<Set<KtFile>, ProjectResolutionFacade>(10, 5) {
override fun createValue(files: Set<KtFile>) = createFacadeForFilesWithSpecialModuleInfo(files)
},
LibraryModificationTracker.getInstance(project),
ProjectRootModificationTracker.getInstance(project),
ScriptDependenciesModificationTracker.getInstance(project)
)
}
private fun getFacadeForScripts(files: Set<KtFile>): ProjectResolutionFacade {
val cachedValue: SLRUCache<Set<KtFile>, ProjectResolutionFacade> =
CachedValuesManager.getManager(project).getCachedValue(project, scriptsCacheProvider)
return synchronized(cachedValue) {
cachedValue.get(files)
}
}
private fun getFacadeToAnalyzeFiles(files: Collection<KtFile>, platform: TargetPlatform): ResolutionFacade {
val file = files.first()
val moduleInfo = file.getModuleInfo()
val specialFiles = files.filterNotInProjectSource(moduleInfo)
val scripts = specialFiles.filterScripts()
if (scripts.isNotEmpty()) {
val projectFacade = getFacadeForScripts(scripts)
return ModuleResolutionFacadeImpl(projectFacade, moduleInfo).createdFor(scripts, moduleInfo)
}
if (specialFiles.isNotEmpty()) {
val projectFacade = getFacadeForSpecialFiles(specialFiles)
return ModuleResolutionFacadeImpl(projectFacade, moduleInfo).createdFor(specialFiles, moduleInfo)
}
return getResolutionFacadeByModuleInfo(moduleInfo, platform).createdFor(emptyList(), moduleInfo, platform)
}
override fun getResolutionFacadeByFile(file: PsiFile, platform: TargetPlatform): ResolutionFacade? {
if (!ProjectRootsUtil.isInProjectOrLibraryContent(file)) {
return null
}
assert(file !is PsiCodeFragment)
val moduleInfo = file.getModuleInfo()
return getResolutionFacadeByModuleInfo(moduleInfo, platform)
}
private fun getResolutionFacadeByModuleInfo(moduleInfo: IdeaModuleInfo, platform: TargetPlatform): ResolutionFacade {
val settings = moduleInfo.platformSettings(platform)
return getResolutionFacadeByModuleInfoAndSettings(moduleInfo, settings)
}
private fun getResolutionFacadeByModuleInfoAndSettings(
moduleInfo: IdeaModuleInfo,
settings: PlatformAnalysisSettings
): ResolutionFacade {
val projectFacade = when (moduleInfo) {
is ScriptDependenciesInfo.ForProject,
is ScriptDependenciesSourceInfo.ForProject -> facadeForScriptDependenciesForProject
is ScriptDependenciesInfo.ForFile -> createFacadeForScriptDependencies(moduleInfo)
else -> facadeForModules(settings)
}
return ModuleResolutionFacadeImpl(projectFacade, moduleInfo)
}
override fun getResolutionFacadeByModuleInfo(moduleInfo: ModuleInfo, platform: TargetPlatform): ResolutionFacade? =
(moduleInfo as? IdeaModuleInfo)?.let { getResolutionFacadeByModuleInfo(it, platform) }
override fun getResolutionFacadeByModuleInfo(moduleInfo: ModuleInfo, settings: PlatformAnalysisSettings): ResolutionFacade? {
val ideaModuleInfo = moduleInfo as? IdeaModuleInfo ?: return null
return getResolutionFacadeByModuleInfoAndSettings(ideaModuleInfo, settings)
}
private fun Collection<KtFile>.filterNotInProjectSource(moduleInfo: IdeaModuleInfo): Set<KtFile> {
return mapNotNull {
if (it is KtCodeFragment) it.getContextFile() else it
}.filter {
!ProjectRootsUtil.isInProjectSource(it) || !moduleInfo.contentScope().contains(it)
}.toSet()
}
private fun Collection<KtFile>.filterScripts(): Set<KtFile> {
return mapNotNull {
if (it is KtCodeFragment) it.getContextFile() else it
}.filter { it.isScript() }.toSet()
}
private fun KtCodeFragment.getContextFile(): KtFile? {
val contextElement = context ?: return null
val contextFile = (contextElement as? KtElement)?.containingKtFile
?: throw AssertionError("Analyzing kotlin code fragment of type ${this::class.java} with java context of type ${contextElement::class.java}")
return if (contextFile is KtCodeFragment) contextFile.getContextFile() else contextFile
}
private companion object {
private val SUPPRESS_ANNOTATION_SHORT_NAME = StandardNames.FqNames.suppress.shortName().identifier
}
}
fun IdeaModuleInfo.supportsAdditionalBuiltInsMembers(project: Project): Boolean {
return IDELanguageSettingsProvider
.getLanguageVersionSettings(this, project)
.supportsFeature(LanguageFeature.AdditionalBuiltInsMembers)
}
val IdeaModuleInfo.sdk: Sdk? get() = dependencies().firstIsInstanceOrNull<SdkInfo>()?.sdk

View File

@@ -1,46 +0,0 @@
/*
* 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.caches.resolve
import com.intellij.openapi.components.PersistentStateComponent
import com.intellij.openapi.components.State
import com.intellij.openapi.components.Storage
import com.intellij.openapi.diagnostic.logger
import com.intellij.openapi.project.Project
import com.intellij.util.xmlb.XmlSerializerUtil
import org.jetbrains.kotlin.analyzer.ModuleInfo
import org.jetbrains.kotlin.analyzer.moduleInfo
import org.jetbrains.kotlin.caches.project.cacheInvalidatingOnRootModifications
import org.jetbrains.kotlin.caches.resolve.KotlinCacheService
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
import org.jetbrains.kotlin.idea.caches.project.*
import org.jetbrains.kotlin.idea.project.libraryToSourceAnalysisEnabled
import org.jetbrains.kotlin.progress.ProgressIndicatorAndCompilationCanceledStatus
import org.jetbrains.kotlin.resolve.ResolutionAnchorProvider
/**
* This component provides capabilities for correct highlighting for projects with source-dependent libraries.
* The issue with this kind of libraries is that their declarations are resolved by ResolverForProject
* that have no access to project sources by itself. The necessary path back to project sources can be provided
* manually for the libraries in project via resolution anchors. Anchor by itself is a source module which is mapped
* to a library and used during resolution as a fallback.
*/
class KotlinIdeResolutionAnchorService(
val project: Project
) : ResolutionAnchorProvider {
override fun getResolutionAnchor(moduleDescriptor: ModuleDescriptor): ModuleDescriptor? {
if (!project.libraryToSourceAnalysisEnabled) return null
val moduleToAnchor = ResolutionAnchorCacheService.getInstance(project).resolutionAnchorsForLibraries
val moduleInfo = moduleDescriptor.moduleInfo ?: return null
val keyModuleInfo = if (moduleInfo is SourceForBinaryModuleInfo) moduleInfo.binariesModuleInfo else moduleInfo
val mapped = moduleToAnchor[keyModuleInfo] ?: return null
return KotlinCacheService.getInstance(project)
.getResolutionFacadeByModuleInfo(mapped, mapped.platform)
?.moduleDescriptor
}
}

View File

@@ -1,80 +0,0 @@
/*
* Copyright 2000-2018 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.caches.resolve
import com.intellij.openapi.project.Project
import com.intellij.psi.PsiClass
import com.intellij.psi.PsiManager
import org.jetbrains.kotlin.asJava.KotlinAsJavaSupport
import org.jetbrains.kotlin.asJava.classes.KtLightClassForFacade
import org.jetbrains.kotlin.asJava.classes.KtLightClassForFacadeImpl
import org.jetbrains.kotlin.asJava.toLightClass
import org.jetbrains.kotlin.fileClasses.JvmFileClassUtil
import org.jetbrains.kotlin.fileClasses.javaFileFacadeFqName
import org.jetbrains.kotlin.idea.caches.project.getModuleInfo
import org.jetbrains.kotlin.idea.caches.resolve.util.isInDumbMode
import org.jetbrains.kotlin.psi.KtClassOrObject
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.psi.KtFileClassProvider
import org.jetbrains.kotlin.psi.analysisContext
import org.jetbrains.kotlin.platform.jvm.isJvm
import org.jetbrains.kotlin.scripting.definitions.runReadAction
class KtFileClassProviderImpl(val project: Project) : KtFileClassProvider {
override fun getFileClasses(file: KtFile): Array<PsiClass> {
if (file.project.isInDumbMode()) {
return PsiClass.EMPTY_ARRAY
}
// TODO We don't currently support finding light classes for scripts
if (file.isCompiled || runReadAction { file.isScript() }) {
return PsiClass.EMPTY_ARRAY
}
val result = arrayListOf<PsiClass>()
file.declarations.filterIsInstance<KtClassOrObject>().map { it.toLightClass() }.filterNotNullTo(result)
val moduleInfo = file.getModuleInfo()
// prohibit obtaining light classes for non-jvm modules trough KtFiles
// common files might be in fact compiled to jvm and thus correspond to a PsiClass
// this API does not provide context (like GSS) to be able to determine if this file is in fact seen through a jvm module
// this also fixes a problem where a Java JUnit run configuration producer would produce run configurations for a common file
if (!moduleInfo.platform.isJvm()) return emptyArray()
val jvmClassInfo = JvmFileClassUtil.getFileClassInfoNoResolve(file)
val fileClassFqName = file.javaFileFacadeFqName
val kotlinAsJavaSupport = KotlinAsJavaSupport.getInstance(project)
val facadeClasses = when {
file.analysisContext != null && file.hasTopLevelCallables() ->
listOf(
KtLightClassForFacadeImpl.createForSyntheticFile(
PsiManager.getInstance(
file.project
), fileClassFqName, file
)
)
jvmClassInfo.withJvmMultifileClass ->
kotlinAsJavaSupport.getFacadeClasses(fileClassFqName, moduleInfo.contentScope())
file.hasTopLevelCallables() ->
(kotlinAsJavaSupport as IDEKotlinAsJavaSupport).createLightClassForFileFacade(
fileClassFqName, listOf(file), moduleInfo
)
else -> emptyList<PsiClass>()
}
facadeClasses.filterTo(result) {
it is KtLightClassForFacade && file in it.files
}
return result.toTypedArray()
}
}

View File

@@ -1,142 +0,0 @@
/*
* Copyright 2010-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.idea.caches.resolve
import com.intellij.openapi.project.Project
import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.analyzer.AnalysisResult
import org.jetbrains.kotlin.analyzer.ResolverForProject
import org.jetbrains.kotlin.container.get
import org.jetbrains.kotlin.container.getService
import org.jetbrains.kotlin.container.tryGetService
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
import org.jetbrains.kotlin.idea.FrontendInternals
import org.jetbrains.kotlin.diagnostics.DiagnosticSink
import org.jetbrains.kotlin.idea.caches.project.IdeaModuleInfo
import org.jetbrains.kotlin.idea.project.ResolveElementCache
import org.jetbrains.kotlin.idea.resolve.ResolutionFacade
import org.jetbrains.kotlin.idea.util.application.runWithCancellationCheck
import org.jetbrains.kotlin.psi.KtDeclaration
import org.jetbrains.kotlin.psi.KtElement
import org.jetbrains.kotlin.psi.KtPsiUtil
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.lazy.AbsentDescriptorHandler
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
import org.jetbrains.kotlin.resolve.lazy.ResolveSession
internal class ModuleResolutionFacadeImpl(
private val projectFacade: ProjectResolutionFacade,
private val moduleInfo: IdeaModuleInfo
) : ResolutionFacade, ResolutionFacadeModuleDescriptorProvider {
override val project: Project
get() = projectFacade.project
//TODO: ideally we would like to store moduleDescriptor once and for all
// but there are some usages that use resolutionFacade and mutate the psi leading to recomputation of underlying structures
override val moduleDescriptor: ModuleDescriptor
get() = findModuleDescriptor(moduleInfo)
override fun findModuleDescriptor(ideaModuleInfo: IdeaModuleInfo) = projectFacade.findModuleDescriptor(ideaModuleInfo)
override fun analyze(element: KtElement, bodyResolveMode: BodyResolveMode): BindingContext {
return analyze(listOf(element), bodyResolveMode)
}
override fun analyze(elements: Collection<KtElement>, bodyResolveMode: BodyResolveMode): BindingContext {
ResolveInDispatchThreadManager.assertNoResolveInDispatchThread()
when (elements.size) {
0 -> return BindingContext.EMPTY
1 -> {
runWithCancellationCheck {
projectFacade.fetchAnalysisResultsForElement(elements.first())?.bindingContext
}?.let { return it }
}
}
@OptIn(FrontendInternals::class)
val resolveElementCache = getFrontendService(elements.first(), ResolveElementCache::class.java)
return runWithCancellationCheck {
resolveElementCache.resolveToElements(elements, bodyResolveMode)
}
}
override fun analyzeWithAllCompilerChecks(
elements: Collection<KtElement>,
callback: DiagnosticSink.DiagnosticsCallback?
): AnalysisResult {
ResolveInDispatchThreadManager.assertNoResolveInDispatchThread()
return runWithCancellationCheck {
projectFacade.getAnalysisResultsForElements(elements, callback)
}
}
override fun resolveToDescriptor(declaration: KtDeclaration, bodyResolveMode: BodyResolveMode): DeclarationDescriptor =
runWithCancellationCheck {
if (KtPsiUtil.isLocal(declaration)) {
val bindingContext = analyze(declaration, bodyResolveMode)
bindingContext[BindingContext.DECLARATION_TO_DESCRIPTOR, declaration]
?: getFrontendService(moduleInfo, AbsentDescriptorHandler::class.java).diagnoseDescriptorNotFound(declaration)
} else {
ResolveInDispatchThreadManager.assertNoResolveInDispatchThread()
val resolveSession = projectFacade.resolverForElement(declaration).componentProvider.get<ResolveSession>()
resolveSession.resolveToDescriptor(declaration)
}
}
@FrontendInternals
override fun <T : Any> getFrontendService(serviceClass: Class<T>): T = getFrontendService(moduleInfo, serviceClass)
override fun <T : Any> getIdeService(serviceClass: Class<T>): T {
return projectFacade.resolverForModuleInfo(moduleInfo).componentProvider.create(serviceClass)
}
@FrontendInternals
override fun <T : Any> getFrontendService(element: PsiElement, serviceClass: Class<T>): T {
return projectFacade.resolverForElement(element).componentProvider.getService(serviceClass)
}
@FrontendInternals
override fun <T : Any> tryGetFrontendService(element: PsiElement, serviceClass: Class<T>): T? {
return projectFacade.resolverForElement(element).componentProvider.tryGetService(serviceClass)
}
private fun <T : Any> getFrontendService(ideaModuleInfo: IdeaModuleInfo, serviceClass: Class<T>): T {
return projectFacade.resolverForModuleInfo(ideaModuleInfo).componentProvider.getService(serviceClass)
}
@FrontendInternals
override fun <T : Any> getFrontendService(moduleDescriptor: ModuleDescriptor, serviceClass: Class<T>): T {
return projectFacade.resolverForDescriptor(moduleDescriptor).componentProvider.getService(serviceClass)
}
override fun getResolverForProject(): ResolverForProject<IdeaModuleInfo> {
return projectFacade.getResolverForProject()
}
}
fun ResolutionFacade.findModuleDescriptor(ideaModuleInfo: IdeaModuleInfo): ModuleDescriptor {
return (this as ResolutionFacadeModuleDescriptorProvider).findModuleDescriptor(ideaModuleInfo)
}
interface ResolutionFacadeModuleDescriptorProvider {
fun findModuleDescriptor(ideaModuleInfo: IdeaModuleInfo): ModuleDescriptor
}

Some files were not shown because too many files have changed in this diff Show More