diff --git a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDataClassConverters.kt b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDataClassConverters.kt index 8735fbc91aa..97795825f1b 100644 --- a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDataClassConverters.kt +++ b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDataClassConverters.kt @@ -5390,6 +5390,27 @@ internal val KT_DIAGNOSTIC_CONVERTER = KtDiagnosticConverterBuilder.buildConvert token, ) } + add(FirJvmErrors.JAVA_MODULE_DOES_NOT_DEPEND_ON_MODULE) { firDiagnostic -> + JavaModuleDoesNotDependOnModuleImpl( + firDiagnostic.a, + firDiagnostic as KtPsiDiagnostic, + token, + ) + } + add(FirJvmErrors.JAVA_MODULE_DOES_NOT_READ_UNNAMED_MODULE) { firDiagnostic -> + JavaModuleDoesNotReadUnnamedModuleImpl( + firDiagnostic as KtPsiDiagnostic, + token, + ) + } + add(FirJvmErrors.JAVA_MODULE_DOES_NOT_EXPORT_PACKAGE) { firDiagnostic -> + JavaModuleDoesNotExportPackageImpl( + firDiagnostic.a, + firDiagnostic.b, + firDiagnostic as KtPsiDiagnostic, + token, + ) + } add(FirJvmErrors.JVM_DEFAULT_IN_DECLARATION) { firDiagnostic -> JvmDefaultInDeclarationImpl( firDiagnostic.a, diff --git a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDiagnostics.kt b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDiagnostics.kt index 450b3e32f2d..628557966c2 100644 --- a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDiagnostics.kt +++ b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDiagnostics.kt @@ -3751,6 +3751,21 @@ sealed interface KtFirDiagnostic : KtDiagnosticWithPsi { override val diagnosticClass get() = IllegalJavaLangRecordSupertype::class } + interface JavaModuleDoesNotDependOnModule : KtFirDiagnostic { + override val diagnosticClass get() = JavaModuleDoesNotDependOnModule::class + val moduleName: String + } + + interface JavaModuleDoesNotReadUnnamedModule : KtFirDiagnostic { + override val diagnosticClass get() = JavaModuleDoesNotReadUnnamedModule::class + } + + interface JavaModuleDoesNotExportPackage : KtFirDiagnostic { + override val diagnosticClass get() = JavaModuleDoesNotExportPackage::class + val moduleName: String + val packageName: String + } + interface JvmDefaultInDeclaration : KtFirDiagnostic { override val diagnosticClass get() = JvmDefaultInDeclaration::class val annotation: String diff --git a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDiagnosticsImpl.kt b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDiagnosticsImpl.kt index 0d8f0883b7d..1ee83ff95fe 100644 --- a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDiagnosticsImpl.kt +++ b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDiagnosticsImpl.kt @@ -4528,6 +4528,24 @@ internal class IllegalJavaLangRecordSupertypeImpl( token: KtLifetimeToken, ) : KtAbstractFirDiagnostic(firDiagnostic, token), KtFirDiagnostic.IllegalJavaLangRecordSupertype +internal class JavaModuleDoesNotDependOnModuleImpl( + override val moduleName: String, + firDiagnostic: KtPsiDiagnostic, + token: KtLifetimeToken, +) : KtAbstractFirDiagnostic(firDiagnostic, token), KtFirDiagnostic.JavaModuleDoesNotDependOnModule + +internal class JavaModuleDoesNotReadUnnamedModuleImpl( + firDiagnostic: KtPsiDiagnostic, + token: KtLifetimeToken, +) : KtAbstractFirDiagnostic(firDiagnostic, token), KtFirDiagnostic.JavaModuleDoesNotReadUnnamedModule + +internal class JavaModuleDoesNotExportPackageImpl( + override val moduleName: String, + override val packageName: String, + firDiagnostic: KtPsiDiagnostic, + token: KtLifetimeToken, +) : KtAbstractFirDiagnostic(firDiagnostic, token), KtFirDiagnostic.JavaModuleDoesNotExportPackage + internal class JvmDefaultInDeclarationImpl( override val annotation: String, firDiagnostic: KtPsiDiagnostic, diff --git a/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/FirJvmDiagnosticsList.kt b/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/FirJvmDiagnosticsList.kt index 8d84530f501..ec0d161591b 100644 --- a/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/FirJvmDiagnosticsList.kt +++ b/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/FirJvmDiagnosticsList.kt @@ -7,20 +7,16 @@ package org.jetbrains.kotlin.fir.checkers.generator.diagnostics import com.intellij.psi.PsiElement import org.jetbrains.kotlin.config.LanguageFeature.* +import org.jetbrains.kotlin.diagnostics.Severity import org.jetbrains.kotlin.fir.checkers.generator.diagnostics.model.DiagnosticList import org.jetbrains.kotlin.fir.checkers.generator.diagnostics.model.PositioningStrategy -import org.jetbrains.kotlin.fir.expressions.FirExpression import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol import org.jetbrains.kotlin.fir.symbols.impl.FirNamedFunctionSymbol import org.jetbrains.kotlin.fir.types.ConeKotlinType import org.jetbrains.kotlin.name.ClassId import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.Name -import org.jetbrains.kotlin.psi.KtAnnotationEntry -import org.jetbrains.kotlin.psi.KtDeclaration -import org.jetbrains.kotlin.psi.KtElement -import org.jetbrains.kotlin.psi.KtExpression -import org.jetbrains.kotlin.psi.KtNamedFunction +import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.util.PrivateForInline @Suppress("ClassName", "unused") @@ -135,6 +131,17 @@ object JVM_DIAGNOSTICS_LIST : DiagnosticList("FirJvmErrors") { val ILLEGAL_JAVA_LANG_RECORD_SUPERTYPE by error() } + val MODULES by object : DiagnosticGroup("JVM Modules") { + val JAVA_MODULE_DOES_NOT_DEPEND_ON_MODULE by error { + parameter("moduleName") + } + val JAVA_MODULE_DOES_NOT_READ_UNNAMED_MODULE by error() + val JAVA_MODULE_DOES_NOT_EXPORT_PACKAGE by error() { + parameter("moduleName") + parameter("packageName") + } + } + val JVM_DEFAULT by object : DiagnosticGroup("JVM Default") { val JVM_DEFAULT_IN_DECLARATION by error(PositioningStrategy.DECLARATION_SIGNATURE_OR_DEFAULT) { parameter("annotation") diff --git a/compiler/fir/checkers/checkers.jvm/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/jvm/FirJvmErrors.kt b/compiler/fir/checkers/checkers.jvm/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/jvm/FirJvmErrors.kt index b00f8109ea5..026593a924c 100644 --- a/compiler/fir/checkers/checkers.jvm/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/jvm/FirJvmErrors.kt +++ b/compiler/fir/checkers/checkers.jvm/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/jvm/FirJvmErrors.kt @@ -99,6 +99,11 @@ object FirJvmErrors { val JVM_RECORD_EXTENDS_CLASS: KtDiagnosticFactory1 by error1(SourceElementPositioningStrategies.ACTUAL_DECLARATION_NAME) val ILLEGAL_JAVA_LANG_RECORD_SUPERTYPE: KtDiagnosticFactory0 by error0() + // JVM Modules + val JAVA_MODULE_DOES_NOT_DEPEND_ON_MODULE: KtDiagnosticFactory1 by error1() + val JAVA_MODULE_DOES_NOT_READ_UNNAMED_MODULE: KtDiagnosticFactory0 by error0() + val JAVA_MODULE_DOES_NOT_EXPORT_PACKAGE: KtDiagnosticFactory2 by error2() + // JVM Default val JVM_DEFAULT_IN_DECLARATION: KtDiagnosticFactory1 by error1(SourceElementPositioningStrategies.DECLARATION_SIGNATURE_OR_DEFAULT) val JVM_DEFAULT_WITH_COMPATIBILITY_IN_DECLARATION: KtDiagnosticFactory0 by error0() diff --git a/compiler/fir/checkers/checkers.jvm/src/org/jetbrains/kotlin/fir/analysis/diagnostics/jvm/FirJvmErrorsDefaultMessages.kt b/compiler/fir/checkers/checkers.jvm/src/org/jetbrains/kotlin/fir/analysis/diagnostics/jvm/FirJvmErrorsDefaultMessages.kt index f1d268690f6..59006f945fd 100644 --- a/compiler/fir/checkers/checkers.jvm/src/org/jetbrains/kotlin/fir/analysis/diagnostics/jvm/FirJvmErrorsDefaultMessages.kt +++ b/compiler/fir/checkers/checkers.jvm/src/org/jetbrains/kotlin/fir/analysis/diagnostics/jvm/FirJvmErrorsDefaultMessages.kt @@ -34,6 +34,9 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.jvm.FirJvmErrors.INAPPLICAB import org.jetbrains.kotlin.fir.analysis.diagnostics.jvm.FirJvmErrors.INAPPLICABLE_JVM_NAME import org.jetbrains.kotlin.fir.analysis.diagnostics.jvm.FirJvmErrors.INNER_JVM_RECORD import org.jetbrains.kotlin.fir.analysis.diagnostics.jvm.FirJvmErrors.INTERFACE_CANT_CALL_DEFAULT_METHOD_VIA_SUPER +import org.jetbrains.kotlin.fir.analysis.diagnostics.jvm.FirJvmErrors.JAVA_MODULE_DOES_NOT_DEPEND_ON_MODULE +import org.jetbrains.kotlin.fir.analysis.diagnostics.jvm.FirJvmErrors.JAVA_MODULE_DOES_NOT_EXPORT_PACKAGE +import org.jetbrains.kotlin.fir.analysis.diagnostics.jvm.FirJvmErrors.JAVA_MODULE_DOES_NOT_READ_UNNAMED_MODULE import org.jetbrains.kotlin.fir.analysis.diagnostics.jvm.FirJvmErrors.JAVA_SAM_INTERFACE_CONSTRUCTOR_REFERENCE import org.jetbrains.kotlin.fir.analysis.diagnostics.jvm.FirJvmErrors.JAVA_TYPE_MISMATCH import org.jetbrains.kotlin.fir.analysis.diagnostics.jvm.FirJvmErrors.JVM_DEFAULT_IN_DECLARATION @@ -182,6 +185,19 @@ object FirJvmErrorsDefaultMessages : BaseDiagnosticRendererFactory() { map.put(NON_DATA_CLASS_JVM_RECORD, "Only data classes are allowed to be marked as '@JvmRecord'.") map.put(ILLEGAL_JAVA_LANG_RECORD_SUPERTYPE, "Classes cannot have explicit 'java.lang.Record' supertype.") + map.put( + JAVA_MODULE_DOES_NOT_DEPEND_ON_MODULE, + "Symbol is declared in module ''{0}'', which the current module does not depend on.", STRING, + ) + map.put( + JAVA_MODULE_DOES_NOT_READ_UNNAMED_MODULE, + "Symbol is declared in an unnamed module which is not read by current module." + ) + map.put( + JAVA_MODULE_DOES_NOT_EXPORT_PACKAGE, + "Symbol is declared in module ''{0}'' which does not export package ''{1}''.", STRING, STRING, + ) + map.put(OVERRIDE_CANNOT_BE_STATIC, "Override member cannot be '@JvmStatic' in an object.") map.put( JVM_STATIC_NOT_IN_OBJECT_OR_CLASS_COMPANION, diff --git a/compiler/fir/checkers/checkers.jvm/src/org/jetbrains/kotlin/fir/analysis/jvm/checkers/JvmExpressionCheckers.kt b/compiler/fir/checkers/checkers.jvm/src/org/jetbrains/kotlin/fir/analysis/jvm/checkers/JvmExpressionCheckers.kt index 30dcacf2c4a..bf2c27c38c0 100644 --- a/compiler/fir/checkers/checkers.jvm/src/org/jetbrains/kotlin/fir/analysis/jvm/checkers/JvmExpressionCheckers.kt +++ b/compiler/fir/checkers/checkers.jvm/src/org/jetbrains/kotlin/fir/analysis/jvm/checkers/JvmExpressionCheckers.kt @@ -20,6 +20,7 @@ object JvmExpressionCheckers : ExpressionCheckers() { FirInterfaceDefaultMethodCallChecker, FirJavaSamInterfaceConstructorReferenceChecker, FirQualifiedAccessJavaNullabilityWarningChecker, + FirJvmModuleAccessibilityQualifiedAccessChecker, ) override val propertyAccessExpressionCheckers: Set @@ -82,4 +83,9 @@ object JvmExpressionCheckers : ExpressionCheckers() { get() = setOf( FirJavaUnnecessaryNotNullChecker, ) + + override val resolvedQualifierCheckers: Set + get() = setOf( + FirJvmModuleAccessibilityResolvedQualifierChecker, + ) } diff --git a/compiler/fir/checkers/checkers.jvm/src/org/jetbrains/kotlin/fir/analysis/jvm/checkers/JvmTypeCheckers.kt b/compiler/fir/checkers/checkers.jvm/src/org/jetbrains/kotlin/fir/analysis/jvm/checkers/JvmTypeCheckers.kt index 4730152af5e..9cc59030671 100644 --- a/compiler/fir/checkers/checkers.jvm/src/org/jetbrains/kotlin/fir/analysis/jvm/checkers/JvmTypeCheckers.kt +++ b/compiler/fir/checkers/checkers.jvm/src/org/jetbrains/kotlin/fir/analysis/jvm/checkers/JvmTypeCheckers.kt @@ -7,10 +7,12 @@ package org.jetbrains.kotlin.fir.analysis.jvm.checkers import org.jetbrains.kotlin.fir.analysis.checkers.type.* import org.jetbrains.kotlin.fir.analysis.jvm.checkers.type.FirFunctionalTypeParameterNameChecker +import org.jetbrains.kotlin.fir.analysis.jvm.checkers.type.FirJvmModuleAccessibilityTypeChecker object JvmTypeCheckers : TypeCheckers() { override val typeRefCheckers: Set = setOf( FirDynamicUnsupportedChecker, FirFunctionalTypeParameterNameChecker, + FirJvmModuleAccessibilityTypeChecker, ) } diff --git a/compiler/fir/checkers/checkers.jvm/src/org/jetbrains/kotlin/fir/analysis/jvm/checkers/expression/FirJvmModuleAccessibilityQualifiedAccessChecker.kt b/compiler/fir/checkers/checkers.jvm/src/org/jetbrains/kotlin/fir/analysis/jvm/checkers/expression/FirJvmModuleAccessibilityQualifiedAccessChecker.kt new file mode 100644 index 00000000000..71efdaf2122 --- /dev/null +++ b/compiler/fir/checkers/checkers.jvm/src/org/jetbrains/kotlin/fir/analysis/jvm/checkers/expression/FirJvmModuleAccessibilityQualifiedAccessChecker.kt @@ -0,0 +1,93 @@ +/* + * Copyright 2010-2023 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.analysis.jvm.checkers.expression + +import com.intellij.openapi.vfs.VirtualFile +import org.jetbrains.kotlin.KtVirtualFileSourceFile +import org.jetbrains.kotlin.diagnostics.DiagnosticReporter +import org.jetbrains.kotlin.diagnostics.reportOn +import org.jetbrains.kotlin.fir.FirElement +import org.jetbrains.kotlin.fir.analysis.checkers.MppCheckerKind +import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext +import org.jetbrains.kotlin.fir.analysis.checkers.expression.FirQualifiedAccessExpressionChecker +import org.jetbrains.kotlin.fir.analysis.diagnostics.jvm.FirJvmErrors +import org.jetbrains.kotlin.fir.containingClassLookupTag +import org.jetbrains.kotlin.fir.declarations.utils.sourceElement +import org.jetbrains.kotlin.fir.expressions.FirQualifiedAccessExpression +import org.jetbrains.kotlin.fir.expressions.toReference +import org.jetbrains.kotlin.fir.java.JavaBinarySourceElement +import org.jetbrains.kotlin.fir.modules.javaModuleResolverProvider +import org.jetbrains.kotlin.fir.packageFqName +import org.jetbrains.kotlin.fir.references.toResolvedCallableSymbol +import org.jetbrains.kotlin.fir.resolve.toFirRegularClassSymbol +import org.jetbrains.kotlin.fir.symbols.SymbolInternals +import org.jetbrains.kotlin.fir.symbols.impl.FirClassSymbol +import org.jetbrains.kotlin.load.kotlin.JvmPackagePartSource +import org.jetbrains.kotlin.load.kotlin.KotlinJvmBinarySourceElement +import org.jetbrains.kotlin.load.kotlin.VirtualFileKotlinClass +import org.jetbrains.kotlin.name.FqName +import org.jetbrains.kotlin.resolve.jvm.modules.JavaModuleResolver + +object FirJvmModuleAccessibilityQualifiedAccessChecker : FirQualifiedAccessExpressionChecker(MppCheckerKind.Common) { + override fun check(expression: FirQualifiedAccessExpression, context: CheckerContext, reporter: DiagnosticReporter) { + val callableSymbol = expression.calleeReference.toResolvedCallableSymbol() ?: return + if (callableSymbol.origin.fromSource) return + + val containingClass = callableSymbol.containingClassLookupTag() + if (containingClass != null) { + val containingClassSymbol = containingClass.toFirRegularClassSymbol(context.session) ?: return + checkClassAccess(context, containingClassSymbol, expression, reporter) + } else { + val containerSource = callableSymbol.containerSource as? JvmPackagePartSource ?: return + val virtualFile = (containerSource.knownJvmBinaryClass as? VirtualFileKotlinClass)?.file ?: return + checkPackageAccess(context, virtualFile, containerSource.className.packageFqName, expression, reporter) + } + } + + internal fun checkClassAccess(context: CheckerContext, symbol: FirClassSymbol<*>, element: FirElement, reporter: DiagnosticReporter) { + if (symbol.origin.fromSource) return + + @OptIn(SymbolInternals::class) + val sourceElement = symbol.fir.sourceElement + val virtualFile = when (sourceElement) { + is JavaBinarySourceElement -> sourceElement.virtualFile + is KotlinJvmBinarySourceElement -> (sourceElement.binaryClass as? VirtualFileKotlinClass)?.file ?: return + else -> return + } + + checkPackageAccess(context, virtualFile, symbol.packageFqName(), element, reporter) + } + + internal fun checkPackageAccess( + context: CheckerContext, + fileFromPackage: VirtualFile, + referencedPackageFqName: FqName, + element: FirElement, + reporter: DiagnosticReporter, + ) { + val fileFromOurModule = (context.containingFile?.sourceFile as? KtVirtualFileSourceFile)?.virtualFile + + val diagnostic = context.session.javaModuleResolverProvider.javaModuleResolver.checkAccessibility( + fileFromOurModule, fileFromPackage, referencedPackageFqName + ) ?: return + + val source = element.toReference(context.session)?.source ?: element.source + when (diagnostic) { + is JavaModuleResolver.AccessError.ModuleDoesNotExportPackage -> { + reporter.reportOn( + source, FirJvmErrors.JAVA_MODULE_DOES_NOT_EXPORT_PACKAGE, diagnostic.dependencyModuleName, + referencedPackageFqName.asString(), context, + ) + } + is JavaModuleResolver.AccessError.ModuleDoesNotReadModule -> { + reporter.reportOn(source, FirJvmErrors.JAVA_MODULE_DOES_NOT_DEPEND_ON_MODULE, diagnostic.dependencyModuleName, context) + } + JavaModuleResolver.AccessError.ModuleDoesNotReadUnnamedModule -> { + reporter.reportOn(source, FirJvmErrors.JAVA_MODULE_DOES_NOT_READ_UNNAMED_MODULE, context) + } + } + } +} diff --git a/compiler/fir/checkers/checkers.jvm/src/org/jetbrains/kotlin/fir/analysis/jvm/checkers/expression/FirJvmModuleAccessibilityResolvedQualifierChecker.kt b/compiler/fir/checkers/checkers.jvm/src/org/jetbrains/kotlin/fir/analysis/jvm/checkers/expression/FirJvmModuleAccessibilityResolvedQualifierChecker.kt new file mode 100644 index 00000000000..705a81fa2f2 --- /dev/null +++ b/compiler/fir/checkers/checkers.jvm/src/org/jetbrains/kotlin/fir/analysis/jvm/checkers/expression/FirJvmModuleAccessibilityResolvedQualifierChecker.kt @@ -0,0 +1,22 @@ +/* + * Copyright 2010-2024 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.analysis.jvm.checkers.expression + +import org.jetbrains.kotlin.diagnostics.DiagnosticReporter +import org.jetbrains.kotlin.fir.analysis.checkers.MppCheckerKind +import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext +import org.jetbrains.kotlin.fir.analysis.checkers.expression.FirResolvedQualifierChecker +import org.jetbrains.kotlin.fir.expressions.FirResolvedQualifier +import org.jetbrains.kotlin.fir.symbols.impl.FirClassSymbol + +object FirJvmModuleAccessibilityResolvedQualifierChecker : FirResolvedQualifierChecker(MppCheckerKind.Common) { + override fun check(expression: FirResolvedQualifier, context: CheckerContext, reporter: DiagnosticReporter) { + val symbol = expression.symbol + if (symbol is FirClassSymbol<*>) { + FirJvmModuleAccessibilityQualifiedAccessChecker.checkClassAccess(context, symbol, expression, reporter) + } + } +} diff --git a/compiler/fir/checkers/checkers.jvm/src/org/jetbrains/kotlin/fir/analysis/jvm/checkers/type/FirJvmModuleAccessibilityTypeChecker.kt b/compiler/fir/checkers/checkers.jvm/src/org/jetbrains/kotlin/fir/analysis/jvm/checkers/type/FirJvmModuleAccessibilityTypeChecker.kt new file mode 100644 index 00000000000..ebf79ae8467 --- /dev/null +++ b/compiler/fir/checkers/checkers.jvm/src/org/jetbrains/kotlin/fir/analysis/jvm/checkers/type/FirJvmModuleAccessibilityTypeChecker.kt @@ -0,0 +1,22 @@ +/* + * Copyright 2010-2024 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.analysis.jvm.checkers.type + +import org.jetbrains.kotlin.diagnostics.DiagnosticReporter +import org.jetbrains.kotlin.fir.analysis.checkers.MppCheckerKind +import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext +import org.jetbrains.kotlin.fir.analysis.checkers.type.FirTypeRefChecker +import org.jetbrains.kotlin.fir.analysis.jvm.checkers.expression.FirJvmModuleAccessibilityQualifiedAccessChecker +import org.jetbrains.kotlin.fir.types.FirTypeRef +import org.jetbrains.kotlin.fir.types.coneTypeOrNull +import org.jetbrains.kotlin.fir.types.toRegularClassSymbol + +object FirJvmModuleAccessibilityTypeChecker : FirTypeRefChecker(MppCheckerKind.Common) { + override fun check(typeRef: FirTypeRef, context: CheckerContext, reporter: DiagnosticReporter) { + val classSymbol = typeRef.coneTypeOrNull?.toRegularClassSymbol(context.session) ?: return + FirJvmModuleAccessibilityQualifiedAccessChecker.checkClassAccess(context, classSymbol, typeRef, reporter) + } +} diff --git a/compiler/fir/checkers/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/FirNonSuppressibleErrorNames.kt b/compiler/fir/checkers/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/FirNonSuppressibleErrorNames.kt index 9e9e6d836b5..c580dddaf3f 100644 --- a/compiler/fir/checkers/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/FirNonSuppressibleErrorNames.kt +++ b/compiler/fir/checkers/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/FirNonSuppressibleErrorNames.kt @@ -616,6 +616,9 @@ val FIR_NON_SUPPRESSIBLE_ERROR_NAMES: Set = setOf( "DELEGATION_BY_IN_JVM_RECORD", "JVM_RECORD_EXTENDS_CLASS", "ILLEGAL_JAVA_LANG_RECORD_SUPERTYPE", + "JAVA_MODULE_DOES_NOT_DEPEND_ON_MODULE", + "JAVA_MODULE_DOES_NOT_READ_UNNAMED_MODULE", + "JAVA_MODULE_DOES_NOT_EXPORT_PACKAGE", "JVM_DEFAULT_IN_DECLARATION", "JVM_DEFAULT_WITH_COMPATIBILITY_IN_DECLARATION", "JVM_DEFAULT_WITH_COMPATIBILITY_NOT_ON_INTERFACE", diff --git a/compiler/fir/entrypoint/src/org/jetbrains/kotlin/fir/session/ComponentsContainers.kt b/compiler/fir/entrypoint/src/org/jetbrains/kotlin/fir/session/ComponentsContainers.kt index b4a508b27a1..dae725124f3 100644 --- a/compiler/fir/entrypoint/src/org/jetbrains/kotlin/fir/session/ComponentsContainers.kt +++ b/compiler/fir/entrypoint/src/org/jetbrains/kotlin/fir/session/ComponentsContainers.kt @@ -23,11 +23,7 @@ import org.jetbrains.kotlin.fir.analysis.jvm.checkers.FirJvmInlineCheckerCompone import org.jetbrains.kotlin.fir.analysis.jvm.checkers.FirJvmPrimaryConstructorSuperTypeCheckerPlatformComponent import org.jetbrains.kotlin.fir.caches.FirCachesFactory import org.jetbrains.kotlin.fir.caches.FirThreadUnsafeCachesFactory -import org.jetbrains.kotlin.fir.declarations.FirDeclarationOverloadabilityHelper -import org.jetbrains.kotlin.fir.declarations.FirTypeSpecificityComparatorProvider -import org.jetbrains.kotlin.fir.declarations.SealedClassInheritorsProvider -import org.jetbrains.kotlin.fir.declarations.SealedClassInheritorsProviderImpl -import org.jetbrains.kotlin.fir.declarations.FirAnnotationsPlatformSpecificSupportComponent +import org.jetbrains.kotlin.fir.declarations.* import org.jetbrains.kotlin.fir.deserialization.FirDeserializationExtension import org.jetbrains.kotlin.fir.extensions.* import org.jetbrains.kotlin.fir.java.FirJavaVisibilityChecker @@ -39,6 +35,7 @@ import org.jetbrains.kotlin.fir.java.enhancement.FirAnnotationTypeQualifierResol import org.jetbrains.kotlin.fir.java.enhancement.FirEnhancedSymbolsStorage import org.jetbrains.kotlin.fir.java.enhancement.JavaCompilerRequiredAnnotationEnhancementProvider import org.jetbrains.kotlin.fir.java.scopes.JavaOverridabilityRules +import org.jetbrains.kotlin.fir.modules.FirJavaModuleResolverProvider import org.jetbrains.kotlin.fir.resolve.* import org.jetbrains.kotlin.fir.resolve.calls.ConeCallConflictResolverFactory import org.jetbrains.kotlin.fir.resolve.calls.FirDeclarationOverloadabilityHelperImpl @@ -133,6 +130,7 @@ fun FirSession.registerJavaComponents( javaModuleResolver: JavaModuleResolver, predefinedComponents: FirSharableJavaComponents? = null, ) { + register(FirJavaModuleResolverProvider::class, FirJavaModuleResolverProvider(javaModuleResolver)) val jsr305State = languageVersionSettings.getFlag(JvmAnalysisFlags.javaTypeEnhancementState) register( FirAnnotationTypeQualifierResolver::class, diff --git a/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/JavaBinarySourceElement.kt b/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/JavaBinarySourceElement.kt index dba94aea380..bdacddb7958 100644 --- a/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/JavaBinarySourceElement.kt +++ b/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/JavaBinarySourceElement.kt @@ -5,10 +5,14 @@ package org.jetbrains.kotlin.fir.java +import com.intellij.openapi.vfs.VirtualFile import org.jetbrains.kotlin.descriptors.SourceElement import org.jetbrains.kotlin.descriptors.SourceFile import org.jetbrains.kotlin.load.java.structure.impl.classFiles.BinaryJavaClass class JavaBinarySourceElement(val javaClass: BinaryJavaClass) : SourceElement { + val virtualFile: VirtualFile + get() = javaClass.virtualFile + override fun getContainingFile(): SourceFile = SourceFile.NO_SOURCE_FILE } diff --git a/compiler/fir/java/src/org/jetbrains/kotlin/fir/modules/FirJavaModuleResolverProvider.kt b/compiler/fir/java/src/org/jetbrains/kotlin/fir/modules/FirJavaModuleResolverProvider.kt new file mode 100644 index 00000000000..b7c434ebd9b --- /dev/null +++ b/compiler/fir/java/src/org/jetbrains/kotlin/fir/modules/FirJavaModuleResolverProvider.kt @@ -0,0 +1,14 @@ +/* + * Copyright 2010-2024 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.modules + +import org.jetbrains.kotlin.fir.FirSession +import org.jetbrains.kotlin.fir.FirSessionComponent +import org.jetbrains.kotlin.resolve.jvm.modules.JavaModuleResolver + +class FirJavaModuleResolverProvider(val javaModuleResolver: JavaModuleResolver) : FirSessionComponent + +val FirSession.javaModuleResolverProvider: FirJavaModuleResolverProvider by FirSession.sessionComponentAccessor() diff --git a/compiler/testData/javaModules/automaticModuleInternalJdkPackageUsage/jvmStatUsage.fir.txt b/compiler/testData/javaModules/automaticModuleInternalJdkPackageUsage/jvmStatUsage.fir.txt new file mode 100644 index 00000000000..649683ffc4c --- /dev/null +++ b/compiler/testData/javaModules/automaticModuleInternalJdkPackageUsage/jvmStatUsage.fir.txt @@ -0,0 +1,4 @@ +compiler/testData/javaModules/automaticModuleInternalJdkPackageUsage/jvmStatUsage/Test.kt:1:8: error: symbol is declared in module 'jdk.internal.jvmstat' which does not export package 'sun.jvmstat.monitor'. +val z: sun.jvmstat.monitor.Monitor? = null + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +COMPILATION_ERROR diff --git a/compiler/testData/javaModules/noDependencyOnNamed/main.fir.txt b/compiler/testData/javaModules/noDependencyOnNamed/main.fir.txt new file mode 100644 index 00000000000..02a6a59cd5f --- /dev/null +++ b/compiler/testData/javaModules/noDependencyOnNamed/main.fir.txt @@ -0,0 +1,13 @@ +compiler/testData/javaModules/noDependencyOnNamed/main/usage.kt:3:15: error: symbol is declared in module 'lib', which the current module does not depend on. +fun test(foo: Foo) { + ^^^ +compiler/testData/javaModules/noDependencyOnNamed/main/usage.kt:4:9: error: symbol is declared in module 'lib', which the current module does not depend on. + foo.field + ^^^^^ +compiler/testData/javaModules/noDependencyOnNamed/main/usage.kt:5:9: error: symbol is declared in module 'lib', which the current module does not depend on. + foo.method() + ^^^^^^ +compiler/testData/javaModules/noDependencyOnNamed/main/usage.kt:6:5: error: symbol is declared in module 'lib', which the current module does not depend on. + Foo() + ^^^ +COMPILATION_ERROR diff --git a/compiler/testData/javaModules/noDependencyOnUnnamed/main.fir.txt b/compiler/testData/javaModules/noDependencyOnUnnamed/main.fir.txt new file mode 100644 index 00000000000..ed8b326a5e5 --- /dev/null +++ b/compiler/testData/javaModules/noDependencyOnUnnamed/main.fir.txt @@ -0,0 +1,13 @@ +compiler/testData/javaModules/noDependencyOnUnnamed/main/usage.kt:3:15: error: symbol is declared in an unnamed module which is not read by current module. +fun test(foo: Foo) { + ^^^ +compiler/testData/javaModules/noDependencyOnUnnamed/main/usage.kt:4:9: error: symbol is declared in an unnamed module which is not read by current module. + foo.field + ^^^^^ +compiler/testData/javaModules/noDependencyOnUnnamed/main/usage.kt:5:9: error: symbol is declared in an unnamed module which is not read by current module. + foo.method() + ^^^^^^ +compiler/testData/javaModules/noDependencyOnUnnamed/main/usage.kt:6:5: error: symbol is declared in an unnamed module which is not read by current module. + Foo() + ^^^ +COMPILATION_ERROR diff --git a/compiler/testData/javaModules/simpleUseNonExportedPackage/moduleB.fir.txt b/compiler/testData/javaModules/simpleUseNonExportedPackage/moduleB.fir.txt new file mode 100644 index 00000000000..90d67ec39c4 --- /dev/null +++ b/compiler/testData/javaModules/simpleUseNonExportedPackage/moduleB.fir.txt @@ -0,0 +1,37 @@ +compiler/testData/javaModules/simpleUseNonExportedPackage/moduleB/usage.kt:8:9: error: symbol is declared in module 'moduleA' which does not export package 'a.impl'. +val a3: AImpl = A.getInstance() + ^^^^^ +compiler/testData/javaModules/simpleUseNonExportedPackage/moduleB/usage.kt:9:18: error: symbol is declared in module 'moduleA' which does not export package 'a.impl'. +val a4: String = AImpl.method() + ^^^^^ +compiler/testData/javaModules/simpleUseNonExportedPackage/moduleB/usage.kt:9:24: error: symbol is declared in module 'moduleA' which does not export package 'a.impl'. +val a4: String = AImpl.method() + ^^^^^^ +compiler/testData/javaModules/simpleUseNonExportedPackage/moduleB/usage.kt:10:18: error: symbol is declared in module 'moduleA' which does not export package 'a.impl'. +val a5: String = AImpl.field + ^^^^^ +compiler/testData/javaModules/simpleUseNonExportedPackage/moduleB/usage.kt:10:24: error: symbol is declared in module 'moduleA' which does not export package 'a.impl'. +val a5: String = AImpl.field + ^^^^^ +compiler/testData/javaModules/simpleUseNonExportedPackage/moduleB/usage.kt:14:9: error: symbol is declared in module 'moduleA' which does not export package 'a.impl'. +val k3: KImpl = K.getInstance() + ^^^^^ +compiler/testData/javaModules/simpleUseNonExportedPackage/moduleB/usage.kt:15:18: error: symbol is declared in module 'moduleA' which does not export package 'a.impl'. +val k4: String = KImpl.method() + ^^^^^ +compiler/testData/javaModules/simpleUseNonExportedPackage/moduleB/usage.kt:15:24: error: symbol is declared in module 'moduleA' which does not export package 'a.impl'. +val k4: String = KImpl.method() + ^^^^^^ +compiler/testData/javaModules/simpleUseNonExportedPackage/moduleB/usage.kt:16:18: error: symbol is declared in module 'moduleA' which does not export package 'a.impl'. +val k5: String = KImpl.field + ^^^^^ +compiler/testData/javaModules/simpleUseNonExportedPackage/moduleB/usage.kt:16:24: error: symbol is declared in module 'moduleA' which does not export package 'a.impl'. +val k5: String = KImpl.field + ^^^^^ +compiler/testData/javaModules/simpleUseNonExportedPackage/moduleB/usage.kt:18:19: error: symbol is declared in module 'moduleA' which does not export package 'a.impl'. +val kf1: String = fileField + ^^^^^^^^^ +compiler/testData/javaModules/simpleUseNonExportedPackage/moduleB/usage.kt:19:19: error: symbol is declared in module 'moduleA' which does not export package 'a.impl'. +val kf2: String = fileMethod() + ^^^^^^^^^^ +COMPILATION_ERROR diff --git a/compiler/testData/javaModules/specifyPathToModuleInfoInArguments/moduleB.fir.txt b/compiler/testData/javaModules/specifyPathToModuleInfoInArguments/moduleB.fir.txt new file mode 100644 index 00000000000..128b82f4670 --- /dev/null +++ b/compiler/testData/javaModules/specifyPathToModuleInfoInArguments/moduleB.fir.txt @@ -0,0 +1,7 @@ +compiler/testData/javaModules/specifyPathToModuleInfoInArguments/moduleB/usage.kt:9:5: error: symbol is declared in module 'moduleA' which does not export package 'unexported'. + Unexported() + ^^^^^^^^^^ +compiler/testData/javaModules/specifyPathToModuleInfoInArguments/moduleB/usage.kt:13:5: error: unresolved reference 'Unrelated'. + Unrelated() + ^^^^^^^^^ +COMPILATION_ERROR diff --git a/compiler/testData/javaModules/unnamedDependsOnNamed/moduleB.fir.txt b/compiler/testData/javaModules/unnamedDependsOnNamed/moduleB.fir.txt new file mode 100644 index 00000000000..2a7c8051940 --- /dev/null +++ b/compiler/testData/javaModules/unnamedDependsOnNamed/moduleB.fir.txt @@ -0,0 +1,4 @@ +compiler/testData/javaModules/unnamedDependsOnNamed/moduleB/usage.kt:7:5: error: symbol is declared in module 'moduleA' which does not export package 'foo.impl'. + Impl() // should be error + ^^^^ +COMPILATION_ERROR diff --git a/compiler/tests/org/jetbrains/kotlin/jvm/modules/AbstractFirJavaModulesIntegrationTest.kt b/compiler/tests/org/jetbrains/kotlin/jvm/modules/AbstractFirJavaModulesIntegrationTest.kt index 62035776f9a..4f2f70f453c 100644 --- a/compiler/tests/org/jetbrains/kotlin/jvm/modules/AbstractFirJavaModulesIntegrationTest.kt +++ b/compiler/tests/org/jetbrains/kotlin/jvm/modules/AbstractFirJavaModulesIntegrationTest.kt @@ -9,14 +9,5 @@ import org.jetbrains.kotlin.config.LanguageVersion import java.io.File abstract class AbstractFirJavaModulesIntegrationTest(jdkVersion: Int, jdkHome: File) : AbstractJavaModulesIntegrationTest( - jdkVersion, jdkHome, maxOf(LanguageVersion.KOTLIN_2_0, LanguageVersion.LATEST_STABLE), -) { - override fun muteForK2(test: () -> Unit) { - try { - test() - } catch (e: Throwable) { - return - } - fail("Looks like this test can be unmuted. Remove the call to `muteForK2`.") - } -} + jdkVersion, jdkHome, LanguageVersion.LATEST_STABLE, +) diff --git a/compiler/tests/org/jetbrains/kotlin/jvm/modules/AbstractJavaModulesIntegrationTest.kt b/compiler/tests/org/jetbrains/kotlin/jvm/modules/AbstractJavaModulesIntegrationTest.kt index 4eef1e67a7e..bc95f93155b 100644 --- a/compiler/tests/org/jetbrains/kotlin/jvm/modules/AbstractJavaModulesIntegrationTest.kt +++ b/compiler/tests/org/jetbrains/kotlin/jvm/modules/AbstractJavaModulesIntegrationTest.kt @@ -25,10 +25,6 @@ abstract class AbstractJavaModulesIntegrationTest( override val testDataPath: String get() = "compiler/testData/javaModules/" - protected open fun muteForK2(test: () -> Unit) { - test() - } - private fun module( name: String, modulePath: List = emptyList(), @@ -120,8 +116,7 @@ abstract class AbstractJavaModulesIntegrationTest( module("moduleB", listOf(a)) } - // TODO (KT-60797): missing JAVA_MODULE_DOES_NOT_EXPORT_PACKAGE. - fun testSimpleUseNonExportedPackage() = muteForK2 { + fun testSimpleUseNonExportedPackage() { val a = module("moduleA") module("moduleB", listOf(a)) } @@ -133,8 +128,7 @@ abstract class AbstractJavaModulesIntegrationTest( module("moduleD", listOf(a, b, c)) } - // TODO (KT-60797): missing JAVA_MODULE_DOES_NOT_EXPORT_PACKAGE. - fun testUnnamedDependsOnNamed() = muteForK2 { + fun testUnnamedDependsOnNamed() { val a = module("moduleA") module("moduleB", listOf(a), listOf("moduleA")) @@ -174,8 +168,7 @@ abstract class AbstractJavaModulesIntegrationTest( } } - // TODO (KT-60797): missing JAVA_MODULE_DOES_NOT_EXPORT_PACKAGE. - fun testAutomaticModuleInternalJdkPackageUsage() = muteForK2 { + fun testAutomaticModuleInternalJdkPackageUsage() { module("jvmStatUsage") } @@ -217,8 +210,7 @@ abstract class AbstractJavaModulesIntegrationTest( module("moduleD", listOf(c, b, a)) } - // TODO (KT-60797): missing JAVA_MODULE_DOES_NOT_EXPORT_PACKAGE. - fun testSpecifyPathToModuleInfoInArguments() = muteForK2 { + fun testSpecifyPathToModuleInfoInArguments() { val a = module("moduleA") val kotlinOptions = mutableListOf( @@ -349,15 +341,13 @@ abstract class AbstractJavaModulesIntegrationTest( }) } - // TODO (KT-60797): missing JAVA_MODULE_DOES_NOT_DEPEND_ON_MODULE. - fun testNoDependencyOnNamed() = muteForK2 { + fun testNoDependencyOnNamed() { // This is a test on the JAVA_MODULE_DOES_NOT_DEPEND_ON_MODULE diagnostic. val lib = module("lib") module("main", listOf(lib), listOf("lib")) } - // TODO (KT-60797): missing JAVA_MODULE_DOES_NOT_READ_UNNAMED_MODULE. - fun testNoDependencyOnUnnamed() = muteForK2 { + fun testNoDependencyOnUnnamed() { // This is a test on the JAVA_MODULE_DOES_NOT_READ_UNNAMED_MODULE diagnostic. // Most of the other tests in this class are compiling modules to jars, however here we need to compile the module to a directory. // The reason is twofold: