FIR: add EXPERIMENTAL_API_USAGE reporting
This commit is contained in:
+1
-1
@@ -7,7 +7,7 @@ interface FirFunctionCall : FirBase
|
||||
fun foo(statements: List<FirBase>, arguments: List<FirBase>, explicitReceiver: FirBase): List<FirFunctionCall> {
|
||||
|
||||
val firstCalls = with(statements.last() as FirFunctionCall) setCall@{
|
||||
buildList {
|
||||
<!EXPERIMENTAL_API_USAGE_ERROR!>buildList<!> {
|
||||
add(this@setCall)
|
||||
with(arguments.last() as FirFunctionCall) plusCall@{
|
||||
add(this@plusCall)
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
fun <T> foo(@BuilderInference block: MutableList<T>.() -> Unit): T = null!!
|
||||
fun <T> foo(@<!EXPERIMENTAL_API_USAGE_ERROR!>BuilderInference<!> block: MutableList<T>.() -> Unit): T = null!!
|
||||
|
||||
fun takeString(s: String) {}
|
||||
|
||||
|
||||
+1
-1
@@ -2,7 +2,7 @@ class DropDownComponent<T : Any>(val initialValues: List<T>)
|
||||
|
||||
fun test(strings: List<String>) {
|
||||
val dropDown = DropDownComponent(
|
||||
initialValues = buildList {
|
||||
initialValues = <!EXPERIMENTAL_API_USAGE_ERROR!>buildList<!> {
|
||||
addAll(strings)
|
||||
}
|
||||
)
|
||||
|
||||
Vendored
+2
-2
@@ -1,5 +1,5 @@
|
||||
fun test_1() {
|
||||
val list = buildList {
|
||||
val list = <!EXPERIMENTAL_API_USAGE_ERROR!>buildList<!> {
|
||||
add("")
|
||||
}
|
||||
takeList(list)
|
||||
@@ -12,7 +12,7 @@ fun test_2() {
|
||||
takeList(list)
|
||||
}
|
||||
|
||||
fun <E> myBuildList(@BuilderInference builderAction: MutableList<E>.() -> Unit): List<E> {
|
||||
fun <E> myBuildList(@<!EXPERIMENTAL_API_USAGE_ERROR!>BuilderInference<!> builderAction: MutableList<E>.() -> Unit): List<E> {
|
||||
return ArrayList<E>().apply(builderAction)
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -2,7 +2,7 @@ interface KtScope {
|
||||
fun getAllNames(): Set<String>
|
||||
}
|
||||
|
||||
inline fun <E> buildSet(@BuilderInference builderAction: MutableSet<E>.() -> Unit): Set<E> {
|
||||
inline fun <E> buildSet(@<!EXPERIMENTAL_API_USAGE_ERROR!>BuilderInference<!> builderAction: MutableSet<E>.() -> Unit): Set<E> {
|
||||
return null!!
|
||||
}
|
||||
|
||||
|
||||
+6
@@ -34863,6 +34863,12 @@ public class FirOldFrontendDiagnosticsTestGenerated extends AbstractFirDiagnosti
|
||||
runTest("compiler/testData/diagnostics/testsWithStdLib/experimental/experimentalIsNotEnabled.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("experimentalSetter.kt")
|
||||
public void testExperimentalSetter() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/testsWithStdLib/experimental/experimentalSetter.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("fullFqNameUsage.kt")
|
||||
public void testFullFqNameUsage() throws Exception {
|
||||
|
||||
+6
@@ -34863,6 +34863,12 @@ public class FirOldFrontendDiagnosticsWithLightTreeTestGenerated extends Abstrac
|
||||
runTest("compiler/testData/diagnostics/testsWithStdLib/experimental/experimentalIsNotEnabled.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("experimentalSetter.kt")
|
||||
public void testExperimentalSetter() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/testsWithStdLib/experimental/experimentalSetter.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("fullFqNameUsage.kt")
|
||||
public void testFullFqNameUsage() throws Exception {
|
||||
|
||||
+6
@@ -23,6 +23,11 @@ object CommonExpressionCheckers : ExpressionCheckers() {
|
||||
FirDeprecationChecker,
|
||||
)
|
||||
|
||||
override val qualifiedAccessCheckers: Set<FirQualifiedAccessChecker>
|
||||
get() = setOf(
|
||||
FirOptInUsageAccessChecker,
|
||||
)
|
||||
|
||||
override val qualifiedAccessExpressionCheckers: Set<FirQualifiedAccessExpressionChecker>
|
||||
get() = setOf(
|
||||
FirCallableReferenceChecker,
|
||||
@@ -115,6 +120,7 @@ object CommonExpressionCheckers : ExpressionCheckers() {
|
||||
override val resolvedQualifierCheckers: Set<FirResolvedQualifierChecker>
|
||||
get() = setOf(
|
||||
FirStandaloneQualifierChecker,
|
||||
FirOptInUsageQualifierChecker,
|
||||
)
|
||||
|
||||
override val equalityOperatorCallCheckers: Set<FirEqualityOperatorCallChecker>
|
||||
|
||||
+1
@@ -12,5 +12,6 @@ object CommonTypeCheckers : TypeCheckers() {
|
||||
FirTypeAnnotationChecker,
|
||||
FirSuspendModifierChecker,
|
||||
FirDeprecatedTypeChecker,
|
||||
FirOptInUsageTypeRefChecker,
|
||||
)
|
||||
}
|
||||
|
||||
+1
-1
@@ -32,7 +32,7 @@ object FirClassLiteralChecker : FirGetClassCallChecker() {
|
||||
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) {
|
||||
if (fqName == OptInNames.REQUIRES_OPT_IN_FQ_NAME || fqName == OptInNames.OPT_IN_FQ_NAME) {
|
||||
reporter.reportOn(argument.source, FirErrors.EXPERIMENTAL_CAN_ONLY_BE_USED_AS_ANNOTATION, context)
|
||||
}
|
||||
}
|
||||
|
||||
+43
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* 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.FirFakeSourceElementKind
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.DiagnosticReporter
|
||||
import org.jetbrains.kotlin.fir.declarations.FirAnnotatedDeclaration
|
||||
import org.jetbrains.kotlin.fir.declarations.FirProperty
|
||||
import org.jetbrains.kotlin.fir.declarations.FirResolvePhase
|
||||
import org.jetbrains.kotlin.fir.expressions.FirQualifiedAccess
|
||||
import org.jetbrains.kotlin.fir.expressions.FirVariableAssignment
|
||||
import org.jetbrains.kotlin.fir.references.FirResolvedNamedReference
|
||||
import org.jetbrains.kotlin.fir.symbols.SymbolInternals
|
||||
import org.jetbrains.kotlin.fir.symbols.ensureResolved
|
||||
|
||||
object FirOptInUsageAccessChecker : FirQualifiedAccessChecker() {
|
||||
@OptIn(SymbolInternals::class)
|
||||
override fun check(expression: FirQualifiedAccess, context: CheckerContext, reporter: DiagnosticReporter) {
|
||||
val sourceKind = expression.source?.kind
|
||||
if (sourceKind is FirFakeSourceElementKind.DataClassGeneratedMembers ||
|
||||
sourceKind is FirFakeSourceElementKind.PropertyFromParameter
|
||||
) return
|
||||
val reference = expression.calleeReference as? FirResolvedNamedReference ?: return
|
||||
val resolvedSymbol = reference.resolvedSymbol
|
||||
resolvedSymbol.ensureResolved(FirResolvePhase.STATUS)
|
||||
val fir = resolvedSymbol.fir as? FirAnnotatedDeclaration ?: return
|
||||
with(FirOptInUsageBaseChecker) {
|
||||
if (expression is FirVariableAssignment && fir is FirProperty) {
|
||||
val experimentalities = fir.loadExperimentalities(context, fromSetter = true) +
|
||||
loadExperimentalitiesFromTypeArguments(context, expression.typeArguments)
|
||||
reportNotAcceptedExperimentalities(experimentalities, expression.lValue, context, reporter)
|
||||
return
|
||||
}
|
||||
val experimentalities = fir.loadExperimentalities(context, fromSetter = false) +
|
||||
loadExperimentalitiesFromTypeArguments(context, expression.typeArguments)
|
||||
reportNotAcceptedExperimentalities(experimentalities, expression, context, reporter)
|
||||
}
|
||||
}
|
||||
}
|
||||
+220
-1
@@ -5,6 +5,16 @@
|
||||
|
||||
package org.jetbrains.kotlin.fir.analysis.checkers.expression
|
||||
|
||||
import org.jetbrains.kotlin.config.AnalysisFlags
|
||||
import org.jetbrains.kotlin.descriptors.annotations.AnnotationUseSiteTarget
|
||||
import org.jetbrains.kotlin.fir.*
|
||||
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.*
|
||||
import org.jetbrains.kotlin.fir.declarations.utils.isLocal
|
||||
import org.jetbrains.kotlin.fir.declarations.FirRegularClass
|
||||
import org.jetbrains.kotlin.fir.declarations.findArgumentByName
|
||||
import org.jetbrains.kotlin.fir.declarations.getAnnotationByFqName
|
||||
@@ -12,12 +22,22 @@ import org.jetbrains.kotlin.fir.declarations.FirResolvePhase
|
||||
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.fir.resolve.fullyExpandedType
|
||||
import org.jetbrains.kotlin.fir.resolve.toFirRegularClass
|
||||
import org.jetbrains.kotlin.fir.resolve.toSymbol
|
||||
import org.jetbrains.kotlin.fir.scopes.ProcessorAction
|
||||
import org.jetbrains.kotlin.fir.scopes.processDirectlyOverriddenFunctions
|
||||
import org.jetbrains.kotlin.fir.scopes.processDirectlyOverriddenProperties
|
||||
import org.jetbrains.kotlin.fir.types.*
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.fir.symbols.SymbolInternals
|
||||
import org.jetbrains.kotlin.fir.symbols.ensureResolved
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirRegularClassSymbol
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.resolve.checkers.Experimentality
|
||||
import org.jetbrains.kotlin.resolve.checkers.OptInNames
|
||||
import org.jetbrains.kotlin.utils.SmartSet
|
||||
import org.jetbrains.kotlin.utils.addIfNotNull
|
||||
|
||||
internal object FirOptInUsageBaseChecker {
|
||||
fun FirRegularClassSymbol.loadExperimentalityForMarkerAnnotation(): Experimentality? {
|
||||
@@ -26,8 +46,140 @@ internal object FirOptInUsageBaseChecker {
|
||||
return fir.loadExperimentalityForMarkerAnnotation()
|
||||
}
|
||||
|
||||
fun loadExperimentalitiesFromTypeArguments(
|
||||
context: CheckerContext,
|
||||
typeArguments: List<FirTypeProjection>
|
||||
): Set<Experimentality> {
|
||||
if (typeArguments.isEmpty()) return emptySet()
|
||||
return loadExperimentalitiesFromConeArguments(context, typeArguments.map { it.toConeTypeProjection() })
|
||||
}
|
||||
|
||||
fun loadExperimentalitiesFromConeArguments(
|
||||
context: CheckerContext,
|
||||
typeArguments: List<ConeTypeProjection>
|
||||
): Set<Experimentality> {
|
||||
if (typeArguments.isEmpty()) return emptySet()
|
||||
val result = SmartSet.create<Experimentality>()
|
||||
typeArguments.forEach {
|
||||
if (!it.isStarProjection) it.type?.addExperimentalities(context, result)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
fun FirAnnotatedDeclaration.loadExperimentalities(
|
||||
context: CheckerContext, fromSetter: Boolean
|
||||
): Set<Experimentality> = loadExperimentalities(
|
||||
context, knownExperimentalities = null, visited = mutableSetOf(), fromSetter
|
||||
)
|
||||
|
||||
@OptIn(SymbolInternals::class)
|
||||
private fun FirAnnotatedDeclaration.loadExperimentalities(
|
||||
context: CheckerContext,
|
||||
knownExperimentalities: SmartSet<Experimentality>?,
|
||||
visited: MutableSet<FirAnnotatedDeclaration>,
|
||||
fromSetter: Boolean,
|
||||
): Set<Experimentality> {
|
||||
if (!visited.add(this)) return emptySet()
|
||||
val result = knownExperimentalities ?: SmartSet.create()
|
||||
val session = context.session
|
||||
if (this is FirCallableDeclaration) {
|
||||
val parentClass = containingClass()?.toFirRegularClass(session)
|
||||
if (this.isSubstitutionOrIntersectionOverride) {
|
||||
val parentClassScope = parentClass?.unsubstitutedScope(context)
|
||||
if (this is FirSimpleFunction) {
|
||||
parentClassScope?.processDirectlyOverriddenFunctions(symbol) {
|
||||
it.fir.loadExperimentalities(context, result, visited, fromSetter = false)
|
||||
ProcessorAction.NEXT
|
||||
}
|
||||
} else if (this is FirProperty) {
|
||||
parentClassScope?.processDirectlyOverriddenProperties(symbol) {
|
||||
it.fir.loadExperimentalities(context, result, visited, fromSetter)
|
||||
ProcessorAction.NEXT
|
||||
}
|
||||
}
|
||||
}
|
||||
if (this !is FirConstructor) {
|
||||
// Note: coneType here crashes on overridden members
|
||||
returnTypeRef.coneTypeSafe<ConeKotlinType>().addExperimentalities(context, result, visited)
|
||||
receiverTypeRef?.coneTypeSafe<ConeKotlinType>().addExperimentalities(context, result, visited)
|
||||
if (this is FirSimpleFunction) {
|
||||
valueParameters.forEach {
|
||||
it.returnTypeRef.coneTypeSafe<ConeKotlinType>().addExperimentalities(context, result, visited)
|
||||
}
|
||||
}
|
||||
}
|
||||
parentClass?.loadExperimentalities(context, result, visited, fromSetter = false)
|
||||
if (fromSetter && this is FirProperty) {
|
||||
setter?.loadExperimentalities(context, result, visited, fromSetter = false)
|
||||
}
|
||||
} else if (this is FirRegularClass && !this.isLocal) {
|
||||
val parentClassSymbol = symbol.outerClassSymbol(context)
|
||||
parentClassSymbol?.fir?.loadExperimentalities(context, result, visited, fromSetter = false)
|
||||
}
|
||||
|
||||
for (annotation in annotations) {
|
||||
val annotationType = annotation.annotationTypeRef.coneTypeSafe<ConeClassLikeType>()
|
||||
if (annotation.useSiteTarget != AnnotationUseSiteTarget.PROPERTY_SETTER || fromSetter) {
|
||||
result.addIfNotNull(
|
||||
annotationType?.lookupTag?.toFirRegularClass(
|
||||
session
|
||||
)?.loadExperimentalityForMarkerAnnotation()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
if (this is FirTypeAlias) {
|
||||
expandedTypeRef.coneType.addExperimentalities(context, result, visited)
|
||||
}
|
||||
|
||||
if (getAnnotationByFqName(OptInNames.WAS_EXPERIMENTAL_FQ_NAME) != null) {
|
||||
val accessibility = checkSinceKotlinVersionAccessibility(context)
|
||||
if (accessibility is FirSinceKotlinAccessibility.NotAccessibleButWasExperimental) {
|
||||
accessibility.markerClasses.forEach {
|
||||
it.ensureResolved(FirResolvePhase.STATUS)
|
||||
result.addIfNotNull(it.fir.loadExperimentalityForMarkerAnnotation())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: getAnnotationsOnContainingModule
|
||||
return result
|
||||
}
|
||||
|
||||
@OptIn(SymbolInternals::class)
|
||||
private fun ConeKotlinType?.addExperimentalities(
|
||||
context: CheckerContext,
|
||||
result: SmartSet<Experimentality>,
|
||||
visited: MutableSet<FirAnnotatedDeclaration> = mutableSetOf()
|
||||
) {
|
||||
when (this) {
|
||||
!is ConeClassLikeType -> return
|
||||
else -> {
|
||||
val expandedType = fullyExpandedType(context.session)
|
||||
if (this === expandedType) {
|
||||
expandedType.lookupTag.toFirRegularClass(context.session)?.loadExperimentalities(
|
||||
context, result, visited, fromSetter = false
|
||||
)
|
||||
typeArguments.forEach {
|
||||
if (!it.isStarProjection) it.type?.addExperimentalities(context, result, visited)
|
||||
}
|
||||
} else {
|
||||
val symbol = lookupTag.toSymbol(context.session)
|
||||
if (symbol != null) {
|
||||
symbol.ensureResolved(FirResolvePhase.STATUS)
|
||||
symbol.fir.loadExperimentalities(context, result, visited, fromSetter = false)
|
||||
}
|
||||
expandedType.typeArguments.forEach {
|
||||
if (!it.isStarProjection) it.type?.addExperimentalities(context, result, visited)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun FirRegularClass.loadExperimentalityForMarkerAnnotation(): Experimentality? {
|
||||
val experimental = getAnnotationByFqName(OptInNames.REQUIRES_OPT_IN_FQ_NAME) ?: return null
|
||||
val experimental = getAnnotationByFqName(OptInNames.REQUIRES_OPT_IN_FQ_NAME)
|
||||
?: return null
|
||||
|
||||
val levelArgument = experimental.findArgumentByName(LEVEL) as? FirQualifiedAccessExpression
|
||||
val levelName = (levelArgument?.calleeReference as? FirResolvedNamedReference)?.name?.asString()
|
||||
@@ -36,6 +188,73 @@ internal object FirOptInUsageBaseChecker {
|
||||
return Experimentality(symbol.classId.asSingleFqName(), level.severity, message)
|
||||
}
|
||||
|
||||
fun reportNotAcceptedExperimentalities(
|
||||
experimentalities: Collection<Experimentality>,
|
||||
element: FirElement,
|
||||
context: CheckerContext,
|
||||
reporter: DiagnosticReporter
|
||||
) {
|
||||
for ((annotationFqName, severity, message) in experimentalities) {
|
||||
if (!isExperimentalityAcceptableInContext(annotationFqName, element, context)) {
|
||||
val diagnostic = when (severity) {
|
||||
Experimentality.Severity.WARNING -> FirErrors.EXPERIMENTAL_API_USAGE
|
||||
Experimentality.Severity.ERROR -> FirErrors.EXPERIMENTAL_API_USAGE_ERROR
|
||||
}
|
||||
val reportedMessage = message ?: when (severity) {
|
||||
Experimentality.Severity.WARNING -> "This declaration is experimental and its usage should be marked"
|
||||
Experimentality.Severity.ERROR -> "This declaration is experimental and its usage must be marked"
|
||||
}
|
||||
reporter.reportOn(element.source, diagnostic, annotationFqName, reportedMessage, context)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun isExperimentalityAcceptableInContext(
|
||||
annotationFqName: FqName,
|
||||
element: FirElement,
|
||||
context: CheckerContext
|
||||
): Boolean {
|
||||
val languageVersionSettings = context.session.languageVersionSettings
|
||||
val fqNameAsString = annotationFqName.asString()
|
||||
if (fqNameAsString in languageVersionSettings.getFlag(AnalysisFlags.useExperimental)) {
|
||||
return true
|
||||
}
|
||||
for (declaration in context.containingDeclarations) {
|
||||
if (declaration !is FirAnnotatedDeclaration) continue
|
||||
if (declaration.isExperimentalityAcceptable(annotationFqName)) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
for (accessOrAnnotation in context.qualifiedAccessOrAnnotationCalls) {
|
||||
if (accessOrAnnotation.isExperimentalityAcceptable(annotationFqName)) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
if (element !is FirAnnotationContainer) return false
|
||||
return element.isExperimentalityAcceptable(annotationFqName)
|
||||
}
|
||||
|
||||
private fun FirAnnotationContainer.isExperimentalityAcceptable(annotationFqName: FqName): Boolean {
|
||||
return getAnnotationByFqName(annotationFqName) != null || isAnnotatedWithUseExperimentalOf(annotationFqName)
|
||||
}
|
||||
|
||||
private fun FirAnnotationContainer.isAnnotatedWithUseExperimentalOf(annotationFqName: FqName): Boolean {
|
||||
for (annotation in annotations) {
|
||||
val coneType = annotation.annotationTypeRef.coneType as? ConeClassLikeType
|
||||
if (coneType?.lookupTag?.classId?.asSingleFqName() != OptInNames.OPT_IN_FQ_NAME) {
|
||||
continue
|
||||
}
|
||||
val annotationClasses = annotation.findArgumentByName(OptInNames.USE_EXPERIMENTAL_ANNOTATION_CLASS) ?: continue
|
||||
if (annotationClasses.extractClassesFromArgument().any {
|
||||
it.classId.asSingleFqName() == annotationFqName
|
||||
}
|
||||
) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
private val LEVEL = Name.identifier("level")
|
||||
private val MESSAGE = Name.identifier("message")
|
||||
|
||||
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* 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.context.CheckerContext
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.DiagnosticReporter
|
||||
import org.jetbrains.kotlin.fir.declarations.FirResolvePhase
|
||||
import org.jetbrains.kotlin.fir.expressions.FirResolvedQualifier
|
||||
import org.jetbrains.kotlin.fir.symbols.SymbolInternals
|
||||
import org.jetbrains.kotlin.fir.symbols.ensureResolved
|
||||
|
||||
object FirOptInUsageQualifierChecker : FirResolvedQualifierChecker() {
|
||||
@OptIn(SymbolInternals::class)
|
||||
override fun check(expression: FirResolvedQualifier, context: CheckerContext, reporter: DiagnosticReporter) {
|
||||
val symbol = expression.symbol ?: return
|
||||
symbol.ensureResolved(FirResolvePhase.STATUS)
|
||||
val fir = symbol.fir
|
||||
with(FirOptInUsageBaseChecker) {
|
||||
val experimentalities = fir.loadExperimentalities(context, fromSetter = false)
|
||||
reportNotAcceptedExperimentalities(experimentalities, expression, context, reporter)
|
||||
}
|
||||
}
|
||||
}
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* 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.type
|
||||
|
||||
import org.jetbrains.kotlin.fir.FirRealSourceElementKind
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.expression.FirOptInUsageBaseChecker
|
||||
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.FirResolvePhase
|
||||
import org.jetbrains.kotlin.fir.expressions.FirAnnotationCall
|
||||
import org.jetbrains.kotlin.fir.resolve.toSymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.SymbolInternals
|
||||
import org.jetbrains.kotlin.fir.symbols.ensureResolved
|
||||
import org.jetbrains.kotlin.fir.types.ConeClassLikeType
|
||||
import org.jetbrains.kotlin.fir.types.FirTypeRef
|
||||
import org.jetbrains.kotlin.fir.types.coneTypeSafe
|
||||
import org.jetbrains.kotlin.resolve.checkers.OptInNames
|
||||
|
||||
object FirOptInUsageTypeRefChecker : FirTypeRefChecker() {
|
||||
@OptIn(SymbolInternals::class)
|
||||
override fun check(typeRef: FirTypeRef, context: CheckerContext, reporter: DiagnosticReporter) {
|
||||
if (typeRef.source?.kind !is FirRealSourceElementKind) return
|
||||
val coneType = typeRef.coneTypeSafe<ConeClassLikeType>() ?: return
|
||||
val symbol = coneType.lookupTag.toSymbol(context.session) ?: return
|
||||
symbol.ensureResolved(FirResolvePhase.STATUS)
|
||||
val fqName = symbol.classId.asSingleFqName()
|
||||
val lastAnnotationCall = context.qualifiedAccessOrAnnotationCalls.lastOrNull() as? FirAnnotationCall
|
||||
if ((lastAnnotationCall == null || lastAnnotationCall.annotationTypeRef !== typeRef) &&
|
||||
(fqName == OptInNames.REQUIRES_OPT_IN_FQ_NAME || fqName == OptInNames.OPT_IN_FQ_NAME)
|
||||
) {
|
||||
reporter.reportOn(typeRef.source, FirErrors.EXPERIMENTAL_CAN_ONLY_BE_USED_AS_ANNOTATION, context)
|
||||
}
|
||||
|
||||
with(FirOptInUsageBaseChecker) {
|
||||
val experimentalities = symbol.fir.loadExperimentalities(context, fromSetter = false) +
|
||||
loadExperimentalitiesFromConeArguments(context, coneType.typeArguments.toList())
|
||||
reportNotAcceptedExperimentalities(experimentalities, typeRef, context, reporter)
|
||||
}
|
||||
}
|
||||
}
|
||||
+1
@@ -57,6 +57,7 @@ class ValueParameter(
|
||||
initializer = buildQualifiedAccessExpression {
|
||||
source = propertySource
|
||||
calleeReference = buildPropertyFromParameterResolvedNamedReference {
|
||||
source = propertySource
|
||||
this.name = name
|
||||
resolvedSymbol = this@ValueParameter.firValueParameter.symbol
|
||||
source = propertySource
|
||||
|
||||
Reference in New Issue
Block a user