FIR IDE: simplify completion context retrieving

This commit is contained in:
Ilya Kirillov
2021-04-19 18:59:04 +02:00
committed by TeamCityServer
parent f8efe3b5df
commit a5c33c8d42
8 changed files with 173 additions and 196 deletions
@@ -70,12 +70,10 @@ internal class FirModuleResolveStateForCompletion(
} }
override fun lazyResolveDeclarationForCompletion( override fun lazyResolveDeclarationForCompletion(
firFunction: FirDeclaration, firDeclaration: FirDeclaration,
containerFirFile: FirFile, containerFirFile: FirFile,
firIdeProvider: FirProvider,
toPhase: FirResolvePhase,
) { ) {
originalState.lazyResolveDeclarationForCompletion(firFunction, containerFirFile, firIdeProvider, toPhase) originalState.lazyResolveDeclarationForCompletion(firDeclaration, containerFirFile)
} }
override fun getFirFile(declaration: FirDeclaration, cache: ModuleFileCache): FirFile? { override fun getFirFile(declaration: FirDeclaration, cache: ModuleFileCache): FirFile? {
@@ -157,19 +157,17 @@ internal class FirModuleResolveStateImpl(
} }
override fun lazyResolveDeclarationForCompletion( override fun lazyResolveDeclarationForCompletion(
firFunction: FirDeclaration, firDeclaration: FirDeclaration,
containerFirFile: FirFile, containerFirFile: FirFile,
firIdeProvider: FirProvider,
toPhase: FirResolvePhase,
) { ) {
firFileBuilder.runCustomResolveWithPCECheck(containerFirFile, rootModuleSession.cache) { firFileBuilder.runCustomResolveWithPCECheck(containerFirFile, rootModuleSession.cache) {
firLazyDeclarationResolver.runLazyResolveWithoutLock( firLazyDeclarationResolver.runLazyResolveWithoutLock(
firFunction, firDeclaration,
rootModuleSession.cache, rootModuleSession.cache,
containerFirFile, containerFirFile,
firIdeProvider, containerFirFile.session.firIdeProvider,
fromPhase = firFunction.resolvePhase, fromPhase = firDeclaration.resolvePhase,
toPhase, toPhase = FirResolvePhase.BODY_RESOLVE,
towerDataContextCollector = collector, towerDataContextCollector = collector,
checkPCE = true checkPCE = true
) )
@@ -0,0 +1,118 @@
/*
* 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.fir.low.level.api.api
import org.jetbrains.kotlin.fir.FirElement
import org.jetbrains.kotlin.fir.builder.RawFirFragmentForLazyBodiesBuilder
import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.fir.declarations.builder.buildPropertyAccessorCopy
import org.jetbrains.kotlin.fir.declarations.builder.buildSimpleFunctionCopy
import org.jetbrains.kotlin.fir.expressions.FirReturnExpression
import org.jetbrains.kotlin.fir.visitors.FirVisitorVoid
import org.jetbrains.kotlin.idea.fir.low.level.api.providers.firIdeProvider
import org.jetbrains.kotlin.psi.KtDeclaration
import org.jetbrains.kotlin.psi.KtNamedFunction
import org.jetbrains.kotlin.psi.KtProperty
object DeclarationCopyBuilder {
fun createDeclarationCopy(
originalFirDeclaration: FirDeclaration,
fakeKtDeclaration: KtDeclaration,
state: FirModuleResolveState,
): FirDeclaration {
return when (fakeKtDeclaration) {
is KtNamedFunction -> buildFunctionCopy(
fakeKtDeclaration,
originalFirDeclaration as FirSimpleFunction,
state
)
is KtProperty -> buildPropertyCopy(
fakeKtDeclaration,
originalFirDeclaration as FirProperty,
state
)
else -> error("Unsupported declaration ${fakeKtDeclaration::class.simpleName}")
}
}
private fun buildFunctionCopy(
element: KtNamedFunction,
originalFunction: FirSimpleFunction,
state: FirModuleResolveState,
): FirSimpleFunction {
val builtFunction = createCopy(element, originalFunction) as FirSimpleFunction
// right now we can't resolve builtFunction header properly, as it built right in air,
// without file, which is now required for running stages other then body resolve, so we
// take original function header (which is resolved) and copy replacing body with body from builtFunction
return buildSimpleFunctionCopy(originalFunction) {
body = builtFunction.body
symbol = builtFunction.symbol
resolvePhase = minOf(originalFunction.resolvePhase, FirResolvePhase.DECLARATIONS)
source = builtFunction.source
session = state.rootModuleSession
}.apply { reassignAllReturnTargets(builtFunction) }
}
private fun buildPropertyCopy(
element: KtProperty,
originalProperty: FirProperty,
state: FirModuleResolveState
): FirProperty {
val builtProperty = createCopy(element, originalProperty) as FirProperty
val originalSetter = originalProperty.setter
val builtSetter = builtProperty.setter
// setter has a header with `value` parameter, and we want it type to be resolved
val copySetter = if (originalSetter != null && builtSetter != null) {
buildPropertyAccessorCopy(originalSetter) {
body = builtSetter.body
symbol = builtSetter.symbol
resolvePhase = minOf(builtSetter.resolvePhase, FirResolvePhase.DECLARATIONS)
source = builtSetter.source
session = state.rootModuleSession
}.apply { reassignAllReturnTargets(builtSetter) }
} else {
builtSetter
}
return org.jetbrains.kotlin.fir.declarations.builder.buildPropertyCopy(originalProperty) {
symbol = builtProperty.symbol
initializer = builtProperty.initializer
getter = builtProperty.getter
setter = copySetter
resolvePhase = minOf(originalProperty.resolvePhase, FirResolvePhase.DECLARATIONS)
source = builtProperty.source
session = state.rootModuleSession
}
}
fun createCopy(
fakeKtDeclaration: KtDeclaration,
originalFirDeclaration: FirDeclaration,
): FirDeclaration {
return RawFirFragmentForLazyBodiesBuilder.build(
session = originalFirDeclaration.session,
baseScopeProvider = originalFirDeclaration.session.firIdeProvider.kotlinScopeProvider,
designation = originalFirDeclaration.collectDesignation().fullDesignation,
declaration = fakeKtDeclaration
)
}
private fun FirFunction<*>.reassignAllReturnTargets(from: FirFunction<*>) {
this.accept(object : FirVisitorVoid() {
override fun visitElement(element: FirElement) {
if (element is FirReturnExpression && element.target.labeledElement == from) {
element.target.bind(this@reassignAllReturnTargets)
}
element.acceptChildren(this)
}
})
}
}
@@ -83,10 +83,8 @@ abstract class FirModuleResolveState {
// todo temporary, used only in completion // todo temporary, used only in completion
internal abstract fun lazyResolveDeclarationForCompletion( internal abstract fun lazyResolveDeclarationForCompletion(
firFunction: FirDeclaration, firDeclaration: FirDeclaration,
containerFirFile: FirFile, containerFirFile: FirFile,
firIdeProvider: FirProvider,
toPhase: FirResolvePhase,
) )
internal abstract fun getFirFile(declaration: FirDeclaration, cache: ModuleFileCache): FirFile? internal abstract fun getFirFile(declaration: FirDeclaration, cache: ModuleFileCache): FirFile?
@@ -5,20 +5,11 @@
package org.jetbrains.kotlin.idea.fir.low.level.api.api package org.jetbrains.kotlin.idea.fir.low.level.api.api
import org.jetbrains.kotlin.fir.FirElement
import org.jetbrains.kotlin.fir.declarations.* import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.fir.declarations.builder.buildPropertyAccessorCopy
import org.jetbrains.kotlin.fir.declarations.builder.buildPropertyCopy
import org.jetbrains.kotlin.fir.declarations.builder.buildSimpleFunctionCopy
import org.jetbrains.kotlin.fir.expressions.FirReturnExpression
import org.jetbrains.kotlin.fir.symbols.impl.FirNamedFunctionSymbol
import org.jetbrains.kotlin.fir.visitors.FirVisitorVoid
import org.jetbrains.kotlin.idea.fir.low.level.api.FirModuleResolveStateForCompletion import org.jetbrains.kotlin.idea.fir.low.level.api.FirModuleResolveStateForCompletion
import org.jetbrains.kotlin.idea.fir.low.level.api.FirModuleResolveStateImpl import org.jetbrains.kotlin.idea.fir.low.level.api.FirModuleResolveStateImpl
import org.jetbrains.kotlin.idea.fir.low.level.api.providers.FirIdeProvider import org.jetbrains.kotlin.psi.KtDeclaration
import org.jetbrains.kotlin.idea.fir.low.level.api.providers.firIdeProvider import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.psi.KtNamedFunction
import org.jetbrains.kotlin.psi.KtProperty
object LowLevelFirApiFacadeForCompletion { object LowLevelFirApiFacadeForCompletion {
fun getResolveStateForCompletion(originalState: FirModuleResolveState): FirModuleResolveState { fun getResolveStateForCompletion(originalState: FirModuleResolveState): FirModuleResolveState {
@@ -26,102 +17,17 @@ object LowLevelFirApiFacadeForCompletion {
return FirModuleResolveStateForCompletion(originalState.project, originalState) return FirModuleResolveStateForCompletion(originalState.project, originalState)
} }
fun recordCompletionContextForFunction( fun recordCompletionContextForDeclaration(
firFile: FirFile, firFile: FirFile,
fakeElement: KtNamedFunction, fakeKtDeclaration: KtDeclaration,
originalElement: KtNamedFunction, originalKtDeclaration: KtDeclaration,
fakeContainingFile: KtFile,
state: FirModuleResolveState, state: FirModuleResolveState,
) { ) {
val firIdeProvider = firFile.session.firIdeProvider val originalFirDeclaration = originalKtDeclaration.getOrBuildFirOfType<FirDeclaration>(state)
val fakeFirDeclaration = DeclarationCopyBuilder.createDeclarationCopy(originalFirDeclaration, fakeKtDeclaration, state)
val originalFunction = state.getOrBuildFirFor(originalElement) as FirSimpleFunction state.lazyResolveDeclarationForCompletion(fakeFirDeclaration, firFile)
val copyFunction = buildFunctionCopyForCompletion(firIdeProvider, fakeElement, originalFunction, state) state.recordPsiToFirMappingsForCompletionFrom(fakeFirDeclaration, firFile, fakeContainingFile)
state.lazyResolveDeclarationForCompletion(copyFunction, firFile, firIdeProvider, FirResolvePhase.BODY_RESOLVE)
state.recordPsiToFirMappingsForCompletionFrom(copyFunction, firFile, fakeElement.containingKtFile)
}
fun recordCompletionContextForProperty(
firFile: FirFile,
fakeElement: KtProperty,
originalElement: KtProperty,
state: FirModuleResolveState,
) {
val firIdeProvider = firFile.session.firIdeProvider
val originalProperty = state.getOrBuildFirFor(originalElement) as FirProperty
val copyProperty = buildPropertyCopyForCompletion(firIdeProvider, fakeElement, originalProperty, state)
state.lazyResolveDeclarationForCompletion(copyProperty, firFile, firIdeProvider, FirResolvePhase.BODY_RESOLVE)
state.recordPsiToFirMappingsForCompletionFrom(copyProperty, firFile, fakeElement.containingKtFile)
}
private fun buildFunctionCopyForCompletion(
firIdeProvider: FirIdeProvider,
element: KtNamedFunction,
originalFunction: FirSimpleFunction,
state: FirModuleResolveState,
): FirSimpleFunction {
val builtFunction = firIdeProvider.buildFunctionWithBody(element, originalFunction)
// right now we can't resolve builtFunction header properly, as it built right in air,
// without file, which is now required for running stages other then body resolve, so we
// take original function header (which is resolved) and copy replacing body with body from builtFunction
return buildSimpleFunctionCopy(originalFunction) {
body = builtFunction.body
symbol = builtFunction.symbol as FirNamedFunctionSymbol
resolvePhase = minOf(originalFunction.resolvePhase, FirResolvePhase.DECLARATIONS)
source = builtFunction.source
session = state.rootModuleSession
}.apply { reassignAllReturnTargets(builtFunction) }
}
private fun buildPropertyCopyForCompletion(
firIdeProvider: FirIdeProvider,
element: KtProperty,
originalProperty: FirProperty,
state: FirModuleResolveState
): FirProperty {
val builtProperty = firIdeProvider.buildPropertyWithBody(element, originalProperty)
val originalSetter = originalProperty.setter
val builtSetter = builtProperty.setter
// setter has a header with `value` parameter, and we want it type to be resolved
val copySetter = if (originalSetter != null && builtSetter != null) {
buildPropertyAccessorCopy(originalSetter) {
body = builtSetter.body
symbol = builtSetter.symbol
resolvePhase = minOf(builtSetter.resolvePhase, FirResolvePhase.DECLARATIONS)
source = builtSetter.source
session = state.rootModuleSession
}.apply { reassignAllReturnTargets(builtSetter) }
} else {
builtSetter
}
return buildPropertyCopy(originalProperty) {
symbol = builtProperty.symbol
initializer = builtProperty.initializer
getter = builtProperty.getter
setter = copySetter
resolvePhase = minOf(originalProperty.resolvePhase, FirResolvePhase.DECLARATIONS)
source = builtProperty.source
session = state.rootModuleSession
}
}
private fun FirFunction<*>.reassignAllReturnTargets(from: FirFunction<*>) {
this.accept(object : FirVisitorVoid() {
override fun visitElement(element: FirElement) {
if (element is FirReturnExpression && element.target.labeledElement == from) {
element.target.bind(this@reassignAllReturnTargets)
}
element.acceptChildren(this)
}
})
} }
} }
@@ -11,6 +11,7 @@ import org.jetbrains.kotlin.fir.psi
import org.jetbrains.kotlin.fir.symbols.AbstractFirBasedSymbol import org.jetbrains.kotlin.fir.symbols.AbstractFirBasedSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirFunctionSymbol import org.jetbrains.kotlin.fir.symbols.impl.FirFunctionSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirPropertySymbol import org.jetbrains.kotlin.fir.symbols.impl.FirPropertySymbol
import org.jetbrains.kotlin.idea.fir.low.level.api.api.DeclarationCopyBuilder
import org.jetbrains.kotlin.idea.fir.low.level.api.diagnostics.FileDiagnosticRetriever import org.jetbrains.kotlin.idea.fir.low.level.api.diagnostics.FileDiagnosticRetriever
import org.jetbrains.kotlin.idea.fir.low.level.api.diagnostics.FileStructureElementDiagnostics import org.jetbrains.kotlin.idea.fir.low.level.api.diagnostics.FileStructureElementDiagnostics
import org.jetbrains.kotlin.idea.fir.low.level.api.diagnostics.SingleNonLocalDeclarationDiagnosticRetriever import org.jetbrains.kotlin.idea.fir.low.level.api.diagnostics.SingleNonLocalDeclarationDiagnosticRetriever
@@ -79,7 +80,7 @@ internal class ReanalyzableFunctionStructureElement(
towerDataContextCollector: FirTowerDataContextCollector, towerDataContextCollector: FirTowerDataContextCollector,
): ReanalyzableFunctionStructureElement { ): ReanalyzableFunctionStructureElement {
val originalFunction = firSymbol.fir as FirSimpleFunction val originalFunction = firSymbol.fir as FirSimpleFunction
val newFunction = firIdeProvider.buildFunctionWithBody(newKtDeclaration, originalFunction) as FirSimpleFunction val newFunction = DeclarationCopyBuilder.createCopy(newKtDeclaration, originalFunction) as FirSimpleFunction
return FileStructureUtil.withDeclarationReplaced(firFile, cache, originalFunction, newFunction) { return FileStructureUtil.withDeclarationReplaced(firFile, cache, originalFunction, newFunction) {
firLazyDeclarationResolver.lazyResolveDeclaration( firLazyDeclarationResolver.lazyResolveDeclaration(
@@ -121,7 +122,7 @@ internal class ReanalyzablePropertyStructureElement(
towerDataContextCollector: FirTowerDataContextCollector, towerDataContextCollector: FirTowerDataContextCollector,
): ReanalyzablePropertyStructureElement { ): ReanalyzablePropertyStructureElement {
val originalProperty = firSymbol.fir val originalProperty = firSymbol.fir
val newProperty = firIdeProvider.buildPropertyWithBody(newKtDeclaration, originalProperty) val newProperty = DeclarationCopyBuilder.createCopy(newKtDeclaration, originalProperty) as FirProperty
return FileStructureUtil.withDeclarationReplaced(firFile, cache, originalProperty, newProperty) { return FileStructureUtil.withDeclarationReplaced(firFile, cache, originalProperty, newProperty) {
firLazyDeclarationResolver.lazyResolveDeclaration( firLazyDeclarationResolver.lazyResolveDeclaration(
@@ -91,32 +91,6 @@ internal class FirIdeProvider(
override fun getFirFilesByPackage(fqName: FqName): List<FirFile> = error("Should not be called in FIR IDE") override fun getFirFilesByPackage(fqName: FqName): List<FirFile> = error("Should not be called in FIR IDE")
// TODO move out of here
// used only for completion
fun buildFunctionWithBody(
ktNamedFunction: KtNamedFunction,
original: FirFunction<*>,
): FirFunction<*> {
return RawFirFragmentForLazyBodiesBuilder.build(
session = original.session,
baseScopeProvider = original.session.firIdeProvider.kotlinScopeProvider,
designation = original.collectDesignation().fullDesignation,
declaration = ktNamedFunction
) as FirFunction<*>
}
fun buildPropertyWithBody(
ktNamedFunction: KtProperty,
original: FirProperty,
): FirProperty {
return RawFirFragmentForLazyBodiesBuilder.build(
session = original.session,
baseScopeProvider = original.session.firIdeProvider.kotlinScopeProvider,
designation = original.collectDesignation().fullDesignation,
declaration = ktNamedFunction
) as FirProperty
}
@FirProviderInternals @FirProviderInternals
override fun recordGeneratedClass(owner: FirAnnotatedDeclaration, klass: FirRegularClass) { override fun recordGeneratedClass(owner: FirAnnotatedDeclaration, klass: FirRegularClass) {
TODO() TODO()
@@ -5,6 +5,7 @@
package org.jetbrains.kotlin.idea.frontend.api.fir.utils package org.jetbrains.kotlin.idea.frontend.api.fir.utils
import com.intellij.psi.util.PsiTreeUtil
import com.intellij.psi.util.parentsOfType import com.intellij.psi.util.parentsOfType
import org.jetbrains.kotlin.fir.declarations.FirFile import org.jetbrains.kotlin.fir.declarations.FirFile
import org.jetbrains.kotlin.idea.fir.low.level.api.api.FirModuleResolveState import org.jetbrains.kotlin.idea.fir.low.level.api.api.FirModuleResolveState
@@ -13,30 +14,30 @@ import org.jetbrains.kotlin.idea.fir.low.level.api.util.originalDeclaration
import org.jetbrains.kotlin.idea.util.getElementTextInContext import org.jetbrains.kotlin.idea.util.getElementTextInContext
import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.containingClassOrObject import org.jetbrains.kotlin.psi.psiUtil.containingClassOrObject
import org.jetbrains.kotlin.psi.psiUtil.getNonStrictParentOfType import kotlin.reflect.KClass
internal sealed class EnclosingDeclarationContext { internal sealed class EnclosingDeclarationContext {
abstract val fakeKtEnclosingDeclaration: KtDeclaration
abstract val originalKtEnclosingDeclaration: KtDeclaration
companion object { companion object {
private fun KtDeclaration.canBeEnclosingDeclaration(): Boolean = when (this) {
is KtNamedFunction -> isTopLevel || containingClassOrObject?.isLocal == false
is KtProperty -> isTopLevel || containingClassOrObject?.isLocal == false
is KtClassOrObject -> !isLocal
is KtTypeAlias -> isTopLevel() || containingClassOrObject?.isLocal == false
else -> false
}
fun detect(originalFile: KtFile, positionInFakeFile: KtElement): EnclosingDeclarationContext { fun detect(originalFile: KtFile, positionInFakeFile: KtElement): EnclosingDeclarationContext {
val fakeFunction = positionInFakeFile.parentsOfType<KtNamedFunction>().firstOrNull { val fakeKtDeclaration = positionInFakeFile.parentsOfType<KtNamedDeclaration>().firstOrNull { ktDeclaration ->
it.isTopLevel || it.containingClassOrObject?.isLocal == false ktDeclaration.canBeEnclosingDeclaration()
} }
if (fakeFunction != null) { if (fakeKtDeclaration != null) {
val originalFunction = originalFile.findDeclarationOfTypeAt<KtNamedFunction>(fakeFunction.textOffset) val originalDeclaration = originalFile.findDeclarationOfTypeAt(fakeKtDeclaration.textOffset, fakeKtDeclaration::class)
?: error("Cannot find original function matching to ${fakeFunction.getElementTextInContext()} in $originalFile") ?: error("Cannot find original function matching to ${fakeKtDeclaration.getElementTextInContext()} in $originalFile")
recordOriginalDeclaration(originalFunction, fakeFunction) recordOriginalDeclaration(originalDeclaration, fakeKtDeclaration)
return FunctionContext(fakeFunction, originalFunction) return EnclosingDeclarationContextImpl(fakeKtDeclaration, originalDeclaration)
}
val fakeProperty = positionInFakeFile.parentsOfType<KtProperty>().firstOrNull {
it.isTopLevel || it.containingClassOrObject?.isLocal == false
}
if (fakeProperty != null) {
val originalProperty = originalFile.findDeclarationOfTypeAt<KtProperty>(fakeProperty.textOffset)
?: error("Cannot find original property matching to ${fakeProperty.getElementTextInContext()} in $originalFile")
recordOriginalDeclaration(originalProperty, fakeProperty)
return PropertyContext(fakeProperty, originalProperty)
} }
error("Cannot find enclosing declaration for ${positionInFakeFile.getElementTextInContext()}") error("Cannot find enclosing declaration for ${positionInFakeFile.getElementTextInContext()}")
@@ -55,41 +56,24 @@ internal sealed class EnclosingDeclarationContext {
} }
} }
internal class FunctionContext( internal class EnclosingDeclarationContextImpl(
val fakeEnclosingFunction: KtNamedFunction, override val fakeKtEnclosingDeclaration: KtDeclaration,
val originalEnclosingFunction: KtNamedFunction override val originalKtEnclosingDeclaration: KtDeclaration
) : EnclosingDeclarationContext() ) : EnclosingDeclarationContext()
internal class PropertyContext(
val fakeEnclosingProperty: KtProperty,
val originalEnclosingProperty: KtProperty
) : EnclosingDeclarationContext()
internal val EnclosingDeclarationContext.fakeEnclosingDeclaration: KtCallableDeclaration
get() = when (this) {
is FunctionContext -> fakeEnclosingFunction
is PropertyContext -> fakeEnclosingProperty
}
internal fun EnclosingDeclarationContext.recordCompletionContext(originalFirFile: FirFile, firResolveState: FirModuleResolveState) { internal fun EnclosingDeclarationContext.recordCompletionContext(originalFirFile: FirFile, firResolveState: FirModuleResolveState) {
when (this) { LowLevelFirApiFacadeForCompletion.recordCompletionContextForDeclaration(
is FunctionContext -> LowLevelFirApiFacadeForCompletion.recordCompletionContextForFunction( originalFirFile,
originalFirFile, fakeKtEnclosingDeclaration,
fakeEnclosingFunction, originalKtEnclosingDeclaration,
originalEnclosingFunction, state = firResolveState,
state = firResolveState fakeContainingFile = fakeKtEnclosingDeclaration.containingKtFile
) )
is PropertyContext -> LowLevelFirApiFacadeForCompletion.recordCompletionContextForProperty(
originalFirFile,
fakeEnclosingProperty,
originalEnclosingProperty,
state = firResolveState
)
}
} }
private inline fun <reified T : KtElement> KtFile.findDeclarationOfTypeAt(offset: Int): T? = private fun <T : KtElement> KtFile.findDeclarationOfTypeAt(offset: Int, declarationType: KClass<T>): T? {
findElementAt(offset) val elementAtOffset = findElementAt(offset) ?: return null
?.getNonStrictParentOfType<T>() return PsiTreeUtil.getParentOfType(elementAtOffset, declarationType.java, false)?.takeIf { it.textOffset == offset }
?.takeIf { it.textOffset == offset } }