FIR IDE: Move fir resolving functionality from idea module to idea-frontend-fir
This commit is contained in:
-53
@@ -1,53 +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.trackers
|
||||
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.util.SimpleModificationTracker
|
||||
import com.intellij.pom.tree.TreeAspect
|
||||
import org.jetbrains.kotlin.idea.KotlinLanguage
|
||||
|
||||
/**
|
||||
* Tested in OutOfBlockModificationTestGenerated
|
||||
*/
|
||||
// FIX ME WHEN BUNCH 193 REMOVED
|
||||
class KotlinCodeBlockModificationListener(
|
||||
project: Project,
|
||||
treeAspect: TreeAspect
|
||||
) : KotlinCodeBlockModificationListenerCompat(project) {
|
||||
|
||||
init {
|
||||
init(
|
||||
treeAspect,
|
||||
incOCBCounter = { ktFile ->
|
||||
kotlinOutOfCodeBlockTrackerImpl.incModificationCount()
|
||||
perModuleOutOfCodeBlockTrackerUpdater.onKotlinPhysicalFileOutOfBlockChange(ktFile, true)
|
||||
},
|
||||
kotlinOutOfCodeBlockTrackerProducer = {
|
||||
SimpleModificationTracker()
|
||||
},
|
||||
psiModificationTrackerListener = {
|
||||
@Suppress("UnstableApiUsage")
|
||||
val kotlinTrackerInternalIDECount =
|
||||
modificationTrackerImpl.forLanguage(KotlinLanguage.INSTANCE).modificationCount
|
||||
if (kotlinModificationTracker == kotlinTrackerInternalIDECount) {
|
||||
// Some update that we are not sure is from Kotlin language, as Kotlin language tracker wasn't changed
|
||||
kotlinOutOfCodeBlockTrackerImpl.incModificationCount()
|
||||
} else {
|
||||
kotlinModificationTracker = kotlinTrackerInternalIDECount
|
||||
}
|
||||
|
||||
perModuleOutOfCodeBlockTrackerUpdater.onPsiModificationTrackerUpdate()
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun getInstance(project: Project): KotlinCodeBlockModificationListener =
|
||||
project.getComponent(KotlinCodeBlockModificationListener::class.java)
|
||||
}
|
||||
}
|
||||
-50
@@ -1,50 +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.trackers
|
||||
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.util.SimpleModificationTracker
|
||||
import com.intellij.pom.tree.TreeAspect
|
||||
import com.intellij.psi.util.PsiModificationTracker
|
||||
import org.jetbrains.kotlin.idea.KotlinLanguage
|
||||
import org.jetbrains.kotlin.idea.util.application.getServiceSafe
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
|
||||
/**
|
||||
* Tested in OutOfBlockModificationTestGenerated
|
||||
*/
|
||||
// FIX ME WHEN BUNCH 193 REMOVED
|
||||
class KotlinCodeBlockModificationListener(project: Project) : KotlinCodeBlockModificationListenerCompat(project) {
|
||||
|
||||
init {
|
||||
init(
|
||||
TreeAspect.getInstance(project),
|
||||
incOCBCounter = { ktFile ->
|
||||
kotlinOutOfCodeBlockTrackerImpl.incModificationCount()
|
||||
perModuleOutOfCodeBlockTrackerUpdater.onKotlinPhysicalFileOutOfBlockChange(ktFile, true)
|
||||
},
|
||||
kotlinOutOfCodeBlockTrackerProducer = {
|
||||
SimpleModificationTracker()
|
||||
},
|
||||
psiModificationTrackerListener = {
|
||||
val kotlinTrackerInternalIDECount =
|
||||
modificationTrackerImpl.forLanguage(KotlinLanguage.INSTANCE).modificationCount
|
||||
if (kotlinModificationTracker == kotlinTrackerInternalIDECount) {
|
||||
// Some update that we are not sure is from Kotlin language, as Kotlin language tracker wasn't changed
|
||||
kotlinOutOfCodeBlockTrackerImpl.incModificationCount()
|
||||
} else {
|
||||
kotlinModificationTracker = kotlinTrackerInternalIDECount
|
||||
}
|
||||
|
||||
perModuleOutOfCodeBlockTrackerUpdater.onPsiModificationTrackerUpdate()
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun getInstance(project: Project): KotlinCodeBlockModificationListener = project.getServiceSafe()
|
||||
}
|
||||
}
|
||||
-360
@@ -1,360 +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.trackers
|
||||
|
||||
import com.intellij.lang.ASTNode
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.util.Key
|
||||
import com.intellij.openapi.util.ModificationTracker
|
||||
import com.intellij.openapi.util.SimpleModificationTracker
|
||||
import com.intellij.openapi.vfs.VirtualFile
|
||||
import com.intellij.pom.PomManager
|
||||
import com.intellij.pom.PomModelAspect
|
||||
import com.intellij.pom.event.PomModelEvent
|
||||
import com.intellij.pom.event.PomModelListener
|
||||
import com.intellij.pom.tree.TreeAspect
|
||||
import com.intellij.pom.tree.events.TreeChangeEvent
|
||||
import com.intellij.pom.tree.events.impl.ChangeInfoImpl
|
||||
import com.intellij.psi.*
|
||||
import com.intellij.psi.impl.PsiManagerImpl
|
||||
import com.intellij.psi.impl.PsiModificationTrackerImpl
|
||||
import com.intellij.psi.impl.PsiTreeChangeEventImpl
|
||||
import com.intellij.psi.impl.PsiTreeChangeEventImpl.PsiEventType.CHILD_MOVED
|
||||
import com.intellij.psi.impl.PsiTreeChangeEventImpl.PsiEventType.PROPERTY_CHANGED
|
||||
import com.intellij.psi.impl.PsiTreeChangePreprocessor
|
||||
import com.intellij.psi.util.PsiModificationTracker
|
||||
import com.intellij.psi.util.PsiTreeUtil
|
||||
import org.jetbrains.kotlin.kdoc.psi.api.KDoc
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.psi.psiUtil.getTopmostParentOfType
|
||||
import org.jetbrains.kotlin.psi.psiUtil.isAncestor
|
||||
|
||||
val KOTLIN_CONSOLE_KEY = Key.create<Boolean>("kotlin.console")
|
||||
|
||||
/**
|
||||
* Tested in OutOfBlockModificationTestGenerated
|
||||
*/
|
||||
// FIX ME WHEN BUNCH 193 REMOVED
|
||||
abstract class KotlinCodeBlockModificationListenerCompat(protected val project: Project) : PsiTreeChangePreprocessor {
|
||||
protected val modificationTrackerImpl: PsiModificationTrackerImpl =
|
||||
PsiModificationTracker.SERVICE.getInstance(project) as PsiModificationTrackerImpl
|
||||
|
||||
@Volatile
|
||||
protected var kotlinModificationTracker: Long = 0
|
||||
|
||||
protected lateinit var kotlinOutOfCodeBlockTrackerImpl: SimpleModificationTracker
|
||||
|
||||
lateinit var kotlinOutOfCodeBlockTracker: ModificationTracker
|
||||
|
||||
internal val perModuleOutOfCodeBlockTrackerUpdater = KotlinModuleOutOfCodeBlockModificationTracker.Updater(project)
|
||||
|
||||
protected fun init(
|
||||
treeAspect: TreeAspect,
|
||||
incOCBCounter: (KtFile) -> Unit,
|
||||
psiModificationTrackerListener: PsiModificationTracker.Listener,
|
||||
kotlinOutOfCodeBlockTrackerProducer: () -> SimpleModificationTracker,
|
||||
isLanguageTrackerEnabled: Boolean = true,
|
||||
) {
|
||||
kotlinOutOfCodeBlockTrackerImpl = kotlinOutOfCodeBlockTrackerProducer()
|
||||
kotlinOutOfCodeBlockTracker = kotlinOutOfCodeBlockTrackerImpl
|
||||
val model = PomManager.getModel(project)
|
||||
val messageBusConnection = project.messageBus.connect(project)
|
||||
model.addModelListener(object : PomModelListener {
|
||||
override fun isAspectChangeInteresting(aspect: PomModelAspect): Boolean {
|
||||
return aspect == treeAspect
|
||||
}
|
||||
|
||||
override fun modelChanged(event: PomModelEvent) {
|
||||
val changeSet = event.getChangeSet(treeAspect) as TreeChangeEvent? ?: return
|
||||
val ktFile = changeSet.rootElement.psi.containingFile as? KtFile ?: return
|
||||
|
||||
incFileModificationCount(ktFile)
|
||||
|
||||
val changedElements = changeSet.changedElements
|
||||
|
||||
// skip change if it contains only virtual/fake change
|
||||
if (changedElements.isNotEmpty()) {
|
||||
// ignore formatting (whitespaces etc)
|
||||
if (isFormattingChange(changeSet) || isCommentChange(changeSet)) return
|
||||
}
|
||||
|
||||
val inBlockElements = inBlockModifications(changedElements)
|
||||
|
||||
val physical = ktFile.isPhysical
|
||||
if (inBlockElements.isEmpty()) {
|
||||
messageBusConnection.deliverImmediately()
|
||||
|
||||
if (physical && !isReplLine(ktFile.virtualFile) && ktFile !is KtTypeCodeFragment) {
|
||||
incOCBCounter(ktFile)
|
||||
}
|
||||
|
||||
ktFile.incOutOfBlockModificationCount()
|
||||
} else if (physical) {
|
||||
inBlockElements.forEach { it.containingKtFile.addInBlockModifiedItem(it) }
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
if (isLanguageTrackerEnabled) {
|
||||
(PsiManager.getInstance(project) as PsiManagerImpl).addTreeChangePreprocessor(this)
|
||||
}
|
||||
messageBusConnection.subscribe(PsiModificationTracker.TOPIC, psiModificationTrackerListener)
|
||||
}
|
||||
|
||||
override fun treeChanged(event: PsiTreeChangeEventImpl) {
|
||||
if (!PsiModificationTrackerImpl.canAffectPsi(event)) {
|
||||
return
|
||||
}
|
||||
|
||||
// Copy logic from PsiModificationTrackerImpl.treeChanged(). Some out-of-code-block events are written to language modification
|
||||
// tracker in PsiModificationTrackerImpl but don't have correspondent PomModelEvent. Increase kotlinOutOfCodeBlockTracker
|
||||
// manually if needed.
|
||||
val outOfCodeBlock = when (event.code) {
|
||||
PROPERTY_CHANGED ->
|
||||
event.propertyName === PsiTreeChangeEvent.PROP_UNLOADED_PSI || event.propertyName === PsiTreeChangeEvent.PROP_ROOTS
|
||||
CHILD_MOVED -> event.oldParent is PsiDirectory || event.newParent is PsiDirectory
|
||||
else -> event.parent is PsiDirectory
|
||||
}
|
||||
|
||||
if (outOfCodeBlock) {
|
||||
kotlinOutOfCodeBlockTrackerImpl.incModificationCount()
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private fun isReplLine(file: VirtualFile): Boolean {
|
||||
return file.getUserData(KOTLIN_CONSOLE_KEY) == true
|
||||
}
|
||||
|
||||
private fun incFileModificationCount(file: KtFile) {
|
||||
val tracker = file.getUserData(PER_FILE_MODIFICATION_TRACKER)
|
||||
?: file.putUserDataIfAbsent(PER_FILE_MODIFICATION_TRACKER, SimpleModificationTracker())
|
||||
tracker.incModificationCount()
|
||||
}
|
||||
|
||||
private fun inBlockModifications(elements: Array<ASTNode>): List<KtElement> {
|
||||
// When a code fragment is reparsed, Intellij doesn't do an AST diff and considers the entire
|
||||
// contents to be replaced, which is represented in a POM event as an empty list of changed elements
|
||||
|
||||
return elements.map { element ->
|
||||
val modificationScope = getInsideCodeBlockModificationScope(element.psi) ?: return emptyList()
|
||||
modificationScope.blockDeclaration
|
||||
}
|
||||
}
|
||||
|
||||
private fun isSpecificChange(changeSet: TreeChangeEvent, precondition: (ASTNode?) -> Boolean): Boolean =
|
||||
changeSet.changedElements.all { changedElement ->
|
||||
val changesByElement = changeSet.getChangesByElement(changedElement)
|
||||
changesByElement.affectedChildren.all { affectedChild ->
|
||||
if (!precondition(affectedChild)) return@all false
|
||||
val changeByChild = changesByElement.getChangeByChild(affectedChild)
|
||||
return@all if (changeByChild is ChangeInfoImpl) {
|
||||
val oldChild = changeByChild.oldChild
|
||||
precondition(oldChild)
|
||||
} else false
|
||||
}
|
||||
}
|
||||
|
||||
private fun isCommentChange(changeSet: TreeChangeEvent): Boolean =
|
||||
isSpecificChange(changeSet) { it is PsiComment || it is KDoc }
|
||||
|
||||
private fun isFormattingChange(changeSet: TreeChangeEvent): Boolean =
|
||||
isSpecificChange(changeSet) { it is PsiWhiteSpace }
|
||||
|
||||
/**
|
||||
* Has to be aligned with [getInsideCodeBlockModificationScope] :
|
||||
*
|
||||
* result of analysis has to be reflected in dirty scope,
|
||||
* the only difference is whitespaces and comments
|
||||
*/
|
||||
fun getInsideCodeBlockModificationDirtyScope(element: PsiElement): PsiElement? {
|
||||
if (!element.isPhysical) return null
|
||||
// dirty scope for whitespaces and comments is the element itself
|
||||
if (element is PsiWhiteSpace || element is PsiComment || element is KDoc) return element
|
||||
|
||||
return getInsideCodeBlockModificationScope(element)?.blockDeclaration ?: null
|
||||
}
|
||||
|
||||
fun getInsideCodeBlockModificationScope(element: PsiElement): BlockModificationScopeElement? {
|
||||
val lambda = element.getTopmostParentOfType<KtLambdaExpression>()
|
||||
if (lambda is KtLambdaExpression) {
|
||||
lambda.getTopmostParentOfType<KtSuperTypeCallEntry>()?.getTopmostParentOfType<KtClassOrObject>()?.let {
|
||||
return BlockModificationScopeElement(it, it)
|
||||
}
|
||||
}
|
||||
|
||||
val blockDeclaration =
|
||||
KtPsiUtil.getTopmostParentOfTypes(element, *BLOCK_DECLARATION_TYPES) as? KtDeclaration ?: return null
|
||||
// KtPsiUtil.getTopmostParentOfType<KtClassOrObject>(element) as? KtDeclaration ?: return null
|
||||
|
||||
// should not be local declaration
|
||||
if (KtPsiUtil.isLocal(blockDeclaration))
|
||||
return null
|
||||
|
||||
when (blockDeclaration) {
|
||||
is KtNamedFunction -> {
|
||||
// if (blockDeclaration.visibilityModifierType()?.toVisibility() == Visibilities.PRIVATE) {
|
||||
// topClassLikeDeclaration(blockDeclaration)?.let {
|
||||
// return BlockModificationScopeElement(it, it)
|
||||
// }
|
||||
// }
|
||||
if (blockDeclaration.hasBlockBody()) {
|
||||
// case like `fun foo(): String {...<caret>...}`
|
||||
return blockDeclaration.bodyExpression
|
||||
?.takeIf { it.isAncestor(element) }
|
||||
?.let { BlockModificationScopeElement(blockDeclaration, it) }
|
||||
} else if (blockDeclaration.hasDeclaredReturnType()) {
|
||||
// case like `fun foo(): String = b<caret>labla`
|
||||
return blockDeclaration.initializer
|
||||
?.takeIf { it.isAncestor(element) }
|
||||
?.let { BlockModificationScopeElement(blockDeclaration, it) }
|
||||
}
|
||||
}
|
||||
|
||||
is KtProperty -> {
|
||||
// if (blockDeclaration.visibilityModifierType()?.toVisibility() == Visibilities.PRIVATE) {
|
||||
// topClassLikeDeclaration(blockDeclaration)?.let {
|
||||
// return BlockModificationScopeElement(it, it)
|
||||
// }
|
||||
// }
|
||||
if (blockDeclaration.typeReference != null) {
|
||||
val accessors =
|
||||
blockDeclaration.accessors.map { it.initializer ?: it.bodyExpression }
|
||||
|
||||
val accessorList = if (blockDeclaration.initializer.isAncestor(element) &&
|
||||
// call expression changes in property initializer are OCB, see KT-38443
|
||||
KtPsiUtil.getTopmostParentOfTypes(element, KtCallExpression::class.java) == null
|
||||
) {
|
||||
accessors + blockDeclaration.initializer
|
||||
} else {
|
||||
accessors
|
||||
}
|
||||
|
||||
for (accessor in accessorList) {
|
||||
accessor?.takeIf {
|
||||
it.isAncestor(element) &&
|
||||
// adding annotations to accessor is the same as change contract of property
|
||||
(element !is KtAnnotated || element.annotationEntries.isEmpty())
|
||||
}
|
||||
?.let { expression ->
|
||||
val declaration =
|
||||
KtPsiUtil.getTopmostParentOfTypes(blockDeclaration, KtClassOrObject::class.java) as? KtElement ?:
|
||||
// ktFile to check top level property declarations
|
||||
return null
|
||||
return BlockModificationScopeElement(declaration, expression)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
is KtScriptInitializer -> {
|
||||
return (blockDeclaration.body as? KtCallExpression)
|
||||
?.lambdaArguments
|
||||
?.lastOrNull()
|
||||
?.getLambdaExpression()
|
||||
?.takeIf { it.isAncestor(element) }
|
||||
?.let { BlockModificationScopeElement(blockDeclaration, it) }
|
||||
}
|
||||
|
||||
is KtClassInitializer -> {
|
||||
blockDeclaration
|
||||
.takeIf { it.isAncestor(element) }
|
||||
?.let { ktClassInitializer ->
|
||||
(PsiTreeUtil.getParentOfType(blockDeclaration, KtClassOrObject::class.java))?.let {
|
||||
return BlockModificationScopeElement(it, ktClassInitializer)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
is KtSecondaryConstructor -> {
|
||||
blockDeclaration
|
||||
?.takeIf {
|
||||
it.bodyExpression?.isAncestor(element) ?: false || it.getDelegationCallOrNull()?.isAncestor(element) ?: false
|
||||
}?.let { ktConstructor ->
|
||||
PsiTreeUtil.getParentOfType(blockDeclaration, KtClassOrObject::class.java)?.let {
|
||||
return BlockModificationScopeElement(it, ktConstructor)
|
||||
}
|
||||
}
|
||||
}
|
||||
// is KtClassOrObject -> {
|
||||
// return when (element) {
|
||||
// is KtProperty, is KtNamedFunction -> {
|
||||
// if ((element as? KtModifierListOwner)?.visibilityModifierType()?.toVisibility() == Visibilities.PRIVATE)
|
||||
// BlockModificationScopeElement(blockDeclaration, blockDeclaration) else null
|
||||
// }
|
||||
// else -> null
|
||||
// }
|
||||
// }
|
||||
|
||||
else -> throw IllegalStateException()
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
data class BlockModificationScopeElement(val blockDeclaration: KtElement, val element: KtElement)
|
||||
|
||||
fun isBlockDeclaration(declaration: KtDeclaration): Boolean {
|
||||
return BLOCK_DECLARATION_TYPES.any { it.isInstance(declaration) }
|
||||
}
|
||||
|
||||
private val BLOCK_DECLARATION_TYPES = arrayOf<Class<out KtDeclaration>>(
|
||||
KtProperty::class.java,
|
||||
KtNamedFunction::class.java,
|
||||
KtClassInitializer::class.java,
|
||||
KtSecondaryConstructor::class.java,
|
||||
KtScriptInitializer::class.java
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private val PER_FILE_MODIFICATION_TRACKER = Key<SimpleModificationTracker>("FILE_OUT_OF_BLOCK_MODIFICATION_COUNT")
|
||||
|
||||
val KtFile.perFileModificationTracker: ModificationTracker
|
||||
get() = putUserDataIfAbsent(PER_FILE_MODIFICATION_TRACKER, SimpleModificationTracker())
|
||||
|
||||
private val FILE_OUT_OF_BLOCK_MODIFICATION_COUNT = Key<Long>("FILE_OUT_OF_BLOCK_MODIFICATION_COUNT")
|
||||
|
||||
val KtFile.outOfBlockModificationCount: Long by NotNullableUserDataProperty(FILE_OUT_OF_BLOCK_MODIFICATION_COUNT, 0)
|
||||
|
||||
private fun KtFile.incOutOfBlockModificationCount() {
|
||||
clearInBlockModifications()
|
||||
|
||||
val count = getUserData(FILE_OUT_OF_BLOCK_MODIFICATION_COUNT) ?: 0
|
||||
putUserData(FILE_OUT_OF_BLOCK_MODIFICATION_COUNT, count + 1)
|
||||
}
|
||||
|
||||
/**
|
||||
* inBlockModifications is a collection of block elements those have in-block modifications
|
||||
*/
|
||||
private val IN_BLOCK_MODIFICATIONS = Key<MutableCollection<KtElement>>("IN_BLOCK_MODIFICATIONS")
|
||||
private val FILE_IN_BLOCK_MODIFICATION_COUNT = Key<Long>("FILE_IN_BLOCK_MODIFICATION_COUNT")
|
||||
|
||||
val KtFile.inBlockModificationCount: Long by NotNullableUserDataProperty(FILE_IN_BLOCK_MODIFICATION_COUNT, 0)
|
||||
|
||||
val KtFile.inBlockModifications: Collection<KtElement>
|
||||
get() {
|
||||
val collection = getUserData(IN_BLOCK_MODIFICATIONS)
|
||||
return collection ?: emptySet()
|
||||
}
|
||||
|
||||
private fun KtFile.addInBlockModifiedItem(element: KtElement) {
|
||||
val collection = putUserDataIfAbsent(IN_BLOCK_MODIFICATIONS, mutableSetOf())
|
||||
synchronized(collection) {
|
||||
collection.add(element)
|
||||
}
|
||||
val count = getUserData(FILE_IN_BLOCK_MODIFICATION_COUNT) ?: 0
|
||||
putUserData(FILE_IN_BLOCK_MODIFICATION_COUNT, count + 1)
|
||||
}
|
||||
|
||||
fun KtFile.clearInBlockModifications() {
|
||||
val collection = getUserData(IN_BLOCK_MODIFICATIONS)
|
||||
collection?.let {
|
||||
synchronized(it) {
|
||||
it.clear()
|
||||
}
|
||||
}
|
||||
}
|
||||
-24
@@ -1,24 +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.trackers
|
||||
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.util.ModificationTracker
|
||||
import com.intellij.psi.util.PsiModificationTracker
|
||||
import org.jetbrains.kotlin.analyzer.KotlinModificationTrackerService
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
|
||||
class KotlinIDEModificationTrackerService(project: Project) :
|
||||
KotlinModificationTrackerService() {
|
||||
|
||||
override val modificationTracker: ModificationTracker = PsiModificationTracker.SERVICE.getInstance(project)
|
||||
|
||||
override val outOfBlockModificationTracker: ModificationTracker =
|
||||
KotlinCodeBlockModificationListener.getInstance(project).kotlinOutOfCodeBlockTracker
|
||||
|
||||
override fun fileModificationTracker(file: KtFile): ModificationTracker =
|
||||
file.perFileModificationTracker
|
||||
}
|
||||
-118
@@ -1,118 +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.trackers
|
||||
|
||||
import com.intellij.openapi.module.Module
|
||||
import com.intellij.openapi.module.ModuleUtil
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.roots.ModuleRootManager
|
||||
import com.intellij.openapi.util.ModificationTracker
|
||||
import com.intellij.util.CommonProcessors
|
||||
import org.jetbrains.annotations.TestOnly
|
||||
import org.jetbrains.kotlin.caches.project.cacheByClassInvalidatingOnRootModifications
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
|
||||
class KotlinModuleOutOfCodeBlockModificationTracker private constructor(private val module: Module, private val updater: Updater) :
|
||||
ModificationTracker {
|
||||
|
||||
constructor(module: Module) :
|
||||
this(module, KotlinCodeBlockModificationListener.getInstance(module.project).perModuleOutOfCodeBlockTrackerUpdater)
|
||||
|
||||
private val kotlinOutOfCodeBlockTracker = KotlinCodeBlockModificationListener.getInstance(module.project).kotlinOutOfCodeBlockTracker
|
||||
|
||||
private val dependencies by lazy {
|
||||
// Avoid implicit capturing for this to make CachedValueStabilityChecker happy
|
||||
val module = module
|
||||
|
||||
module.cacheByClassInvalidatingOnRootModifications(KeyForCachedDependencies::class.java) {
|
||||
HashSet<Module>().also { resultModuleSet ->
|
||||
ModuleRootManager.getInstance(module).orderEntries().recursively().forEachModule(
|
||||
CommonProcessors.CollectProcessor(resultModuleSet)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
object KeyForCachedDependencies
|
||||
|
||||
override fun getModificationCount(): Long {
|
||||
val currentGlobalCount = kotlinOutOfCodeBlockTracker.modificationCount
|
||||
|
||||
if (updater.hasPerModuleModificationCounts()) {
|
||||
val selfCount = updater.getModificationCount(module)
|
||||
if (selfCount == currentGlobalCount) return selfCount
|
||||
|
||||
var maxCount = selfCount
|
||||
for (dependency in dependencies) {
|
||||
val depCount = updater.getModificationCount(dependency)
|
||||
if (depCount == currentGlobalCount) return currentGlobalCount
|
||||
if (depCount > maxCount) maxCount = depCount
|
||||
}
|
||||
return maxCount
|
||||
}
|
||||
|
||||
return currentGlobalCount
|
||||
}
|
||||
|
||||
companion object {
|
||||
@TestOnly
|
||||
fun getModificationCount(module: Module): Long {
|
||||
val updater = KotlinCodeBlockModificationListener.getInstance(module.project).perModuleOutOfCodeBlockTrackerUpdater
|
||||
return updater.getModificationCount(module)
|
||||
}
|
||||
}
|
||||
|
||||
internal class Updater(project: Project) {
|
||||
private val kotlinOfOfCodeBlockTracker by lazy {
|
||||
KotlinCodeBlockModificationListener.getInstance(project).kotlinOutOfCodeBlockTracker
|
||||
}
|
||||
|
||||
private val perModuleModCount = mutableMapOf<Module, Long>()
|
||||
|
||||
private var lastAffectedModule: Module? = null
|
||||
|
||||
private var lastAffectedModuleModCount = -1L
|
||||
|
||||
// All modifications since that count are known to be single-module modifications reflected in
|
||||
// perModuleModCount map
|
||||
private var perModuleChangesHighWatermark: Long? = null
|
||||
|
||||
internal fun getModificationCount(module: Module): Long {
|
||||
return perModuleModCount[module] ?: perModuleChangesHighWatermark ?: kotlinOfOfCodeBlockTracker.modificationCount
|
||||
}
|
||||
|
||||
internal fun hasPerModuleModificationCounts() = perModuleChangesHighWatermark != null
|
||||
|
||||
internal fun onKotlinPhysicalFileOutOfBlockChange(ktFile: KtFile, immediateUpdatesProcess: Boolean) {
|
||||
lastAffectedModule = ModuleUtil.findModuleForPsiElement(ktFile)
|
||||
lastAffectedModuleModCount = kotlinOfOfCodeBlockTracker.modificationCount
|
||||
|
||||
if (immediateUpdatesProcess) {
|
||||
onPsiModificationTrackerUpdate(0)
|
||||
}
|
||||
}
|
||||
|
||||
internal fun onPsiModificationTrackerUpdate(customIncrement: Int = 0) {
|
||||
val newModCount = kotlinOfOfCodeBlockTracker.modificationCount
|
||||
val affectedModule = lastAffectedModule
|
||||
if (affectedModule != null && newModCount == lastAffectedModuleModCount + customIncrement) {
|
||||
if (perModuleChangesHighWatermark == null) {
|
||||
perModuleChangesHighWatermark = lastAffectedModuleModCount
|
||||
}
|
||||
perModuleModCount[affectedModule] = newModCount
|
||||
} else {
|
||||
// Some updates were not processed in our code so they probably came from other languages. Invalidate all.
|
||||
clean()
|
||||
}
|
||||
}
|
||||
|
||||
private fun clean() {
|
||||
perModuleChangesHighWatermark = null
|
||||
lastAffectedModule = null
|
||||
perModuleModCount.clear()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,44 +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.fir
|
||||
|
||||
import org.jetbrains.kotlin.fir.FirSession
|
||||
import org.jetbrains.kotlin.fir.analysis.collectors.AbstractDiagnosticCollector
|
||||
import org.jetbrains.kotlin.fir.analysis.collectors.registerAllComponents
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.DiagnosticReporter
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirDiagnostic
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirPsiDiagnostic
|
||||
import org.jetbrains.kotlin.psi.KtElement
|
||||
|
||||
class FirIdeDiagnosticsCollector(session: FirSession, private val resolveState: FirModuleResolveState) : AbstractDiagnosticCollector(session) {
|
||||
|
||||
init {
|
||||
registerAllComponents()
|
||||
}
|
||||
|
||||
private inner class Reporter : DiagnosticReporter() {
|
||||
override fun report(diagnostic: FirDiagnostic<*>?) {
|
||||
if (diagnostic !is FirPsiDiagnostic<*>) return
|
||||
val psi = diagnostic.element.psi as? KtElement ?: return
|
||||
resolveState.record(psi, diagnostic.asPsiBasedDiagnostic())
|
||||
}
|
||||
}
|
||||
|
||||
private lateinit var reporter: Reporter
|
||||
|
||||
override fun initializeCollector() {
|
||||
reporter = Reporter()
|
||||
}
|
||||
|
||||
override fun getCollectedDiagnostics(): Iterable<FirDiagnostic<*>> {
|
||||
// Not necessary in IDE
|
||||
return emptyList()
|
||||
}
|
||||
|
||||
override fun runCheck(block: (DiagnosticReporter) -> Unit) {
|
||||
block(reporter)
|
||||
}
|
||||
}
|
||||
@@ -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.fir
|
||||
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.psi.search.GlobalSearchScope
|
||||
import org.jetbrains.kotlin.analyzer.ModuleInfo
|
||||
import org.jetbrains.kotlin.fir.FirModuleBasedSession
|
||||
import org.jetbrains.kotlin.fir.analysis.registerCheckersComponent
|
||||
import org.jetbrains.kotlin.fir.java.FirProjectSessionProvider
|
||||
import org.jetbrains.kotlin.fir.java.JavaSymbolProvider
|
||||
import org.jetbrains.kotlin.fir.registerCommonComponents
|
||||
import org.jetbrains.kotlin.fir.registerResolveComponents
|
||||
import org.jetbrains.kotlin.fir.resolve.calls.jvm.registerJvmCallConflictResolverFactory
|
||||
import org.jetbrains.kotlin.fir.resolve.firProvider
|
||||
import org.jetbrains.kotlin.fir.resolve.providers.FirProvider
|
||||
import org.jetbrains.kotlin.fir.resolve.providers.FirSymbolProvider
|
||||
import org.jetbrains.kotlin.fir.resolve.providers.impl.FirCompositeSymbolProvider
|
||||
import org.jetbrains.kotlin.fir.resolve.scopes.wrapScopeWithJvmMapped
|
||||
import org.jetbrains.kotlin.fir.scopes.KotlinScopeProvider
|
||||
|
||||
|
||||
class FirIdeJavaModuleBasedSession(
|
||||
moduleInfo: ModuleInfo,
|
||||
sessionProvider: FirProjectSessionProvider
|
||||
) : FirModuleBasedSession(moduleInfo, sessionProvider) {
|
||||
companion object {
|
||||
fun create(
|
||||
project: Project,
|
||||
moduleInfo: ModuleInfo,
|
||||
sessionProvider: FirProjectSessionProvider,
|
||||
scope: GlobalSearchScope
|
||||
): FirIdeJavaModuleBasedSession {
|
||||
return FirIdeJavaModuleBasedSession(moduleInfo, sessionProvider).apply {
|
||||
registerCommonComponents()
|
||||
registerResolveComponents()
|
||||
registerCheckersComponent()
|
||||
registerJvmCallConflictResolverFactory()
|
||||
|
||||
val firIdeProvider = FirIdeProvider(project, scope, this, KotlinScopeProvider(::wrapScopeWithJvmMapped))
|
||||
|
||||
register(FirProvider::class, firIdeProvider)
|
||||
register(FirIdeProvider::class, firIdeProvider)
|
||||
|
||||
register(
|
||||
FirSymbolProvider::class,
|
||||
FirCompositeSymbolProvider(
|
||||
listOf(
|
||||
firProvider,
|
||||
JavaSymbolProvider(this, sessionProvider.project, scope),
|
||||
FirIdeModuleDependenciesSymbolProvider(this)
|
||||
)
|
||||
) as FirSymbolProvider
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
-48
@@ -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.fir
|
||||
|
||||
import org.jetbrains.kotlin.fir.resolve.providers.FirSymbolProvider
|
||||
import org.jetbrains.kotlin.fir.resolve.providers.impl.FirDependenciesSymbolProviderImpl
|
||||
import org.jetbrains.kotlin.fir.dependenciesWithoutSelf
|
||||
import org.jetbrains.kotlin.fir.resolve.firSymbolProvider
|
||||
import org.jetbrains.kotlin.idea.caches.project.IdeaModuleInfo
|
||||
import org.jetbrains.kotlin.fir.java.FirLibrarySession
|
||||
import org.jetbrains.kotlin.fir.java.FirProjectSessionProvider
|
||||
import org.jetbrains.kotlin.idea.caches.project.ModuleSourceInfo
|
||||
import org.jetbrains.kotlin.idea.caches.project.isLibraryClasses
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.IDEPackagePartProvider
|
||||
|
||||
|
||||
class FirIdeModuleDependenciesSymbolProvider(
|
||||
session: FirIdeJavaModuleBasedSession
|
||||
) : FirDependenciesSymbolProviderImpl(session) {
|
||||
|
||||
override val dependencyProviders: List<FirSymbolProvider> by lazy {
|
||||
val moduleInfo = session.moduleInfo
|
||||
val sessionProvider = session.sessionProvider ?: return@lazy emptyList<FirSymbolProvider>()
|
||||
val project = sessionProvider.project
|
||||
val stateService = FirIdeResolveStateService.getInstance(project)
|
||||
moduleInfo.dependenciesWithoutSelf().filterIsInstance<IdeaModuleInfo>().mapNotNull { dependencyInfo ->
|
||||
val resolveState = stateService.getResolveState(dependencyInfo)
|
||||
val dependencySession = when {
|
||||
dependencyInfo is ModuleSourceInfo -> {
|
||||
resolveState.getSession(project, dependencyInfo)
|
||||
}
|
||||
dependencyInfo.isLibraryClasses() -> {
|
||||
val dependencyScope = dependencyInfo.contentScope()
|
||||
FirLibrarySession.create(
|
||||
dependencyInfo, sessionProvider as FirProjectSessionProvider,
|
||||
dependencyScope, project, IDEPackagePartProvider(dependencyScope)
|
||||
)
|
||||
}
|
||||
else -> return@mapNotNull null
|
||||
}
|
||||
dependencySession.firSymbolProvider
|
||||
}.toList()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,166 +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.fir
|
||||
|
||||
import com.intellij.openapi.progress.ProcessCanceledException
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.psi.search.GlobalSearchScope
|
||||
import org.jetbrains.kotlin.fir.FirSession
|
||||
import org.jetbrains.kotlin.fir.builder.RawFirBuilder
|
||||
import org.jetbrains.kotlin.fir.declarations.*
|
||||
import org.jetbrains.kotlin.fir.psi
|
||||
import org.jetbrains.kotlin.fir.resolve.providers.FirProvider
|
||||
import org.jetbrains.kotlin.fir.resolve.providers.FirProviderInternals
|
||||
import org.jetbrains.kotlin.fir.resolve.providers.impl.FirProviderImpl
|
||||
import org.jetbrains.kotlin.fir.scopes.FirScope
|
||||
import org.jetbrains.kotlin.fir.scopes.KotlinScopeProvider
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirClassLikeSymbol
|
||||
import org.jetbrains.kotlin.idea.stubindex.*
|
||||
import org.jetbrains.kotlin.name.ClassId
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.psi.KtElement
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
|
||||
class FirIdeProvider(
|
||||
val project: Project,
|
||||
val scope: GlobalSearchScope,
|
||||
val session: FirSession,
|
||||
kotlinScopeProvider: KotlinScopeProvider
|
||||
) : FirProvider() {
|
||||
private val cacheProvider = FirProviderImpl(session, kotlinScopeProvider)
|
||||
|
||||
data class FirFileWithStamp(val file: FirFile, val stamp: Long)
|
||||
|
||||
private val files = mutableMapOf<KtFile, FirFileWithStamp>()
|
||||
|
||||
override val isPhasedFirAllowed: Boolean
|
||||
get() = true
|
||||
|
||||
override fun getFirClassifierByFqName(classId: ClassId): FirClassLikeDeclaration<*>? {
|
||||
return cacheProvider.getFirClassifierByFqName(classId) ?: run {
|
||||
try {
|
||||
val classes = KotlinFullClassNameIndex.getInstance().get(classId.asSingleFqName().asString(), project, scope)
|
||||
val ktClass = classes.firstOrNull {
|
||||
classId.packageFqName == it.containingKtFile.packageFqName
|
||||
} ?: return null // TODO: what if two of them?
|
||||
|
||||
val ktFile = ktClass.containingKtFile
|
||||
getOrBuildFile(ktFile)
|
||||
|
||||
cacheProvider.getFirClassifierByFqName(classId)
|
||||
} catch (e: ProcessCanceledException) {
|
||||
return null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun getOrBuildFile(ktFile: KtFile): FirFile {
|
||||
val modificationStamp = ktFile.modificationStamp
|
||||
files[ktFile]?.let { (firFile, stamp) ->
|
||||
if (stamp == modificationStamp) {
|
||||
return firFile
|
||||
}
|
||||
}
|
||||
return synchronized(ktFile) {
|
||||
var fileWithStamp = files[ktFile]
|
||||
if (fileWithStamp != null && fileWithStamp.stamp == modificationStamp) {
|
||||
fileWithStamp.file
|
||||
} else {
|
||||
val file = RawFirBuilder(session, cacheProvider.kotlinScopeProvider, stubMode = false).buildFirFile(ktFile)
|
||||
cacheProvider.recordFile(file)
|
||||
fileWithStamp = FirFileWithStamp(file, modificationStamp)
|
||||
files[ktFile] = fileWithStamp
|
||||
file
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun getFile(ktFile: KtFile): FirFile? {
|
||||
val (firFile, stamp) = files[ktFile] ?: return null
|
||||
if (stamp == ktFile.modificationStamp) return firFile
|
||||
return null
|
||||
}
|
||||
|
||||
override fun getClassLikeSymbolByFqName(classId: ClassId): FirClassLikeSymbol<*>? {
|
||||
return getFirClassifierByFqName(classId)?.symbol
|
||||
}
|
||||
|
||||
override fun getTopLevelCallableSymbols(packageFqName: FqName, name: Name): List<FirCallableSymbol<*>> {
|
||||
val packagePrefix = if (packageFqName.isRoot) "" else "$packageFqName."
|
||||
return try {
|
||||
val topLevelFunctions = KotlinTopLevelFunctionFqnNameIndex.getInstance()["$packagePrefix$name", project, scope]
|
||||
val topLevelProperties = KotlinTopLevelPropertyFqnNameIndex.getInstance()["$packagePrefix$name", project, scope]
|
||||
topLevelFunctions.forEach { getOrBuildFile(it.containingKtFile) }
|
||||
topLevelProperties.forEach { getOrBuildFile(it.containingKtFile) }
|
||||
cacheProvider.getTopLevelCallableSymbols(packageFqName, name)
|
||||
} catch (e: ProcessCanceledException) {
|
||||
emptyList()
|
||||
}
|
||||
}
|
||||
|
||||
override fun getFirClassifierContainerFile(fqName: ClassId): FirFile {
|
||||
getFirClassifierByFqName(fqName) // Necessary to ensure cacheProvider contains this classifier
|
||||
return cacheProvider.getFirClassifierContainerFile(fqName)
|
||||
}
|
||||
|
||||
override fun getFirClassifierContainerFileIfAny(fqName: ClassId): FirFile? {
|
||||
getFirClassifierByFqName(fqName) // Necessary to ensure cacheProvider contains this classifier
|
||||
return cacheProvider.getFirClassifierContainerFileIfAny(fqName)
|
||||
}
|
||||
|
||||
override fun getFirClassifierContainerFile(symbol: FirClassLikeSymbol<*>): FirFile {
|
||||
return getFirClassifierContainerFileIfAny(symbol)
|
||||
?: error("Couldn't find container for ${symbol.classId}")
|
||||
}
|
||||
|
||||
override fun getFirClassifierContainerFileIfAny(symbol: FirClassLikeSymbol<*>): FirFile? {
|
||||
val psi = symbol.fir.source?.psi
|
||||
if (psi is KtElement) {
|
||||
return try {
|
||||
val ktFile = psi.containingKtFile
|
||||
getOrBuildFile(ktFile)
|
||||
} catch (e: ProcessCanceledException) {
|
||||
null
|
||||
}
|
||||
}
|
||||
return getFirClassifierContainerFileIfAny(symbol.classId)
|
||||
}
|
||||
|
||||
override fun getFirCallableContainerFile(symbol: FirCallableSymbol<*>): FirFile? {
|
||||
return cacheProvider.getFirCallableContainerFile(symbol)
|
||||
}
|
||||
|
||||
override fun getFirFilesByPackage(fqName: FqName): List<FirFile> {
|
||||
return try {
|
||||
val files = KotlinExactPackagesIndex.getInstance()[fqName.asString(), project, scope]
|
||||
files.forEach { getOrBuildFile(it) }
|
||||
cacheProvider.getFirFilesByPackage(fqName)
|
||||
} catch (e: ProcessCanceledException) {
|
||||
emptyList()
|
||||
}
|
||||
}
|
||||
|
||||
override fun getNestedClassifierScope(classId: ClassId): FirScope? {
|
||||
getFirClassifierByFqName(classId)
|
||||
return cacheProvider.getNestedClassifierScope(classId)
|
||||
}
|
||||
|
||||
@FirProviderInternals
|
||||
override fun recordGeneratedClass(owner: FirAnnotatedDeclaration, klass: FirRegularClass) {
|
||||
// TODO: check that this implementation is correct
|
||||
cacheProvider.recordGeneratedClass(owner, klass)
|
||||
}
|
||||
|
||||
@FirProviderInternals
|
||||
override fun recordGeneratedMember(owner: FirAnnotatedDeclaration, klass: FirDeclaration) {
|
||||
// TODO: check that this implementation is correct
|
||||
cacheProvider.recordGeneratedMember(owner, klass)
|
||||
}
|
||||
}
|
||||
|
||||
val FirSession.firIdeProvider: FirIdeProvider by FirSession.sessionComponentAccessor()
|
||||
@@ -1,64 +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.fir
|
||||
|
||||
import com.intellij.openapi.components.ServiceManager
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.util.ModificationTracker
|
||||
import org.jetbrains.kotlin.analyzer.KotlinModificationTrackerService
|
||||
import org.jetbrains.kotlin.analyzer.TrackableModuleInfo
|
||||
import org.jetbrains.kotlin.fir.java.FirProjectSessionProvider
|
||||
import org.jetbrains.kotlin.idea.caches.project.IdeaModuleInfo
|
||||
|
||||
interface FirIdeResolveStateService {
|
||||
companion object {
|
||||
fun getInstance(project: Project): FirIdeResolveStateService =
|
||||
ServiceManager.getService(project, FirIdeResolveStateService::class.java)!!
|
||||
}
|
||||
|
||||
val fallbackModificationTracker: ModificationTracker?
|
||||
|
||||
fun getResolveState(moduleInfo: IdeaModuleInfo): FirModuleResolveState
|
||||
}
|
||||
|
||||
private class FirModuleData(val state: FirModuleResolveState, val modificationTracker: ModificationTracker?) {
|
||||
val modificationCount: Long = modificationTracker?.modificationCount ?: Long.MIN_VALUE
|
||||
|
||||
fun isOutOfDate(): Boolean {
|
||||
val currentModCount = modificationTracker?.modificationCount
|
||||
return currentModCount != null && currentModCount > modificationCount
|
||||
}
|
||||
}
|
||||
|
||||
class FirIdeResolveStateServiceImpl(val project: Project) : FirIdeResolveStateService {
|
||||
private val stateCache = mutableMapOf<IdeaModuleInfo, FirModuleData>()
|
||||
|
||||
private fun createResolveState(): FirModuleResolveState {
|
||||
val provider = FirProjectSessionProvider(project)
|
||||
return FirModuleResolveStateImpl(provider)
|
||||
}
|
||||
|
||||
private fun createModuleData(moduleInfo: IdeaModuleInfo): FirModuleData {
|
||||
val state = createResolveState()
|
||||
val modificationTracker = (moduleInfo as? TrackableModuleInfo)?.createModificationTracker() ?: fallbackModificationTracker
|
||||
return FirModuleData(state, modificationTracker)
|
||||
}
|
||||
|
||||
// TODO: multi thread protection
|
||||
override fun getResolveState(moduleInfo: IdeaModuleInfo): FirModuleResolveState {
|
||||
var moduleData = stateCache.getOrPut(moduleInfo) {
|
||||
createModuleData(moduleInfo)
|
||||
}
|
||||
if (moduleData.isOutOfDate()) {
|
||||
moduleData = createModuleData(moduleInfo)
|
||||
stateCache[moduleInfo] = moduleData
|
||||
}
|
||||
return moduleData.state
|
||||
}
|
||||
|
||||
override val fallbackModificationTracker: ModificationTracker? =
|
||||
KotlinModificationTrackerService.getInstance(project).outOfBlockModificationTracker
|
||||
}
|
||||
@@ -1,112 +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.fir
|
||||
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.PsiElementVisitor
|
||||
import org.jetbrains.kotlin.diagnostics.Diagnostic
|
||||
import org.jetbrains.kotlin.fir.FirElement
|
||||
import org.jetbrains.kotlin.fir.FirSession
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirDiagnostic
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirPsiDiagnostic
|
||||
import org.jetbrains.kotlin.fir.declarations.FirFile
|
||||
import org.jetbrains.kotlin.fir.extensions.BunchOfRegisteredExtensions
|
||||
import org.jetbrains.kotlin.fir.extensions.extensionService
|
||||
import org.jetbrains.kotlin.fir.extensions.registerExtensions
|
||||
import org.jetbrains.kotlin.fir.java.FirProjectSessionProvider
|
||||
import org.jetbrains.kotlin.idea.caches.project.ModuleSourceInfo
|
||||
import org.jetbrains.kotlin.idea.caches.project.getModuleInfo
|
||||
import org.jetbrains.kotlin.psi.KtElement
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
|
||||
interface FirModuleResolveState {
|
||||
val sessionProvider: FirProjectSessionProvider
|
||||
|
||||
fun getSession(psi: KtElement): FirSession {
|
||||
val moduleInfo = psi.getModuleInfo() as ModuleSourceInfo
|
||||
return getSession(psi.project, moduleInfo)
|
||||
}
|
||||
|
||||
fun getSession(project: Project, moduleInfo: ModuleSourceInfo): FirSession {
|
||||
sessionProvider.getSession(moduleInfo)?.let { return it }
|
||||
return synchronized(moduleInfo.module) {
|
||||
val session = sessionProvider.getSession(moduleInfo) ?: FirIdeJavaModuleBasedSession.create(
|
||||
project, moduleInfo, sessionProvider, moduleInfo.contentScope()
|
||||
).also { moduleBasedSession ->
|
||||
sessionProvider.sessionCache[moduleInfo] = moduleBasedSession
|
||||
}
|
||||
session.also {
|
||||
it.extensionService.registerExtensions(BunchOfRegisteredExtensions.empty())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
operator fun get(psi: KtElement): FirElement?
|
||||
|
||||
fun getDiagnostics(psi: KtElement): List<Diagnostic>
|
||||
|
||||
fun hasDiagnosticsForFile(file: KtFile): Boolean
|
||||
|
||||
fun record(psi: KtElement, fir: FirElement)
|
||||
|
||||
fun record(psi: KtElement, diagnostic: Diagnostic)
|
||||
|
||||
fun setDiagnosticsForFile(file: KtFile, fir: FirFile, diagnostics: Iterable<FirDiagnostic<*>> = emptyList())
|
||||
}
|
||||
|
||||
class FirModuleResolveStateImpl(override val sessionProvider: FirProjectSessionProvider) : FirModuleResolveState {
|
||||
private val cache = mutableMapOf<KtElement, FirElement>()
|
||||
|
||||
private val diagnosticCache = mutableMapOf<KtElement, MutableList<Diagnostic>>()
|
||||
|
||||
private val diagnosedFiles = mutableMapOf<KtFile, Long>()
|
||||
|
||||
override fun get(psi: KtElement): FirElement? = cache[psi]
|
||||
|
||||
override fun getDiagnostics(psi: KtElement): List<Diagnostic> {
|
||||
return diagnosticCache[psi] ?: emptyList()
|
||||
}
|
||||
|
||||
override fun hasDiagnosticsForFile(file: KtFile): Boolean {
|
||||
val previousStamp = diagnosedFiles[file] ?: return false
|
||||
if (file.modificationStamp == previousStamp) {
|
||||
return true
|
||||
}
|
||||
diagnosedFiles.remove(file)
|
||||
file.accept(object : PsiElementVisitor() {
|
||||
override fun visitElement(element: PsiElement) {
|
||||
cache.remove(element)
|
||||
diagnosticCache.remove(element)
|
||||
element.acceptChildren(this)
|
||||
super.visitElement(element)
|
||||
}
|
||||
})
|
||||
return false
|
||||
}
|
||||
|
||||
override fun record(psi: KtElement, fir: FirElement) {
|
||||
cache[psi] = fir
|
||||
}
|
||||
|
||||
override fun record(psi: KtElement, diagnostic: Diagnostic) {
|
||||
val list = diagnosticCache.getOrPut(psi) { mutableListOf() }
|
||||
list += diagnostic
|
||||
}
|
||||
|
||||
override fun setDiagnosticsForFile(file: KtFile, fir: FirFile, diagnostics: Iterable<FirDiagnostic<*>>) {
|
||||
for (diagnostic in diagnostics) {
|
||||
require(diagnostic is FirPsiDiagnostic<*>)
|
||||
val psi = diagnostic.element.psi as? KtElement ?: continue
|
||||
record(psi, diagnostic.asPsiBasedDiagnostic())
|
||||
}
|
||||
|
||||
diagnosedFiles[file] = file.modificationStamp
|
||||
}
|
||||
}
|
||||
|
||||
fun KtElement.firResolveState(): FirModuleResolveState =
|
||||
FirIdeResolveStateService.getInstance(project).getResolveState(getModuleInfo())
|
||||
@@ -1,25 +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.fir
|
||||
|
||||
import com.intellij.openapi.util.registry.Registry
|
||||
|
||||
// Just a wrapper to see whether resolve works via FIR or not
|
||||
object FirResolution {
|
||||
private const val optionName = "kotlin.use.fir.resolution"
|
||||
|
||||
private val initialEnabledValue: Boolean by lazy {
|
||||
Registry.`is`(optionName, /* defaultValue = */ true)
|
||||
}
|
||||
|
||||
private var changedEnabledValue: Boolean? = null
|
||||
|
||||
var enabled: Boolean
|
||||
get() = changedEnabledValue ?: initialEnabledValue
|
||||
set(value) {
|
||||
changedEnabledValue = value
|
||||
}
|
||||
}
|
||||
@@ -1,286 +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.fir
|
||||
|
||||
import com.intellij.openapi.progress.ProgressIndicatorProvider
|
||||
import com.intellij.psi.PsiElement
|
||||
import org.jetbrains.kotlin.fir.FirElement
|
||||
import org.jetbrains.kotlin.fir.FirSession
|
||||
import org.jetbrains.kotlin.fir.declarations.*
|
||||
import org.jetbrains.kotlin.fir.psi
|
||||
import org.jetbrains.kotlin.fir.references.*
|
||||
import org.jetbrains.kotlin.fir.render
|
||||
import org.jetbrains.kotlin.fir.resolve.*
|
||||
import org.jetbrains.kotlin.fir.resolve.providers.FirProvider
|
||||
import org.jetbrains.kotlin.fir.resolve.providers.getClassDeclaredCallableSymbols
|
||||
import org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.FirBodyResolveTransformer
|
||||
import org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.createReturnTypeCalculatorForIDE
|
||||
import org.jetbrains.kotlin.fir.resolve.transformers.runResolve
|
||||
import org.jetbrains.kotlin.fir.scopes.impl.FirPackageMemberScope
|
||||
import org.jetbrains.kotlin.fir.symbols.CallableId
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol
|
||||
import org.jetbrains.kotlin.fir.types.FirErrorTypeRef
|
||||
import org.jetbrains.kotlin.fir.types.FirUserTypeRef
|
||||
import org.jetbrains.kotlin.fir.visitors.CompositeTransformResult
|
||||
import org.jetbrains.kotlin.fir.visitors.FirVisitorVoid
|
||||
import org.jetbrains.kotlin.fir.visitors.compose
|
||||
import org.jetbrains.kotlin.name.ClassId
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.psi.psiUtil.containingClassOrObject
|
||||
import org.jetbrains.kotlin.util.containingNonLocalDeclaration
|
||||
|
||||
private val FirResolvePhase.stubMode: Boolean
|
||||
get() = this <= FirResolvePhase.DECLARATIONS
|
||||
|
||||
private fun KtClassOrObject.relativeFqName(): FqName {
|
||||
val className = this.nameAsSafeName
|
||||
val parentFqName = this.containingClassOrObject?.relativeFqName()
|
||||
return parentFqName?.child(className) ?: FqName.topLevel(className)
|
||||
}
|
||||
|
||||
private fun FirFile.findCallableMember(
|
||||
provider: FirProvider, callableMember: KtCallableDeclaration,
|
||||
packageFqName: FqName, klassFqName: FqName?, declName: Name
|
||||
): FirCallableDeclaration<*> {
|
||||
if (klassFqName != null) {
|
||||
return provider.getClassDeclaredCallableSymbols(ClassId(packageFqName, klassFqName, false), declName)
|
||||
.find { symbol: FirCallableSymbol<*> ->
|
||||
symbol.fir.psi == callableMember
|
||||
}?.fir ?: error("Cannot find FIR callable declaration ${CallableId(packageFqName, klassFqName, declName)}")
|
||||
}
|
||||
// NB: not sure it's correct to use member scope provider from here (because of possible changes)
|
||||
val memberScope = FirPackageMemberScope(this.packageFqName, session)
|
||||
var result: FirCallableDeclaration<*>? = null
|
||||
val processor = { symbol: FirCallableSymbol<*> ->
|
||||
val fir = symbol.fir
|
||||
if (result == null && fir.psi == callableMember) {
|
||||
result = fir
|
||||
}
|
||||
}
|
||||
if (callableMember is KtNamedFunction || callableMember is KtConstructor<*>) {
|
||||
memberScope.processFunctionsByName(declName, processor)
|
||||
} else {
|
||||
memberScope.processPropertiesByName(declName, processor)
|
||||
}
|
||||
|
||||
return result
|
||||
?: error("Cannot find FIR callable declaration ${CallableId(packageFqName, klassFqName, declName)}")
|
||||
}
|
||||
|
||||
fun KtCallableDeclaration.getOrBuildFir(
|
||||
state: FirModuleResolveState,
|
||||
phase: FirResolvePhase = FirResolvePhase.DECLARATIONS
|
||||
): FirCallableDeclaration<*> {
|
||||
val session = state.getSession(this)
|
||||
|
||||
val file = this.containingKtFile
|
||||
val packageFqName = file.packageFqName
|
||||
val klassFqName = this.containingClassOrObject?.relativeFqName()
|
||||
val declName = this.nameAsSafeName
|
||||
|
||||
val firProvider = session.firIdeProvider
|
||||
val firFile = firProvider.getOrBuildFile(file)
|
||||
val firMemberSymbol = firFile.findCallableMember(firProvider, this, packageFqName, klassFqName, declName).symbol
|
||||
val firMemberDeclaration = firMemberSymbol.fir
|
||||
if (firMemberDeclaration.resolvePhase >= phase) {
|
||||
return firMemberDeclaration
|
||||
}
|
||||
synchronized(firFile) {
|
||||
firMemberDeclaration.runResolve(firFile, firProvider, phase, state)
|
||||
}
|
||||
return firMemberDeclaration
|
||||
}
|
||||
|
||||
fun KtClassOrObject.getOrBuildFir(
|
||||
state: FirModuleResolveState,
|
||||
phase: FirResolvePhase = FirResolvePhase.DECLARATIONS
|
||||
): FirMemberDeclaration {
|
||||
val session = state.getSession(this)
|
||||
|
||||
val file = this.containingKtFile
|
||||
val packageFqName = file.packageFqName
|
||||
val klassFqName = this.relativeFqName()
|
||||
|
||||
val firProvider = session.firIdeProvider
|
||||
val firFile = firProvider.getOrBuildFile(file)
|
||||
|
||||
val firClassOrEnumEntry = if (this is KtEnumEntry) {
|
||||
val firEnumClass = firProvider.getFirClassifierByFqName(ClassId(packageFqName, klassFqName.parent(), false)) as FirRegularClass
|
||||
firEnumClass.declarations.first { it is FirEnumEntry && it.name == this.nameAsSafeName } as FirMemberDeclaration
|
||||
} else {
|
||||
firProvider.getFirClassifierByFqName(ClassId(packageFqName, klassFqName, false)) as FirRegularClass
|
||||
}
|
||||
if (firClassOrEnumEntry.resolvePhase >= phase) {
|
||||
return firClassOrEnumEntry
|
||||
}
|
||||
synchronized(firFile) {
|
||||
firClassOrEnumEntry.runResolve(firFile, firProvider, phase, state)
|
||||
}
|
||||
return firClassOrEnumEntry
|
||||
}
|
||||
|
||||
private fun KtFile.getOrBuildRawFirFile(state: FirModuleResolveState): Pair<FirIdeProvider, FirFile> {
|
||||
val session = state.getSession(this)
|
||||
val firProvider = session.firIdeProvider
|
||||
return firProvider to firProvider.getOrBuildFile(this)
|
||||
}
|
||||
|
||||
fun KtFile.getOrBuildFir(
|
||||
state: FirModuleResolveState,
|
||||
phase: FirResolvePhase = FirResolvePhase.DECLARATIONS
|
||||
): FirFile {
|
||||
val (firProvider, firFile) = getOrBuildRawFirFile(state)
|
||||
if (phase <= FirResolvePhase.DECLARATIONS && firFile.resolvePhase >= phase) {
|
||||
return firFile
|
||||
}
|
||||
synchronized(firFile) {
|
||||
firFile.runResolve(firFile, firProvider, phase, state)
|
||||
}
|
||||
return firFile
|
||||
}
|
||||
|
||||
fun KtFile.getOrBuildFirWithDiagnostics(state: FirModuleResolveState): FirFile {
|
||||
val (_, firFile) = getOrBuildRawFirFile(state)
|
||||
val currentResolvePhase = firFile.resolvePhase
|
||||
if (currentResolvePhase < FirResolvePhase.BODY_RESOLVE) {
|
||||
synchronized(firFile) {
|
||||
firFile.runResolve(toPhase = FirResolvePhase.BODY_RESOLVE, fromPhase = currentResolvePhase)
|
||||
}
|
||||
}
|
||||
|
||||
ProgressIndicatorProvider.checkCanceled() // ???
|
||||
if (state.hasDiagnosticsForFile(this)) return firFile
|
||||
|
||||
FirIdeDiagnosticsCollector(firFile.session, state).collectDiagnostics(firFile)
|
||||
state.setDiagnosticsForFile(this, firFile)
|
||||
return firFile
|
||||
}
|
||||
|
||||
private fun FirDeclaration.runResolve(
|
||||
file: FirFile,
|
||||
firProvider: FirIdeProvider,
|
||||
toPhase: FirResolvePhase,
|
||||
state: FirModuleResolveState
|
||||
) {
|
||||
val nonLazyPhase = minOf(toPhase, FirResolvePhase.DECLARATIONS)
|
||||
file.runResolve(toPhase = nonLazyPhase, fromPhase = this.resolvePhase)
|
||||
if (toPhase <= nonLazyPhase) return
|
||||
val designation = mutableListOf<FirDeclaration>(file)
|
||||
if (this !is FirFile) {
|
||||
val id = when (this) {
|
||||
is FirCallableDeclaration<*> -> {
|
||||
this.symbol.callableId.classId
|
||||
}
|
||||
is FirRegularClass -> {
|
||||
this.symbol.classId
|
||||
}
|
||||
else -> error("Unsupported: ${render()}")
|
||||
}
|
||||
val outerClasses = generateSequence(id) { classId ->
|
||||
classId.outerClassId
|
||||
}.mapTo(mutableListOf()) { firProvider.getFirClassifierByFqName(it)!! }
|
||||
designation += outerClasses.asReversed()
|
||||
if (this is FirCallableDeclaration<*>) {
|
||||
designation += this
|
||||
}
|
||||
}
|
||||
if (designation.all { it.resolvePhase >= toPhase }) {
|
||||
return
|
||||
}
|
||||
val scopeSession = ScopeSession()
|
||||
val transformer = FirDesignatedBodyResolveTransformerForIDE(
|
||||
designation.iterator(), state.getSession(psi as KtElement),
|
||||
scopeSession,
|
||||
implicitTypeOnly = toPhase == FirResolvePhase.IMPLICIT_TYPES_BODY_RESOLVE
|
||||
)
|
||||
file.transform<FirFile, ResolutionMode>(transformer, ResolutionMode.ContextDependent)
|
||||
}
|
||||
|
||||
private class FirDesignatedBodyResolveTransformerForIDE(
|
||||
private val designation: Iterator<FirElement>,
|
||||
session: FirSession,
|
||||
scopeSession: ScopeSession,
|
||||
implicitTypeOnly: Boolean
|
||||
) : FirBodyResolveTransformer(
|
||||
session,
|
||||
phase = FirResolvePhase.IMPLICIT_TYPES_BODY_RESOLVE,
|
||||
implicitTypeOnly = implicitTypeOnly,
|
||||
scopeSession = scopeSession,
|
||||
returnTypeCalculator = createReturnTypeCalculatorForIDE(session, scopeSession)
|
||||
) {
|
||||
override fun transformDeclarationContent(declaration: FirDeclaration, data: ResolutionMode): CompositeTransformResult<FirDeclaration> {
|
||||
if (designation.hasNext()) {
|
||||
designation.next().visitNoTransform(this, data)
|
||||
return declaration.compose()
|
||||
}
|
||||
|
||||
return super.transformDeclarationContent(declaration, data)
|
||||
}
|
||||
}
|
||||
|
||||
fun KtElement.getOrBuildFir(
|
||||
state: FirModuleResolveState,
|
||||
phase: FirResolvePhase = FirResolvePhase.BODY_RESOLVE
|
||||
): FirElement {
|
||||
val containerFir: FirDeclaration =
|
||||
when (val container = this.containingNonLocalDeclaration()) {
|
||||
is KtCallableDeclaration -> container.getOrBuildFir(state, phase)
|
||||
is KtClassOrObject -> container.getOrBuildFir(state, phase)
|
||||
null -> containingKtFile.getOrBuildFir(state, phase)
|
||||
else -> error("Unsupported: ${container.text}")
|
||||
}
|
||||
|
||||
val psi = when (this) {
|
||||
is KtPropertyDelegate -> this.expression ?: this
|
||||
else -> this
|
||||
}
|
||||
return state[this] ?: run {
|
||||
containerFir.accept(object : FirVisitorVoid() {
|
||||
override fun visitElement(element: FirElement) {
|
||||
(element.psi as? KtElement)?.let {
|
||||
state.record(it, element)
|
||||
}
|
||||
element.acceptChildren(this)
|
||||
}
|
||||
|
||||
override fun visitReference(reference: FirReference) {}
|
||||
|
||||
override fun visitControlFlowGraphReference(controlFlowGraphReference: FirControlFlowGraphReference) {}
|
||||
|
||||
override fun visitNamedReference(namedReference: FirNamedReference) {}
|
||||
|
||||
override fun visitResolvedNamedReference(resolvedNamedReference: FirResolvedNamedReference) {}
|
||||
|
||||
override fun visitDelegateFieldReference(delegateFieldReference: FirDelegateFieldReference) {}
|
||||
|
||||
override fun visitBackingFieldReference(backingFieldReference: FirBackingFieldReference) {}
|
||||
|
||||
override fun visitSuperReference(superReference: FirSuperReference) {}
|
||||
|
||||
override fun visitThisReference(thisReference: FirThisReference) {}
|
||||
|
||||
override fun visitErrorTypeRef(errorTypeRef: FirErrorTypeRef) {}
|
||||
|
||||
override fun visitUserTypeRef(userTypeRef: FirUserTypeRef) {
|
||||
userTypeRef.acceptChildren(this)
|
||||
}
|
||||
})
|
||||
var current: PsiElement? = psi
|
||||
while (current is KtElement) {
|
||||
val mappedFir = state[current]
|
||||
if (mappedFir != null) {
|
||||
if (current != this) {
|
||||
state.record(current, mappedFir)
|
||||
}
|
||||
return mappedFir
|
||||
}
|
||||
current = current.parent
|
||||
}
|
||||
error("FirElement is not found for: $text")
|
||||
}
|
||||
}
|
||||
@@ -1,17 +1,6 @@
|
||||
/*
|
||||
* 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.
|
||||
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.idea.highlighter
|
||||
@@ -59,6 +48,7 @@ open class KotlinPsiChecker : Annotator, HighlightRangeExtension {
|
||||
|
||||
if (!KotlinHighlightingUtil.shouldHighlight(file)) return
|
||||
|
||||
//todo move all fir stuff to fir plugin
|
||||
if (FirResolution.enabled) {
|
||||
annotateElementUsingFrontendIR(element, file, holder)
|
||||
} else {
|
||||
|
||||
-164
@@ -1,164 +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.references
|
||||
|
||||
import com.intellij.psi.PsiElement
|
||||
import org.jetbrains.kotlin.fir.FirSession
|
||||
import org.jetbrains.kotlin.fir.declarations.*
|
||||
import org.jetbrains.kotlin.fir.expressions.*
|
||||
import org.jetbrains.kotlin.fir.psi
|
||||
import org.jetbrains.kotlin.fir.references.*
|
||||
import org.jetbrains.kotlin.fir.resolve.firSymbolProvider
|
||||
import org.jetbrains.kotlin.fir.resolve.toSymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.AbstractFirBasedSymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.ConeClassLikeLookupTagImpl
|
||||
import org.jetbrains.kotlin.fir.types.ConeLookupTagBasedType
|
||||
import org.jetbrains.kotlin.fir.types.FirResolvedTypeRef
|
||||
import org.jetbrains.kotlin.idea.fir.firResolveState
|
||||
import org.jetbrains.kotlin.idea.fir.getOrBuildFir
|
||||
import org.jetbrains.kotlin.name.ClassId
|
||||
import org.jetbrains.kotlin.psi.KtDotQualifiedExpression
|
||||
import org.jetbrains.kotlin.psi.KtElement
|
||||
import org.jetbrains.kotlin.psi.KtPackageDirective
|
||||
import org.jetbrains.kotlin.psi.psiUtil.getNonStrictParentOfType
|
||||
|
||||
object FirReferenceResolveHelper {
|
||||
fun FirResolvedTypeRef.toTargetPsi(session: FirSession): PsiElement? {
|
||||
val type = type as? ConeLookupTagBasedType ?: return null
|
||||
return (type.lookupTag.toSymbol(session) as? AbstractFirBasedSymbol<*>)?.fir?.psi
|
||||
}
|
||||
|
||||
fun ClassId.toTargetPsi(session: FirSession, calleeReference: FirReference? = null): PsiElement? {
|
||||
val classLikeDeclaration = ConeClassLikeLookupTagImpl(this).toSymbol(session)?.fir
|
||||
if (classLikeDeclaration is FirRegularClass) {
|
||||
if (calleeReference is FirResolvedNamedReference) {
|
||||
val callee = calleeReference.resolvedSymbol.fir as? FirCallableMemberDeclaration
|
||||
// TODO: check callee owner directly?
|
||||
if (callee !is FirConstructor && callee?.isStatic != true) {
|
||||
classLikeDeclaration.companionObject?.let { return it.psi }
|
||||
}
|
||||
}
|
||||
}
|
||||
return classLikeDeclaration?.psi
|
||||
}
|
||||
|
||||
fun FirReference.toTargetPsi(session: FirSession): PsiElement? {
|
||||
return when (this) {
|
||||
is FirResolvedNamedReference -> {
|
||||
resolvedSymbol.fir.psi
|
||||
}
|
||||
is FirResolvedCallableReference -> {
|
||||
resolvedSymbol.fir.psi
|
||||
}
|
||||
is FirThisReference -> {
|
||||
boundSymbol?.fir?.psi
|
||||
}
|
||||
is FirSuperReference -> {
|
||||
(superTypeRef as? FirResolvedTypeRef)?.toTargetPsi(session)
|
||||
}
|
||||
else -> {
|
||||
null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun resolveToPsiElements(ref: AbstractKtReference<KtElement>): Collection<PsiElement> {
|
||||
val expression = ref.expression
|
||||
val state = expression.firResolveState()
|
||||
val session = state.getSession(expression)
|
||||
when (val fir = expression.getOrBuildFir(state)) {
|
||||
is FirResolvable -> {
|
||||
return listOfNotNull(fir.calleeReference.toTargetPsi(session))
|
||||
}
|
||||
is FirResolvedTypeRef -> {
|
||||
return listOfNotNull(fir.toTargetPsi(session))
|
||||
}
|
||||
is FirResolvedQualifier -> {
|
||||
val classId = fir.classId ?: return emptyList()
|
||||
// Distinguish A.foo() from A(.Companion).foo()
|
||||
// Make expression.parent as? KtDotQualifiedExpression local function
|
||||
var parent = expression.parent as? KtDotQualifiedExpression
|
||||
while (parent != null) {
|
||||
val selectorExpression = parent.selectorExpression ?: break
|
||||
if (selectorExpression === expression) {
|
||||
parent = parent.parent as? KtDotQualifiedExpression
|
||||
continue
|
||||
}
|
||||
val parentFir = selectorExpression.getOrBuildFir(state)
|
||||
if (parentFir is FirQualifiedAccess) {
|
||||
return listOfNotNull(classId.toTargetPsi(session, parentFir.calleeReference))
|
||||
}
|
||||
parent = parent.parent as? KtDotQualifiedExpression
|
||||
}
|
||||
return listOfNotNull(classId.toTargetPsi(session))
|
||||
}
|
||||
is FirAnnotationCall -> {
|
||||
val type = fir.typeRef as? FirResolvedTypeRef ?: return emptyList()
|
||||
return listOfNotNull(type.toTargetPsi(session))
|
||||
}
|
||||
is FirResolvedImport -> {
|
||||
var parent = expression.parent
|
||||
while (parent is KtDotQualifiedExpression) {
|
||||
if (parent.selectorExpression !== expression) {
|
||||
// Special: package reference in the middle of import directive
|
||||
// import a.<caret>b.c.SomeClass
|
||||
// TODO: return reference to PsiPackage
|
||||
return listOf(expression)
|
||||
}
|
||||
parent = parent.parent
|
||||
}
|
||||
val classId = fir.resolvedClassId
|
||||
if (classId != null) {
|
||||
return listOfNotNull(classId.toTargetPsi(session))
|
||||
}
|
||||
val name = fir.importedName ?: return emptyList()
|
||||
val symbolProvider = session.firSymbolProvider
|
||||
return symbolProvider.getTopLevelCallableSymbols(fir.packageFqName, name).mapNotNull { it.fir.psi } +
|
||||
listOfNotNull(symbolProvider.getClassLikeSymbolByFqName(ClassId(fir.packageFqName, name))?.fir?.psi)
|
||||
}
|
||||
is FirFile -> {
|
||||
if (expression.getNonStrictParentOfType<KtPackageDirective>() != null) {
|
||||
// Special: package reference in the middle of package directive
|
||||
return listOf(expression)
|
||||
}
|
||||
return listOfNotNull(fir.psi)
|
||||
}
|
||||
is FirArrayOfCall -> {
|
||||
// We can't yet find PsiElement for arrayOf, intArrayOf, etc.
|
||||
return emptyList()
|
||||
}
|
||||
is FirErrorNamedReference -> {
|
||||
return emptyList()
|
||||
}
|
||||
else -> {
|
||||
// Handle situation when we're in the middle/beginning of qualifier
|
||||
// <caret>A.B.C.foo() or A.<caret>B.C.foo()
|
||||
// NB: in this case we get some parent FIR, like FirBlock, FirProperty, FirFunction or the like
|
||||
var parent = expression.parent as? KtDotQualifiedExpression
|
||||
var unresolvedCounter = 1
|
||||
while (parent != null) {
|
||||
val selectorExpression = parent.selectorExpression ?: break
|
||||
if (selectorExpression === expression) {
|
||||
parent = parent.parent as? KtDotQualifiedExpression
|
||||
continue
|
||||
}
|
||||
val parentFir = selectorExpression.getOrBuildFir(state)
|
||||
if (parentFir is FirResolvedQualifier) {
|
||||
var classId = parentFir.classId
|
||||
while (unresolvedCounter > 0) {
|
||||
unresolvedCounter--
|
||||
classId = classId?.outerClassId
|
||||
}
|
||||
return listOfNotNull(classId?.toTargetPsi(session))
|
||||
}
|
||||
parent = parent.parent as? KtDotQualifiedExpression
|
||||
unresolvedCounter++
|
||||
}
|
||||
return emptyList()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -24,9 +24,6 @@ import org.jetbrains.kotlin.fir.types.ConeLookupTagBasedType
|
||||
import org.jetbrains.kotlin.fir.types.FirResolvedTypeRef
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.analyze
|
||||
import org.jetbrains.kotlin.idea.codeInsight.DescriptorToSourceUtilsIde
|
||||
import org.jetbrains.kotlin.idea.fir.FirResolution
|
||||
import org.jetbrains.kotlin.idea.fir.firResolveState
|
||||
import org.jetbrains.kotlin.idea.fir.getOrBuildFir
|
||||
import org.jetbrains.kotlin.idea.util.application.runWithCancellationCheck
|
||||
import org.jetbrains.kotlin.name.ClassId
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
@@ -66,10 +63,6 @@ object KotlinDescriptorsBasedReferenceResolver : ResolveCache.PolyVariantResolve
|
||||
class KotlinResolveResult(element: PsiElement) : PsiElementResolveResult(element)
|
||||
|
||||
private fun resolveToPsiElements(ref: KtDescriptorsBasedReference): Collection<PsiElement> {
|
||||
if (FirResolution.enabled) {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
return FirReferenceResolveHelper.resolveToPsiElements(ref as AbstractKtReference<KtElement>)
|
||||
}
|
||||
val bindingContext = ref.element.analyze(BodyResolveMode.PARTIAL)
|
||||
return resolveToPsiElements(ref, bindingContext, ref.getTargetDescriptors(bindingContext))
|
||||
}
|
||||
|
||||
@@ -45,6 +45,9 @@ class KtSimpleNameReferenceDescriptorsImpl(
|
||||
override fun doCanBeReferenceTo(candidateTarget: PsiElement): Boolean =
|
||||
canBeReferenceTo(candidateTarget)
|
||||
|
||||
override fun isReferenceToWithoutExtensionChecking(candidateTarget: PsiElement): Boolean =
|
||||
matchesTarget(candidateTarget)
|
||||
|
||||
override fun getTargetDescriptors(context: BindingContext): Collection<DeclarationDescriptor> {
|
||||
return SmartList<DeclarationDescriptor>().apply {
|
||||
// Replace Java property with its accessor(s)
|
||||
|
||||
Reference in New Issue
Block a user