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
This commit is contained in:
committed by
Space Team
parent
ac901c6d07
commit
5ca7ab9801
+13
-6
@@ -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<PsiElement>()
|
||||
}
|
||||
|
||||
val MODULES by object : DiagnosticGroup("JVM Modules") {
|
||||
val JAVA_MODULE_DOES_NOT_DEPEND_ON_MODULE by error<PsiElement> {
|
||||
parameter<String>("moduleName")
|
||||
}
|
||||
val JAVA_MODULE_DOES_NOT_READ_UNNAMED_MODULE by error<PsiElement>()
|
||||
val JAVA_MODULE_DOES_NOT_EXPORT_PACKAGE by error<PsiElement>() {
|
||||
parameter<String>("moduleName")
|
||||
parameter<String>("packageName")
|
||||
}
|
||||
}
|
||||
|
||||
val JVM_DEFAULT by object : DiagnosticGroup("JVM Default") {
|
||||
val JVM_DEFAULT_IN_DECLARATION by error<KtElement>(PositioningStrategy.DECLARATION_SIGNATURE_OR_DEFAULT) {
|
||||
parameter<String>("annotation")
|
||||
|
||||
+5
@@ -99,6 +99,11 @@ object FirJvmErrors {
|
||||
val JVM_RECORD_EXTENDS_CLASS: KtDiagnosticFactory1<ConeKotlinType> by error1<PsiElement, ConeKotlinType>(SourceElementPositioningStrategies.ACTUAL_DECLARATION_NAME)
|
||||
val ILLEGAL_JAVA_LANG_RECORD_SUPERTYPE: KtDiagnosticFactory0 by error0<PsiElement>()
|
||||
|
||||
// JVM Modules
|
||||
val JAVA_MODULE_DOES_NOT_DEPEND_ON_MODULE: KtDiagnosticFactory1<String> by error1<PsiElement, String>()
|
||||
val JAVA_MODULE_DOES_NOT_READ_UNNAMED_MODULE: KtDiagnosticFactory0 by error0<PsiElement>()
|
||||
val JAVA_MODULE_DOES_NOT_EXPORT_PACKAGE: KtDiagnosticFactory2<String, String> by error2<PsiElement, String, String>()
|
||||
|
||||
// JVM Default
|
||||
val JVM_DEFAULT_IN_DECLARATION: KtDiagnosticFactory1<String> by error1<KtElement, String>(SourceElementPositioningStrategies.DECLARATION_SIGNATURE_OR_DEFAULT)
|
||||
val JVM_DEFAULT_WITH_COMPATIBILITY_IN_DECLARATION: KtDiagnosticFactory0 by error0<KtElement>()
|
||||
|
||||
+16
@@ -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,
|
||||
|
||||
+6
@@ -20,6 +20,7 @@ object JvmExpressionCheckers : ExpressionCheckers() {
|
||||
FirInterfaceDefaultMethodCallChecker,
|
||||
FirJavaSamInterfaceConstructorReferenceChecker,
|
||||
FirQualifiedAccessJavaNullabilityWarningChecker,
|
||||
FirJvmModuleAccessibilityQualifiedAccessChecker,
|
||||
)
|
||||
|
||||
override val propertyAccessExpressionCheckers: Set<FirPropertyAccessExpressionChecker>
|
||||
@@ -82,4 +83,9 @@ object JvmExpressionCheckers : ExpressionCheckers() {
|
||||
get() = setOf(
|
||||
FirJavaUnnecessaryNotNullChecker,
|
||||
)
|
||||
|
||||
override val resolvedQualifierCheckers: Set<FirResolvedQualifierChecker>
|
||||
get() = setOf(
|
||||
FirJvmModuleAccessibilityResolvedQualifierChecker,
|
||||
)
|
||||
}
|
||||
|
||||
+2
@@ -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<FirTypeRefChecker> = setOf(
|
||||
FirDynamicUnsupportedChecker,
|
||||
FirFunctionalTypeParameterNameChecker,
|
||||
FirJvmModuleAccessibilityTypeChecker,
|
||||
)
|
||||
}
|
||||
|
||||
+93
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+22
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
+22
@@ -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)
|
||||
}
|
||||
}
|
||||
+3
@@ -616,6 +616,9 @@ val FIR_NON_SUPPRESSIBLE_ERROR_NAMES: Set<String> = 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",
|
||||
|
||||
Reference in New Issue
Block a user