From 04324b67230e9d8f8ce26d92b88dbbd674789dbb Mon Sep 17 00:00:00 2001 From: Simon Ogorodnik Date: Fri, 19 Apr 2019 16:52:10 +0300 Subject: [PATCH] Provide overloads with explicit resolutionFacade for resolutionApi To ensure stable results of subsequent resolution api method calls, it is require to provide same resolution facade, as getResolutionFacade function doesn't guarantee to have same result in subsequent calls due to soft caching --- .../caches/resolve/extendedResolutionApi.kt | 102 ++++++++++++++++++ .../idea/caches/resolve/resolutionApi.kt | 101 ++++++++++------- 2 files changed, 164 insertions(+), 39 deletions(-) create mode 100644 idea/ide-common/src/org/jetbrains/kotlin/idea/caches/resolve/extendedResolutionApi.kt diff --git a/idea/ide-common/src/org/jetbrains/kotlin/idea/caches/resolve/extendedResolutionApi.kt b/idea/ide-common/src/org/jetbrains/kotlin/idea/caches/resolve/extendedResolutionApi.kt new file mode 100644 index 00000000000..8a3365ba5a5 --- /dev/null +++ b/idea/ide-common/src/org/jetbrains/kotlin/idea/caches/resolve/extendedResolutionApi.kt @@ -0,0 +1,102 @@ +/* + * Copyright 2010-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license + * that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.idea.caches.resolve + +import org.jetbrains.kotlin.analyzer.AnalysisResult +import org.jetbrains.kotlin.descriptors.* +import org.jetbrains.kotlin.idea.resolve.ResolutionFacade +import org.jetbrains.kotlin.psi.* +import org.jetbrains.kotlin.resolve.BindingContext +import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall +import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall +import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode +import org.jetbrains.kotlin.resolve.lazy.NoDescriptorForDeclarationException + +/** + * This function throws exception when resolveToDescriptorIfAny returns null, otherwise works equivalently. + */ +fun KtDeclaration.unsafeResolveToDescriptor( + resolutionFacade: ResolutionFacade, + bodyResolveMode: BodyResolveMode = BodyResolveMode.FULL +): DeclarationDescriptor = + resolveToDescriptorIfAny(resolutionFacade, bodyResolveMode) ?: throw NoDescriptorForDeclarationException(this) + + +/** + * This function first uses declaration resolvers to resolve this declaration and/or additional declarations (e.g. its parent), + * and then takes the relevant descriptor from binding context. + * The exact set of declarations to resolve depends on bodyResolveMode + */ +fun KtDeclaration.resolveToDescriptorIfAny( + resolutionFacade: ResolutionFacade, + bodyResolveMode: BodyResolveMode = BodyResolveMode.PARTIAL +): DeclarationDescriptor? { + //TODO: BodyResolveMode.PARTIAL is not quite safe! + val context = analyze(resolutionFacade, bodyResolveMode) + return if (this is KtParameter && hasValOrVar()) { + context.get(BindingContext.PRIMARY_CONSTRUCTOR_PARAMETER, this) + } else { + context.get(BindingContext.DECLARATION_TO_DESCRIPTOR, this) + } +} + +fun KtClassOrObject.resolveToDescriptorIfAny( + resolutionFacade: ResolutionFacade, + bodyResolveMode: BodyResolveMode = BodyResolveMode.PARTIAL +): ClassDescriptor? { + return (this as KtDeclaration).resolveToDescriptorIfAny(resolutionFacade, bodyResolveMode) as? ClassDescriptor +} + +fun KtNamedFunction.resolveToDescriptorIfAny( + resolutionFacade: ResolutionFacade, + bodyResolveMode: BodyResolveMode = BodyResolveMode.PARTIAL +): FunctionDescriptor? { + return (this as KtDeclaration).resolveToDescriptorIfAny(resolutionFacade, bodyResolveMode) as? FunctionDescriptor +} + +fun KtProperty.resolveToDescriptorIfAny( + resolutionFacade: ResolutionFacade, + bodyResolveMode: BodyResolveMode = BodyResolveMode.PARTIAL +): VariableDescriptor? { + return (this as KtDeclaration).resolveToDescriptorIfAny(resolutionFacade, bodyResolveMode) as? VariableDescriptor +} + +fun KtParameter.resolveToParameterDescriptorIfAny( + resolutionFacade: ResolutionFacade, + bodyResolveMode: BodyResolveMode = BodyResolveMode.PARTIAL +): ValueParameterDescriptor? { + val context = analyze(resolutionFacade, bodyResolveMode) + return context.get(BindingContext.VALUE_PARAMETER, this) as? ValueParameterDescriptor +} + +fun KtElement.resolveToCall( + resolutionFacade: ResolutionFacade, + bodyResolveMode: BodyResolveMode = BodyResolveMode.PARTIAL +): ResolvedCall? = + getResolvedCall(analyze(resolutionFacade, bodyResolveMode)) + + +@JvmOverloads +fun KtElement.analyze( + resolutionFacade: ResolutionFacade, + bodyResolveMode: BodyResolveMode = BodyResolveMode.FULL +): BindingContext = + resolutionFacade.analyze(this, bodyResolveMode) + +fun KtElement.analyzeAndGetResult(resolutionFacade: ResolutionFacade): AnalysisResult { + return AnalysisResult.success(resolutionFacade.analyze(this), resolutionFacade.moduleDescriptor) +} + +// This function is used on declarations to make analysis not only declaration itself but also it content: +// body for declaration with body, initializer & accessors for properties +fun KtDeclaration.analyzeWithContent(resolutionFacade: ResolutionFacade): BindingContext = + resolutionFacade.analyzeWithAllCompilerChecks(listOf(this)).bindingContext + +// This function is used to make full analysis of declaration container. +// All its declarations, including their content (see above), are analyzed. +inline fun T.analyzeWithContent(resolutionFacade: ResolutionFacade): BindingContext where T : KtDeclarationContainer, T : KtElement = + resolutionFacade.analyzeWithAllCompilerChecks(listOf(this)).bindingContext + diff --git a/idea/ide-common/src/org/jetbrains/kotlin/idea/caches/resolve/resolutionApi.kt b/idea/ide-common/src/org/jetbrains/kotlin/idea/caches/resolve/resolutionApi.kt index b89e0e7080a..24f69ebf324 100644 --- a/idea/ide-common/src/org/jetbrains/kotlin/idea/caches/resolve/resolutionApi.kt +++ b/idea/ide-common/src/org/jetbrains/kotlin/idea/caches/resolve/resolutionApi.kt @@ -28,64 +28,74 @@ import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.BindingTraceContext import org.jetbrains.kotlin.resolve.ImportPath import org.jetbrains.kotlin.resolve.QualifiedExpressionResolver -import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall -import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode -import org.jetbrains.kotlin.resolve.lazy.NoDescriptorForDeclarationException fun KtElement.getResolutionFacade(): ResolutionFacade = - KotlinCacheService.getInstance(project).getResolutionFacade(listOf(this)) + KotlinCacheService.getInstance(project).getResolutionFacade(listOf(this)) /** * For local declarations is equivalent to unsafeResolveToDescriptor(bodyResolveMode) * * But for non-local declarations it ignores bodyResolveMode and uses LazyDeclarationResolver directly */ -@Deprecated(message = "This function has unclear semantics. Please use either unsafeResolveToDescriptor or resolveToDescriptorIfAny instead", - replaceWith = ReplaceWith("unsafeResolveToDescriptor")) +@Deprecated( + message = "This function has unclear semantics. Please use either unsafeResolveToDescriptor or resolveToDescriptorIfAny instead", + replaceWith = ReplaceWith("unsafeResolveToDescriptor") +) fun KtDeclaration.resolveToDescriptor(bodyResolveMode: BodyResolveMode = BodyResolveMode.FULL): DeclarationDescriptor = - getResolutionFacade().resolveToDescriptor(this, bodyResolveMode) + getResolutionFacade().resolveToDescriptor(this, bodyResolveMode) /** * This function throws exception when resolveToDescriptorIfAny returns null, otherwise works equivalently. + * + * **Please, use overload with providing resolutionFacade for stable results of subsequent calls** */ -fun KtDeclaration.unsafeResolveToDescriptor(bodyResolveMode: BodyResolveMode = BodyResolveMode.FULL): DeclarationDescriptor = - resolveToDescriptorIfAny(bodyResolveMode) ?: throw NoDescriptorForDeclarationException(this) +fun KtDeclaration.unsafeResolveToDescriptor( + bodyResolveMode: BodyResolveMode = BodyResolveMode.FULL +): DeclarationDescriptor = + unsafeResolveToDescriptor(getResolutionFacade(), bodyResolveMode) /** * This function first uses declaration resolvers to resolve this declaration and/or additional declarations (e.g. its parent), * and then takes the relevant descriptor from binding context. * The exact set of declarations to resolve depends on bodyResolveMode + * + * **Please, use overload with providing resolutionFacade for stable results of subsequent calls** */ -fun KtDeclaration.resolveToDescriptorIfAny(bodyResolveMode: BodyResolveMode = BodyResolveMode.PARTIAL): DeclarationDescriptor? { - //TODO: BodyResolveMode.PARTIAL is not quite safe! - val context = analyze(bodyResolveMode) - return if (this is KtParameter && hasValOrVar()) { - context.get(BindingContext.PRIMARY_CONSTRUCTOR_PARAMETER, this) - } else { - context.get(BindingContext.DECLARATION_TO_DESCRIPTOR, this) - } -} +fun KtDeclaration.resolveToDescriptorIfAny( + bodyResolveMode: BodyResolveMode = BodyResolveMode.PARTIAL +): DeclarationDescriptor? = + resolveToDescriptorIfAny(getResolutionFacade(), bodyResolveMode) -fun KtClassOrObject.resolveToDescriptorIfAny(bodyResolveMode: BodyResolveMode = BodyResolveMode.PARTIAL): ClassDescriptor? { - return (this as KtDeclaration).resolveToDescriptorIfAny(bodyResolveMode) as? ClassDescriptor -} +/** + * **Please, use overload with providing resolutionFacade for stable results of subsequent calls** + */ +fun KtClassOrObject.resolveToDescriptorIfAny(bodyResolveMode: BodyResolveMode = BodyResolveMode.PARTIAL) = + resolveToDescriptorIfAny(getResolutionFacade(), bodyResolveMode) -fun KtNamedFunction.resolveToDescriptorIfAny(bodyResolveMode: BodyResolveMode = BodyResolveMode.PARTIAL): FunctionDescriptor? { - return (this as KtDeclaration).resolveToDescriptorIfAny(bodyResolveMode) as? FunctionDescriptor -} +/** + * **Please, use overload with providing resolutionFacade for stable results of subsequent calls** + */ +fun KtNamedFunction.resolveToDescriptorIfAny(bodyResolveMode: BodyResolveMode = BodyResolveMode.PARTIAL) = + resolveToDescriptorIfAny(getResolutionFacade(), bodyResolveMode) -fun KtProperty.resolveToDescriptorIfAny(bodyResolveMode: BodyResolveMode = BodyResolveMode.PARTIAL): VariableDescriptor? { - return (this as KtDeclaration).resolveToDescriptorIfAny(bodyResolveMode) as? VariableDescriptor -} +/** + * **Please, use overload with providing resolutionFacade for stable results of subsequent calls** + */ +fun KtProperty.resolveToDescriptorIfAny(bodyResolveMode: BodyResolveMode = BodyResolveMode.PARTIAL) = + resolveToDescriptorIfAny(getResolutionFacade(), bodyResolveMode) -fun KtParameter.resolveToParameterDescriptorIfAny(bodyResolveMode: BodyResolveMode = BodyResolveMode.PARTIAL): ValueParameterDescriptor? { - val context = analyze(bodyResolveMode) - return context.get(BindingContext.VALUE_PARAMETER, this) as? ValueParameterDescriptor -} +/** + * **Please, use overload with providing resolutionFacade for stable results of subsequent calls** + */ +fun KtParameter.resolveToParameterDescriptorIfAny(bodyResolveMode: BodyResolveMode = BodyResolveMode.PARTIAL) = + resolveToParameterDescriptorIfAny(getResolutionFacade(), bodyResolveMode) -fun KtElement.resolveToCall(bodyResolveMode: BodyResolveMode = BodyResolveMode.PARTIAL): ResolvedCall? = - getResolvedCall(analyze(bodyResolveMode)) +/** + * **Please, use overload with providing resolutionFacade for stable results of subsequent calls** + */ +fun KtElement.resolveToCall(bodyResolveMode: BodyResolveMode = BodyResolveMode.PARTIAL) = + resolveToCall(getResolutionFacade(), bodyResolveMode) fun KtFile.resolveImportReference(fqName: FqName): Collection { val facade = getResolutionFacade() @@ -111,26 +121,39 @@ fun KtFile.resolveImportReference(fqName: FqName): Collection T.analyzeWithContent(): BindingContext where T : KtDeclarationContainer, T : KtElement = - getResolutionFacade().analyzeWithAllCompilerChecks(listOf(this)).bindingContext + analyzeWithContent(getResolutionFacade()) /** * This function is expected to produce the same result as compiler for the whole file content (including diagnostics,