FIR: add OptIn checkers draft + some annotation / SinceKotlin utilities
This commit is contained in:
+1
@@ -1,3 +1,4 @@
|
||||
// !USE_EXPERIMENTAL: kotlin.RequiresOptIn
|
||||
import kotlin.contracts.*
|
||||
|
||||
@OptIn(ExperimentalContracts::class)
|
||||
|
||||
+1
@@ -1,3 +1,4 @@
|
||||
// !USE_EXPERIMENTAL: kotlin.RequiresOptIn
|
||||
import kotlin.contracts.*
|
||||
|
||||
@OptIn(ExperimentalContracts::class)
|
||||
|
||||
+1
@@ -1,3 +1,4 @@
|
||||
// !USE_EXPERIMENTAL: kotlin.RequiresOptIn
|
||||
import kotlin.contracts.*
|
||||
|
||||
@OptIn(ExperimentalContracts::class)
|
||||
|
||||
+1
@@ -1,3 +1,4 @@
|
||||
// !USE_EXPERIMENTAL: kotlin.RequiresOptIn
|
||||
import kotlin.contracts.*
|
||||
|
||||
@OptIn(ExperimentalContracts::class)
|
||||
|
||||
+1
@@ -1,3 +1,4 @@
|
||||
// !USE_EXPERIMENTAL: kotlin.RequiresOptIn
|
||||
import kotlin.contracts.*
|
||||
|
||||
@OptIn(ExperimentalContracts::class)
|
||||
|
||||
+1
@@ -1,3 +1,4 @@
|
||||
// !USE_EXPERIMENTAL: kotlin.RequiresOptIn
|
||||
import kotlin.contracts.*
|
||||
|
||||
@OptIn(ExperimentalContracts::class)
|
||||
|
||||
+1
@@ -1,3 +1,4 @@
|
||||
// !USE_EXPERIMENTAL: kotlin.RequiresOptIn
|
||||
import kotlin.contracts.*
|
||||
|
||||
@OptIn(ExperimentalContracts::class)
|
||||
|
||||
+31
@@ -23,6 +23,7 @@ import org.jetbrains.kotlin.fir.symbols.impl.*
|
||||
import org.jetbrains.kotlin.fir.types.ConeKotlinType
|
||||
import org.jetbrains.kotlin.lexer.KtKeywordToken
|
||||
import org.jetbrains.kotlin.lexer.KtModifierKeywordToken
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.resolve.ForbiddenNamedArgumentsTarget
|
||||
@@ -212,6 +213,36 @@ object DIAGNOSTICS_LIST : DiagnosticList("FirErrors") {
|
||||
val INAPPLICABLE_FILE_TARGET by error<KtAnnotationEntry>(PositioningStrategy.ANNOTATION_USE_SITE)
|
||||
}
|
||||
|
||||
val EXPERIMENTAL by object : DiagnosticGroup("OptIn-related") {
|
||||
val EXPERIMENTAL_API_USAGE by warning<PsiElement>(PositioningStrategy.REFERENCE_BY_QUALIFIED) {
|
||||
parameter<FqName>("optInMarkerFqName")
|
||||
parameter<String>("message")
|
||||
}
|
||||
val EXPERIMENTAL_API_USAGE_ERROR by error<PsiElement>(PositioningStrategy.REFERENCE_BY_QUALIFIED) {
|
||||
parameter<FqName>("optInMarkerFqName")
|
||||
parameter<String>("message")
|
||||
}
|
||||
val EXPERIMENTAL_OVERRIDE by warning<PsiElement> {
|
||||
parameter<FqName>("optInMarkerFqName")
|
||||
parameter<String>("message")
|
||||
}
|
||||
val EXPERIMENTAL_OVERRIDE_ERROR by error<PsiElement> {
|
||||
parameter<FqName>("optInMarkerFqName")
|
||||
parameter<String>("message")
|
||||
}
|
||||
val EXPERIMENTAL_IS_NOT_ENABLED by warning<KtAnnotationEntry>(PositioningStrategy.REFERENCED_NAME_BY_QUALIFIED)
|
||||
val EXPERIMENTAL_CAN_ONLY_BE_USED_AS_ANNOTATION by error<PsiElement>()
|
||||
val EXPERIMENTAL_MARKER_CAN_ONLY_BE_USED_AS_ANNOTATION_OR_ARGUMENT_IN_USE_EXPERIMENTAL by error<PsiElement>()
|
||||
val USE_EXPERIMENTAL_WITHOUT_ARGUMENTS by warning<KtAnnotationEntry>()
|
||||
val USE_EXPERIMENTAL_ARGUMENT_IS_NOT_MARKER by warning<KtAnnotationEntry> {
|
||||
parameter<FqName>("notMarkerFqName")
|
||||
}
|
||||
val EXPERIMENTAL_ANNOTATION_WITH_WRONG_TARGET by error<KtAnnotationEntry> {
|
||||
parameter<String>("target")
|
||||
}
|
||||
val EXPERIMENTAL_ANNOTATION_WITH_WRONG_RETENTION by error<KtAnnotationEntry>()
|
||||
}
|
||||
|
||||
val EXPOSED_VISIBILITY by object : DiagnosticGroup("Exposed visibility") {
|
||||
val EXPOSED_TYPEALIAS_EXPANDED_TYPE by exposedVisibilityError<KtNamedDeclaration>(PositioningStrategy.DECLARATION_NAME)
|
||||
val EXPOSED_FUNCTION_RETURN_TYPE by exposedVisibilityError<KtNamedDeclaration>(PositioningStrategy.DECLARATION_NAME)
|
||||
|
||||
@@ -34,6 +34,7 @@ import org.jetbrains.kotlin.fir.symbols.impl.FirVariableSymbol
|
||||
import org.jetbrains.kotlin.fir.types.ConeKotlinType
|
||||
import org.jetbrains.kotlin.lexer.KtKeywordToken
|
||||
import org.jetbrains.kotlin.lexer.KtModifierKeywordToken
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.psi.KtAnnotation
|
||||
import org.jetbrains.kotlin.psi.KtAnnotationEntry
|
||||
@@ -200,6 +201,19 @@ object FirErrors {
|
||||
val REDUNDANT_ANNOTATION_TARGET by warning1<KtAnnotationEntry, String>()
|
||||
val INAPPLICABLE_FILE_TARGET by error0<KtAnnotationEntry>(SourceElementPositioningStrategies.ANNOTATION_USE_SITE)
|
||||
|
||||
// OptIn-related
|
||||
val EXPERIMENTAL_API_USAGE by warning2<PsiElement, FqName, String>(SourceElementPositioningStrategies.REFERENCE_BY_QUALIFIED)
|
||||
val EXPERIMENTAL_API_USAGE_ERROR by error2<PsiElement, FqName, String>(SourceElementPositioningStrategies.REFERENCE_BY_QUALIFIED)
|
||||
val EXPERIMENTAL_OVERRIDE by warning2<PsiElement, FqName, String>()
|
||||
val EXPERIMENTAL_OVERRIDE_ERROR by error2<PsiElement, FqName, String>()
|
||||
val EXPERIMENTAL_IS_NOT_ENABLED by warning0<KtAnnotationEntry>(SourceElementPositioningStrategies.REFERENCED_NAME_BY_QUALIFIED)
|
||||
val EXPERIMENTAL_CAN_ONLY_BE_USED_AS_ANNOTATION by error0<PsiElement>()
|
||||
val EXPERIMENTAL_MARKER_CAN_ONLY_BE_USED_AS_ANNOTATION_OR_ARGUMENT_IN_USE_EXPERIMENTAL by error0<PsiElement>()
|
||||
val USE_EXPERIMENTAL_WITHOUT_ARGUMENTS by warning0<KtAnnotationEntry>()
|
||||
val USE_EXPERIMENTAL_ARGUMENT_IS_NOT_MARKER by warning1<KtAnnotationEntry, FqName>()
|
||||
val EXPERIMENTAL_ANNOTATION_WITH_WRONG_TARGET by error1<KtAnnotationEntry, String>()
|
||||
val EXPERIMENTAL_ANNOTATION_WITH_WRONG_RETENTION by error0<KtAnnotationEntry>()
|
||||
|
||||
// Exposed visibility
|
||||
val EXPOSED_TYPEALIAS_EXPANDED_TYPE by error3<KtNamedDeclaration, EffectiveVisibility, FirMemberDeclaration, EffectiveVisibility>(SourceElementPositioningStrategies.DECLARATION_NAME)
|
||||
val EXPOSED_FUNCTION_RETURN_TYPE by error3<KtNamedDeclaration, EffectiveVisibility, FirMemberDeclaration, EffectiveVisibility>(SourceElementPositioningStrategies.DECLARATION_NAME)
|
||||
|
||||
+1
@@ -71,6 +71,7 @@ object CommonDeclarationCheckers : DeclarationCheckers() {
|
||||
override val regularClassCheckers: Set<FirRegularClassChecker>
|
||||
get() = setOf(
|
||||
FirAnnotationClassDeclarationChecker,
|
||||
FirOptInAnnotationClassChecker,
|
||||
FirCommonConstructorDelegationIssuesChecker,
|
||||
FirConstructorInInterfaceChecker,
|
||||
FirDelegationSuperCallInEnumConstructorChecker,
|
||||
|
||||
+1
@@ -12,6 +12,7 @@ object CommonExpressionCheckers : ExpressionCheckers() {
|
||||
override val annotationCallCheckers: Set<FirAnnotationCallChecker>
|
||||
get() = setOf(
|
||||
FirAnnotationArgumentChecker,
|
||||
FirOptInAnnotationCallChecker,
|
||||
)
|
||||
|
||||
override val basicExpressionCheckers: Set<FirBasicExpressionChecker>
|
||||
|
||||
+21
-7
@@ -7,6 +7,7 @@ package org.jetbrains.kotlin.fir.analysis.checkers
|
||||
|
||||
import org.jetbrains.kotlin.builtins.StandardNames
|
||||
import org.jetbrains.kotlin.descriptors.annotations.KotlinTarget
|
||||
import org.jetbrains.kotlin.fir.FirAnnotationContainer
|
||||
import org.jetbrains.kotlin.fir.FirSession
|
||||
import org.jetbrains.kotlin.fir.declarations.FirAnnotatedDeclaration
|
||||
import org.jetbrains.kotlin.fir.declarations.FirRegularClass
|
||||
@@ -14,6 +15,7 @@ import org.jetbrains.kotlin.fir.expressions.*
|
||||
import org.jetbrains.kotlin.fir.references.FirResolvedNamedReference
|
||||
import org.jetbrains.kotlin.fir.resolve.fullyExpandedType
|
||||
import org.jetbrains.kotlin.fir.resolve.toSymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirRegularClassSymbol
|
||||
import org.jetbrains.kotlin.fir.types.ConeClassLikeType
|
||||
import org.jetbrains.kotlin.fir.types.FirErrorTypeRef
|
||||
import org.jetbrains.kotlin.fir.types.coneType
|
||||
@@ -49,11 +51,7 @@ fun FirAnnotationCall.getAllowedAnnotationTargets(session: FirSession): Set<Kotl
|
||||
fun FirRegularClass.getAllowedAnnotationTargets(): Set<KotlinTarget> {
|
||||
val targetAnnotation = getTargetAnnotation() ?: return defaultAnnotationTargets
|
||||
if (targetAnnotation.argumentList.arguments.isEmpty()) return emptySet()
|
||||
val arguments = when (val targetArgument = targetAnnotation.findSingleArgumentByName(TARGET_PARAMETER_NAME)) {
|
||||
is FirVarargArgumentsExpression -> targetArgument.arguments
|
||||
is FirArrayOfCall -> targetArgument.arguments
|
||||
else -> return defaultAnnotationTargets
|
||||
}
|
||||
val arguments = targetAnnotation.findSingleArgumentByName(TARGET_PARAMETER_NAME)?.unfoldArrayOrVararg().orEmpty()
|
||||
|
||||
return arguments.mapNotNullTo(mutableSetOf()) { argument ->
|
||||
val targetExpression = argument as? FirQualifiedAccessExpression
|
||||
@@ -70,7 +68,7 @@ fun FirAnnotatedDeclaration.getTargetAnnotation(): FirAnnotationCall? {
|
||||
return getAnnotationByFqName(StandardNames.FqNames.target)
|
||||
}
|
||||
|
||||
fun FirAnnotatedDeclaration.getAnnotationByFqName(fqName: FqName): FirAnnotationCall? {
|
||||
fun FirAnnotationContainer.getAnnotationByFqName(fqName: FqName): FirAnnotationCall? {
|
||||
return annotations.find {
|
||||
(it.annotationTypeRef.coneType as? ConeClassLikeType)?.lookupTag?.classId?.asSingleFqName() == fqName
|
||||
}
|
||||
@@ -79,7 +77,7 @@ fun FirAnnotatedDeclaration.getAnnotationByFqName(fqName: FqName): FirAnnotation
|
||||
fun FirAnnotationCall.findSingleArgumentByName(name: Name): FirExpression? {
|
||||
val argumentMapping = argumentMapping
|
||||
if (argumentMapping != null) {
|
||||
return argumentMapping.keys.firstOrNull()?.takeIf { argumentMapping[it]?.name == name }
|
||||
return argumentMapping.keys.firstOrNull()?.takeIf { argumentMapping[it]?.name == name }?.unwrapArgument()
|
||||
}
|
||||
// NB: we have to consider both cases, because deserializer does not create argument mapping
|
||||
val arguments = argumentList.arguments
|
||||
@@ -87,3 +85,19 @@ fun FirAnnotationCall.findSingleArgumentByName(name: Name): FirExpression? {
|
||||
return firstArgument.takeIf { it.name == name }?.expression
|
||||
}
|
||||
|
||||
fun FirExpression.extractClassesFromArgument(): List<FirRegularClassSymbol> {
|
||||
return unfoldArrayOrVararg().mapNotNull {
|
||||
if (it !is FirGetClassCall) return@mapNotNull null
|
||||
val qualifier = it.argument as? FirResolvedQualifier ?: return@mapNotNull null
|
||||
qualifier.symbol as? FirRegularClassSymbol
|
||||
}
|
||||
}
|
||||
|
||||
private fun FirExpression.unfoldArrayOrVararg(): List<FirExpression> {
|
||||
return when (this) {
|
||||
is FirVarargArgumentsExpression -> arguments
|
||||
is FirArrayOfCall -> arguments
|
||||
else -> return emptyList()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+98
@@ -0,0 +1,98 @@
|
||||
/*
|
||||
* Copyright 2010-2021 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.checkers
|
||||
|
||||
import org.jetbrains.kotlin.config.ApiVersion
|
||||
import org.jetbrains.kotlin.fir.FirSession
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
|
||||
import org.jetbrains.kotlin.fir.declarations.FirAnnotatedDeclaration
|
||||
import org.jetbrains.kotlin.fir.declarations.FirConstructor
|
||||
import org.jetbrains.kotlin.fir.declarations.FirTypeAlias
|
||||
import org.jetbrains.kotlin.fir.expressions.FirConstExpression
|
||||
import org.jetbrains.kotlin.fir.expressions.arguments
|
||||
import org.jetbrains.kotlin.fir.languageVersionSettings
|
||||
import org.jetbrains.kotlin.fir.resolve.symbolProvider
|
||||
import org.jetbrains.kotlin.fir.resolve.toSymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirRegularClassSymbol
|
||||
import org.jetbrains.kotlin.fir.types.ConeClassLikeType
|
||||
import org.jetbrains.kotlin.fir.types.coneType
|
||||
import org.jetbrains.kotlin.resolve.SINCE_KOTLIN_FQ_NAME
|
||||
import org.jetbrains.kotlin.resolve.checkers.OptInNames
|
||||
|
||||
sealed class FirSinceKotlinAccessibility {
|
||||
object Accessible : FirSinceKotlinAccessibility()
|
||||
|
||||
data class NotAccessibleButWasExperimental(
|
||||
val version: ApiVersion,
|
||||
val markerClasses: List<FirRegularClassSymbol>
|
||||
) : FirSinceKotlinAccessibility()
|
||||
|
||||
data class NotAccessible(
|
||||
val version: ApiVersion
|
||||
) : FirSinceKotlinAccessibility()
|
||||
}
|
||||
|
||||
private data class FirSinceKotlinValue(
|
||||
val apiVersion: ApiVersion,
|
||||
val wasExperimentalMarkerClasses: List<FirRegularClassSymbol>
|
||||
)
|
||||
|
||||
fun FirAnnotatedDeclaration.checkSinceKotlinVersionAccessibility(context: CheckerContext): FirSinceKotlinAccessibility {
|
||||
val value = getOwnSinceKotlinVersion(context.session)
|
||||
val version = value?.apiVersion
|
||||
val languageVersionSettings = context.session.languageVersionSettings
|
||||
|
||||
// Allow access in the following cases:
|
||||
// 1) There's no @SinceKotlin annotation for this descriptor
|
||||
// 2) There's a @SinceKotlin annotation but its value is some unrecognizable nonsense
|
||||
// 3) The value as a version is not greater than our API version
|
||||
if (version == null || version <= languageVersionSettings.apiVersion) return FirSinceKotlinAccessibility.Accessible
|
||||
|
||||
val wasExperimentalFqNames = value.wasExperimentalMarkerClasses
|
||||
if (wasExperimentalFqNames.isNotEmpty()) {
|
||||
return FirSinceKotlinAccessibility.NotAccessibleButWasExperimental(version, wasExperimentalFqNames)
|
||||
}
|
||||
|
||||
return FirSinceKotlinAccessibility.NotAccessible(version)
|
||||
}
|
||||
|
||||
private fun FirAnnotatedDeclaration.getOwnSinceKotlinVersion(session: FirSession): FirSinceKotlinValue? {
|
||||
var result: FirSinceKotlinValue? = null
|
||||
|
||||
// TODO: use-site targeted annotations
|
||||
fun FirAnnotatedDeclaration.consider() {
|
||||
val sinceKotlinSingleArgument = getAnnotationByFqName(SINCE_KOTLIN_FQ_NAME)?.arguments?.singleOrNull()
|
||||
val apiVersion = ((sinceKotlinSingleArgument as? FirConstExpression<*>)?.value as? String)?.let(ApiVersion.Companion::parse)
|
||||
if (apiVersion != null) {
|
||||
// TODO: combine wasExperimentalMarkerClasses in case of several associated declarations with the same maximal API version
|
||||
if (result == null || apiVersion > result!!.apiVersion) {
|
||||
result = FirSinceKotlinValue(apiVersion, loadWasExperimentalMarkerClasses())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.consider()
|
||||
if (this is FirConstructor) {
|
||||
val classId = symbol.callableId.classId
|
||||
if (classId != null) {
|
||||
val classSymbol = session.symbolProvider.getClassLikeSymbolByFqName(classId)
|
||||
classSymbol?.fir?.consider()
|
||||
}
|
||||
}
|
||||
|
||||
if (this is FirTypeAlias) {
|
||||
(this.expandedTypeRef.coneType as? ConeClassLikeType)?.lookupTag?.toSymbol(session)?.fir?.consider()
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
private fun FirAnnotatedDeclaration.loadWasExperimentalMarkerClasses(): List<FirRegularClassSymbol> {
|
||||
val wasExperimental = getAnnotationByFqName(OptInNames.WAS_EXPERIMENTAL_FQ_NAME) ?: return emptyList()
|
||||
val annotationClasses = wasExperimental.findSingleArgumentByName(OptInNames.WAS_EXPERIMENTAL_ANNOTATION_CLASS) ?: return emptyList()
|
||||
return annotationClasses.extractClassesFromArgument()
|
||||
}
|
||||
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Copyright 2010-2021 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.checkers.declaration
|
||||
|
||||
import org.jetbrains.kotlin.descriptors.ClassKind
|
||||
import org.jetbrains.kotlin.descriptors.annotations.KotlinTarget
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.*
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.DiagnosticReporter
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.reportOn
|
||||
import org.jetbrains.kotlin.fir.declarations.FirRegularClass
|
||||
import org.jetbrains.kotlin.resolve.checkers.Experimentality
|
||||
import org.jetbrains.kotlin.resolve.checkers.OptInNames
|
||||
|
||||
object FirOptInAnnotationClassChecker : FirRegularClassChecker() {
|
||||
override fun check(declaration: FirRegularClass, context: CheckerContext, reporter: DiagnosticReporter) {
|
||||
if (declaration.classKind != ClassKind.ANNOTATION_CLASS) return
|
||||
if (declaration.getAnnotationByFqName(OptInNames.REQUIRES_OPT_IN_FQ_NAME) == null) return
|
||||
if (declaration.getRetention() == AnnotationRetention.SOURCE) {
|
||||
val target = declaration.getRetentionAnnotation()
|
||||
reporter.reportOn(target?.source, FirErrors.EXPERIMENTAL_ANNOTATION_WITH_WRONG_RETENTION, context)
|
||||
|
||||
}
|
||||
val wrongTargets = declaration.getAllowedAnnotationTargets().intersect(Experimentality.WRONG_TARGETS_FOR_MARKER)
|
||||
if (wrongTargets.isNotEmpty()) {
|
||||
val target = declaration.getTargetAnnotation()
|
||||
reporter.reportOn(
|
||||
target?.source,
|
||||
FirErrors.EXPERIMENTAL_ANNOTATION_WITH_WRONG_TARGET,
|
||||
wrongTargets.joinToString(transform = KotlinTarget::description),
|
||||
context
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
+8
-1
@@ -20,11 +20,19 @@ import org.jetbrains.kotlin.fir.scopes.impl.toConeType
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirTypeParameterSymbol
|
||||
import org.jetbrains.kotlin.fir.types.*
|
||||
import org.jetbrains.kotlin.lexer.KtTokens.QUEST
|
||||
import org.jetbrains.kotlin.resolve.checkers.OptInNames
|
||||
|
||||
object FirClassLiteralChecker : FirGetClassCallChecker() {
|
||||
override fun check(expression: FirGetClassCall, context: CheckerContext, reporter: DiagnosticReporter) {
|
||||
val source = expression.source ?: return
|
||||
if (source.kind is FirFakeSourceElementKind) return
|
||||
val argument = expression.argument
|
||||
if (argument is FirResolvedQualifier) {
|
||||
val fqName = argument.classId?.asSingleFqName()
|
||||
if (fqName in OptInNames.EXPERIMENTAL_FQ_NAMES || fqName in OptInNames.USE_EXPERIMENTAL_FQ_NAMES) {
|
||||
reporter.reportOn(argument.source, FirErrors.EXPERIMENTAL_CAN_ONLY_BE_USED_AS_ANNOTATION, context)
|
||||
}
|
||||
}
|
||||
|
||||
// Note that raw FIR drops marked nullability "?" in, e.g., `A?::class`, `A<T?>::class`, or `A<T?>?::class`.
|
||||
// That is, AST structures for those expressions have token type QUEST, whereas FIR element doesn't have any information about it.
|
||||
@@ -36,7 +44,6 @@ object FirClassLiteralChecker : FirGetClassCallChecker() {
|
||||
//
|
||||
// Only the 2nd example is valid, and we want to check if token type QUEST doesn't exist at the same level as COLONCOLON.
|
||||
val markedNullable = source.getChild(QUEST, depth = 1) != null
|
||||
val argument = expression.argument
|
||||
val isNullable = markedNullable ||
|
||||
(argument as? FirResolvedQualifier)?.isNullableLHSForCallableReference == true ||
|
||||
argument.typeRef.coneType.isMarkedNullable ||
|
||||
|
||||
+63
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* Copyright 2010-2021 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.checkers.expression
|
||||
|
||||
import org.jetbrains.kotlin.config.AnalysisFlags
|
||||
import org.jetbrains.kotlin.fir.FirSourceElement
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.extractClassesFromArgument
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.findSingleArgumentByName
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.DiagnosticReporter
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.reportOn
|
||||
import org.jetbrains.kotlin.fir.expressions.*
|
||||
import org.jetbrains.kotlin.fir.languageVersionSettings
|
||||
import org.jetbrains.kotlin.fir.types.ConeClassLikeType
|
||||
import org.jetbrains.kotlin.fir.types.coneTypeSafe
|
||||
import org.jetbrains.kotlin.resolve.checkers.OptInNames
|
||||
|
||||
object FirOptInAnnotationCallChecker : FirAnnotationCallChecker() {
|
||||
override fun check(expression: FirAnnotationCall, context: CheckerContext, reporter: DiagnosticReporter) {
|
||||
val lookupTag = expression.annotationTypeRef.coneTypeSafe<ConeClassLikeType>()?.lookupTag ?: return
|
||||
val fqName = lookupTag.classId.asSingleFqName()
|
||||
val isMarker = fqName == OptInNames.REQUIRES_OPT_IN_FQ_NAME
|
||||
val isOptIn = fqName == OptInNames.OPT_IN_FQ_NAME
|
||||
if (isMarker || isOptIn) {
|
||||
checkUsageOfKotlinExperimentalOrUseExperimental(expression.source, context, reporter)
|
||||
if (isOptIn) {
|
||||
val arguments = expression.arguments
|
||||
if (arguments.isEmpty()) {
|
||||
reporter.reportOn(expression.source, FirErrors.USE_EXPERIMENTAL_WITHOUT_ARGUMENTS, context)
|
||||
} else {
|
||||
val annotationClasses = expression.findSingleArgumentByName(OptInNames.USE_EXPERIMENTAL_ANNOTATION_CLASS)
|
||||
for (classSymbol in annotationClasses?.extractClassesFromArgument().orEmpty()) {
|
||||
with(FirOptInUsageBaseChecker) {
|
||||
if (classSymbol.fir.loadExperimentalityForMarkerAnnotation() == null) {
|
||||
reporter.reportOn(
|
||||
expression.source,
|
||||
FirErrors.USE_EXPERIMENTAL_ARGUMENT_IS_NOT_MARKER,
|
||||
classSymbol.classId.asSingleFqName(),
|
||||
context
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun checkUsageOfKotlinExperimentalOrUseExperimental(
|
||||
element: FirSourceElement?,
|
||||
context: CheckerContext,
|
||||
reporter: DiagnosticReporter
|
||||
) {
|
||||
val useExperimentalFqNames = context.session.languageVersionSettings.getFlag(AnalysisFlags.useExperimental)
|
||||
if (OptInNames.REQUIRES_OPT_IN_FQ_NAME.asString() !in useExperimentalFqNames) {
|
||||
reporter.reportOn(element, FirErrors.EXPERIMENTAL_IS_NOT_ENABLED, context)
|
||||
}
|
||||
}
|
||||
}
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright 2010-2021 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.checkers.expression
|
||||
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.findSingleArgumentByName
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.getAnnotationByFqName
|
||||
import org.jetbrains.kotlin.fir.declarations.FirRegularClass
|
||||
import org.jetbrains.kotlin.fir.expressions.FirConstExpression
|
||||
import org.jetbrains.kotlin.fir.expressions.FirQualifiedAccessExpression
|
||||
import org.jetbrains.kotlin.fir.references.FirResolvedNamedReference
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.resolve.checkers.Experimentality
|
||||
import org.jetbrains.kotlin.resolve.checkers.OptInNames
|
||||
|
||||
object FirOptInUsageBaseChecker {
|
||||
internal fun FirRegularClass.loadExperimentalityForMarkerAnnotation(): Experimentality? {
|
||||
val experimental = getAnnotationByFqName(OptInNames.REQUIRES_OPT_IN_FQ_NAME) ?: return null
|
||||
|
||||
val levelArgument = experimental.findSingleArgumentByName(LEVEL) as? FirQualifiedAccessExpression
|
||||
val levelName = (levelArgument?.calleeReference as? FirResolvedNamedReference)?.name?.asString()
|
||||
val level = OptInLevel.values().firstOrNull { it.name == levelName } ?: OptInLevel.DEFAULT
|
||||
val message = (experimental.findSingleArgumentByName(MESSAGE) as? FirConstExpression<*>)?.value as? String
|
||||
return Experimentality(symbol.classId.asSingleFqName(), level.severity, message)
|
||||
}
|
||||
|
||||
private val LEVEL = Name.identifier("level")
|
||||
private val MESSAGE = Name.identifier("message")
|
||||
|
||||
private enum class OptInLevel(val severity: Experimentality.Severity) {
|
||||
WARNING(Experimentality.Severity.WARNING),
|
||||
ERROR(Experimentality.Severity.ERROR),
|
||||
DEFAULT(Experimentality.DEFAULT_SEVERITY)
|
||||
}
|
||||
}
|
||||
+53
-6
@@ -7,6 +7,7 @@ package org.jetbrains.kotlin.fir.analysis.diagnostics
|
||||
|
||||
import org.jetbrains.kotlin.diagnostics.rendering.*
|
||||
import org.jetbrains.kotlin.diagnostics.rendering.Renderers.RENDER_POSITION_VARIANCE
|
||||
import org.jetbrains.kotlin.diagnostics.rendering.Renderers.STRING
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirDiagnosticRenderers.AMBIGUOUS_CALLS
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirDiagnosticRenderers.DECLARATION_NAME
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirDiagnosticRenderers.FIR
|
||||
@@ -117,6 +118,15 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.EXPECTED_DELEGATE
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.EXPECTED_LATEINIT_PROPERTY
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.EXPECTED_PRIVATE_DECLARATION
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.EXPECTED_PROPERTY_INITIALIZER
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.EXPERIMENTAL_ANNOTATION_WITH_WRONG_RETENTION
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.EXPERIMENTAL_ANNOTATION_WITH_WRONG_TARGET
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.EXPERIMENTAL_API_USAGE
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.EXPERIMENTAL_API_USAGE_ERROR
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.EXPERIMENTAL_CAN_ONLY_BE_USED_AS_ANNOTATION
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.EXPERIMENTAL_IS_NOT_ENABLED
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.EXPERIMENTAL_MARKER_CAN_ONLY_BE_USED_AS_ANNOTATION_OR_ARGUMENT_IN_USE_EXPERIMENTAL
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.EXPERIMENTAL_OVERRIDE
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.EXPERIMENTAL_OVERRIDE_ERROR
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.EXPLICIT_DELEGATION_CALL_REQUIRED
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.EXPOSED_FUNCTION_RETURN_TYPE
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.EXPOSED_PARAMETER_TYPE
|
||||
@@ -336,6 +346,8 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.USELESS_ELVIS
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.USELESS_ELVIS_RIGHT_IS_NULL
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.USELESS_IS_CHECK
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.USELESS_VARARG_ON_PARAMETER
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.USE_EXPERIMENTAL_ARGUMENT_IS_NOT_MARKER
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.USE_EXPERIMENTAL_WITHOUT_ARGUMENTS
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.VALUE_CLASS_CANNOT_BE_CLONEABLE
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.VALUE_PARAMETER_WITH_NO_TYPE_ANNOTATION
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.VAL_OR_VAR_ON_CATCH_PARAMETER
|
||||
@@ -363,7 +375,6 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.WRONG_MODIFIER_TA
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.WRONG_NUMBER_OF_TYPE_ARGUMENTS
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.WRONG_SETTER_PARAMETER_TYPE
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.WRONG_SETTER_RETURN_TYPE
|
||||
import org.jetbrains.kotlin.resolve.VarianceConflictDiagnosticData
|
||||
|
||||
@Suppress("unused")
|
||||
class FirDefaultErrorMessages : DefaultErrorMessages.Extension {
|
||||
@@ -399,10 +410,10 @@ class FirDefaultErrorMessages : DefaultErrorMessages.Extension {
|
||||
map.put(VARIABLE_EXPECTED, "Variable expected")
|
||||
map.put(DELEGATION_IN_INTERFACE, "Interfaces cannot use delegation")
|
||||
map.put(NESTED_CLASS_NOT_ALLOWED, "{0} is not allowed here", TO_STRING)
|
||||
map.put(VAL_OR_VAR_ON_LOOP_PARAMETER, "''{0}'' on loop parameter is not allowed", TO_STRING);
|
||||
map.put(VAL_OR_VAR_ON_FUN_PARAMETER, "''{0}'' on function parameter is not allowed", TO_STRING);
|
||||
map.put(VAL_OR_VAR_ON_CATCH_PARAMETER, "''{0}'' on catch parameter is not allowed", TO_STRING);
|
||||
map.put(VAL_OR_VAR_ON_SECONDARY_CONSTRUCTOR_PARAMETER, "''{0}'' on secondary constructor parameter is not allowed", TO_STRING);
|
||||
map.put(VAL_OR_VAR_ON_LOOP_PARAMETER, "''{0}'' on loop parameter is not allowed", TO_STRING)
|
||||
map.put(VAL_OR_VAR_ON_FUN_PARAMETER, "''{0}'' on function parameter is not allowed", TO_STRING)
|
||||
map.put(VAL_OR_VAR_ON_CATCH_PARAMETER, "''{0}'' on catch parameter is not allowed", TO_STRING)
|
||||
map.put(VAL_OR_VAR_ON_SECONDARY_CONSTRUCTOR_PARAMETER, "''{0}'' on secondary constructor parameter is not allowed", TO_STRING)
|
||||
|
||||
// Unresolved
|
||||
map.put(INVISIBLE_REFERENCE, "Symbol {0} is invisible", SYMBOL)
|
||||
@@ -528,6 +539,36 @@ class FirDefaultErrorMessages : DefaultErrorMessages.Extension {
|
||||
TO_STRING
|
||||
)
|
||||
|
||||
// OptIn
|
||||
map.put(EXPERIMENTAL_API_USAGE, "{1}", TO_STRING, STRING)
|
||||
map.put(EXPERIMENTAL_API_USAGE_ERROR, "{1}", TO_STRING, STRING)
|
||||
|
||||
map.put(EXPERIMENTAL_OVERRIDE, "{1}", TO_STRING, STRING)
|
||||
map.put(EXPERIMENTAL_OVERRIDE_ERROR, "{1}", TO_STRING, STRING)
|
||||
|
||||
map.put(EXPERIMENTAL_IS_NOT_ENABLED, "This class can only be used with the compiler argument '-Xopt-in=kotlin.RequiresOptIn'")
|
||||
map.put(EXPERIMENTAL_CAN_ONLY_BE_USED_AS_ANNOTATION, "This class can only be used as an annotation")
|
||||
map.put(
|
||||
EXPERIMENTAL_MARKER_CAN_ONLY_BE_USED_AS_ANNOTATION_OR_ARGUMENT_IN_USE_EXPERIMENTAL,
|
||||
"This class can only be used as an annotation or as an argument to @OptIn"
|
||||
)
|
||||
map.put(USE_EXPERIMENTAL_WITHOUT_ARGUMENTS, "@OptIn without any arguments has no effect")
|
||||
map.put(
|
||||
USE_EXPERIMENTAL_ARGUMENT_IS_NOT_MARKER,
|
||||
"Annotation ''{0}'' is not an opt-in requirement marker, therefore its usage in @OptIn is ignored",
|
||||
TO_STRING
|
||||
)
|
||||
map.put(
|
||||
EXPERIMENTAL_ANNOTATION_WITH_WRONG_TARGET,
|
||||
"Opt-in requirement marker annotation cannot be used on the following code elements: {0}. Please remove these targets",
|
||||
STRING
|
||||
)
|
||||
map.put(
|
||||
EXPERIMENTAL_ANNOTATION_WITH_WRONG_RETENTION,
|
||||
"Opt-in requirement marker annotation cannot be used with SOURCE retention. Please replace retention with BINARY"
|
||||
)
|
||||
|
||||
|
||||
// Exposed visibility group // #
|
||||
map.put(
|
||||
EXPOSED_TYPEALIAS_EXPANDED_TYPE,
|
||||
@@ -585,7 +626,13 @@ class FirDefaultErrorMessages : DefaultErrorMessages.Extension {
|
||||
map.put(TYPE_MISMATCH, "Type mismatch: inferred type is {1} but {0} was expected", TO_STRING, TO_STRING)
|
||||
map.put(THROWABLE_TYPE_MISMATCH, "Throwable type mismatch: actual type is {0}", TO_STRING)
|
||||
map.put(CONDITION_TYPE_MISMATCH, "Condition type mismatch: inferred type is {0} but Boolean was expected", TO_STRING)
|
||||
map.put(ARGUMENT_TYPE_MISMATCH, "Argument type mismatch: actual type is {1} but {0} was expected", RENDER_TYPE, RENDER_TYPE, NOT_RENDERED)
|
||||
map.put(
|
||||
ARGUMENT_TYPE_MISMATCH,
|
||||
"Argument type mismatch: actual type is {1} but {0} was expected",
|
||||
RENDER_TYPE,
|
||||
RENDER_TYPE,
|
||||
NOT_RENDERED
|
||||
)
|
||||
map.put(ASSIGNMENT_TYPE_MISMATCH, "Assignment type mismatch: actual type is {1} but {0} was expected", RENDER_TYPE, RENDER_TYPE)
|
||||
map.put(
|
||||
RESULT_TYPE_MISMATCH,
|
||||
|
||||
+1
-10
@@ -33,15 +33,6 @@ import org.jetbrains.kotlin.resolve.descriptorUtil.getAnnotationRetention
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
|
||||
|
||||
class ExperimentalMarkerDeclarationAnnotationChecker(private val module: ModuleDescriptor) : AdditionalAnnotationChecker {
|
||||
companion object {
|
||||
private val WRONG_TARGETS_FOR_MARKER = setOf(
|
||||
KotlinTarget.EXPRESSION,
|
||||
KotlinTarget.FILE,
|
||||
KotlinTarget.TYPE,
|
||||
KotlinTarget.TYPE_PARAMETER
|
||||
)
|
||||
}
|
||||
|
||||
override fun checkEntries(
|
||||
entries: List<KtAnnotationEntry>,
|
||||
actualTargets: List<KotlinTarget>,
|
||||
@@ -154,7 +145,7 @@ class ExperimentalMarkerDeclarationAnnotationChecker(private val module: ModuleD
|
||||
if (targetEntry != null) {
|
||||
val (entry, descriptor) = targetEntry
|
||||
val allowedTargets = AnnotationChecker.loadAnnotationTargets(descriptor!!) ?: return
|
||||
val wrongTargets = allowedTargets.intersect(WRONG_TARGETS_FOR_MARKER)
|
||||
val wrongTargets = allowedTargets.intersect(Experimentality.WRONG_TARGETS_FOR_MARKER)
|
||||
if (wrongTargets.isNotEmpty()) {
|
||||
trace.report(
|
||||
Errors.EXPERIMENTAL_ANNOTATION_WITH_WRONG_TARGET.on(
|
||||
|
||||
+2
-2
@@ -1,6 +1,6 @@
|
||||
// FILE: api.kt
|
||||
|
||||
@RequiresOptIn
|
||||
@<!EXPERIMENTAL_IS_NOT_ENABLED!>RequiresOptIn<!>
|
||||
@Retention(AnnotationRetention.BINARY)
|
||||
annotation class Marker
|
||||
|
||||
@@ -18,7 +18,7 @@ fun use2() {
|
||||
f()
|
||||
}
|
||||
|
||||
@OptIn(Marker::class)
|
||||
@<!EXPERIMENTAL_IS_NOT_ENABLED!>OptIn<!>(Marker::class)
|
||||
fun use3() {
|
||||
f()
|
||||
}
|
||||
|
||||
+5
-5
@@ -12,21 +12,21 @@ import kotlin.annotation.AnnotationTarget.*
|
||||
annotation class E1
|
||||
|
||||
@RequiresOptIn(level = RequiresOptIn.Level.WARNING)
|
||||
@Target(FILE)
|
||||
<!EXPERIMENTAL_ANNOTATION_WITH_WRONG_TARGET!>@Target(FILE)<!>
|
||||
annotation class E2
|
||||
|
||||
@RequiresOptIn(level = RequiresOptIn.Level.WARNING)
|
||||
@Target(EXPRESSION)
|
||||
@Retention(AnnotationRetention.SOURCE)
|
||||
<!EXPERIMENTAL_ANNOTATION_WITH_WRONG_TARGET!>@Target(EXPRESSION)<!>
|
||||
<!EXPERIMENTAL_ANNOTATION_WITH_WRONG_RETENTION!>@Retention(AnnotationRetention.SOURCE)<!>
|
||||
annotation class E3
|
||||
|
||||
@RequiresOptIn(level = RequiresOptIn.Level.WARNING)
|
||||
@Target(TYPE_PARAMETER)
|
||||
<!EXPERIMENTAL_ANNOTATION_WITH_WRONG_TARGET!>@Target(TYPE_PARAMETER)<!>
|
||||
@Retention(AnnotationRetention.BINARY)
|
||||
annotation class E3A
|
||||
|
||||
@RequiresOptIn(level = RequiresOptIn.Level.WARNING)
|
||||
@Target(TYPE)
|
||||
<!EXPERIMENTAL_ANNOTATION_WITH_WRONG_TARGET!>@Target(TYPE)<!>
|
||||
@Retention(AnnotationRetention.BINARY)
|
||||
annotation class E3B
|
||||
|
||||
|
||||
-9
@@ -1,9 +0,0 @@
|
||||
// !USE_EXPERIMENTAL: kotlin.RequiresOptIn
|
||||
|
||||
annotation class NotAMarker
|
||||
|
||||
@OptIn
|
||||
fun f1() {}
|
||||
|
||||
@OptIn(NotAMarker::class)
|
||||
fun f2() {}
|
||||
+1
@@ -1,3 +1,4 @@
|
||||
// FIR_IDENTICAL
|
||||
// !USE_EXPERIMENTAL: kotlin.RequiresOptIn
|
||||
|
||||
annotation class NotAMarker
|
||||
|
||||
+4
-4
@@ -35,10 +35,10 @@ fun f4(u: OptIn0 /* TODO */) {}
|
||||
annotation class VarargKClasses(vararg val k: KClass<*>)
|
||||
|
||||
@VarargKClasses(
|
||||
RequiresOptIn::class,
|
||||
OptIn::class,
|
||||
kotlin.RequiresOptIn::class,
|
||||
kotlin.OptIn::class
|
||||
<!EXPERIMENTAL_CAN_ONLY_BE_USED_AS_ANNOTATION!>RequiresOptIn<!>::class,
|
||||
<!EXPERIMENTAL_CAN_ONLY_BE_USED_AS_ANNOTATION!>OptIn<!>::class,
|
||||
<!EXPERIMENTAL_CAN_ONLY_BE_USED_AS_ANNOTATION!>kotlin.RequiresOptIn<!>::class,
|
||||
<!EXPERIMENTAL_CAN_ONLY_BE_USED_AS_ANNOTATION!>kotlin.OptIn<!>::class
|
||||
)
|
||||
fun f5() {}
|
||||
|
||||
|
||||
@@ -15,5 +15,5 @@ class A {
|
||||
}
|
||||
}
|
||||
|
||||
@RequiresOptIn(level = RequiresOptIn.Level.ERROR)
|
||||
@<!EXPERIMENTAL_IS_NOT_ENABLED!>RequiresOptIn<!>(level = RequiresOptIn.Level.ERROR)
|
||||
annotation class ExperimentalTime
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
|
||||
package org.jetbrains.kotlin.resolve.checkers
|
||||
|
||||
import org.jetbrains.kotlin.descriptors.annotations.KotlinTarget
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
|
||||
data class Experimentality(val annotationFqName: FqName, val severity: Severity, val message: String?) {
|
||||
@@ -12,5 +13,12 @@ data class Experimentality(val annotationFqName: FqName, val severity: Severity,
|
||||
|
||||
companion object {
|
||||
val DEFAULT_SEVERITY = Severity.ERROR
|
||||
|
||||
val WRONG_TARGETS_FOR_MARKER = setOf(
|
||||
KotlinTarget.EXPRESSION,
|
||||
KotlinTarget.FILE,
|
||||
KotlinTarget.TYPE,
|
||||
KotlinTarget.TYPE_PARAMETER
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
+3
-1
@@ -26,6 +26,7 @@ import org.jetbrains.kotlin.idea.frontend.api.symbols.*
|
||||
import org.jetbrains.kotlin.idea.frontend.api.types.KtType
|
||||
import org.jetbrains.kotlin.lexer.KtKeywordToken
|
||||
import org.jetbrains.kotlin.lexer.KtModifierKeywordToken
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.psi.KtExpression
|
||||
import org.jetbrains.kotlin.resolve.ForbiddenNamedArgumentsTarget
|
||||
@@ -252,7 +253,8 @@ private object FirToKtConversionCreator {
|
||||
ForbiddenNamedArgumentsTarget::class,
|
||||
LanguageFeature::class,
|
||||
LanguageVersionSettings::class,
|
||||
Variance::class
|
||||
Variance::class,
|
||||
FqName::class,
|
||||
)
|
||||
|
||||
private val KType.kClass: KClass<*>
|
||||
|
||||
+76
@@ -717,6 +717,82 @@ internal val KT_DIAGNOSTIC_CONVERTER = KtDiagnosticConverterBuilder.buildConvert
|
||||
token,
|
||||
)
|
||||
}
|
||||
add(FirErrors.EXPERIMENTAL_API_USAGE) { firDiagnostic ->
|
||||
ExperimentalApiUsageImpl(
|
||||
firDiagnostic.a,
|
||||
firDiagnostic.b,
|
||||
firDiagnostic as FirPsiDiagnostic<*>,
|
||||
token,
|
||||
)
|
||||
}
|
||||
add(FirErrors.EXPERIMENTAL_API_USAGE_ERROR) { firDiagnostic ->
|
||||
ExperimentalApiUsageErrorImpl(
|
||||
firDiagnostic.a,
|
||||
firDiagnostic.b,
|
||||
firDiagnostic as FirPsiDiagnostic<*>,
|
||||
token,
|
||||
)
|
||||
}
|
||||
add(FirErrors.EXPERIMENTAL_OVERRIDE) { firDiagnostic ->
|
||||
ExperimentalOverrideImpl(
|
||||
firDiagnostic.a,
|
||||
firDiagnostic.b,
|
||||
firDiagnostic as FirPsiDiagnostic<*>,
|
||||
token,
|
||||
)
|
||||
}
|
||||
add(FirErrors.EXPERIMENTAL_OVERRIDE_ERROR) { firDiagnostic ->
|
||||
ExperimentalOverrideErrorImpl(
|
||||
firDiagnostic.a,
|
||||
firDiagnostic.b,
|
||||
firDiagnostic as FirPsiDiagnostic<*>,
|
||||
token,
|
||||
)
|
||||
}
|
||||
add(FirErrors.EXPERIMENTAL_IS_NOT_ENABLED) { firDiagnostic ->
|
||||
ExperimentalIsNotEnabledImpl(
|
||||
firDiagnostic as FirPsiDiagnostic<*>,
|
||||
token,
|
||||
)
|
||||
}
|
||||
add(FirErrors.EXPERIMENTAL_CAN_ONLY_BE_USED_AS_ANNOTATION) { firDiagnostic ->
|
||||
ExperimentalCanOnlyBeUsedAsAnnotationImpl(
|
||||
firDiagnostic as FirPsiDiagnostic<*>,
|
||||
token,
|
||||
)
|
||||
}
|
||||
add(FirErrors.EXPERIMENTAL_MARKER_CAN_ONLY_BE_USED_AS_ANNOTATION_OR_ARGUMENT_IN_USE_EXPERIMENTAL) { firDiagnostic ->
|
||||
ExperimentalMarkerCanOnlyBeUsedAsAnnotationOrArgumentInUseExperimentalImpl(
|
||||
firDiagnostic as FirPsiDiagnostic<*>,
|
||||
token,
|
||||
)
|
||||
}
|
||||
add(FirErrors.USE_EXPERIMENTAL_WITHOUT_ARGUMENTS) { firDiagnostic ->
|
||||
UseExperimentalWithoutArgumentsImpl(
|
||||
firDiagnostic as FirPsiDiagnostic<*>,
|
||||
token,
|
||||
)
|
||||
}
|
||||
add(FirErrors.USE_EXPERIMENTAL_ARGUMENT_IS_NOT_MARKER) { firDiagnostic ->
|
||||
UseExperimentalArgumentIsNotMarkerImpl(
|
||||
firDiagnostic.a,
|
||||
firDiagnostic as FirPsiDiagnostic<*>,
|
||||
token,
|
||||
)
|
||||
}
|
||||
add(FirErrors.EXPERIMENTAL_ANNOTATION_WITH_WRONG_TARGET) { firDiagnostic ->
|
||||
ExperimentalAnnotationWithWrongTargetImpl(
|
||||
firDiagnostic.a,
|
||||
firDiagnostic as FirPsiDiagnostic<*>,
|
||||
token,
|
||||
)
|
||||
}
|
||||
add(FirErrors.EXPERIMENTAL_ANNOTATION_WITH_WRONG_RETENTION) { firDiagnostic ->
|
||||
ExperimentalAnnotationWithWrongRetentionImpl(
|
||||
firDiagnostic as FirPsiDiagnostic<*>,
|
||||
token,
|
||||
)
|
||||
}
|
||||
add(FirErrors.EXPOSED_TYPEALIAS_EXPANDED_TYPE) { firDiagnostic ->
|
||||
ExposedTypealiasExpandedTypeImpl(
|
||||
firDiagnostic.a,
|
||||
|
||||
+55
@@ -26,6 +26,7 @@ import org.jetbrains.kotlin.idea.frontend.api.symbols.KtVariableSymbol
|
||||
import org.jetbrains.kotlin.idea.frontend.api.types.KtType
|
||||
import org.jetbrains.kotlin.lexer.KtKeywordToken
|
||||
import org.jetbrains.kotlin.lexer.KtModifierKeywordToken
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.psi.KtAnnotation
|
||||
import org.jetbrains.kotlin.psi.KtAnnotationEntry
|
||||
@@ -513,6 +514,60 @@ sealed class KtFirDiagnostic<PSI: PsiElement> : KtDiagnosticWithPsi<PSI> {
|
||||
override val diagnosticClass get() = InapplicableFileTarget::class
|
||||
}
|
||||
|
||||
abstract class ExperimentalApiUsage : KtFirDiagnostic<PsiElement>() {
|
||||
override val diagnosticClass get() = ExperimentalApiUsage::class
|
||||
abstract val optInMarkerFqName: FqName
|
||||
abstract val message: String
|
||||
}
|
||||
|
||||
abstract class ExperimentalApiUsageError : KtFirDiagnostic<PsiElement>() {
|
||||
override val diagnosticClass get() = ExperimentalApiUsageError::class
|
||||
abstract val optInMarkerFqName: FqName
|
||||
abstract val message: String
|
||||
}
|
||||
|
||||
abstract class ExperimentalOverride : KtFirDiagnostic<PsiElement>() {
|
||||
override val diagnosticClass get() = ExperimentalOverride::class
|
||||
abstract val optInMarkerFqName: FqName
|
||||
abstract val message: String
|
||||
}
|
||||
|
||||
abstract class ExperimentalOverrideError : KtFirDiagnostic<PsiElement>() {
|
||||
override val diagnosticClass get() = ExperimentalOverrideError::class
|
||||
abstract val optInMarkerFqName: FqName
|
||||
abstract val message: String
|
||||
}
|
||||
|
||||
abstract class ExperimentalIsNotEnabled : KtFirDiagnostic<KtAnnotationEntry>() {
|
||||
override val diagnosticClass get() = ExperimentalIsNotEnabled::class
|
||||
}
|
||||
|
||||
abstract class ExperimentalCanOnlyBeUsedAsAnnotation : KtFirDiagnostic<PsiElement>() {
|
||||
override val diagnosticClass get() = ExperimentalCanOnlyBeUsedAsAnnotation::class
|
||||
}
|
||||
|
||||
abstract class ExperimentalMarkerCanOnlyBeUsedAsAnnotationOrArgumentInUseExperimental : KtFirDiagnostic<PsiElement>() {
|
||||
override val diagnosticClass get() = ExperimentalMarkerCanOnlyBeUsedAsAnnotationOrArgumentInUseExperimental::class
|
||||
}
|
||||
|
||||
abstract class UseExperimentalWithoutArguments : KtFirDiagnostic<KtAnnotationEntry>() {
|
||||
override val diagnosticClass get() = UseExperimentalWithoutArguments::class
|
||||
}
|
||||
|
||||
abstract class UseExperimentalArgumentIsNotMarker : KtFirDiagnostic<KtAnnotationEntry>() {
|
||||
override val diagnosticClass get() = UseExperimentalArgumentIsNotMarker::class
|
||||
abstract val notMarkerFqName: FqName
|
||||
}
|
||||
|
||||
abstract class ExperimentalAnnotationWithWrongTarget : KtFirDiagnostic<KtAnnotationEntry>() {
|
||||
override val diagnosticClass get() = ExperimentalAnnotationWithWrongTarget::class
|
||||
abstract val target: String
|
||||
}
|
||||
|
||||
abstract class ExperimentalAnnotationWithWrongRetention : KtFirDiagnostic<KtAnnotationEntry>() {
|
||||
override val diagnosticClass get() = ExperimentalAnnotationWithWrongRetention::class
|
||||
}
|
||||
|
||||
abstract class ExposedTypealiasExpandedType : KtFirDiagnostic<KtNamedDeclaration>() {
|
||||
override val diagnosticClass get() = ExposedTypealiasExpandedType::class
|
||||
abstract val elementVisibility: EffectiveVisibility
|
||||
|
||||
+88
@@ -28,6 +28,7 @@ import org.jetbrains.kotlin.idea.frontend.api.tokens.ValidityToken
|
||||
import org.jetbrains.kotlin.idea.frontend.api.types.KtType
|
||||
import org.jetbrains.kotlin.lexer.KtKeywordToken
|
||||
import org.jetbrains.kotlin.lexer.KtModifierKeywordToken
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.psi.KtAnnotation
|
||||
import org.jetbrains.kotlin.psi.KtAnnotationEntry
|
||||
@@ -832,6 +833,93 @@ internal class InapplicableFileTargetImpl(
|
||||
override val firDiagnostic: FirPsiDiagnostic<*> by weakRef(firDiagnostic)
|
||||
}
|
||||
|
||||
internal class ExperimentalApiUsageImpl(
|
||||
override val optInMarkerFqName: FqName,
|
||||
override val message: String,
|
||||
firDiagnostic: FirPsiDiagnostic<*>,
|
||||
override val token: ValidityToken,
|
||||
) : KtFirDiagnostic.ExperimentalApiUsage(), KtAbstractFirDiagnostic<PsiElement> {
|
||||
override val firDiagnostic: FirPsiDiagnostic<*> by weakRef(firDiagnostic)
|
||||
}
|
||||
|
||||
internal class ExperimentalApiUsageErrorImpl(
|
||||
override val optInMarkerFqName: FqName,
|
||||
override val message: String,
|
||||
firDiagnostic: FirPsiDiagnostic<*>,
|
||||
override val token: ValidityToken,
|
||||
) : KtFirDiagnostic.ExperimentalApiUsageError(), KtAbstractFirDiagnostic<PsiElement> {
|
||||
override val firDiagnostic: FirPsiDiagnostic<*> by weakRef(firDiagnostic)
|
||||
}
|
||||
|
||||
internal class ExperimentalOverrideImpl(
|
||||
override val optInMarkerFqName: FqName,
|
||||
override val message: String,
|
||||
firDiagnostic: FirPsiDiagnostic<*>,
|
||||
override val token: ValidityToken,
|
||||
) : KtFirDiagnostic.ExperimentalOverride(), KtAbstractFirDiagnostic<PsiElement> {
|
||||
override val firDiagnostic: FirPsiDiagnostic<*> by weakRef(firDiagnostic)
|
||||
}
|
||||
|
||||
internal class ExperimentalOverrideErrorImpl(
|
||||
override val optInMarkerFqName: FqName,
|
||||
override val message: String,
|
||||
firDiagnostic: FirPsiDiagnostic<*>,
|
||||
override val token: ValidityToken,
|
||||
) : KtFirDiagnostic.ExperimentalOverrideError(), KtAbstractFirDiagnostic<PsiElement> {
|
||||
override val firDiagnostic: FirPsiDiagnostic<*> by weakRef(firDiagnostic)
|
||||
}
|
||||
|
||||
internal class ExperimentalIsNotEnabledImpl(
|
||||
firDiagnostic: FirPsiDiagnostic<*>,
|
||||
override val token: ValidityToken,
|
||||
) : KtFirDiagnostic.ExperimentalIsNotEnabled(), KtAbstractFirDiagnostic<KtAnnotationEntry> {
|
||||
override val firDiagnostic: FirPsiDiagnostic<*> by weakRef(firDiagnostic)
|
||||
}
|
||||
|
||||
internal class ExperimentalCanOnlyBeUsedAsAnnotationImpl(
|
||||
firDiagnostic: FirPsiDiagnostic<*>,
|
||||
override val token: ValidityToken,
|
||||
) : KtFirDiagnostic.ExperimentalCanOnlyBeUsedAsAnnotation(), KtAbstractFirDiagnostic<PsiElement> {
|
||||
override val firDiagnostic: FirPsiDiagnostic<*> by weakRef(firDiagnostic)
|
||||
}
|
||||
|
||||
internal class ExperimentalMarkerCanOnlyBeUsedAsAnnotationOrArgumentInUseExperimentalImpl(
|
||||
firDiagnostic: FirPsiDiagnostic<*>,
|
||||
override val token: ValidityToken,
|
||||
) : KtFirDiagnostic.ExperimentalMarkerCanOnlyBeUsedAsAnnotationOrArgumentInUseExperimental(), KtAbstractFirDiagnostic<PsiElement> {
|
||||
override val firDiagnostic: FirPsiDiagnostic<*> by weakRef(firDiagnostic)
|
||||
}
|
||||
|
||||
internal class UseExperimentalWithoutArgumentsImpl(
|
||||
firDiagnostic: FirPsiDiagnostic<*>,
|
||||
override val token: ValidityToken,
|
||||
) : KtFirDiagnostic.UseExperimentalWithoutArguments(), KtAbstractFirDiagnostic<KtAnnotationEntry> {
|
||||
override val firDiagnostic: FirPsiDiagnostic<*> by weakRef(firDiagnostic)
|
||||
}
|
||||
|
||||
internal class UseExperimentalArgumentIsNotMarkerImpl(
|
||||
override val notMarkerFqName: FqName,
|
||||
firDiagnostic: FirPsiDiagnostic<*>,
|
||||
override val token: ValidityToken,
|
||||
) : KtFirDiagnostic.UseExperimentalArgumentIsNotMarker(), KtAbstractFirDiagnostic<KtAnnotationEntry> {
|
||||
override val firDiagnostic: FirPsiDiagnostic<*> by weakRef(firDiagnostic)
|
||||
}
|
||||
|
||||
internal class ExperimentalAnnotationWithWrongTargetImpl(
|
||||
override val target: String,
|
||||
firDiagnostic: FirPsiDiagnostic<*>,
|
||||
override val token: ValidityToken,
|
||||
) : KtFirDiagnostic.ExperimentalAnnotationWithWrongTarget(), KtAbstractFirDiagnostic<KtAnnotationEntry> {
|
||||
override val firDiagnostic: FirPsiDiagnostic<*> by weakRef(firDiagnostic)
|
||||
}
|
||||
|
||||
internal class ExperimentalAnnotationWithWrongRetentionImpl(
|
||||
firDiagnostic: FirPsiDiagnostic<*>,
|
||||
override val token: ValidityToken,
|
||||
) : KtFirDiagnostic.ExperimentalAnnotationWithWrongRetention(), KtAbstractFirDiagnostic<KtAnnotationEntry> {
|
||||
override val firDiagnostic: FirPsiDiagnostic<*> by weakRef(firDiagnostic)
|
||||
}
|
||||
|
||||
internal class ExposedTypealiasExpandedTypeImpl(
|
||||
override val elementVisibility: EffectiveVisibility,
|
||||
override val restrictingDeclaration: KtSymbol,
|
||||
|
||||
Reference in New Issue
Block a user