From 4cb5483c1352902a31b67eb9fdcd18c70bb66863 Mon Sep 17 00:00:00 2001 From: Alexander Udalov Date: Wed, 6 Dec 2017 17:45:33 +0100 Subject: [PATCH] Introduce CheckerContext and ClassifierUsageCheckerContext To reduce the number of parameters in classifier usage checker implementations, and to unify the API with call checkers --- .../checkers/JvmModuleAccessibilityChecker.kt | 18 +-- .../kotlin/resolve/LazyTopDownAnalyzer.kt | 7 +- .../resolve/calls/checkers/CallChecker.kt | 9 +- .../ApiVersionClassifierUsageChecker.kt | 17 +-- .../kotlin/resolve/checkers/CheckerContext.kt | 29 ++++ .../checkers/ClassifierUsageChecker.kt | 125 +++++++++--------- .../DeprecatedClassifierUsageChecker.kt | 16 +-- .../checkers/MissingDependencyClassChecker.kt | 15 +-- 8 files changed, 118 insertions(+), 118 deletions(-) create mode 100644 compiler/frontend/src/org/jetbrains/kotlin/resolve/checkers/CheckerContext.kt diff --git a/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/checkers/JvmModuleAccessibilityChecker.kt b/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/checkers/JvmModuleAccessibilityChecker.kt index 56ff3e1d47a..bdc318f4d5a 100644 --- a/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/checkers/JvmModuleAccessibilityChecker.kt +++ b/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/checkers/JvmModuleAccessibilityChecker.kt @@ -19,7 +19,6 @@ package org.jetbrains.kotlin.resolve.jvm.checkers import com.intellij.openapi.project.Project import com.intellij.openapi.vfs.VirtualFile import com.intellij.psi.PsiElement -import org.jetbrains.kotlin.config.LanguageVersionSettings import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.diagnostics.Diagnostic import org.jetbrains.kotlin.load.java.descriptors.getImplClassNameForDeserialized @@ -28,14 +27,13 @@ import org.jetbrains.kotlin.load.java.structure.impl.VirtualFileBoundJavaClass import org.jetbrains.kotlin.load.kotlin.KotlinJvmBinaryPackageSourceElement import org.jetbrains.kotlin.load.kotlin.KotlinJvmBinarySourceElement import org.jetbrains.kotlin.load.kotlin.VirtualFileKotlinClass -import org.jetbrains.kotlin.resolve.BindingTrace -import org.jetbrains.kotlin.resolve.DeprecationResolver import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils import org.jetbrains.kotlin.resolve.DescriptorUtils import org.jetbrains.kotlin.resolve.calls.checkers.CallChecker import org.jetbrains.kotlin.resolve.calls.checkers.CallCheckerContext import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall import org.jetbrains.kotlin.resolve.checkers.ClassifierUsageChecker +import org.jetbrains.kotlin.resolve.checkers.ClassifierUsageCheckerContext import org.jetbrains.kotlin.resolve.jvm.diagnostics.ErrorsJvm.* import org.jetbrains.kotlin.resolve.jvm.modules.JavaModuleResolver import org.jetbrains.kotlin.resolve.jvm.modules.JavaModuleResolver.AccessError.* @@ -101,27 +99,21 @@ class JvmModuleAccessibilityChecker(project: Project) : CallChecker { } inner class ClassifierUsage : ClassifierUsageChecker { - override fun check( - targetDescriptor: ClassifierDescriptor, - trace: BindingTrace, - element: PsiElement, - languageVersionSettings: LanguageVersionSettings, - deprecationResolver: DeprecationResolver - ) { + override fun check(targetDescriptor: ClassifierDescriptor, element: PsiElement, context: ClassifierUsageCheckerContext) { val virtualFile = element.containingFile.virtualFile when (targetDescriptor) { is ClassDescriptor -> { - diagnosticFor(targetDescriptor, targetDescriptor, virtualFile, element)?.let(trace::report) + diagnosticFor(targetDescriptor, targetDescriptor, virtualFile, element)?.let(context.trace::report) } is TypeAliasDescriptor -> { val containingClassOrPackage = DescriptorUtils.getParentOfType(targetDescriptor, ClassOrPackageFragmentDescriptor::class.java) if (containingClassOrPackage != null) { - diagnosticFor(containingClassOrPackage, targetDescriptor, virtualFile, element)?.let(trace::report) + diagnosticFor(containingClassOrPackage, targetDescriptor, virtualFile, element)?.let(context.trace::report) } val expandedClass = targetDescriptor.expandedType.constructor.declarationDescriptor as? ClassDescriptor if (expandedClass != null) { - diagnosticFor(expandedClass, expandedClass, virtualFile, element)?.let(trace::report) + diagnosticFor(expandedClass, expandedClass, virtualFile, element)?.let(context.trace::report) } } } diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/LazyTopDownAnalyzer.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/LazyTopDownAnalyzer.kt index b0c8e67b2e9..1468e77c1e2 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/LazyTopDownAnalyzer.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/LazyTopDownAnalyzer.kt @@ -28,6 +28,8 @@ import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowInfo import org.jetbrains.kotlin.resolve.checkers.ClassifierUsageChecker +import org.jetbrains.kotlin.resolve.checkers.ClassifierUsageCheckerContext +import org.jetbrains.kotlin.resolve.checkers.checkClassifierUsages import org.jetbrains.kotlin.resolve.lazy.* import org.jetbrains.kotlin.resolve.lazy.descriptors.LazyClassDescriptor import org.jetbrains.kotlin.resolve.lazy.descriptors.LazyScriptDescriptor @@ -223,7 +225,10 @@ class LazyTopDownAnalyzer( resolveImportsInAllFiles(c) - ClassifierUsageChecker.check(declarations, trace, languageVersionSettings, deprecationResolver, classifierUsageCheckers) + checkClassifierUsages( + declarations, classifierUsageCheckers, + ClassifierUsageCheckerContext(trace, languageVersionSettings, deprecationResolver) + ) return c } diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/checkers/CallChecker.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/checkers/CallChecker.kt index 1dcdb5f81c9..070cf80e1c2 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/checkers/CallChecker.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/checkers/CallChecker.kt @@ -23,6 +23,7 @@ import org.jetbrains.kotlin.resolve.DeprecationResolver import org.jetbrains.kotlin.resolve.calls.context.ResolutionContext import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowInfo +import org.jetbrains.kotlin.resolve.checkers.CheckerContext import org.jetbrains.kotlin.resolve.scopes.LexicalScope import org.jetbrains.kotlin.types.DeferredType import org.jetbrains.kotlin.types.KotlinType @@ -37,10 +38,10 @@ interface CallChecker { class CallCheckerContext( val resolutionContext: ResolutionContext<*>, - val trace: BindingTrace, - val languageVersionSettings: LanguageVersionSettings, - val deprecationResolver: DeprecationResolver -) { + override val trace: BindingTrace, + override val languageVersionSettings: LanguageVersionSettings, + override val deprecationResolver: DeprecationResolver +) : CheckerContext { val scope: LexicalScope get() = resolutionContext.scope diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/checkers/ApiVersionClassifierUsageChecker.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/checkers/ApiVersionClassifierUsageChecker.kt index 052879337e9..a4f27fe9974 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/checkers/ApiVersionClassifierUsageChecker.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/checkers/ApiVersionClassifierUsageChecker.kt @@ -17,23 +17,16 @@ package org.jetbrains.kotlin.resolve.checkers import com.intellij.psi.PsiElement -import org.jetbrains.kotlin.config.LanguageVersionSettings import org.jetbrains.kotlin.descriptors.ClassifierDescriptor import org.jetbrains.kotlin.diagnostics.Errors -import org.jetbrains.kotlin.resolve.BindingTrace -import org.jetbrains.kotlin.resolve.DeprecationResolver import org.jetbrains.kotlin.resolve.checkSinceKotlinVersionAccessibility object ApiVersionClassifierUsageChecker : ClassifierUsageChecker { - override fun check( - targetDescriptor: ClassifierDescriptor, - trace: BindingTrace, - element: PsiElement, - languageVersionSettings: LanguageVersionSettings, - deprecationResolver: DeprecationResolver - ) { - targetDescriptor.checkSinceKotlinVersionAccessibility(languageVersionSettings) { version -> - trace.report(Errors.API_NOT_AVAILABLE.on(element, version.versionString, languageVersionSettings.apiVersion.versionString)) + override fun check(targetDescriptor: ClassifierDescriptor, element: PsiElement, context: ClassifierUsageCheckerContext) { + targetDescriptor.checkSinceKotlinVersionAccessibility(context.languageVersionSettings) { version -> + context.trace.report( + Errors.API_NOT_AVAILABLE.on(element, version.versionString, context.languageVersionSettings.apiVersion.versionString) + ) } } } diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/checkers/CheckerContext.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/checkers/CheckerContext.kt new file mode 100644 index 00000000000..f6a5583294a --- /dev/null +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/checkers/CheckerContext.kt @@ -0,0 +1,29 @@ +/* + * Copyright 2010-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.resolve.checkers + +import org.jetbrains.kotlin.config.LanguageVersionSettings +import org.jetbrains.kotlin.resolve.BindingTrace +import org.jetbrains.kotlin.resolve.DeprecationResolver + +interface CheckerContext { + val trace: BindingTrace + + val languageVersionSettings: LanguageVersionSettings + + val deprecationResolver: DeprecationResolver +} diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/checkers/ClassifierUsageChecker.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/checkers/ClassifierUsageChecker.kt index 1525fd5ee32..b8a56fbbdad 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/checkers/ClassifierUsageChecker.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/checkers/ClassifierUsageChecker.kt @@ -32,77 +32,74 @@ import org.jetbrains.kotlin.resolve.DeprecationResolver import org.jetbrains.kotlin.resolve.DescriptorUtils interface ClassifierUsageChecker { - fun check( - targetDescriptor: ClassifierDescriptor, - trace: BindingTrace, - element: PsiElement, - languageVersionSettings: LanguageVersionSettings, - deprecationResolver: DeprecationResolver - ) + fun check(targetDescriptor: ClassifierDescriptor, element: PsiElement, context: ClassifierUsageCheckerContext) +} - companion object { - fun check( - declarations: Collection, - trace: BindingTrace, - languageVersionSettings: LanguageVersionSettings, - deprecationResolver: DeprecationResolver, - checkers: Iterable - ) { - val visitor = object : KtTreeVisitorVoid() { - override fun visitReferenceExpression(expression: KtReferenceExpression) { - super.visitReferenceExpression(expression) +class ClassifierUsageCheckerContext( + override val trace: BindingTrace, + override val languageVersionSettings: LanguageVersionSettings, + override val deprecationResolver: DeprecationResolver +) : CheckerContext - if (expression is KtNameReferenceExpression && expression.getReferencedNameElementType() == KtTokens.SUPER_KEYWORD) { - // Do not run checkers here because super expressions is not a proper expression. For example, - // it doesn't make sense to report deprecation on "super" in "super.foo()" if the super class is deprecated. - return - } - val target = getReferencedClassifier(expression) ?: return +fun checkClassifierUsages( + declarations: Collection, + checkers: Iterable, + context: ClassifierUsageCheckerContext +) { + val visitor = object : KtTreeVisitorVoid() { + override fun visitReferenceExpression(expression: KtReferenceExpression) { + super.visitReferenceExpression(expression) - runCheckersWithTarget(target, expression) - - getReferenceToCompanionViaClassifier(expression, target)?.let { referenceClassifier -> - val outerClass = target.containingDeclaration as ClassDescriptor - runCheckersWithTarget(outerClass, expression) - if (referenceClassifier is TypeAliasDescriptor) { - runCheckersWithTarget(referenceClassifier, expression) - } - } - } - - private fun runCheckersWithTarget(target: ClassifierDescriptor, expression: KtReferenceExpression) { - for (checker in checkers) { - checker.check(target, trace, expression, languageVersionSettings, deprecationResolver) - } - } - - private fun getReferencedClassifier(expression: KtReferenceExpression): ClassifierDescriptor? { - val target = trace.get(BindingContext.REFERENCE_TARGET, expression) as? ClassifierDescriptor - if (target != null) return target - - // "Comparable" in "import java.lang.Comparable" references both a class and a SAM constructor and prevents - // REFERENCE_TARGET from being recorded in favor of AMBIGUOUS_REFERENCE_TARGET. But we must still run checkers - // to report if there's something wrong with the class. We characterize this case below by the following properties: - // 1) Exactly one of the references is a classifier - // 2) All references refer to the same source element, i.e. their source is the same - val targets = trace.get(BindingContext.AMBIGUOUS_REFERENCE_TARGET, expression) ?: return null - if (targets.groupBy { (it as? DeclarationDescriptorWithSource)?.source }.size != 1) return null - return targets.filterIsInstance().singleOrNull() - } - - private fun getReferenceToCompanionViaClassifier( - expression: KtReferenceExpression, - target: ClassifierDescriptor - ): ClassifierDescriptor? { - if (!DescriptorUtils.isCompanionObject(target)) return null - return trace.get(BindingContext.SHORT_REFERENCE_TO_COMPANION_OBJECT, expression) - } + if (expression is KtNameReferenceExpression && expression.getReferencedNameElementType() == KtTokens.SUPER_KEYWORD) { + // Do not run checkers here because super expressions is not a proper expression. For example, + // it doesn't make sense to report deprecation on "super" in "super.foo()" if the super class is deprecated. + return } - for (declaration in declarations) { - declaration.accept(visitor) + val target = getReferencedClassifier(expression) ?: return + + runCheckersWithTarget(target, expression) + + getReferenceToCompanionViaClassifier(expression, target)?.let { referenceClassifier -> + val outerClass = target.containingDeclaration as ClassDescriptor + runCheckersWithTarget(outerClass, expression) + if (referenceClassifier is TypeAliasDescriptor) { + runCheckersWithTarget(referenceClassifier, expression) + } } } + + private fun runCheckersWithTarget(target: ClassifierDescriptor, expression: KtReferenceExpression) { + for (checker in checkers) { + checker.check(target, expression, context) + } + } + + private fun getReferencedClassifier(expression: KtReferenceExpression): ClassifierDescriptor? { + val target = context.trace.get(BindingContext.REFERENCE_TARGET, expression) as? ClassifierDescriptor + if (target != null) return target + + // "Comparable" in "import java.lang.Comparable" references both a class and a SAM constructor and prevents + // REFERENCE_TARGET from being recorded in favor of AMBIGUOUS_REFERENCE_TARGET. But we must still run checkers + // to report if there's something wrong with the class. We characterize this case below by the following properties: + // 1) Exactly one of the references is a classifier + // 2) All references refer to the same source element, i.e. their source is the same + val targets = context.trace.get(BindingContext.AMBIGUOUS_REFERENCE_TARGET, expression) ?: return null + if (targets.groupBy { (it as? DeclarationDescriptorWithSource)?.source }.size != 1) return null + return targets.filterIsInstance().singleOrNull() + } + + private fun getReferenceToCompanionViaClassifier( + expression: KtReferenceExpression, + target: ClassifierDescriptor + ): ClassifierDescriptor? { + if (!DescriptorUtils.isCompanionObject(target)) return null + return context.trace.get(BindingContext.SHORT_REFERENCE_TO_COMPANION_OBJECT, expression) + } + } + + for (declaration in declarations) { + declaration.accept(visitor) } } diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/checkers/DeprecatedClassifierUsageChecker.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/checkers/DeprecatedClassifierUsageChecker.kt index 7868306adc4..d6280b352a7 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/checkers/DeprecatedClassifierUsageChecker.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/checkers/DeprecatedClassifierUsageChecker.kt @@ -17,24 +17,16 @@ package org.jetbrains.kotlin.resolve.checkers import com.intellij.psi.PsiElement -import org.jetbrains.kotlin.config.LanguageVersionSettings import org.jetbrains.kotlin.descriptors.ClassifierDescriptor import org.jetbrains.kotlin.psi.KtThisExpression -import org.jetbrains.kotlin.resolve.BindingTrace -import org.jetbrains.kotlin.resolve.DeprecationResolver import org.jetbrains.kotlin.resolve.createDeprecationDiagnostic class DeprecatedClassifierUsageChecker : ClassifierUsageChecker { - override fun check( - targetDescriptor: ClassifierDescriptor, - trace: BindingTrace, - element: PsiElement, - languageVersionSettings: LanguageVersionSettings, - deprecationResolver: DeprecationResolver - ) { + override fun check(targetDescriptor: ClassifierDescriptor, element: PsiElement, context: ClassifierUsageCheckerContext) { if (element.parent is KtThisExpression) return - for (deprecation in deprecationResolver.getDeprecations(targetDescriptor)) { - trace.report(createDeprecationDiagnostic(element, deprecation, languageVersionSettings)) + + for (deprecation in context.deprecationResolver.getDeprecations(targetDescriptor)) { + context.trace.report(createDeprecationDiagnostic(element, deprecation, context.languageVersionSettings)) } } } diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/checkers/MissingDependencyClassChecker.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/checkers/MissingDependencyClassChecker.kt index 7369741bff2..69ac3096801 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/checkers/MissingDependencyClassChecker.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/checkers/MissingDependencyClassChecker.kt @@ -17,12 +17,9 @@ package org.jetbrains.kotlin.resolve.checkers import com.intellij.psi.PsiElement -import org.jetbrains.kotlin.config.LanguageVersionSettings import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.diagnostics.Diagnostic import org.jetbrains.kotlin.diagnostics.Errors.* -import org.jetbrains.kotlin.resolve.BindingTrace -import org.jetbrains.kotlin.resolve.DeprecationResolver import org.jetbrains.kotlin.resolve.calls.checkers.CallChecker import org.jetbrains.kotlin.resolve.calls.checkers.CallCheckerContext import org.jetbrains.kotlin.resolve.calls.checkers.isComputingDeferredType @@ -92,17 +89,11 @@ object MissingDependencyClassChecker : CallChecker { } object ClassifierUsage : ClassifierUsageChecker { - override fun check( - targetDescriptor: ClassifierDescriptor, - trace: BindingTrace, - element: PsiElement, - languageVersionSettings: LanguageVersionSettings, - deprecationResolver: DeprecationResolver - ) { - diagnosticFor(targetDescriptor, element)?.let(trace::report) + override fun check(targetDescriptor: ClassifierDescriptor, element: PsiElement, context: ClassifierUsageCheckerContext) { + diagnosticFor(targetDescriptor, element)?.let(context.trace::report) val containerSource = (targetDescriptor as? DeserializedMemberDescriptor)?.containerSource - incompatibilityDiagnosticFor(containerSource, element)?.let(trace::report) + incompatibilityDiagnosticFor(containerSource, element)?.let(context.trace::report) } } }