From 760fb668bb55593b6247c3129c01c3019db8b48e Mon Sep 17 00:00:00 2001 From: Mikhail Glukhikh Date: Fri, 26 Jul 2019 21:25:57 +0300 Subject: [PATCH] Introduce FIR resolution API with lazy resolve to use in IDE Now lazy resolve atomic element is a file (declaration-level resolve) or a callable declaration (expression-level resolve) #KT-24351 In Progress --- .../fir/java/FirJavaModuleBasedSession.kt | 3 + .../kotlin/fir/builder/RawFirBuilder.kt | 3 +- .../jetbrains/kotlin/fir/FirCallResolver.kt | 9 +- .../kotlin/fir/FirModuleBasedSession.kt | 2 - .../kotlin/fir/resolve/ResolveUtils.kt | 6 +- .../kotlin/fir/resolve/SessionHolder.kt | 8 + .../FirAbstractPhaseTransformer.kt | 29 ++- .../transformers/FirBodyResolveTransformer.kt | 25 ++- .../fir/resolve/transformers/PhaseUtils.kt | 34 ++++ .../kotlin/generators/tests/GenerateTests.kt | 5 + .../idea/fir/FirIdeJavaModuleBasedSession.kt | 54 ++++++ .../kotlin/idea/fir/FirResolutionApi.kt | 175 ++++++++++++++++++ .../kotlin/idea/fir/FirResolveState.kt | 69 +++++++ .../kotlin/idea/fir/IdeFirProvider.kt | 99 ++++++++++ idea/idea-core/build.gradle.kts | 4 + .../testData/fir/lazyResolve/simple/callMe.kt | 5 + .../fir/lazyResolve/simple/callMe.txt | 7 + idea/testData/fir/lazyResolve/simple/main.kt | 11 ++ idea/testData/fir/lazyResolve/simple/main.txt | 12 ++ .../fir/lazyResolve/simple/results.txt | 1 + .../fir/lazyResolve/simple/simple.test | 3 + .../fir/lazyResolve/simpleProperty/main.kt | 3 + .../fir/lazyResolve/simpleProperty/main.txt | 4 + .../lazyResolve/simpleProperty/myProperty.kt | 3 + .../lazyResolve/simpleProperty/myProperty.txt | 5 + .../lazyResolve/simpleProperty/results.txt | 1 + .../simpleProperty/simpleProperty.test | 3 + .../idea/fir/AbstractFirLazyResolveTest.kt | 94 ++++++++++ .../idea/fir/FirLazyResolveTestGenerated.java | 41 ++++ 29 files changed, 683 insertions(+), 35 deletions(-) create mode 100644 compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/PhaseUtils.kt create mode 100644 idea/idea-analysis/src/org/jetbrains/kotlin/idea/fir/FirIdeJavaModuleBasedSession.kt create mode 100644 idea/idea-analysis/src/org/jetbrains/kotlin/idea/fir/FirResolutionApi.kt create mode 100644 idea/idea-analysis/src/org/jetbrains/kotlin/idea/fir/FirResolveState.kt create mode 100644 idea/idea-analysis/src/org/jetbrains/kotlin/idea/fir/IdeFirProvider.kt create mode 100644 idea/testData/fir/lazyResolve/simple/callMe.kt create mode 100644 idea/testData/fir/lazyResolve/simple/callMe.txt create mode 100644 idea/testData/fir/lazyResolve/simple/main.kt create mode 100644 idea/testData/fir/lazyResolve/simple/main.txt create mode 100644 idea/testData/fir/lazyResolve/simple/results.txt create mode 100644 idea/testData/fir/lazyResolve/simple/simple.test create mode 100644 idea/testData/fir/lazyResolve/simpleProperty/main.kt create mode 100644 idea/testData/fir/lazyResolve/simpleProperty/main.txt create mode 100644 idea/testData/fir/lazyResolve/simpleProperty/myProperty.kt create mode 100644 idea/testData/fir/lazyResolve/simpleProperty/myProperty.txt create mode 100644 idea/testData/fir/lazyResolve/simpleProperty/results.txt create mode 100644 idea/testData/fir/lazyResolve/simpleProperty/simpleProperty.test create mode 100644 idea/tests/org/jetbrains/kotlin/idea/fir/AbstractFirLazyResolveTest.kt create mode 100644 idea/tests/org/jetbrains/kotlin/idea/fir/FirLazyResolveTestGenerated.java diff --git a/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/FirJavaModuleBasedSession.kt b/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/FirJavaModuleBasedSession.kt index 1f611d99620..a726e31644f 100644 --- a/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/FirJavaModuleBasedSession.kt +++ b/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/FirJavaModuleBasedSession.kt @@ -15,6 +15,7 @@ import org.jetbrains.kotlin.fir.resolve.FirSymbolProvider import org.jetbrains.kotlin.fir.resolve.impl.FirCompositeSymbolProvider import org.jetbrains.kotlin.fir.resolve.impl.FirDependenciesSymbolProviderImpl import org.jetbrains.kotlin.fir.resolve.impl.FirLibrarySymbolProviderImpl +import org.jetbrains.kotlin.fir.resolve.impl.FirProviderImpl import org.jetbrains.kotlin.fir.scopes.impl.FirClassDeclaredMemberScopeProvider import org.jetbrains.kotlin.fir.types.FirCorrespondingSupertypesCache import org.jetbrains.kotlin.load.java.JavaClassFinder @@ -33,6 +34,8 @@ class FirJavaModuleBasedSession( init { sessionProvider.sessionCache[moduleInfo] = this + val firProvider = FirProviderImpl(this) + registerComponent(FirProvider::class, firProvider) registerComponent( FirSymbolProvider::class, diff --git a/compiler/fir/psi2fir/src/org/jetbrains/kotlin/fir/builder/RawFirBuilder.kt b/compiler/fir/psi2fir/src/org/jetbrains/kotlin/fir/builder/RawFirBuilder.kt index 6b5eb71b837..c47f6c46d4a 100644 --- a/compiler/fir/psi2fir/src/org/jetbrains/kotlin/fir/builder/RawFirBuilder.kt +++ b/compiler/fir/psi2fir/src/org/jetbrains/kotlin/fir/builder/RawFirBuilder.kt @@ -169,7 +169,8 @@ class RawFirBuilder(val session: FirSession, val stubMode: Boolean) { } else -> { val result = { bodyExpression }.toFirExpression("Function has no body (but should)") - FirSingleExpressionBlock(result.toReturn()) + // basePsi is null, because 'return' is synthetic & should not be bound to some PSI + FirSingleExpressionBlock(result.toReturn(basePsi = null)) } } diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/FirCallResolver.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/FirCallResolver.kt index dcf3a5c82a0..d277f60c317 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/FirCallResolver.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/FirCallResolver.kt @@ -18,9 +18,8 @@ import org.jetbrains.kotlin.fir.references.FirResolvedCallableReferenceImpl import org.jetbrains.kotlin.fir.references.FirSimpleNamedReference import org.jetbrains.kotlin.fir.resolve.BodyResolveComponents import org.jetbrains.kotlin.fir.resolve.calls.* -import org.jetbrains.kotlin.fir.resolve.transformers.FirBodyResolveTransformer +import org.jetbrains.kotlin.fir.resolve.transformers.* import org.jetbrains.kotlin.fir.resolve.transformers.StoreNameReference -import org.jetbrains.kotlin.fir.resolve.transformers.firUnsafe import org.jetbrains.kotlin.fir.resolve.transformers.resultType import org.jetbrains.kotlin.fir.resolve.typeForQualifier import org.jetbrains.kotlin.fir.resolve.typeFromCallee @@ -226,8 +225,10 @@ class FirCallResolver( val coneSymbol = candidate.symbol when { coneSymbol is FirBackingFieldSymbol -> FirBackingFieldReferenceImpl(psi, coneSymbol) - coneSymbol is FirVariableSymbol && - (coneSymbol !is FirPropertySymbol || coneSymbol.firUnsafe().typeParameters.isEmpty()) -> + coneSymbol is FirVariableSymbol && ( + coneSymbol !is FirPropertySymbol || + (coneSymbol.phasedFir(session) as FirMemberDeclaration).typeParameters.isEmpty() + ) -> FirResolvedCallableReferenceImpl(psi, name, coneSymbol) else -> FirNamedReferenceWithCandidate(psi, name, candidate) } diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/FirModuleBasedSession.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/FirModuleBasedSession.kt index 09f994fe598..d0af4653c23 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/FirModuleBasedSession.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/FirModuleBasedSession.kt @@ -13,8 +13,6 @@ import org.jetbrains.kotlin.fir.scopes.impl.FirClassDeclaredMemberScopeProvider abstract class FirModuleBasedSession(override val moduleInfo: ModuleInfo, sessionProvider: FirSessionProvider?) : FirSessionBase(sessionProvider) { init { - val firProvider = FirProviderImpl(this) - registerComponent(FirProvider::class, firProvider) registerComponent(FirQualifierResolver::class, FirQualifierResolverImpl(this)) registerComponent(FirTypeResolver::class, FirTypeResolverImpl(this)) registerComponent(FirClassDeclaredMemberScopeProvider::class, FirClassDeclaredMemberScopeProvider()) diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/ResolveUtils.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/ResolveUtils.kt index 3b03a26df28..361d1e7b25b 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/ResolveUtils.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/ResolveUtils.kt @@ -178,7 +178,7 @@ fun BodyResolveComponents.typeForQualifier(resolvedQualifier: FirResolvedQualifi val resultType = resolvedQualifier.resultType if (classId != null) { val classSymbol = symbolProvider.getClassLikeSymbolByFqName(classId)!! - val declaration = classSymbol.fir + val declaration = classSymbol.phasedFir if (declaration is FirClass) { if (declaration.classKind == ClassKind.OBJECT) { return resultType.resolvedTypeFromPrototype( @@ -235,7 +235,7 @@ fun BodyResolveComponents.typeFromCallee(access: T): Fi private fun BodyResolveComponents.typeFromSymbol(symbol: ConeSymbol, makeNullable: Boolean): FirResolvedTypeRef { return when (symbol) { is FirCallableSymbol<*> -> { - val returnType = returnTypeCalculator.tryCalculateReturnType(symbol.fir) + val returnType = returnTypeCalculator.tryCalculateReturnType(symbol.phasedFir) if (makeNullable) { returnType.withReplacedConeType( returnType.coneTypeUnsafe().withNullability(ConeNullability.NULLABLE) @@ -245,7 +245,7 @@ private fun BodyResolveComponents.typeFromSymbol(symbol: ConeSymbol, makeNullabl } } is ConeClassifierSymbol -> { - val fir = (symbol as? AbstractFirBasedSymbol<*>)?.fir + val fir = (symbol as? AbstractFirBasedSymbol<*>)?.phasedFir // TODO: unhack if (fir is FirEnumEntry) { (fir.superTypeRefs.firstOrNull() as? FirResolvedTypeRef) ?: FirErrorTypeRefImpl( diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/SessionHolder.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/SessionHolder.kt index f4e58031627..32e247ea45b 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/SessionHolder.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/SessionHolder.kt @@ -7,7 +7,12 @@ package org.jetbrains.kotlin.fir.resolve import com.google.common.collect.SetMultimap import org.jetbrains.kotlin.fir.FirSession +import org.jetbrains.kotlin.fir.declarations.FirDeclaration +import org.jetbrains.kotlin.fir.declarations.FirResolvePhase import org.jetbrains.kotlin.fir.resolve.transformers.ReturnTypeCalculator +import org.jetbrains.kotlin.fir.resolve.transformers.phasedFir +import org.jetbrains.kotlin.fir.symbols.AbstractFirBasedSymbol +import org.jetbrains.kotlin.fir.symbols.FirSymbolOwner import org.jetbrains.kotlin.fir.types.ConeKotlinType import org.jetbrains.kotlin.fir.types.FirTypeRef import org.jetbrains.kotlin.name.Name @@ -21,4 +26,7 @@ interface BodyResolveComponents : SessionHolder { val labels: SetMultimap val noExpectedType: FirTypeRef val symbolProvider: FirSymbolProvider + + val AbstractFirBasedSymbol.phasedFir: D where D : FirDeclaration, D : FirSymbolOwner + get() = phasedFir(session, FirResolvePhase.DECLARATIONS) } \ No newline at end of file diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/FirAbstractPhaseTransformer.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/FirAbstractPhaseTransformer.kt index 2144676aacf..db0e7d4dd83 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/FirAbstractPhaseTransformer.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/FirAbstractPhaseTransformer.kt @@ -30,26 +30,10 @@ abstract class FirAbstractPhaseTransformer( } } - val AbstractFirBasedSymbol.phasedFir: D where D : FirDeclaration, D : FirSymbolOwner + open val AbstractFirBasedSymbol.phasedFir: D where D : FirDeclaration, D : FirSymbolOwner get() { - val result = this.fir val requiredPhase = transformerPhase.prev - val availablePhase = result.resolvePhase - if (availablePhase < requiredPhase) { - var resolvePhase = availablePhase.next - val provider = FirProvider.getInstance(session) - val containingFile = when (this) { - is ConeCallableSymbol -> provider.getFirCallableContainerFile(this) - is ConeClassLikeSymbol -> provider.getFirClassifierContainerFile(this) - else -> null - } ?: throw AssertionError("Cannot get container file by symbol: $this (${result.render()})") - do { - val phaseTransformer = resolvePhase.createTransformerByPhase() - containingFile.transform(phaseTransformer, null) - resolvePhase = resolvePhase.next - } while (resolvePhase <= requiredPhase) - } - return result + return phasedFir(session, requiredPhase) } override fun transformDeclaration(declaration: FirDeclaration, data: D): CompositeTransformResult { @@ -57,4 +41,13 @@ abstract class FirAbstractPhaseTransformer( return super.transformDeclaration(declaration, data) } +} + +fun FirFile.runResolve(toPhase: FirResolvePhase, fromPhase: FirResolvePhase = FirResolvePhase.RAW_FIR) { + var currentPhase = fromPhase + while (currentPhase < toPhase) { + currentPhase = currentPhase.next + val phaseTransformer = currentPhase.createTransformerByPhase() + transform(phaseTransformer, null) + } } \ No newline at end of file diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/FirBodyResolveTransformer.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/FirBodyResolveTransformer.kt index 9d33ef156b6..e97f3b85cf3 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/FirBodyResolveTransformer.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/FirBodyResolveTransformer.kt @@ -21,10 +21,7 @@ import org.jetbrains.kotlin.fir.scopes.addImportingScopes import org.jetbrains.kotlin.fir.scopes.impl.FirLocalScope import org.jetbrains.kotlin.fir.scopes.impl.FirTopLevelDeclaredMemberScope import org.jetbrains.kotlin.fir.scopes.impl.withReplacedConeType -import org.jetbrains.kotlin.fir.symbols.ConeSymbol -import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol -import org.jetbrains.kotlin.fir.symbols.StandardClassIds -import org.jetbrains.kotlin.fir.symbols.invoke +import org.jetbrains.kotlin.fir.symbols.* import org.jetbrains.kotlin.fir.types.* import org.jetbrains.kotlin.fir.types.impl.* import org.jetbrains.kotlin.fir.visitors.CompositeTransformResult @@ -83,6 +80,12 @@ open class FirBodyResolveTransformer( resolutionStageRunner ) + override val AbstractFirBasedSymbol.phasedFir: D where D : FirDeclaration, D : FirSymbolOwner + get() { + val requiredPhase = transformerPhase.prev + return phasedFir(session, requiredPhase) + } + override fun transformFile(file: FirFile, data: Any?): CompositeTransformResult { packageFqName = file.packageFqName this.file = file @@ -778,9 +781,17 @@ private fun inferenceComponents(session: FirSession, returnTypeCalculator: Retur }, session, returnTypeCalculator, scopeSession) -class FirDesignatedBodyResolveTransformer(val designation: Iterator, session: FirSession, scopeSession: ScopeSession) : - FirBodyResolveTransformer(session, phase = FirResolvePhase.IMPLICIT_TYPES_BODY_RESOLVE, implicitTypeOnly = true, scopeSession = scopeSession) { - +class FirDesignatedBodyResolveTransformer( + private val designation: Iterator, + session: FirSession, + scopeSession: ScopeSession = ScopeSession(), + implicitTypeOnly: Boolean = true +) : FirBodyResolveTransformer( + session, + phase = FirResolvePhase.IMPLICIT_TYPES_BODY_RESOLVE, + implicitTypeOnly = implicitTypeOnly, + scopeSession = scopeSession +) { override fun transformElement(element: E, data: Any?): CompositeTransformResult { if (designation.hasNext()) { designation.next().visitNoTransform(this, data) diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/PhaseUtils.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/PhaseUtils.kt new file mode 100644 index 00000000000..9811dbe1a06 --- /dev/null +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/PhaseUtils.kt @@ -0,0 +1,34 @@ +/* + * 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.fir.resolve.transformers + +import org.jetbrains.kotlin.fir.FirSession +import org.jetbrains.kotlin.fir.declarations.FirDeclaration +import org.jetbrains.kotlin.fir.declarations.FirResolvePhase +import org.jetbrains.kotlin.fir.render +import org.jetbrains.kotlin.fir.resolve.FirProvider +import org.jetbrains.kotlin.fir.symbols.AbstractFirBasedSymbol +import org.jetbrains.kotlin.fir.symbols.ConeCallableSymbol +import org.jetbrains.kotlin.fir.symbols.ConeClassLikeSymbol +import org.jetbrains.kotlin.fir.symbols.FirSymbolOwner + +fun AbstractFirBasedSymbol.phasedFir( + session: FirSession, + requiredPhase: FirResolvePhase = FirResolvePhase.DECLARATIONS +): D where D : FirDeclaration, D : FirSymbolOwner { + val result = this.fir + val availablePhase = result.resolvePhase + if (availablePhase < requiredPhase) { + val provider = FirProvider.getInstance(session) + val containingFile = when (this) { + is ConeCallableSymbol -> provider.getFirCallableContainerFile(this) + is ConeClassLikeSymbol -> provider.getFirClassifierContainerFile(this) + else -> null + } ?: throw AssertionError("Cannot get container file by symbol: $this (${result.render()})") + containingFile.runResolve(toPhase = requiredPhase, fromPhase = availablePhase) + } + return result +} diff --git a/generators/tests/org/jetbrains/kotlin/generators/tests/GenerateTests.kt b/generators/tests/org/jetbrains/kotlin/generators/tests/GenerateTests.kt index f68a300b4e0..ec69ab66c70 100644 --- a/generators/tests/org/jetbrains/kotlin/generators/tests/GenerateTests.kt +++ b/generators/tests/org/jetbrains/kotlin/generators/tests/GenerateTests.kt @@ -72,6 +72,7 @@ import org.jetbrains.kotlin.idea.editor.AbstractMultiLineStringIndentTest import org.jetbrains.kotlin.idea.editor.backspaceHandler.AbstractBackspaceHandlerTest import org.jetbrains.kotlin.idea.editor.quickDoc.AbstractQuickDocProviderTest import org.jetbrains.kotlin.idea.filters.AbstractKotlinExceptionFilterTest +import org.jetbrains.kotlin.idea.fir.AbstractFirLazyResolveTest import org.jetbrains.kotlin.idea.fir.AbstractFirMultiModuleResolveTest import org.jetbrains.kotlin.idea.folding.AbstractKotlinFoldingTest import org.jetbrains.kotlin.idea.hierarchy.AbstractHierarchyTest @@ -817,6 +818,10 @@ fun main(args: Array) { testClass { model("fir/multiModule", recursive = false, extension = null) } + + testClass { + model("fir/lazyResolve", extension = "test", singleClass = true, filenameStartsLowerCase = true) + } } testGroup("idea/idea-maven/test", "idea/idea-maven/testData") { diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/fir/FirIdeJavaModuleBasedSession.kt b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/fir/FirIdeJavaModuleBasedSession.kt new file mode 100644 index 00000000000..85b0fed9f44 --- /dev/null +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/fir/FirIdeJavaModuleBasedSession.kt @@ -0,0 +1,54 @@ +/* + * 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.builder.RawFirBuilder +import org.jetbrains.kotlin.fir.java.FirProjectSessionProvider +import org.jetbrains.kotlin.fir.java.JavaSymbolProvider +import org.jetbrains.kotlin.fir.resolve.FirProvider +import org.jetbrains.kotlin.fir.resolve.FirSymbolProvider +import org.jetbrains.kotlin.fir.resolve.impl.FirCompositeSymbolProvider +import org.jetbrains.kotlin.fir.resolve.impl.FirDependenciesSymbolProviderImpl +import org.jetbrains.kotlin.fir.service +import org.jetbrains.kotlin.fir.types.FirCorrespondingSupertypesCache + + +class FirIdeJavaModuleBasedSession( + project: Project, + moduleInfo: ModuleInfo, + sessionProvider: FirProjectSessionProvider, + scope: GlobalSearchScope, + dependenciesProvider: FirSymbolProvider? = null +) : FirModuleBasedSession(moduleInfo, sessionProvider) { + + + init { + sessionProvider.sessionCache[moduleInfo] = this + registerComponent( + FirProvider::class, + IdeFirProvider(project, scope, RawFirBuilder(this, stubMode = false), this) + ) + registerComponent( + FirSymbolProvider::class, + FirCompositeSymbolProvider( + listOf( + service(), + JavaSymbolProvider(this, sessionProvider.project, scope), + dependenciesProvider ?: FirDependenciesSymbolProviderImpl(this) + ) + ) as FirSymbolProvider + ) + + registerComponent( + FirCorrespondingSupertypesCache::class, + FirCorrespondingSupertypesCache(this) + ) + } +} \ No newline at end of file diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/fir/FirResolutionApi.kt b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/fir/FirResolutionApi.kt new file mode 100644 index 00000000000..3e2d48d52ab --- /dev/null +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/fir/FirResolutionApi.kt @@ -0,0 +1,175 @@ +/* + * 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.psi.PsiElement +import org.jetbrains.kotlin.cfg.pseudocode.containingDeclarationForPseudocode +import org.jetbrains.kotlin.fir.FirElement +import org.jetbrains.kotlin.fir.FirReference +import org.jetbrains.kotlin.fir.declarations.* +import org.jetbrains.kotlin.fir.render +import org.jetbrains.kotlin.fir.resolve.FirProvider +import org.jetbrains.kotlin.fir.resolve.transformers.* +import org.jetbrains.kotlin.fir.scopes.ProcessorAction +import org.jetbrains.kotlin.fir.scopes.impl.FirTopLevelDeclaredMemberScope +import org.jetbrains.kotlin.fir.symbols.ConeCallableSymbol +import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol +import org.jetbrains.kotlin.fir.types.FirErrorTypeRef +import org.jetbrains.kotlin.fir.visitors.FirVisitorVoid +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 + +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 +): FirCallableMemberDeclaration<*> { + val memberScope = + if (klassFqName == null) FirTopLevelDeclaredMemberScope(this, session) + else provider.getClassDeclaredMemberScope(ClassId(packageFqName, klassFqName, false))!! + var result: FirCallableMemberDeclaration<*>? = null + val processor = { symbol: ConeCallableSymbol -> + val firSymbol = symbol as? FirBasedSymbol<*> + val fir = firSymbol?.fir as? FirCallableMemberDeclaration<*> + if (fir?.psi == callableMember) { + result = fir + ProcessorAction.STOP + } else { + ProcessorAction.NEXT + } + } + if (callableMember is KtNamedFunction) { + memberScope.processFunctionsByName(declName, processor) + } else { + memberScope.processPropertiesByName(declName, processor) + } + + return result!! +} + +fun KtCallableDeclaration.getOrBuildFir( + state: FirResolveState, + phase: FirResolvePhase = FirResolvePhase.DECLARATIONS +): FirCallableMemberDeclaration<*> { + val session = state.getSession(this) + + val file = this.containingKtFile + val packageFqName = file.packageFqName + val klassFqName = this.containingClassOrObject?.relativeFqName() + val declName = this.nameAsSafeName + + val firProvider = FirProvider.getInstance(session) as IdeFirProvider + val firFile = firProvider.getOrBuildFile(file) + val memberSymbol = firFile.findCallableMember(firProvider, this, packageFqName, klassFqName, declName).symbol + memberSymbol.fir.runResolve(firFile, firProvider, phase, state) + return memberSymbol.fir +} + +fun KtClassOrObject.getOrBuildFir( + state: FirResolveState, + phase: FirResolvePhase = FirResolvePhase.DECLARATIONS +): FirRegularClass { + val session = state.getSession(this) + + val file = this.containingKtFile + val packageFqName = file.packageFqName + val klassFqName = this.relativeFqName() + + val firProvider = FirProvider.getInstance(session) as IdeFirProvider + val firFile = firProvider.getOrBuildFile(file) + val firClass = firProvider.getFirClassifierByFqName(ClassId(packageFqName, klassFqName, false)) as FirRegularClass + firClass.runResolve(firFile, firProvider, phase, state) + return firClass +} + +private fun FirDeclaration.runResolve( + file: FirFile, + firProvider: IdeFirProvider, + toPhase: FirResolvePhase, + state: FirResolveState +) { + val nonLazyPhase = minOf(toPhase, FirResolvePhase.DECLARATIONS) + file.runResolve(toPhase = nonLazyPhase, fromPhase = this.resolvePhase) + if (toPhase > nonLazyPhase) { + val designation = mutableListOf() + designation += file + 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 + } + val transformer = FirDesignatedBodyResolveTransformer( + designation.iterator(), state.getSession(psi as KtElement), + implicitTypeOnly = toPhase == FirResolvePhase.IMPLICIT_TYPES_BODY_RESOLVE + ) + file.transform(transformer, null) + } +} + +fun KtElement.getOrBuildFir( + state: FirResolveState, + phase: FirResolvePhase = FirResolvePhase.BODY_RESOLVE +): FirElement { + val containerFir: FirDeclaration = + when (val container = this.containingDeclarationForPseudocode ?: error("No containing declaration: $text")) { + is KtCallableDeclaration -> container.getOrBuildFir(state, phase) + is KtClassOrObject -> container.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 visitErrorTypeRef(errorTypeRef: FirErrorTypeRef) {} + }) + 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") + } +} diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/fir/FirResolveState.kt b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/fir/FirResolveState.kt new file mode 100644 index 00000000000..8d836725284 --- /dev/null +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/fir/FirResolveState.kt @@ -0,0 +1,69 @@ +/* + * 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 org.jetbrains.kotlin.fir.FirElement +import org.jetbrains.kotlin.fir.FirSession +import org.jetbrains.kotlin.fir.FirSessionProvider +import org.jetbrains.kotlin.fir.dependenciesWithoutSelf +import org.jetbrains.kotlin.fir.java.FirLibrarySession +import org.jetbrains.kotlin.fir.java.FirProjectSessionProvider +import org.jetbrains.kotlin.idea.caches.project.IdeaModuleInfo +import org.jetbrains.kotlin.idea.caches.project.ModuleSourceInfo +import org.jetbrains.kotlin.idea.caches.project.getModuleInfo +import org.jetbrains.kotlin.idea.caches.project.isLibraryClasses +import org.jetbrains.kotlin.idea.caches.resolve.IDEPackagePartProvider +import org.jetbrains.kotlin.psi.KtElement +import org.jetbrains.kotlin.utils.addToStdlib.cast + +private fun createLibrarySession(moduleInfo: IdeaModuleInfo, project: Project, provider: FirProjectSessionProvider): FirLibrarySession { + val contentScope = moduleInfo.contentScope() + return FirLibrarySession.create(moduleInfo, provider, contentScope, project, IDEPackagePartProvider(contentScope)) +} + +interface FirResolveState { + val sessionProvider: FirSessionProvider + + fun getSession(psi: KtElement): FirSession { + val sessionProvider = sessionProvider as FirProjectSessionProvider + val moduleInfo = psi.getModuleInfo() as ModuleSourceInfo + return sessionProvider.getSession(moduleInfo) ?: FirIdeJavaModuleBasedSession( + psi.project, moduleInfo, sessionProvider, moduleInfo.contentScope() + ).also { + val ideaModuleInfo = moduleInfo.cast() + ideaModuleInfo.dependenciesWithoutSelf().forEach { + if (it is IdeaModuleInfo && it.isLibraryClasses()) { + createLibrarySession(it, psi.project, sessionProvider) + } + } + } + } + + operator fun get(psi: KtElement): FirElement? + + fun record(psi: KtElement, fir: FirElement) +} + +class FirResolveStateImpl(override val sessionProvider: FirSessionProvider) : FirResolveState { + private val cache = mutableMapOf() + + override fun get(psi: KtElement): FirElement? = cache[psi] + + override fun record(psi: KtElement, fir: FirElement) { + cache[psi] = fir + } +} + +// TODO: caching +object FirIdeResolveFactory { + fun initiate(psi: KtElement): FirResolveState { + val provider = FirProjectSessionProvider(psi.project) + return FirResolveStateImpl(provider) + } +} + +fun KtElement.firResolveState(): FirResolveState = FirIdeResolveFactory.initiate(this) \ No newline at end of file diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/fir/IdeFirProvider.kt b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/fir/IdeFirProvider.kt new file mode 100644 index 00000000000..6b2231911c5 --- /dev/null +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/fir/IdeFirProvider.kt @@ -0,0 +1,99 @@ +/* + * 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.fir.FirSession +import org.jetbrains.kotlin.fir.builder.RawFirBuilder +import org.jetbrains.kotlin.fir.declarations.FirClassLikeDeclaration +import org.jetbrains.kotlin.fir.declarations.FirFile +import org.jetbrains.kotlin.fir.resolve.FirProvider +import org.jetbrains.kotlin.fir.resolve.ScopeSession +import org.jetbrains.kotlin.fir.resolve.impl.FirProviderImpl +import org.jetbrains.kotlin.fir.scopes.FirScope +import org.jetbrains.kotlin.fir.symbols.ConeCallableSymbol +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.KtFile + +class IdeFirProvider( + val project: Project, + val scope: GlobalSearchScope, + val builder: RawFirBuilder, + val session: FirSession +) : FirProvider() { + private val cacheProvider = FirProviderImpl(session) + // TODO: invalidation? + private val files = mutableMapOf() + + override fun getFirClassifierByFqName(fqName: ClassId): FirClassLikeDeclaration<*>? { + return cacheProvider.getFirClassifierByFqName(fqName) ?: run { + + val classes = KotlinFullClassNameIndex.getInstance().get(fqName.asSingleFqName().asString(), project, scope) + val ktClass = classes.firstOrNull { + fqName.packageFqName == it.containingKtFile.packageFqName + } ?: return null // TODO: what if two of them? + val ktFile = ktClass.containingKtFile + + getOrBuildFile(ktFile) + + cacheProvider.getFirClassifierByFqName(fqName) + } + } + + fun getOrBuildFile(ktFile: KtFile): FirFile { + return files.getOrPut(ktFile) { + val file = builder.buildFirFile(ktFile) + cacheProvider.recordFile(file) + file + } + } + + fun getFile(ktFile: KtFile): FirFile? = files[ktFile] + + override fun getClassLikeSymbolByFqName(classId: ClassId): FirClassLikeSymbol<*>? { + return getFirClassifierByFqName(classId)?.symbol + } + + override fun getTopLevelCallableSymbols(packageFqName: FqName, name: Name): List> { + val packagePrefix = if (packageFqName.isRoot) "" else "$packageFqName." + 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) } + return cacheProvider.getTopLevelCallableSymbols(packageFqName, name) + } + + override fun getFirClassifierContainerFile(fqName: ClassId): FirFile { + getFirClassifierByFqName(fqName) + return cacheProvider.getFirClassifierContainerFile(fqName) + } + + override fun getFirCallableContainerFile(symbol: ConeCallableSymbol): FirFile? { + return cacheProvider.getFirCallableContainerFile(symbol) + } + + override fun getFirFilesByPackage(fqName: FqName): List { + val files = KotlinExactPackagesIndex.getInstance()[fqName.asString(), project, scope] + files.forEach { getOrBuildFile(it) } + return cacheProvider.getFirFilesByPackage(fqName) + } + + override fun getClassDeclaredMemberScope(classId: ClassId): FirScope? { + getFirClassifierByFqName(classId) + return cacheProvider.getClassDeclaredMemberScope(classId) + } + + override fun getClassUseSiteMemberScope(classId: ClassId, useSiteSession: FirSession, scopeSession: ScopeSession): FirScope? { + getFirClassifierByFqName(classId) + return cacheProvider.getClassUseSiteMemberScope(classId, useSiteSession, scopeSession) + } +} \ No newline at end of file diff --git a/idea/idea-core/build.gradle.kts b/idea/idea-core/build.gradle.kts index 9cfab3ff0dd..4278c94a670 100644 --- a/idea/idea-core/build.gradle.kts +++ b/idea/idea-core/build.gradle.kts @@ -18,6 +18,10 @@ dependencies { compile(project(":idea:idea-jps-common")) compile(project(":plugins:android-extensions-compiler")) compile(project(":kotlin-scripting-compiler-impl")) + compile(project(":compiler:fir:psi2fir")) + compile(project(":compiler:fir:fir2ir")) + compile(project(":compiler:fir:resolve")) + compile(project(":compiler:fir:java")) compile(commonDep("org.jetbrains.kotlinx", "kotlinx-coroutines-core")) { isTransitive = false } compile(commonDep("org.jetbrains.kotlinx", "kotlinx-coroutines-jdk8")) { isTransitive = false } diff --git a/idea/testData/fir/lazyResolve/simple/callMe.kt b/idea/testData/fir/lazyResolve/simple/callMe.kt new file mode 100644 index 00000000000..437ec8e24bd --- /dev/null +++ b/idea/testData/fir/lazyResolve/simple/callMe.kt @@ -0,0 +1,5 @@ +fun callMe() = 42 + +fun other() { + val x = 1 +} \ No newline at end of file diff --git a/idea/testData/fir/lazyResolve/simple/callMe.txt b/idea/testData/fir/lazyResolve/simple/callMe.txt new file mode 100644 index 00000000000..c6e72a64dba --- /dev/null +++ b/idea/testData/fir/lazyResolve/simple/callMe.txt @@ -0,0 +1,7 @@ +FILE: callMe.kt + public final fun callMe(): R|kotlin/Int| { + ^callMe Int(42) + } + public final fun other(): R|kotlin/Unit| { + lval x: = Int(1) + } diff --git a/idea/testData/fir/lazyResolve/simple/main.kt b/idea/testData/fir/lazyResolve/simple/main.kt new file mode 100644 index 00000000000..490a09209f7 --- /dev/null +++ b/idea/testData/fir/lazyResolve/simple/main.kt @@ -0,0 +1,11 @@ +fun main() { + callMe() + foo() + bar(1, 2) +} + +fun foo() { + val y = 2.0 +} + +fun bar(x: Int, y: Int) = x + y \ No newline at end of file diff --git a/idea/testData/fir/lazyResolve/simple/main.txt b/idea/testData/fir/lazyResolve/simple/main.txt new file mode 100644 index 00000000000..bb71e2d4252 --- /dev/null +++ b/idea/testData/fir/lazyResolve/simple/main.txt @@ -0,0 +1,12 @@ +FILE: main.kt + public final fun main(): R|kotlin/Unit| { + R|/callMe|() + R|/foo|() + R|/bar|(Int(1), Int(2)) + } + public final fun foo(): R|kotlin/Unit| { + lval y: = Double(2.0) + } + public final fun bar(x: R|kotlin/Int|, y: R|kotlin/Int|): R|kotlin/Int| { + ^bar R|/x|.R|kotlin/Int.plus|(R|/y|) + } diff --git a/idea/testData/fir/lazyResolve/simple/results.txt b/idea/testData/fir/lazyResolve/simple/results.txt new file mode 100644 index 00000000000..df51f9328cc --- /dev/null +++ b/idea/testData/fir/lazyResolve/simple/results.txt @@ -0,0 +1 @@ +R|/callMe|() \ No newline at end of file diff --git a/idea/testData/fir/lazyResolve/simple/simple.test b/idea/testData/fir/lazyResolve/simple/simple.test new file mode 100644 index 00000000000..3099545c162 --- /dev/null +++ b/idea/testData/fir/lazyResolve/simple/simple.test @@ -0,0 +1,3 @@ +{ + "mainFile": "main.kt" +} \ No newline at end of file diff --git a/idea/testData/fir/lazyResolve/simpleProperty/main.kt b/idea/testData/fir/lazyResolve/simpleProperty/main.kt new file mode 100644 index 00000000000..2fedc4388b2 --- /dev/null +++ b/idea/testData/fir/lazyResolve/simpleProperty/main.kt @@ -0,0 +1,3 @@ +package root + +fun foo() = myProperty \ No newline at end of file diff --git a/idea/testData/fir/lazyResolve/simpleProperty/main.txt b/idea/testData/fir/lazyResolve/simpleProperty/main.txt new file mode 100644 index 00000000000..5c3402b866c --- /dev/null +++ b/idea/testData/fir/lazyResolve/simpleProperty/main.txt @@ -0,0 +1,4 @@ +FILE: main.kt + public final fun foo(): R|kotlin/Int| { + ^foo R|root/myProperty| + } diff --git a/idea/testData/fir/lazyResolve/simpleProperty/myProperty.kt b/idea/testData/fir/lazyResolve/simpleProperty/myProperty.kt new file mode 100644 index 00000000000..4e23f77ded7 --- /dev/null +++ b/idea/testData/fir/lazyResolve/simpleProperty/myProperty.kt @@ -0,0 +1,3 @@ +package root + +val myProperty get() = 42 \ No newline at end of file diff --git a/idea/testData/fir/lazyResolve/simpleProperty/myProperty.txt b/idea/testData/fir/lazyResolve/simpleProperty/myProperty.txt new file mode 100644 index 00000000000..4b84e36b746 --- /dev/null +++ b/idea/testData/fir/lazyResolve/simpleProperty/myProperty.txt @@ -0,0 +1,5 @@ +FILE: myProperty.kt + public final val myProperty: R|kotlin/Int| + public get(): R|kotlin/Int| { + ^ Int(42) + } diff --git a/idea/testData/fir/lazyResolve/simpleProperty/results.txt b/idea/testData/fir/lazyResolve/simpleProperty/results.txt new file mode 100644 index 00000000000..91fdd3d6134 --- /dev/null +++ b/idea/testData/fir/lazyResolve/simpleProperty/results.txt @@ -0,0 +1 @@ +R|root/myProperty| diff --git a/idea/testData/fir/lazyResolve/simpleProperty/simpleProperty.test b/idea/testData/fir/lazyResolve/simpleProperty/simpleProperty.test new file mode 100644 index 00000000000..3099545c162 --- /dev/null +++ b/idea/testData/fir/lazyResolve/simpleProperty/simpleProperty.test @@ -0,0 +1,3 @@ +{ + "mainFile": "main.kt" +} \ No newline at end of file diff --git a/idea/tests/org/jetbrains/kotlin/idea/fir/AbstractFirLazyResolveTest.kt b/idea/tests/org/jetbrains/kotlin/idea/fir/AbstractFirLazyResolveTest.kt new file mode 100644 index 00000000000..91bfb2a5b15 --- /dev/null +++ b/idea/tests/org/jetbrains/kotlin/idea/fir/AbstractFirLazyResolveTest.kt @@ -0,0 +1,94 @@ +/* + * 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.google.gson.JsonObject +import com.google.gson.JsonParser +import com.intellij.openapi.util.io.FileUtil +import com.intellij.openapi.vfs.VirtualFile +import com.intellij.psi.PsiDocumentManager +import com.intellij.psi.search.FileTypeIndex +import com.intellij.testFramework.LightProjectDescriptor +import junit.framework.TestCase +import org.jetbrains.kotlin.fir.render +import org.jetbrains.kotlin.fir.resolve.FirProvider +import org.jetbrains.kotlin.idea.KotlinFileType +import org.jetbrains.kotlin.idea.caches.project.IdeaModuleInfo +import org.jetbrains.kotlin.idea.caches.project.productionSourceInfo +import org.jetbrains.kotlin.idea.jsonUtils.getString +import org.jetbrains.kotlin.idea.test.KotlinLightCodeInsightFixtureTestCase +import org.jetbrains.kotlin.idea.test.KotlinLightProjectDescriptor +import org.jetbrains.kotlin.idea.test.KotlinWithJdkAndRuntimeLightProjectDescriptor +import org.jetbrains.kotlin.idea.util.projectStructure.allModules +import org.jetbrains.kotlin.psi.KtCallExpression +import org.jetbrains.kotlin.psi.KtExpression +import org.jetbrains.kotlin.psi.KtFile +import org.jetbrains.kotlin.test.KotlinTestUtils +import java.io.File + +abstract class AbstractFirLazyResolveTest : KotlinLightCodeInsightFixtureTestCase() { + override fun getProjectDescriptor(): LightProjectDescriptor { + if (KotlinTestUtils.isAllFilesPresentTest(getTestName(false))) return super.getProjectDescriptor() + val testFile = File(testDataPath, fileName()) + val config = JsonParser().parse(FileUtil.loadFile(testFile, true)) as JsonObject + val withRuntime = config["withRuntime"]?.asBoolean ?: false + return if (withRuntime) + KotlinWithJdkAndRuntimeLightProjectDescriptor.INSTANCE + else + KotlinLightProjectDescriptor.INSTANCE + } + + fun doTest(path: String) { + val testFile = File(path) + val config = JsonParser().parse(FileUtil.loadFile(testFile, true)) as JsonObject + + val mainFilePath = config.getString("mainFile") + val mainFile = File(testFile.parent, mainFilePath) + val mainFileText = FileUtil.loadFile(mainFile, true) + TestCase.assertTrue("\"\" is missing in file \"$mainFilePath\"", mainFileText.contains("")) + + val projectDir = path.removePrefix(testDataPath).substringBeforeLast('/') + myFixture.copyDirectoryToProject(projectDir, "") + PsiDocumentManager.getInstance(myFixture.project).commitAllDocuments() + + myFixture.configureFromTempProjectFile(mainFilePath) + val referenceToResolve = myFixture.getReferenceAtCaretPositionWithAssertion(mainFilePath) + val elementToResolve = referenceToResolve.element + val expressionToResolve = when { + elementToResolve.parent is KtCallExpression -> elementToResolve.parent + else -> elementToResolve + } as KtExpression + + val resolveState = expressionToResolve.firResolveState() + val firExpression = expressionToResolve.getOrBuildFir(resolveState) + + val resultsDump = firExpression.render() + KotlinTestUtils.assertEqualsToFile(File(testFile.parent, "results.txt"), resultsDump) + + fun expectedTxtPath(virtualFile: VirtualFile): String { + val virtualPath = virtualFile.path.substringAfter("/src/") + var result: String? = null + val root = File(path).parentFile + for (file in root.walkTopDown()) { + if (!file.isDirectory && virtualPath in file.absolutePath) { + result = file.absolutePath.replace(".kt", ".txt") + } + } + return result!! + } + + val moduleInfo = project.allModules().single().productionSourceInfo() as IdeaModuleInfo + val contentScope = moduleInfo.contentScope() + val files = FileTypeIndex.getFiles(KotlinFileType.INSTANCE, contentScope) + for (file in files) { + val psiFile = psiManager.findFile(file) as KtFile + val session = resolveState.getSession(psiFile) + val firProvider = FirProvider.getInstance(session) as IdeFirProvider + val firFile = firProvider.getFile(psiFile) ?: continue + KotlinTestUtils.assertEqualsToFile(File(expectedTxtPath(file)), firFile.render()) + } + } +} \ No newline at end of file diff --git a/idea/tests/org/jetbrains/kotlin/idea/fir/FirLazyResolveTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/fir/FirLazyResolveTestGenerated.java new file mode 100644 index 00000000000..16813e569c1 --- /dev/null +++ b/idea/tests/org/jetbrains/kotlin/idea/fir/FirLazyResolveTestGenerated.java @@ -0,0 +1,41 @@ +/* + * 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.testFramework.TestDataPath; +import org.jetbrains.kotlin.test.JUnit3RunnerWithInners; +import org.jetbrains.kotlin.test.KotlinTestUtils; +import org.jetbrains.kotlin.test.TargetBackend; +import org.jetbrains.kotlin.test.TestMetadata; +import org.junit.runner.RunWith; + +import java.io.File; +import java.util.regex.Pattern; + +/** This class is generated by {@link org.jetbrains.kotlin.generators.tests.TestsPackage}. DO NOT MODIFY MANUALLY */ +@SuppressWarnings("all") +@TestMetadata("idea/testData/fir/lazyResolve") +@TestDataPath("$PROJECT_ROOT") +@RunWith(JUnit3RunnerWithInners.class) +public class FirLazyResolveTestGenerated extends AbstractFirLazyResolveTest { + private void runTest(String testDataFilePath) throws Exception { + KotlinTestUtils.runTest(this::doTest, TargetBackend.ANY, testDataFilePath); + } + + public void testAllFilesPresentInLazyResolve() throws Exception { + KotlinTestUtils.assertAllTestsPresentInSingleGeneratedClass(this.getClass(), new File("idea/testData/fir/lazyResolve"), Pattern.compile("^(.+)\\.test$"), TargetBackend.ANY); + } + + @TestMetadata("simple/simple.test") + public void testSimple_Simple() throws Exception { + runTest("idea/testData/fir/lazyResolve/simple/simple.test"); + } + + @TestMetadata("simpleProperty/simpleProperty.test") + public void testSimpleProperty_SimpleProperty() throws Exception { + runTest("idea/testData/fir/lazyResolve/simpleProperty/simpleProperty.test"); + } +}