diff --git a/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/platform/JvmPlatformConfigurator.kt b/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/platform/JvmPlatformConfigurator.kt index e14d2562a3b..ddc991b9952 100644 --- a/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/platform/JvmPlatformConfigurator.kt +++ b/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/platform/JvmPlatformConfigurator.kt @@ -43,7 +43,7 @@ object JvmPlatformConfigurator : PlatformConfigurator( TypeParameterBoundIsNotArrayChecker(), JvmSyntheticApplicabilityChecker(), StrictfpApplicabilityChecker(), - HeaderImplDeclarationChecker() + HeaderImplDeclarationChecker ), additionalCallCheckers = listOf( diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/checkers/HeaderImplDeclarationChecker.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/checkers/HeaderImplDeclarationChecker.kt index 501466cc6fd..db6d9a7c76e 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/checkers/HeaderImplDeclarationChecker.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/checkers/HeaderImplDeclarationChecker.kt @@ -47,7 +47,7 @@ import org.jetbrains.kotlin.types.typeUtil.asTypeProjection import org.jetbrains.kotlin.utils.SmartList import org.jetbrains.kotlin.utils.keysToMap -class HeaderImplDeclarationChecker(val moduleToCheck: ModuleDescriptor? = null) : DeclarationChecker { +object HeaderImplDeclarationChecker : DeclarationChecker { override fun check( declaration: KtDeclaration, descriptor: DeclarationDescriptor, @@ -61,7 +61,7 @@ class HeaderImplDeclarationChecker(val moduleToCheck: ModuleDescriptor? = null) val checkImpl = !languageVersionSettings.isFlagEnabled(AnalysisFlags.multiPlatformDoNotCheckImpl) if (descriptor.isHeader && declaration.hasModifier(KtTokens.HEADER_KEYWORD)) { - checkHeaderDeclarationHasImplementation(declaration, descriptor, diagnosticHolder, checkImpl) + checkHeaderDeclarationHasImplementation(declaration, descriptor, diagnosticHolder, descriptor.module, checkImpl) } else if (checkImpl && descriptor.isImpl && declaration.hasModifier(KtTokens.IMPL_KEYWORD)) { checkImplementationHasHeaderDeclaration(declaration, descriptor, diagnosticHolder) @@ -69,23 +69,30 @@ class HeaderImplDeclarationChecker(val moduleToCheck: ModuleDescriptor? = null) } fun checkHeaderDeclarationHasImplementation( - reportOn: KtDeclaration, descriptor: MemberDescriptor, diagnosticHolder: DiagnosticSink, checkImpl: Boolean + reportOn: KtDeclaration, + descriptor: MemberDescriptor, + diagnosticHolder: DiagnosticSink, + platformModule: ModuleDescriptor, + checkImpl: Boolean ) { - val compatibility = findImplForHeader(descriptor, checkImpl) + val compatibility = findImplForHeader(descriptor, platformModule, checkImpl) if (compatibility != null && Compatible !in compatibility) { assert(compatibility.keys.all { it is Incompatible }) @Suppress("UNCHECKED_CAST") val incompatibility = compatibility as Map> - diagnosticHolder.report(Errors.HEADER_WITHOUT_IMPLEMENTATION.on( - reportOn, descriptor, moduleToCheck ?: descriptor.module, incompatibility)) + diagnosticHolder.report(Errors.HEADER_WITHOUT_IMPLEMENTATION.on(reportOn, descriptor, platformModule, incompatibility)) } } - private fun findImplForHeader(header: MemberDescriptor, checkImpl: Boolean): Map>? { + private fun findImplForHeader( + header: MemberDescriptor, + platformModule: ModuleDescriptor, + checkImpl: Boolean + ): Map>? { return when (header) { is CallableMemberDescriptor -> { - header.findNamesakesFromTheSameModule().filter { impl -> + header.findNamesakesFromModule(platformModule).filter { impl -> header != impl && // TODO: support non-source definitions (e.g. from Java) DescriptorToSourceUtils.getSourceFromDescriptor(impl) is KtElement @@ -94,7 +101,7 @@ class HeaderImplDeclarationChecker(val moduleToCheck: ModuleDescriptor? = null) } } is ClassDescriptor -> { - header.findClassifiersFromTheSameModule().filter { impl -> + header.findClassifiersFromModule(platformModule).filter { impl -> header != impl && DescriptorToSourceUtils.getSourceFromDescriptor(impl) is KtElement }.groupBy { impl -> @@ -108,7 +115,10 @@ class HeaderImplDeclarationChecker(val moduleToCheck: ModuleDescriptor? = null) private fun checkImplementationHasHeaderDeclaration( reportOn: KtDeclaration, descriptor: MemberDescriptor, diagnosticHolder: DiagnosticSink ) { - val compatibility = findHeaderForImpl(descriptor) + // Using the platform module instead of the common module is sort of fine here because the former always depends on the latter. + // However, it would be clearer to find the common module this platform module implements and look for headers there instead. + // TODO: use common module here + val compatibility = findHeaderForImpl(descriptor, descriptor.module) if (compatibility != null && Compatible !in compatibility) { assert(compatibility.keys.all { it is Incompatible }) @@ -117,16 +127,16 @@ class HeaderImplDeclarationChecker(val moduleToCheck: ModuleDescriptor? = null) } } - private fun findHeaderForImpl(impl: MemberDescriptor): Map>? { + private fun findHeaderForImpl(impl: MemberDescriptor, commonModule: ModuleDescriptor): Map>? { return when (impl) { is CallableMemberDescriptor -> { val container = impl.containingDeclaration val candidates = when (container) { is ClassDescriptor -> { - val headerClass = findHeaderForImpl(container)?.get(Compatible)?.firstOrNull() as? ClassDescriptor + val headerClass = findHeaderForImpl(container, commonModule)?.get(Compatible)?.firstOrNull() as? ClassDescriptor headerClass?.getMembers(impl.name).orEmpty() } - is PackageFragmentDescriptor -> impl.findNamesakesFromTheSameModule() + is PackageFragmentDescriptor -> impl.findNamesakesFromModule(commonModule) else -> return null // do not report anything for incorrect code, e.g. 'impl' local function } @@ -145,7 +155,7 @@ class HeaderImplDeclarationChecker(val moduleToCheck: ModuleDescriptor? = null) } } is ClassifierDescriptorWithTypeParameters -> { - impl.findClassifiersFromTheSameModule().filter { declaration -> + impl.findClassifiersFromModule(commonModule).filter { declaration -> impl != declaration && declaration is ClassDescriptor && declaration.isHeader }.groupBy { header -> @@ -156,26 +166,26 @@ class HeaderImplDeclarationChecker(val moduleToCheck: ModuleDescriptor? = null) } } - fun MemberDescriptor.findCompatibleImplForHeader(): List = - findImplForHeader(this, false)?.get(Compatible).orEmpty() + fun MemberDescriptor.findCompatibleImplForHeader(platformModule: ModuleDescriptor): List = + findImplForHeader(this, platformModule, false)?.get(Compatible).orEmpty() - fun MemberDescriptor.findCompatibleHeaderForImpl(): List = - findHeaderForImpl(this)?.get(Compatible).orEmpty() + fun MemberDescriptor.findCompatibleHeaderForImpl(commonModule: ModuleDescriptor): List = + findHeaderForImpl(this, commonModule)?.get(Compatible).orEmpty() - private fun CallableMemberDescriptor.findNamesakesFromTheSameModule(): Collection { + private fun CallableMemberDescriptor.findNamesakesFromModule(module: ModuleDescriptor): Collection { val packageFqName = (containingDeclaration as? PackageFragmentDescriptor)?.fqName ?: return emptyList() - val myModule = moduleToCheck ?: module - val scope = myModule.getPackage(packageFqName).memberScope + val scope = module.getPackage(packageFqName).memberScope return when (this) { is FunctionDescriptor -> scope.getContributedFunctions(name, NoLookupLocation.FOR_ALREADY_TRACKED) is PropertyDescriptor -> scope.getContributedVariables(name, NoLookupLocation.FOR_ALREADY_TRACKED) else -> throw AssertionError("Unsupported declaration: $this") - } // TODO: only obtain descriptors from our module to start with + } } - private fun ClassifierDescriptorWithTypeParameters.findClassifiersFromTheSameModule(): Collection { - val myModule = moduleToCheck ?: module + private fun ClassifierDescriptorWithTypeParameters.findClassifiersFromModule( + module: ModuleDescriptor + ): Collection { val classId = classId ?: return emptyList() fun MemberScope.getAllClassifiers(name: Name): Collection = @@ -183,7 +193,7 @@ class HeaderImplDeclarationChecker(val moduleToCheck: ModuleDescriptor? = null) .filterIsInstance() val segments = classId.relativeClassName.pathSegments() - var classifiers = myModule.getPackage(classId.packageFqName).memberScope.getAllClassifiers(segments.first()) + var classifiers = module.getPackage(classId.packageFqName).memberScope.getAllClassifiers(segments.first()) for (name in segments.subList(1, segments.size)) { classifiers = classifiers.mapNotNull { classifier -> diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/highlighter/PlatformHeaderAnnotator.kt b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/highlighter/PlatformHeaderAnnotator.kt index a3d123fe16c..ee293bdaae0 100644 --- a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/highlighter/PlatformHeaderAnnotator.kt +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/highlighter/PlatformHeaderAnnotator.kt @@ -54,7 +54,6 @@ class PlatformHeaderAnnotator : Annotator { val descriptor = declaration.toDescriptor() as? MemberDescriptor ?: return Diagnostics.EMPTY if (!descriptor.isHeader) return Diagnostics.EMPTY - val checkers = modulesToCheck.map(::HeaderImplDeclarationChecker) val diagnosticList = mutableListOf() val diagnosticSink = object : DiagnosticSink { override fun report(diagnostic: Diagnostic) { @@ -63,8 +62,10 @@ class PlatformHeaderAnnotator : Annotator { override fun wantsDiagnostics() = true } - for (checker in checkers) { - checker.checkHeaderDeclarationHasImplementation(declaration, descriptor, diagnosticSink, checkImpl = false) + for (module in modulesToCheck) { + HeaderImplDeclarationChecker.checkHeaderDeclarationHasImplementation( + declaration, descriptor, diagnosticSink, module, checkImpl = false + ) } val suppressionCache = KotlinCacheService.getInstance(declaration.project).getSuppressionCache() diff --git a/idea/src/org/jetbrains/kotlin/idea/highlighter/markers/DeclaredHeaderMarker.kt b/idea/src/org/jetbrains/kotlin/idea/highlighter/markers/DeclaredHeaderMarker.kt index 9b0b2d4c551..17d2411d0ea 100644 --- a/idea/src/org/jetbrains/kotlin/idea/highlighter/markers/DeclaredHeaderMarker.kt +++ b/idea/src/org/jetbrains/kotlin/idea/highlighter/markers/DeclaredHeaderMarker.kt @@ -51,8 +51,8 @@ private enum class SourceKind { NONE, PRODUCTION, TEST } fun ModuleDescriptor.hasDeclarationOf(descriptor: MemberDescriptor) = declarationOf(descriptor) != null private fun ModuleDescriptor.declarationOf(descriptor: MemberDescriptor): DeclarationDescriptor? = - with(HeaderImplDeclarationChecker(this)) { - descriptor.findCompatibleHeaderForImpl().firstOrNull() + with(HeaderImplDeclarationChecker) { + descriptor.findCompatibleHeaderForImpl(this@declarationOf).firstOrNull() } fun getHeaderDeclarationTooltip(declaration: KtDeclaration): String? { diff --git a/idea/src/org/jetbrains/kotlin/idea/highlighter/markers/ImplementedHeaderMarker.kt b/idea/src/org/jetbrains/kotlin/idea/highlighter/markers/ImplementedHeaderMarker.kt index 1df4ed52db2..1f8e5d1f421 100644 --- a/idea/src/org/jetbrains/kotlin/idea/highlighter/markers/ImplementedHeaderMarker.kt +++ b/idea/src/org/jetbrains/kotlin/idea/highlighter/markers/ImplementedHeaderMarker.kt @@ -34,8 +34,8 @@ fun ModuleDescriptor.hasImplementationsOf(descriptor: MemberDescriptor) = implementationsOf(descriptor).isNotEmpty() private fun ModuleDescriptor.implementationsOf(descriptor: MemberDescriptor): List = - with(HeaderImplDeclarationChecker(this)) { - descriptor.findCompatibleImplForHeader() + with(HeaderImplDeclarationChecker) { + descriptor.findCompatibleImplForHeader(this@implementationsOf) } fun getPlatformImplementationTooltip(declaration: KtDeclaration): String? { diff --git a/js/js.frontend/src/org/jetbrains/kotlin/js/resolve/JsPlatformConfigurator.kt b/js/js.frontend/src/org/jetbrains/kotlin/js/resolve/JsPlatformConfigurator.kt index f86071e241f..3812070056f 100644 --- a/js/js.frontend/src/org/jetbrains/kotlin/js/resolve/JsPlatformConfigurator.kt +++ b/js/js.frontend/src/org/jetbrains/kotlin/js/resolve/JsPlatformConfigurator.kt @@ -40,7 +40,7 @@ object JsPlatformConfigurator : PlatformConfigurator( JsExternalChecker, JsInheritanceChecker, JsRuntimeAnnotationChecker, JsDynamicDeclarationChecker, - HeaderImplDeclarationChecker() + HeaderImplDeclarationChecker ), additionalCallCheckers = listOf( ReifiedTypeParameterSubstitutionChecker(),