From 5ca7ab9801bfa28ad8b10b43525d682dda89dc7b Mon Sep 17 00:00:00 2001 From: Alexander Udalov Date: Thu, 15 Feb 2024 12:50:41 +0100 Subject: [PATCH] K2 JVM: implement Java modules-related checkers All existing tests use custom test data for FIR because the diagnostic text in FIR has to have a dot at the end. Also, the K2 checker doesn't check usages in imports because there are no "import checkers" in K2 right now, this will need to be fixed later if necessary. #KT-60797 Fixed --- .../diagnostics/KtFirDataClassConverters.kt | 21 +++++ .../api/fir/diagnostics/KtFirDiagnostics.kt | 15 +++ .../fir/diagnostics/KtFirDiagnosticsImpl.kt | 18 ++++ .../diagnostics/FirJvmDiagnosticsList.kt | 19 ++-- .../analysis/diagnostics/jvm/FirJvmErrors.kt | 5 + .../jvm/FirJvmErrorsDefaultMessages.kt | 16 ++++ .../jvm/checkers/JvmExpressionCheckers.kt | 6 ++ .../analysis/jvm/checkers/JvmTypeCheckers.kt | 2 + ...duleAccessibilityQualifiedAccessChecker.kt | 93 +++++++++++++++++++ ...leAccessibilityResolvedQualifierChecker.kt | 22 +++++ .../FirJvmModuleAccessibilityTypeChecker.kt | 22 +++++ .../FirNonSuppressibleErrorNames.kt | 3 + .../fir/session/ComponentsContainers.kt | 8 +- .../fir/java/JavaBinarySourceElement.kt | 4 + .../modules/FirJavaModuleResolverProvider.kt | 14 +++ .../jvmStatUsage.fir.txt | 4 + .../noDependencyOnNamed/main.fir.txt | 13 +++ .../noDependencyOnUnnamed/main.fir.txt | 13 +++ .../moduleB.fir.txt | 37 ++++++++ .../moduleB.fir.txt | 7 ++ .../unnamedDependsOnNamed/moduleB.fir.txt | 4 + .../AbstractFirJavaModulesIntegrationTest.kt | 13 +-- .../AbstractJavaModulesIntegrationTest.kt | 22 ++--- 23 files changed, 343 insertions(+), 38 deletions(-) create mode 100644 compiler/fir/checkers/checkers.jvm/src/org/jetbrains/kotlin/fir/analysis/jvm/checkers/expression/FirJvmModuleAccessibilityQualifiedAccessChecker.kt create mode 100644 compiler/fir/checkers/checkers.jvm/src/org/jetbrains/kotlin/fir/analysis/jvm/checkers/expression/FirJvmModuleAccessibilityResolvedQualifierChecker.kt create mode 100644 compiler/fir/checkers/checkers.jvm/src/org/jetbrains/kotlin/fir/analysis/jvm/checkers/type/FirJvmModuleAccessibilityTypeChecker.kt create mode 100644 compiler/fir/java/src/org/jetbrains/kotlin/fir/modules/FirJavaModuleResolverProvider.kt create mode 100644 compiler/testData/javaModules/automaticModuleInternalJdkPackageUsage/jvmStatUsage.fir.txt create mode 100644 compiler/testData/javaModules/noDependencyOnNamed/main.fir.txt create mode 100644 compiler/testData/javaModules/noDependencyOnUnnamed/main.fir.txt create mode 100644 compiler/testData/javaModules/simpleUseNonExportedPackage/moduleB.fir.txt create mode 100644 compiler/testData/javaModules/specifyPathToModuleInfoInArguments/moduleB.fir.txt create mode 100644 compiler/testData/javaModules/unnamedDependsOnNamed/moduleB.fir.txt 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: