Refactor module usages in HeaderImplDeclarationChecker
Instead of a vague name "moduleToCheck", use explicit names "commonModule" and "platformModule".
This commit is contained in:
+1
-1
@@ -43,7 +43,7 @@ object JvmPlatformConfigurator : PlatformConfigurator(
|
|||||||
TypeParameterBoundIsNotArrayChecker(),
|
TypeParameterBoundIsNotArrayChecker(),
|
||||||
JvmSyntheticApplicabilityChecker(),
|
JvmSyntheticApplicabilityChecker(),
|
||||||
StrictfpApplicabilityChecker(),
|
StrictfpApplicabilityChecker(),
|
||||||
HeaderImplDeclarationChecker()
|
HeaderImplDeclarationChecker
|
||||||
),
|
),
|
||||||
|
|
||||||
additionalCallCheckers = listOf(
|
additionalCallCheckers = listOf(
|
||||||
|
|||||||
+35
-25
@@ -47,7 +47,7 @@ import org.jetbrains.kotlin.types.typeUtil.asTypeProjection
|
|||||||
import org.jetbrains.kotlin.utils.SmartList
|
import org.jetbrains.kotlin.utils.SmartList
|
||||||
import org.jetbrains.kotlin.utils.keysToMap
|
import org.jetbrains.kotlin.utils.keysToMap
|
||||||
|
|
||||||
class HeaderImplDeclarationChecker(val moduleToCheck: ModuleDescriptor? = null) : DeclarationChecker {
|
object HeaderImplDeclarationChecker : DeclarationChecker {
|
||||||
override fun check(
|
override fun check(
|
||||||
declaration: KtDeclaration,
|
declaration: KtDeclaration,
|
||||||
descriptor: DeclarationDescriptor,
|
descriptor: DeclarationDescriptor,
|
||||||
@@ -61,7 +61,7 @@ class HeaderImplDeclarationChecker(val moduleToCheck: ModuleDescriptor? = null)
|
|||||||
|
|
||||||
val checkImpl = !languageVersionSettings.isFlagEnabled(AnalysisFlags.multiPlatformDoNotCheckImpl)
|
val checkImpl = !languageVersionSettings.isFlagEnabled(AnalysisFlags.multiPlatformDoNotCheckImpl)
|
||||||
if (descriptor.isHeader && declaration.hasModifier(KtTokens.HEADER_KEYWORD)) {
|
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)) {
|
else if (checkImpl && descriptor.isImpl && declaration.hasModifier(KtTokens.IMPL_KEYWORD)) {
|
||||||
checkImplementationHasHeaderDeclaration(declaration, descriptor, diagnosticHolder)
|
checkImplementationHasHeaderDeclaration(declaration, descriptor, diagnosticHolder)
|
||||||
@@ -69,23 +69,30 @@ class HeaderImplDeclarationChecker(val moduleToCheck: ModuleDescriptor? = null)
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun checkHeaderDeclarationHasImplementation(
|
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) {
|
if (compatibility != null && Compatible !in compatibility) {
|
||||||
assert(compatibility.keys.all { it is Incompatible })
|
assert(compatibility.keys.all { it is Incompatible })
|
||||||
@Suppress("UNCHECKED_CAST")
|
@Suppress("UNCHECKED_CAST")
|
||||||
val incompatibility = compatibility as Map<Incompatible, Collection<MemberDescriptor>>
|
val incompatibility = compatibility as Map<Incompatible, Collection<MemberDescriptor>>
|
||||||
diagnosticHolder.report(Errors.HEADER_WITHOUT_IMPLEMENTATION.on(
|
diagnosticHolder.report(Errors.HEADER_WITHOUT_IMPLEMENTATION.on(reportOn, descriptor, platformModule, incompatibility))
|
||||||
reportOn, descriptor, moduleToCheck ?: descriptor.module, incompatibility))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun findImplForHeader(header: MemberDescriptor, checkImpl: Boolean): Map<Compatibility, List<MemberDescriptor>>? {
|
private fun findImplForHeader(
|
||||||
|
header: MemberDescriptor,
|
||||||
|
platformModule: ModuleDescriptor,
|
||||||
|
checkImpl: Boolean
|
||||||
|
): Map<Compatibility, List<MemberDescriptor>>? {
|
||||||
return when (header) {
|
return when (header) {
|
||||||
is CallableMemberDescriptor -> {
|
is CallableMemberDescriptor -> {
|
||||||
header.findNamesakesFromTheSameModule().filter { impl ->
|
header.findNamesakesFromModule(platformModule).filter { impl ->
|
||||||
header != impl &&
|
header != impl &&
|
||||||
// TODO: support non-source definitions (e.g. from Java)
|
// TODO: support non-source definitions (e.g. from Java)
|
||||||
DescriptorToSourceUtils.getSourceFromDescriptor(impl) is KtElement
|
DescriptorToSourceUtils.getSourceFromDescriptor(impl) is KtElement
|
||||||
@@ -94,7 +101,7 @@ class HeaderImplDeclarationChecker(val moduleToCheck: ModuleDescriptor? = null)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
is ClassDescriptor -> {
|
is ClassDescriptor -> {
|
||||||
header.findClassifiersFromTheSameModule().filter { impl ->
|
header.findClassifiersFromModule(platformModule).filter { impl ->
|
||||||
header != impl &&
|
header != impl &&
|
||||||
DescriptorToSourceUtils.getSourceFromDescriptor(impl) is KtElement
|
DescriptorToSourceUtils.getSourceFromDescriptor(impl) is KtElement
|
||||||
}.groupBy { impl ->
|
}.groupBy { impl ->
|
||||||
@@ -108,7 +115,10 @@ class HeaderImplDeclarationChecker(val moduleToCheck: ModuleDescriptor? = null)
|
|||||||
private fun checkImplementationHasHeaderDeclaration(
|
private fun checkImplementationHasHeaderDeclaration(
|
||||||
reportOn: KtDeclaration, descriptor: MemberDescriptor, diagnosticHolder: DiagnosticSink
|
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) {
|
if (compatibility != null && Compatible !in compatibility) {
|
||||||
assert(compatibility.keys.all { it is Incompatible })
|
assert(compatibility.keys.all { it is Incompatible })
|
||||||
@@ -117,16 +127,16 @@ class HeaderImplDeclarationChecker(val moduleToCheck: ModuleDescriptor? = null)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun findHeaderForImpl(impl: MemberDescriptor): Map<Compatibility, List<MemberDescriptor>>? {
|
private fun findHeaderForImpl(impl: MemberDescriptor, commonModule: ModuleDescriptor): Map<Compatibility, List<MemberDescriptor>>? {
|
||||||
return when (impl) {
|
return when (impl) {
|
||||||
is CallableMemberDescriptor -> {
|
is CallableMemberDescriptor -> {
|
||||||
val container = impl.containingDeclaration
|
val container = impl.containingDeclaration
|
||||||
val candidates = when (container) {
|
val candidates = when (container) {
|
||||||
is ClassDescriptor -> {
|
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()
|
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
|
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 -> {
|
is ClassifierDescriptorWithTypeParameters -> {
|
||||||
impl.findClassifiersFromTheSameModule().filter { declaration ->
|
impl.findClassifiersFromModule(commonModule).filter { declaration ->
|
||||||
impl != declaration &&
|
impl != declaration &&
|
||||||
declaration is ClassDescriptor && declaration.isHeader
|
declaration is ClassDescriptor && declaration.isHeader
|
||||||
}.groupBy { header ->
|
}.groupBy { header ->
|
||||||
@@ -156,26 +166,26 @@ class HeaderImplDeclarationChecker(val moduleToCheck: ModuleDescriptor? = null)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun MemberDescriptor.findCompatibleImplForHeader(): List<MemberDescriptor> =
|
fun MemberDescriptor.findCompatibleImplForHeader(platformModule: ModuleDescriptor): List<MemberDescriptor> =
|
||||||
findImplForHeader(this, false)?.get(Compatible).orEmpty()
|
findImplForHeader(this, platformModule, false)?.get(Compatible).orEmpty()
|
||||||
|
|
||||||
fun MemberDescriptor.findCompatibleHeaderForImpl(): List<MemberDescriptor> =
|
fun MemberDescriptor.findCompatibleHeaderForImpl(commonModule: ModuleDescriptor): List<MemberDescriptor> =
|
||||||
findHeaderForImpl(this)?.get(Compatible).orEmpty()
|
findHeaderForImpl(this, commonModule)?.get(Compatible).orEmpty()
|
||||||
|
|
||||||
private fun CallableMemberDescriptor.findNamesakesFromTheSameModule(): Collection<CallableMemberDescriptor> {
|
private fun CallableMemberDescriptor.findNamesakesFromModule(module: ModuleDescriptor): Collection<CallableMemberDescriptor> {
|
||||||
val packageFqName = (containingDeclaration as? PackageFragmentDescriptor)?.fqName ?: return emptyList()
|
val packageFqName = (containingDeclaration as? PackageFragmentDescriptor)?.fqName ?: return emptyList()
|
||||||
val myModule = moduleToCheck ?: module
|
val scope = module.getPackage(packageFqName).memberScope
|
||||||
val scope = myModule.getPackage(packageFqName).memberScope
|
|
||||||
|
|
||||||
return when (this) {
|
return when (this) {
|
||||||
is FunctionDescriptor -> scope.getContributedFunctions(name, NoLookupLocation.FOR_ALREADY_TRACKED)
|
is FunctionDescriptor -> scope.getContributedFunctions(name, NoLookupLocation.FOR_ALREADY_TRACKED)
|
||||||
is PropertyDescriptor -> scope.getContributedVariables(name, NoLookupLocation.FOR_ALREADY_TRACKED)
|
is PropertyDescriptor -> scope.getContributedVariables(name, NoLookupLocation.FOR_ALREADY_TRACKED)
|
||||||
else -> throw AssertionError("Unsupported declaration: $this")
|
else -> throw AssertionError("Unsupported declaration: $this")
|
||||||
} // TODO: only obtain descriptors from our module to start with
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun ClassifierDescriptorWithTypeParameters.findClassifiersFromTheSameModule(): Collection<ClassifierDescriptorWithTypeParameters> {
|
private fun ClassifierDescriptorWithTypeParameters.findClassifiersFromModule(
|
||||||
val myModule = moduleToCheck ?: module
|
module: ModuleDescriptor
|
||||||
|
): Collection<ClassifierDescriptorWithTypeParameters> {
|
||||||
val classId = classId ?: return emptyList()
|
val classId = classId ?: return emptyList()
|
||||||
|
|
||||||
fun MemberScope.getAllClassifiers(name: Name): Collection<ClassifierDescriptorWithTypeParameters> =
|
fun MemberScope.getAllClassifiers(name: Name): Collection<ClassifierDescriptorWithTypeParameters> =
|
||||||
@@ -183,7 +193,7 @@ class HeaderImplDeclarationChecker(val moduleToCheck: ModuleDescriptor? = null)
|
|||||||
.filterIsInstance<ClassifierDescriptorWithTypeParameters>()
|
.filterIsInstance<ClassifierDescriptorWithTypeParameters>()
|
||||||
|
|
||||||
val segments = classId.relativeClassName.pathSegments()
|
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)) {
|
for (name in segments.subList(1, segments.size)) {
|
||||||
classifiers = classifiers.mapNotNull { classifier ->
|
classifiers = classifiers.mapNotNull { classifier ->
|
||||||
|
|||||||
+4
-3
@@ -54,7 +54,6 @@ class PlatformHeaderAnnotator : Annotator {
|
|||||||
val descriptor = declaration.toDescriptor() as? MemberDescriptor ?: return Diagnostics.EMPTY
|
val descriptor = declaration.toDescriptor() as? MemberDescriptor ?: return Diagnostics.EMPTY
|
||||||
if (!descriptor.isHeader) return Diagnostics.EMPTY
|
if (!descriptor.isHeader) return Diagnostics.EMPTY
|
||||||
|
|
||||||
val checkers = modulesToCheck.map(::HeaderImplDeclarationChecker)
|
|
||||||
val diagnosticList = mutableListOf<Diagnostic>()
|
val diagnosticList = mutableListOf<Diagnostic>()
|
||||||
val diagnosticSink = object : DiagnosticSink {
|
val diagnosticSink = object : DiagnosticSink {
|
||||||
override fun report(diagnostic: Diagnostic) {
|
override fun report(diagnostic: Diagnostic) {
|
||||||
@@ -63,8 +62,10 @@ class PlatformHeaderAnnotator : Annotator {
|
|||||||
|
|
||||||
override fun wantsDiagnostics() = true
|
override fun wantsDiagnostics() = true
|
||||||
}
|
}
|
||||||
for (checker in checkers) {
|
for (module in modulesToCheck) {
|
||||||
checker.checkHeaderDeclarationHasImplementation(declaration, descriptor, diagnosticSink, checkImpl = false)
|
HeaderImplDeclarationChecker.checkHeaderDeclarationHasImplementation(
|
||||||
|
declaration, descriptor, diagnosticSink, module, checkImpl = false
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
val suppressionCache = KotlinCacheService.getInstance(declaration.project).getSuppressionCache()
|
val suppressionCache = KotlinCacheService.getInstance(declaration.project).getSuppressionCache()
|
||||||
|
|||||||
@@ -51,8 +51,8 @@ private enum class SourceKind { NONE, PRODUCTION, TEST }
|
|||||||
fun ModuleDescriptor.hasDeclarationOf(descriptor: MemberDescriptor) = declarationOf(descriptor) != null
|
fun ModuleDescriptor.hasDeclarationOf(descriptor: MemberDescriptor) = declarationOf(descriptor) != null
|
||||||
|
|
||||||
private fun ModuleDescriptor.declarationOf(descriptor: MemberDescriptor): DeclarationDescriptor? =
|
private fun ModuleDescriptor.declarationOf(descriptor: MemberDescriptor): DeclarationDescriptor? =
|
||||||
with(HeaderImplDeclarationChecker(this)) {
|
with(HeaderImplDeclarationChecker) {
|
||||||
descriptor.findCompatibleHeaderForImpl().firstOrNull()
|
descriptor.findCompatibleHeaderForImpl(this@declarationOf).firstOrNull()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getHeaderDeclarationTooltip(declaration: KtDeclaration): String? {
|
fun getHeaderDeclarationTooltip(declaration: KtDeclaration): String? {
|
||||||
|
|||||||
@@ -34,8 +34,8 @@ fun ModuleDescriptor.hasImplementationsOf(descriptor: MemberDescriptor) =
|
|||||||
implementationsOf(descriptor).isNotEmpty()
|
implementationsOf(descriptor).isNotEmpty()
|
||||||
|
|
||||||
private fun ModuleDescriptor.implementationsOf(descriptor: MemberDescriptor): List<DeclarationDescriptor> =
|
private fun ModuleDescriptor.implementationsOf(descriptor: MemberDescriptor): List<DeclarationDescriptor> =
|
||||||
with(HeaderImplDeclarationChecker(this)) {
|
with(HeaderImplDeclarationChecker) {
|
||||||
descriptor.findCompatibleImplForHeader()
|
descriptor.findCompatibleImplForHeader(this@implementationsOf)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getPlatformImplementationTooltip(declaration: KtDeclaration): String? {
|
fun getPlatformImplementationTooltip(declaration: KtDeclaration): String? {
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ object JsPlatformConfigurator : PlatformConfigurator(
|
|||||||
JsExternalChecker, JsInheritanceChecker,
|
JsExternalChecker, JsInheritanceChecker,
|
||||||
JsRuntimeAnnotationChecker,
|
JsRuntimeAnnotationChecker,
|
||||||
JsDynamicDeclarationChecker,
|
JsDynamicDeclarationChecker,
|
||||||
HeaderImplDeclarationChecker()
|
HeaderImplDeclarationChecker
|
||||||
),
|
),
|
||||||
additionalCallCheckers = listOf(
|
additionalCallCheckers = listOf(
|
||||||
ReifiedTypeParameterSubstitutionChecker(),
|
ReifiedTypeParameterSubstitutionChecker(),
|
||||||
|
|||||||
Reference in New Issue
Block a user