diff --git a/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/FirJvmDiagnosticsList.kt b/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/FirJvmDiagnosticsList.kt index 150913eaa19..f825340cf84 100644 --- a/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/FirJvmDiagnosticsList.kt +++ b/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/FirJvmDiagnosticsList.kt @@ -24,6 +24,7 @@ object JVM_DIAGNOSTICS_LIST : DiagnosticList("FirJvmErrors") { val OVERRIDE_CANNOT_BE_STATIC by error() val JVM_STATIC_NOT_IN_OBJECT_OR_CLASS_COMPANION by error(PositioningStrategy.DECLARATION_SIGNATURE) val JVM_STATIC_NOT_IN_OBJECT_OR_COMPANION by error(PositioningStrategy.DECLARATION_SIGNATURE) + val JVM_STATIC_ON_NON_PUBLIC_MEMBER by error(PositioningStrategy.DECLARATION_SIGNATURE) val INAPPLICABLE_JVM_NAME by error() val ILLEGAL_JVM_NAME by error() diff --git a/compiler/fir/checkers/checkers.jvm/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/jvm/FirJvmErrors.kt b/compiler/fir/checkers/checkers.jvm/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/jvm/FirJvmErrors.kt index 78cb3842148..967d5d1c812 100644 --- a/compiler/fir/checkers/checkers.jvm/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/jvm/FirJvmErrors.kt +++ b/compiler/fir/checkers/checkers.jvm/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/jvm/FirJvmErrors.kt @@ -25,6 +25,7 @@ object FirJvmErrors { val OVERRIDE_CANNOT_BE_STATIC by error0() val JVM_STATIC_NOT_IN_OBJECT_OR_CLASS_COMPANION by error0(SourceElementPositioningStrategies.DECLARATION_SIGNATURE) val JVM_STATIC_NOT_IN_OBJECT_OR_COMPANION by error0(SourceElementPositioningStrategies.DECLARATION_SIGNATURE) + val JVM_STATIC_ON_NON_PUBLIC_MEMBER by error0(SourceElementPositioningStrategies.DECLARATION_SIGNATURE) val INAPPLICABLE_JVM_NAME by error0() val ILLEGAL_JVM_NAME by error0() diff --git a/compiler/fir/checkers/checkers.jvm/src/org/jetbrains/kotlin/fir/analysis/jvm/checkers/declaration/FirJvmStaticChecker.kt b/compiler/fir/checkers/checkers.jvm/src/org/jetbrains/kotlin/fir/analysis/jvm/checkers/declaration/FirJvmStaticChecker.kt index a81d503690f..8bc17addb9e 100644 --- a/compiler/fir/checkers/checkers.jvm/src/org/jetbrains/kotlin/fir/analysis/jvm/checkers/declaration/FirJvmStaticChecker.kt +++ b/compiler/fir/checkers/checkers.jvm/src/org/jetbrains/kotlin/fir/analysis/jvm/checkers/declaration/FirJvmStaticChecker.kt @@ -7,21 +7,25 @@ package org.jetbrains.kotlin.fir.analysis.jvm.checkers.declaration import org.jetbrains.kotlin.config.LanguageFeature import org.jetbrains.kotlin.descriptors.ClassKind +import org.jetbrains.kotlin.descriptors.Visibilities +import org.jetbrains.kotlin.descriptors.Visibility import org.jetbrains.kotlin.descriptors.isInterface import org.jetbrains.kotlin.fir.analysis.checkers.classKind import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext import org.jetbrains.kotlin.fir.analysis.checkers.declaration.FirBasicDeclarationChecker -import org.jetbrains.kotlin.fir.analysis.checkers.getContainingClassSymbol import org.jetbrains.kotlin.fir.analysis.diagnostics.DiagnosticReporter import org.jetbrains.kotlin.fir.analysis.diagnostics.jvm.FirJvmErrors import org.jetbrains.kotlin.fir.analysis.diagnostics.reportOn import org.jetbrains.kotlin.fir.declarations.* import org.jetbrains.kotlin.fir.declarations.utils.isCompanion import org.jetbrains.kotlin.fir.declarations.utils.isOverride +import org.jetbrains.kotlin.fir.declarations.utils.visibility import org.jetbrains.kotlin.fir.expressions.FirAnnotationCall import org.jetbrains.kotlin.fir.languageVersionSettings import org.jetbrains.kotlin.fir.references.FirResolvedNamedReference import org.jetbrains.kotlin.fir.symbols.SymbolInternals +import org.jetbrains.kotlin.fir.symbols.impl.FirClassLikeSymbol +import org.jetbrains.kotlin.fir.symbols.impl.FirRegularClassSymbol import org.jetbrains.kotlin.name.ClassId import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.name.StandardClassIds @@ -34,16 +38,87 @@ object FirJvmStaticChecker : FirBasicDeclarationChecker() { return } - checkOverrideCannotBeStatic(declaration, context, reporter) - checkStaticNotInProperObject(declaration, context, reporter) - } - - private fun checkStaticNotInProperObject(declaration: FirDeclaration, context: CheckerContext, reporter: DiagnosticReporter) { - if (declaration !is FirCallableDeclaration) { + if (declaration !is FirAnnotatedDeclaration) { return } - val containingClassSymbol = declaration.getContainingClassSymbol(context.session) ?: return + if (declaration !is FirMemberDeclaration) { + return + } + + val annotatedParts = declaration.getAnnotatedParts() + + checkOverrideCannotBeStatic(declaration, context, reporter, annotatedParts) + checkStaticNotInProperObject(context, reporter, annotatedParts) + checkStaticNonPublic(declaration, context, reporter, annotatedParts) + } + + private fun checkStaticNonPublic( + declaration: FirMemberDeclaration, + context: CheckerContext, + reporter: DiagnosticReporter, + annotatedParts: List, + ) { + val containingClassSymbol = context.getContainerAt(0) ?: return + var shouldCheck = false + + if (containingClassSymbol.classKind != ClassKind.OBJECT) { + shouldCheck = true + } else { + if (context.containerIsInterface(1)) { + shouldCheck = true + } + } + + if (!shouldCheck) { + return + } + + val minVisibility = declaration.getMinimumVisibility() + + if (minVisibility == Visibilities.Public) { + return + } + + annotatedParts.forEach { + reporter.reportOn(it.source, FirJvmErrors.JVM_STATIC_ON_NON_PUBLIC_MEMBER, context) + } + } + + private fun FirMemberDeclaration.getMinimumVisibility() = when (this) { + is FirProperty -> getMinimumVisibility() + else -> this.visibility + } + + private fun FirProperty.getMinimumVisibility(): Visibility { + var minVisibility = visibility + + getter?.let { + minVisibility = chooseMostSpecific(minVisibility, it.visibility) + } + + setter?.let { + minVisibility = chooseMostSpecific(minVisibility, it.visibility) + } + + return minVisibility + } + + private fun chooseMostSpecific(a: Visibility, b: Visibility): Visibility { + val difference = a.compareTo(b) ?: return a + return if (difference > 0) { + b + } else { + a + } + } + + private fun checkStaticNotInProperObject( + context: CheckerContext, + reporter: DiagnosticReporter, + annotatedParts: List, + ) { + val containingClassSymbol = context.getContainerAt(0) ?: return val supportJvmStaticInInterface = context.session.languageVersionSettings.supportsFeature(LanguageFeature.JvmStaticInInterface) val properDiagnostic = if (supportJvmStaticInInterface) { @@ -56,51 +131,73 @@ object FirJvmStaticChecker : FirBasicDeclarationChecker() { if (containingClassSymbol.classKind != ClassKind.OBJECT) { shouldReport = true - } else { - val declaringClassSymbol = containingClassSymbol.getContainingClassSymbol(context.session) ?: return - - if (declaringClassSymbol.classKind?.isInterface == true && !supportJvmStaticInInterface) { - shouldReport = true - } + } else if ( + containingClassSymbol is FirRegularClassSymbol && + containingClassSymbol.isCompanion && + context.containerIsInterface(1) && + !supportJvmStaticInInterface + ) { + shouldReport = true } if (!shouldReport) { return } - declaration.reportOnProperParts { - if (it.hasAnnotationNamedAs(StandardClassIds.JvmStatic)) { - reporter.reportOn(it.source, properDiagnostic, context) - } + annotatedParts.forEach { + reporter.reportOn(it.source, properDiagnostic, context) } } - private fun checkOverrideCannotBeStatic(declaration: FirDeclaration, context: CheckerContext, reporter: DiagnosticReporter) { - if (declaration !is FirCallableDeclaration) { + private fun checkOverrideCannotBeStatic( + declaration: FirMemberDeclaration, + context: CheckerContext, + reporter: DiagnosticReporter, + annotatedParts: List, + ) { + if (!declaration.isOverride || !context.containerIsNonCompanionObject(0)) { return } - if (!declaration.isOverride || !declaration.isContainerNonCompanionObject(context)) { - return - } - - declaration.reportOnProperParts { - if (it.hasAnnotationNamedAs(StandardClassIds.JvmStatic)) { - reporter.reportOn(it.source, FirJvmErrors.OVERRIDE_CANNOT_BE_STATIC, context) - } + annotatedParts.forEach { + reporter.reportOn(it.source, FirJvmErrors.OVERRIDE_CANNOT_BE_STATIC, context) } } - private fun FirAnnotatedDeclaration.reportOnProperParts(report: (FirAnnotatedDeclaration) -> Unit) { + private fun FirAnnotatedDeclaration.getAnnotatedParts(): List { + return getReportableParts().filter { + it.hasAnnotationNamedAs(StandardClassIds.JvmStatic) + } + } + + private fun FirAnnotatedDeclaration.getReportableParts(): List { + val parts = mutableListOf(this) + if (this is FirProperty) { - // the setter is visited separately - this.getter?.let(report) + fun takeIfNecessary(it: FirPropertyAccessor) { + if (it.visibility.compatibleAndLesser(this.visibility)) { + parts.add(it) + } + } + + this.getter?.let(::takeIfNecessary) + this.setter?.let(::takeIfNecessary) } - report(this) + + return parts } - private fun FirDeclaration.isContainerNonCompanionObject(context: CheckerContext): Boolean { - val containingClassSymbol = this.getContainingClassSymbol(context.session) ?: return false + private fun Visibility.compatibleAndLesser(other: Visibility): Boolean { + val difference = this.compareTo(other) ?: return false + return difference <= 0 + } + + private fun CheckerContext.containerIsInterface(outerLevel: Int): Boolean { + return this.getContainerAt(outerLevel)?.classKind?.isInterface == true + } + + private fun CheckerContext.containerIsNonCompanionObject(outerLevel: Int): Boolean { + val containingClassSymbol = this.getContainerAt(outerLevel) ?: return false @OptIn(SymbolInternals::class) val containingClass = containingClassSymbol.fir.safeAs() ?: return false @@ -108,6 +205,15 @@ object FirJvmStaticChecker : FirBasicDeclarationChecker() { return containingClass.classKind == ClassKind.OBJECT && !containingClass.isCompanion } + private fun CheckerContext.getContainerAt(outerLevel: Int): FirClassLikeSymbol<*>? { + val last = this.containingDeclarations.asReversed().getOrNull(outerLevel) + return if (last is FirClassLikeDeclaration) { + last.symbol + } else { + null + } + } + private fun FirAnnotatedDeclaration.hasAnnotationNamedAs(classId: ClassId): Boolean { return findAnnotation(classId.shortClassName) != null } diff --git a/compiler/fir/checkers/checkers.jvm/src/org/jetbrains/kotlin/fir/analysis/jvm/diagnostics/FirJvmDefaultErrorMessages.kt b/compiler/fir/checkers/checkers.jvm/src/org/jetbrains/kotlin/fir/analysis/jvm/diagnostics/FirJvmDefaultErrorMessages.kt index bf3cd7b7493..770414d810d 100644 --- a/compiler/fir/checkers/checkers.jvm/src/org/jetbrains/kotlin/fir/analysis/jvm/diagnostics/FirJvmDefaultErrorMessages.kt +++ b/compiler/fir/checkers/checkers.jvm/src/org/jetbrains/kotlin/fir/analysis/jvm/diagnostics/FirJvmDefaultErrorMessages.kt @@ -27,6 +27,7 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.jvm.FirJvmErrors.JVM_RECORD import org.jetbrains.kotlin.fir.analysis.diagnostics.jvm.FirJvmErrors.JVM_RECORD_WITHOUT_PRIMARY_CONSTRUCTOR_PARAMETERS import org.jetbrains.kotlin.fir.analysis.diagnostics.jvm.FirJvmErrors.JVM_STATIC_NOT_IN_OBJECT_OR_CLASS_COMPANION import org.jetbrains.kotlin.fir.analysis.diagnostics.jvm.FirJvmErrors.JVM_STATIC_NOT_IN_OBJECT_OR_COMPANION +import org.jetbrains.kotlin.fir.analysis.diagnostics.jvm.FirJvmErrors.JVM_STATIC_ON_NON_PUBLIC_MEMBER import org.jetbrains.kotlin.fir.analysis.diagnostics.jvm.FirJvmErrors.LOCAL_JVM_RECORD import org.jetbrains.kotlin.fir.analysis.diagnostics.jvm.FirJvmErrors.NON_DATA_CLASS_JVM_RECORD import org.jetbrains.kotlin.fir.analysis.diagnostics.jvm.FirJvmErrors.NON_FINAL_JVM_RECORD @@ -108,6 +109,10 @@ object FirJvmDefaultErrorMessages { JVM_STATIC_NOT_IN_OBJECT_OR_COMPANION, "Only members in named objects and companion objects can be annotated with '@JvmStatic'" ) + map.put( + JVM_STATIC_ON_NON_PUBLIC_MEMBER, + "Only public members in interface companion objects can be annotated with '@JvmStatic'" + ) map.put(INAPPLICABLE_JVM_NAME, "'@JvmName' annotation is not applicable to this declaration") map.put(ILLEGAL_JVM_NAME, "Illegal JVM name") diff --git a/compiler/testData/diagnostics/testsWithStdLib/annotations/jvmStatic/functions.fir.kt b/compiler/testData/diagnostics/testsWithStdLib/annotations/jvmStatic/functions.fir.kt index 84b915523f3..160949cca94 100644 --- a/compiler/testData/diagnostics/testsWithStdLib/annotations/jvmStatic/functions.fir.kt +++ b/compiler/testData/diagnostics/testsWithStdLib/annotations/jvmStatic/functions.fir.kt @@ -28,7 +28,7 @@ class A { interface B { companion object { - @JvmStatic fun a1() { + @JvmStatic fun a1() { } } diff --git a/compiler/testData/diagnostics/testsWithStdLib/annotations/jvmStatic/interfaceCompanion_LL12.fir.kt b/compiler/testData/diagnostics/testsWithStdLib/annotations/jvmStatic/interfaceCompanion_LL12.fir.kt index d2b5a849d1c..1c4193a01e6 100644 --- a/compiler/testData/diagnostics/testsWithStdLib/annotations/jvmStatic/interfaceCompanion_LL12.fir.kt +++ b/compiler/testData/diagnostics/testsWithStdLib/annotations/jvmStatic/interfaceCompanion_LL12.fir.kt @@ -2,60 +2,60 @@ // !DIAGNOSTICS: -UNUSED_VARIABLE interface B { companion object { - @JvmStatic fun a1() { + @JvmStatic fun a1() { } - @JvmStatic private fun a2() { + @JvmStatic private fun a2() { } - @JvmStatic protected fun a3() { + @JvmStatic protected fun a3() { } - @JvmStatic internal fun a4() { + @JvmStatic internal fun a4() { } - @JvmStatic external fun a5() + @JvmStatic external fun a5() - @JvmStatic - var foo = 1 + @JvmStatic + var foo = 1 - @JvmStatic - var foo1 = 1 + @JvmStatic + var foo1 = 1 protected set - @JvmStatic - var foo2 = 1 + @JvmStatic + var foo2 = 1 private set - @JvmStatic - private var foo3 = 1 + @JvmStatic + private var foo3 = 1 - @JvmStatic - protected var foo4 = 1 + @JvmStatic + protected var foo4 = 1 - @JvmStatic - protected var foo5 = 1 + @JvmStatic + protected var foo5 = 1 - @JvmStatic - val foo6 = 1 + @JvmStatic + val foo6 = 1 val foo7 = 1 - @JvmStatic get + @JvmStatic get private var foo8 = 1 @JvmStatic public set public var foo9 = 1 - @JvmStatic private set + @JvmStatic private set - @JvmStatic - val foo10: Int external get + @JvmStatic + val foo10: Int external get - val foo11: Int @JvmStatic external get + val foo11: Int @JvmStatic external get } -} \ No newline at end of file +} diff --git a/compiler/testData/diagnostics/testsWithStdLib/annotations/jvmStatic/interfaceCompanion_LL13_16.fir.kt b/compiler/testData/diagnostics/testsWithStdLib/annotations/jvmStatic/interfaceCompanion_LL13_16.fir.kt index 77eac3f4956..640f0bad7eb 100644 --- a/compiler/testData/diagnostics/testsWithStdLib/annotations/jvmStatic/interfaceCompanion_LL13_16.fir.kt +++ b/compiler/testData/diagnostics/testsWithStdLib/annotations/jvmStatic/interfaceCompanion_LL13_16.fir.kt @@ -8,15 +8,15 @@ interface B { } - @JvmStatic private fun a2() { + @JvmStatic private fun a2() { } - @JvmStatic protected fun a3() { + @JvmStatic protected fun a3() { } - @JvmStatic internal fun a4() { + @JvmStatic internal fun a4() { } @@ -25,22 +25,22 @@ interface B { @JvmStatic var foo = 1 - @JvmStatic - var foo1 = 1 + @JvmStatic + var foo1 = 1 protected set - @JvmStatic - var foo2 = 1 + @JvmStatic + var foo2 = 1 private set - @JvmStatic - private var foo3 = 1 + @JvmStatic + private var foo3 = 1 - @JvmStatic - protected var foo4 = 1 + @JvmStatic + protected var foo4 = 1 - @JvmStatic - protected var foo5 = 1 + @JvmStatic + protected var foo5 = 1 @JvmStatic val foo6 = 1 @@ -52,7 +52,7 @@ interface B { @JvmStatic public set public var foo9 = 1 - @JvmStatic private set + @JvmStatic private set @JvmStatic val foo10: Int external get @@ -60,4 +60,4 @@ interface B { val foo11: Int @JvmStatic external get } -} \ No newline at end of file +} diff --git a/compiler/testData/diagnostics/testsWithStdLib/annotations/jvmStatic/interfaceCompanion_LL13_18.fir.kt b/compiler/testData/diagnostics/testsWithStdLib/annotations/jvmStatic/interfaceCompanion_LL13_18.fir.kt index ac24d902e02..2e40383872a 100644 --- a/compiler/testData/diagnostics/testsWithStdLib/annotations/jvmStatic/interfaceCompanion_LL13_18.fir.kt +++ b/compiler/testData/diagnostics/testsWithStdLib/annotations/jvmStatic/interfaceCompanion_LL13_18.fir.kt @@ -7,15 +7,15 @@ interface B { } - @JvmStatic private fun a2() { + @JvmStatic private fun a2() { } - @JvmStatic protected fun a3() { + @JvmStatic protected fun a3() { } - @JvmStatic internal fun a4() { + @JvmStatic internal fun a4() { } @@ -24,22 +24,22 @@ interface B { @JvmStatic var foo = 1 - @JvmStatic - var foo1 = 1 + @JvmStatic + var foo1 = 1 protected set - @JvmStatic - var foo2 = 1 + @JvmStatic + var foo2 = 1 private set - @JvmStatic - private var foo3 = 1 + @JvmStatic + private var foo3 = 1 - @JvmStatic - protected var foo4 = 1 + @JvmStatic + protected var foo4 = 1 - @JvmStatic - protected var foo5 = 1 + @JvmStatic + protected var foo5 = 1 @JvmStatic val foo6 = 1 @@ -51,7 +51,7 @@ interface B { @JvmStatic public set public var foo9 = 1 - @JvmStatic private set + @JvmStatic private set @JvmStatic val foo10: Int external get @@ -59,4 +59,4 @@ interface B { val foo11: Int @JvmStatic external get } -} \ No newline at end of file +} diff --git a/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDataClassConverters.kt b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDataClassConverters.kt index 03c660c7e35..21e6a8b913b 100644 --- a/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDataClassConverters.kt +++ b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDataClassConverters.kt @@ -3682,6 +3682,12 @@ internal val KT_DIAGNOSTIC_CONVERTER = KtDiagnosticConverterBuilder.buildConvert token, ) } + add(FirJvmErrors.JVM_STATIC_ON_NON_PUBLIC_MEMBER) { firDiagnostic -> + JvmStaticOnNonPublicMemberImpl( + firDiagnostic as FirPsiDiagnostic, + token, + ) + } add(FirJvmErrors.INAPPLICABLE_JVM_NAME) { firDiagnostic -> InapplicableJvmNameImpl( firDiagnostic as FirPsiDiagnostic, diff --git a/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDiagnostics.kt b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDiagnostics.kt index 21a5cc8836c..04ec7fcf8ec 100644 --- a/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDiagnostics.kt +++ b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDiagnostics.kt @@ -2562,6 +2562,10 @@ sealed class KtFirDiagnostic : KtDiagnosticWithPsi { override val diagnosticClass get() = JvmStaticNotInObjectOrCompanion::class } + abstract class JvmStaticOnNonPublicMember : KtFirDiagnostic() { + override val diagnosticClass get() = JvmStaticOnNonPublicMember::class + } + abstract class InapplicableJvmName : KtFirDiagnostic() { override val diagnosticClass get() = InapplicableJvmName::class } diff --git a/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDiagnosticsImpl.kt b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDiagnosticsImpl.kt index 4fb65631ae5..47cdab997d1 100644 --- a/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDiagnosticsImpl.kt +++ b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDiagnosticsImpl.kt @@ -4159,6 +4159,13 @@ internal class JvmStaticNotInObjectOrCompanionImpl( override val firDiagnostic: FirPsiDiagnostic by weakRef(firDiagnostic) } +internal class JvmStaticOnNonPublicMemberImpl( + firDiagnostic: FirPsiDiagnostic, + override val token: ValidityToken, +) : KtFirDiagnostic.JvmStaticOnNonPublicMember(), KtAbstractFirDiagnostic { + override val firDiagnostic: FirPsiDiagnostic by weakRef(firDiagnostic) +} + internal class InapplicableJvmNameImpl( firDiagnostic: FirPsiDiagnostic, override val token: ValidityToken,