K2: introduce OptIn language version checker

#KT-60598 Fixed
This commit is contained in:
Mikhail Glukhikh
2023-07-17 11:09:07 +02:00
committed by Space Team
parent 8c305d6143
commit 95bf63d6cb
29 changed files with 196 additions and 49 deletions
@@ -5,9 +5,45 @@
package org.jetbrains.kotlin.fir.analysis.checkers.config
import org.jetbrains.kotlin.config.AnalysisFlags
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
import org.jetbrains.kotlin.fir.declarations.getAnnotationByClassId
import org.jetbrains.kotlin.fir.resolve.providers.symbolProvider
import org.jetbrains.kotlin.fir.resolve.transformers.PackageResolutionResult
import org.jetbrains.kotlin.fir.resolve.transformers.resolveToPackageOrClass
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.resolve.checkers.OptInNames
import org.jetbrains.kotlin.resolve.deprecation.DeprecationLevelValue
object FirOptInLanguageVersionSettingsChecker : FirLanguageVersionSettingsChecker() {
override fun check(context: CheckerContext, rawReport: (Boolean, String) -> Unit) {
context.languageVersionSettings.getFlag(AnalysisFlags.optIn).forEach { fqNameAsString ->
if (fqNameAsString != OptInNames.REQUIRES_OPT_IN_FQ_NAME.asString()) {
checkOptInMarkerArgument(context, fqNameAsString, rawReport)
}
}
}
}
private fun checkOptInMarkerArgument(context: CheckerContext, fqNameAsString: String, rawReport: (Boolean, String) -> Unit) {
val packageOrClass = resolveToPackageOrClass(context.session.symbolProvider, FqName(fqNameAsString))
val symbol = (packageOrClass as? PackageResolutionResult.PackageOrClass)?.classSymbol
if (symbol == null) {
rawReport(
false,
"Opt-in requirement marker $fqNameAsString is unresolved. Please make sure it's present in the module dependencies"
)
return
}
if (symbol.getAnnotationByClassId(OptInNames.REQUIRES_OPT_IN_CLASS_ID, context.session) == null) {
rawReport(false, "Class $fqNameAsString is not an opt-in requirement marker")
return
}
val deprecationInfo = symbol.getDeprecation(context.languageVersionSettings.apiVersion)?.all ?: return
rawReport(
deprecationInfo.deprecationLevel != DeprecationLevelValue.WARNING,
"Opt-in requirement marker $fqNameAsString is deprecated" + deprecationInfo.message?.let { ". $it" }.orEmpty()
)
}
}
@@ -10,7 +10,6 @@ import org.jetbrains.kotlin.diagnostics.impl.BaseDiagnosticsCollector
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.analysis.CheckersComponentInternal
import org.jetbrains.kotlin.fir.analysis.checkers.LanguageVersionSettingsCheckers
import org.jetbrains.kotlin.fir.analysis.checkers.config.FirLanguageVersionSettingsChecker
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
import org.jetbrains.kotlin.fir.analysis.checkersComponent
@@ -21,13 +20,9 @@ class LanguageVersionSettingsDiagnosticComponent(
private val checkers: LanguageVersionSettingsCheckers = session.checkersComponent.languageVersionSettingsCheckers,
) : AbstractDiagnosticCollectorComponent(session, reporter) {
override fun checkSettings(data: CheckerContext) {
checkers.languageVersionSettingsCheckers.check(data)
}
private fun Collection<FirLanguageVersionSettingsChecker>.check(context: CheckerContext) {
val rawReport = (reporter as? BaseDiagnosticsCollector)?.rawReport ?: return
for (checker in this) {
checker.check(context, rawReport)
for (checker in checkers.languageVersionSettingsCheckers) {
checker.check(data, rawReport)
}
}
}
@@ -13,16 +13,12 @@ import org.jetbrains.kotlin.fir.declarations.FirRegularClass
import org.jetbrains.kotlin.fir.declarations.FirResolvePhase
import org.jetbrains.kotlin.fir.declarations.builder.buildErrorImport
import org.jetbrains.kotlin.fir.declarations.builder.buildResolvedImport
import org.jetbrains.kotlin.fir.diagnostics.ConeDiagnostic
import org.jetbrains.kotlin.fir.lookupTracker
import org.jetbrains.kotlin.fir.resolve.ScopeSession
import org.jetbrains.kotlin.fir.resolve.diagnostics.ConeImportFromSingleton
import org.jetbrains.kotlin.fir.resolve.diagnostics.ConeUnresolvedParentInImport
import org.jetbrains.kotlin.fir.resolve.providers.FirSymbolProvider
import org.jetbrains.kotlin.fir.resolve.providers.symbolProvider
import org.jetbrains.kotlin.fir.symbols.impl.FirClassLikeSymbol
import org.jetbrains.kotlin.fir.withFileAnalysisExceptionWrapping
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.FqName
class FirImportResolveProcessor(session: FirSession, scopeSession: ScopeSession) : FirTransformerBasedResolveProcessor(
@@ -100,35 +96,3 @@ open class FirImportResolveTransformer protected constructor(
}
}
}
fun resolveToPackageOrClass(symbolProvider: FirSymbolProvider, fqName: FqName): PackageResolutionResult {
var currentPackage = fqName
val pathSegments = fqName.pathSegments()
var prefixSize = pathSegments.size
while (!currentPackage.isRoot && prefixSize > 0) {
if (symbolProvider.getPackage(currentPackage) != null) {
break
}
currentPackage = currentPackage.parent()
prefixSize--
}
if (currentPackage == fqName) return PackageResolutionResult.PackageOrClass(currentPackage, null, null)
val relativeClassFqName = FqName.fromSegments((prefixSize until pathSegments.size).map { pathSegments[it].asString() })
val classId = ClassId(currentPackage, relativeClassFqName, false)
val symbol = symbolProvider.getClassLikeSymbolByClassId(classId) ?: return PackageResolutionResult.Error(
ConeUnresolvedParentInImport(classId)
)
return PackageResolutionResult.PackageOrClass(currentPackage, relativeClassFqName, symbol)
}
sealed class PackageResolutionResult {
data class PackageOrClass(
val packageFqName: FqName, val relativeClassFqName: FqName?, val classSymbol: FirClassLikeSymbol<*>?
) : PackageResolutionResult()
class Error(val diagnostic: ConeDiagnostic) : PackageResolutionResult()
}
@@ -0,0 +1,45 @@
/*
* 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.resolve.transformers
import org.jetbrains.kotlin.fir.diagnostics.ConeDiagnostic
import org.jetbrains.kotlin.fir.resolve.diagnostics.ConeUnresolvedParentInImport
import org.jetbrains.kotlin.fir.resolve.providers.FirSymbolProvider
import org.jetbrains.kotlin.fir.symbols.impl.FirClassLikeSymbol
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.FqName
fun resolveToPackageOrClass(symbolProvider: FirSymbolProvider, fqName: FqName): PackageResolutionResult {
var currentPackage = fqName
val pathSegments = fqName.pathSegments()
var prefixSize = pathSegments.size
while (!currentPackage.isRoot && prefixSize > 0) {
if (symbolProvider.getPackage(currentPackage) != null) {
break
}
currentPackage = currentPackage.parent()
prefixSize--
}
if (currentPackage == fqName) return PackageResolutionResult.PackageOrClass(currentPackage, null, null)
val relativeClassFqName = FqName.fromSegments((prefixSize until pathSegments.size).map { pathSegments[it].asString() })
val classId = ClassId(currentPackage, relativeClassFqName, false)
val symbol = symbolProvider.getClassLikeSymbolByClassId(classId) ?: return PackageResolutionResult.Error(
ConeUnresolvedParentInImport(classId)
)
return PackageResolutionResult.PackageOrClass(currentPackage, relativeClassFqName, symbol)
}
sealed class PackageResolutionResult {
data class PackageOrClass(
val packageFqName: FqName, val relativeClassFqName: FqName?, val classSymbol: FirClassLikeSymbol<*>?
) : PackageResolutionResult()
class Error(val diagnostic: ConeDiagnostic) : PackageResolutionResult()
}