[FIR] Implement CONFLICTING_IMPORT diagnostic
This commit is contained in:
+4
@@ -951,6 +951,10 @@ object DIAGNOSTICS_LIST : DiagnosticList("FirErrors") {
|
||||
val CANNOT_BE_IMPORTED by error<KtSimpleNameExpression>(PositioningStrategy.IMPORT_LAST_NAME) {
|
||||
parameter<Name>("name")
|
||||
}
|
||||
|
||||
val CONFLICTING_IMPORT by error<KtImportDirective>(PositioningStrategy.IMPORT_LAST_NAME) {
|
||||
parameter<Name>("name")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -48,6 +48,7 @@ import org.jetbrains.kotlin.psi.KtElement
|
||||
import org.jetbrains.kotlin.psi.KtExpression
|
||||
import org.jetbrains.kotlin.psi.KtFunction
|
||||
import org.jetbrains.kotlin.psi.KtIfExpression
|
||||
import org.jetbrains.kotlin.psi.KtImportDirective
|
||||
import org.jetbrains.kotlin.psi.KtModifierListOwner
|
||||
import org.jetbrains.kotlin.psi.KtNamedDeclaration
|
||||
import org.jetbrains.kotlin.psi.KtObjectDeclaration
|
||||
@@ -522,5 +523,6 @@ object FirErrors {
|
||||
val CANNOT_ALL_UNDER_IMPORT_FROM_SINGLETON by error1<KtSimpleNameExpression, Name>(SourceElementPositioningStrategies.IMPORT_LAST_NAME)
|
||||
val PACKAGE_CANNOT_BE_IMPORTED by error0<KtSimpleNameExpression>(SourceElementPositioningStrategies.IMPORT_LAST_NAME)
|
||||
val CANNOT_BE_IMPORTED by error1<KtSimpleNameExpression, Name>(SourceElementPositioningStrategies.IMPORT_LAST_NAME)
|
||||
val CONFLICTING_IMPORT by error1<KtImportDirective, Name>(SourceElementPositioningStrategies.IMPORT_LAST_NAME)
|
||||
|
||||
}
|
||||
|
||||
+52
-5
@@ -6,11 +6,14 @@
|
||||
package org.jetbrains.kotlin.fir.analysis.checkers.declaration
|
||||
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.followAllAlias
|
||||
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.resolve.symbolProvider
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirRegularClassSymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirTypeAliasSymbol
|
||||
import org.jetbrains.kotlin.name.ClassId
|
||||
|
||||
object FirImportsChecker : FirFileChecker() {
|
||||
@@ -24,14 +27,14 @@ object FirImportsChecker : FirFileChecker() {
|
||||
checkCanBeImported(import, context, reporter)
|
||||
}
|
||||
}
|
||||
checkConflictingImports(declaration.imports, context, reporter)
|
||||
}
|
||||
|
||||
private fun checkAllUnderFromEnumEntry(import: FirImport, context: CheckerContext, reporter: DiagnosticReporter) {
|
||||
val fqName = import.importedFqName ?: return
|
||||
if (fqName.isRoot || fqName.parent().isRoot) return
|
||||
val classId = ClassId.topLevel(fqName.parent())
|
||||
val classSymbol = context.session.symbolProvider.getClassLikeSymbolByFqName(classId) ?: return
|
||||
val classFir = classSymbol.fir as? FirRegularClass ?: return
|
||||
val classFir = classId.resolveToClass(context) ?: return
|
||||
if (classFir.isEnumClass && classFir.collectEnumEntries().any { it.name == fqName.shortName() }) {
|
||||
reporter.reportOn(import.source, FirErrors.CANNOT_ALL_UNDER_IMPORT_FROM_SINGLETON, classFir.name, context)
|
||||
}
|
||||
@@ -42,12 +45,12 @@ object FirImportsChecker : FirFileChecker() {
|
||||
val importedName = importedFqName.shortName()
|
||||
//empty name come from LT in some erroneous cases
|
||||
if (importedName.isSpecial || importedName.identifier.isEmpty()) return
|
||||
|
||||
val classId = (import as? FirResolvedImport)?.resolvedClassId
|
||||
if (classId != null) {
|
||||
val classSymbol = context.session.symbolProvider.getClassLikeSymbolByFqName(classId) ?: return
|
||||
val classFir = classSymbol.fir as? FirRegularClass ?: return
|
||||
val classFir = classId.resolveToClass(context) ?: return
|
||||
if (classFir.classKind.isSingleton) return
|
||||
|
||||
|
||||
val illegalImport = classFir.declarations.any {
|
||||
it is FirSimpleFunction && !it.isStatic && it.name == importedName ||
|
||||
it is FirProperty && it.name == importedName
|
||||
@@ -65,4 +68,48 @@ object FirImportsChecker : FirFileChecker() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun checkConflictingImports(imports: List<FirImport>, context: CheckerContext, reporter: DiagnosticReporter) {
|
||||
val interestingImports = imports
|
||||
.filterIsInstance<FirResolvedImport>()
|
||||
.filter { import ->
|
||||
!import.isAllUnder
|
||||
&& import.importedName?.identifierOrNullIfSpecial?.isNotEmpty() == true
|
||||
&& import.resolvesToClass(context)
|
||||
}
|
||||
interestingImports
|
||||
.groupBy { it.aliasName ?: it.importedName!! }
|
||||
.values
|
||||
.filter { it.size > 1 }
|
||||
.forEach { conflicts ->
|
||||
conflicts.forEach {
|
||||
reporter.reportOn(it.source, FirErrors.CONFLICTING_IMPORT, it.importedName!!, context)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun FirResolvedImport.resolvesToClass(context: CheckerContext): Boolean {
|
||||
if (resolvedClassId != null) {
|
||||
if (isAllUnder) return true
|
||||
val parentClass = resolvedClassId!!
|
||||
val relativeClassName = this.relativeClassName ?: return false
|
||||
val importedName = this.importedName ?: return false
|
||||
val innerClassId = ClassId(parentClass.packageFqName, relativeClassName.child(importedName), false)
|
||||
return innerClassId.resolveToClass(context) != null
|
||||
} else {
|
||||
val importedFqName = importedFqName ?: return false
|
||||
if (importedFqName.isRoot) return false
|
||||
val importedClassId = ClassId.topLevel(importedFqName)
|
||||
return importedClassId.resolveToClass(context) != null
|
||||
}
|
||||
}
|
||||
|
||||
private fun ClassId.resolveToClass(context: CheckerContext): FirRegularClass? {
|
||||
val classSymbol = context.session.symbolProvider.getClassLikeSymbolByFqName(this) ?: return null
|
||||
return when (classSymbol) {
|
||||
is FirRegularClassSymbol -> classSymbol.fir
|
||||
is FirTypeAliasSymbol -> classSymbol.fir.followAllAlias(context.session) as? FirRegularClass
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
}
|
||||
+6
@@ -70,6 +70,7 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.COMPONENT_FUNCTIO
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.COMPONENT_FUNCTION_ON_NULLABLE
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.COMPONENT_FUNCTION_RETURN_TYPE_MISMATCH
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.CONDITION_TYPE_MISMATCH
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.CONFLICTING_IMPORT
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.CONFLICTING_OVERLOADS
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.CONFLICTING_PROJECTION
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.CONFLICTING_PROJECTION_IN_TYPEALIAS_EXPANSION
|
||||
@@ -1174,6 +1175,11 @@ class FirDefaultErrorMessages : DefaultErrorMessages.Extension {
|
||||
"Cannot import ''{0}'', functions and properties can be imported only from packages or objects",
|
||||
TO_STRING
|
||||
)
|
||||
map.put(
|
||||
CONFLICTING_IMPORT,
|
||||
"Conflicting import, imported name ''{0}'' is ambiguous",
|
||||
TO_STRING
|
||||
)
|
||||
|
||||
// Extended checkers group
|
||||
map.put(REDUNDANT_VISIBILITY_MODIFIER, "Redundant visibility modifier")
|
||||
|
||||
@@ -6,10 +6,7 @@
|
||||
package org.jetbrains.kotlin.diagnostics
|
||||
|
||||
import com.intellij.openapi.util.TextRange
|
||||
import com.intellij.psi.PsiComment
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.PsiNameIdentifierOwner
|
||||
import com.intellij.psi.PsiWhiteSpace
|
||||
import com.intellij.psi.*
|
||||
import com.intellij.psi.impl.source.tree.LeafPsiElement
|
||||
import com.intellij.psi.tree.TokenSet
|
||||
import org.jetbrains.kotlin.KtNodeTypes
|
||||
@@ -891,6 +888,12 @@ object PositioningStrategies {
|
||||
}
|
||||
|
||||
val IMPORT_LAST_NAME: PositioningStrategy<PsiElement> = object : PositioningStrategy<PsiElement>() {
|
||||
|
||||
override fun isValid(element: PsiElement): Boolean {
|
||||
if (element is PsiErrorElement) return false
|
||||
return !element.children.any { !isValid(it) }
|
||||
}
|
||||
|
||||
override fun mark(element: PsiElement): List<TextRange> {
|
||||
if (element is KtImportDirective) {
|
||||
val importedReference = element.importedReference
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
//FILE:a.kt
|
||||
package a
|
||||
|
||||
import b.O
|
||||
import c.O
|
||||
|
||||
//FILE:b.kt
|
||||
package b
|
||||
|
||||
object O {}
|
||||
|
||||
//FILE:c.kt
|
||||
package c
|
||||
|
||||
object O {}
|
||||
@@ -1,3 +1,4 @@
|
||||
// FIR_IDENTICAL
|
||||
//FILE:a.kt
|
||||
package a
|
||||
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
// FILE: a.kt
|
||||
package a
|
||||
|
||||
class X
|
||||
|
||||
// FILE: b.kt
|
||||
package b
|
||||
|
||||
class X
|
||||
|
||||
// FILE: c.kt
|
||||
package c
|
||||
|
||||
import a.X
|
||||
import b.X
|
||||
|
||||
class Y : <!UNRESOLVED_REFERENCE!>X<!>
|
||||
@@ -1,3 +1,4 @@
|
||||
// FIR_IDENTICAL
|
||||
// FILE: a.kt
|
||||
package a
|
||||
|
||||
|
||||
@@ -1,48 +0,0 @@
|
||||
// !DIAGNOSTICS: -UNUSED_PARAMETER
|
||||
// FILE: 1.kt
|
||||
package a
|
||||
|
||||
class someFun() {}
|
||||
fun someFun(i: Int) {}
|
||||
|
||||
class someVal() {}
|
||||
val Int.someVal: Int get() = 3
|
||||
|
||||
class A
|
||||
|
||||
class B
|
||||
|
||||
// FILE: 2.kt
|
||||
package b
|
||||
|
||||
class someFun
|
||||
class someVal
|
||||
class someAll
|
||||
|
||||
fun A() {}
|
||||
|
||||
class B
|
||||
|
||||
|
||||
// FILE: 3.kt
|
||||
import a.someFun
|
||||
import b.someFun
|
||||
|
||||
import a.someVal
|
||||
import b.someVal
|
||||
|
||||
import a.A
|
||||
import b.A
|
||||
|
||||
// FILE: 4.kt
|
||||
import b.*
|
||||
import a.B
|
||||
|
||||
// FILE: 5.kt
|
||||
package b
|
||||
|
||||
import a.B
|
||||
|
||||
// FILE: 6.kt
|
||||
import a.B
|
||||
import b.B
|
||||
@@ -1,3 +1,4 @@
|
||||
// FIR_IDENTICAL
|
||||
// !DIAGNOSTICS: -UNUSED_PARAMETER
|
||||
// FILE: 1.kt
|
||||
package a
|
||||
|
||||
@@ -1,31 +0,0 @@
|
||||
// !DIAGNOSTICS: -UNUSED_PARAMETER
|
||||
// FILE: a.kt
|
||||
package a
|
||||
|
||||
class A {
|
||||
class B
|
||||
}
|
||||
|
||||
// FILE: b.kt
|
||||
package a
|
||||
|
||||
class D {
|
||||
class B
|
||||
}
|
||||
|
||||
// FILE: c.kt
|
||||
import a.A.B
|
||||
import a.D.B
|
||||
|
||||
fun test(b: <!UNRESOLVED_REFERENCE!>B<!>) {
|
||||
<!UNRESOLVED_REFERENCE!>B<!>()
|
||||
}
|
||||
|
||||
// FILE: d.kt
|
||||
import a.A.*
|
||||
import a.D.*
|
||||
|
||||
// todo ambiguvity here
|
||||
fun test2(b: <!UNRESOLVED_REFERENCE!>B<!>) {
|
||||
<!UNRESOLVED_REFERENCE!>B<!>()
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
// FIR_IDENTICAL
|
||||
// !DIAGNOSTICS: -UNUSED_PARAMETER
|
||||
// FILE: a.kt
|
||||
package a
|
||||
|
||||
@@ -1,164 +0,0 @@
|
||||
// !DIAGNOSTICS: -UNUSED_PARAMETER
|
||||
// FILE: main1.kt
|
||||
package abc1
|
||||
|
||||
@Throws(Exception::class)
|
||||
fun foo1() {}
|
||||
|
||||
@kotlin.Throws(Exception::class)
|
||||
fun foo2() {}
|
||||
|
||||
@kotlin.jvm.Throws(Exception::class)
|
||||
fun foo3() {}
|
||||
|
||||
fun foo5(x: Throws) {}
|
||||
fun foo6(x: kotlin.Throws) {}
|
||||
fun foo7(x: kotlin.jvm.Throws) {}
|
||||
|
||||
// FILE: main2.kt
|
||||
package abc2
|
||||
|
||||
import kotlin.jvm.Throws
|
||||
|
||||
@Throws(Exception::class)
|
||||
fun foo1() {}
|
||||
|
||||
@kotlin.Throws(Exception::class)
|
||||
fun foo2() {}
|
||||
|
||||
@kotlin.jvm.Throws(Exception::class)
|
||||
fun foo3() {}
|
||||
|
||||
fun foo5(x: Throws) {}
|
||||
fun foo6(x: kotlin.Throws) {}
|
||||
fun foo7(x: kotlin.jvm.Throws) {}
|
||||
|
||||
// FILE: main3.kt
|
||||
package abc3
|
||||
|
||||
import kotlin.Throws
|
||||
|
||||
@Throws(Exception::class)
|
||||
fun foo1() {}
|
||||
|
||||
@kotlin.Throws(Exception::class)
|
||||
fun foo2() {}
|
||||
|
||||
@kotlin.jvm.Throws(Exception::class)
|
||||
fun foo3() {}
|
||||
|
||||
fun foo5(x: Throws) {}
|
||||
fun foo6(x: kotlin.Throws) {}
|
||||
fun foo7(x: kotlin.jvm.Throws) {}
|
||||
|
||||
// FILE: main4.kt
|
||||
package abc4
|
||||
|
||||
import kotlin.Throws
|
||||
import kotlin.jvm.Throws
|
||||
|
||||
@Throws(Exception::class)
|
||||
fun foo1() {}
|
||||
|
||||
@kotlin.Throws(Exception::class)
|
||||
fun foo2() {}
|
||||
|
||||
@kotlin.jvm.Throws(Exception::class)
|
||||
fun foo3() {}
|
||||
|
||||
fun foo5(x: Throws) {}
|
||||
fun foo6(x: kotlin.Throws) {}
|
||||
fun foo7(x: kotlin.jvm.Throws) {}
|
||||
|
||||
// FILE: main5.kt
|
||||
package abc5
|
||||
|
||||
import kotlin.jvm.*
|
||||
|
||||
@Throws(Exception::class)
|
||||
fun foo1() {}
|
||||
|
||||
@kotlin.Throws(Exception::class)
|
||||
fun foo2() {}
|
||||
|
||||
@kotlin.jvm.Throws(Exception::class)
|
||||
fun foo3() {}
|
||||
|
||||
fun foo5(x: Throws) {}
|
||||
fun foo6(x: kotlin.Throws) {}
|
||||
fun foo7(x: kotlin.jvm.Throws) {}
|
||||
|
||||
// FILE: main6.kt
|
||||
package abc6
|
||||
|
||||
import kotlin.*
|
||||
|
||||
@Throws(Exception::class)
|
||||
fun foo1() {}
|
||||
|
||||
@kotlin.Throws(Exception::class)
|
||||
fun foo2() {}
|
||||
|
||||
@kotlin.jvm.Throws(Exception::class)
|
||||
fun foo3() {}
|
||||
|
||||
fun foo5(x: Throws) {}
|
||||
fun foo6(x: kotlin.Throws) {}
|
||||
fun foo7(x: kotlin.jvm.Throws) {}
|
||||
|
||||
// FILE: main7.kt
|
||||
package abc7
|
||||
|
||||
import kotlin.*
|
||||
import kotlin.jvm.*
|
||||
|
||||
@Throws(Exception::class)
|
||||
fun foo1() {}
|
||||
|
||||
@kotlin.Throws(Exception::class)
|
||||
fun foo2() {}
|
||||
|
||||
@kotlin.jvm.Throws(Exception::class)
|
||||
fun foo3() {}
|
||||
|
||||
fun foo5(x: Throws) {}
|
||||
fun foo6(x: kotlin.Throws) {}
|
||||
fun foo7(x: kotlin.jvm.Throws) {}
|
||||
|
||||
// FILE: main8.kt
|
||||
package abc8
|
||||
|
||||
import kotlin.*
|
||||
import kotlin.jvm.Throws
|
||||
|
||||
@Throws(Exception::class)
|
||||
fun foo1() {}
|
||||
|
||||
@kotlin.Throws(Exception::class)
|
||||
fun foo2() {}
|
||||
|
||||
@kotlin.jvm.Throws(Exception::class)
|
||||
fun foo3() {}
|
||||
|
||||
fun foo5(x: Throws) {}
|
||||
fun foo6(x: kotlin.Throws) {}
|
||||
fun foo7(x: kotlin.jvm.Throws) {}
|
||||
|
||||
// FILE: main9.kt
|
||||
package abc9
|
||||
|
||||
import kotlin.jvm.*
|
||||
import kotlin.Throws
|
||||
|
||||
@Throws(Exception::class)
|
||||
fun foo1() {}
|
||||
|
||||
@kotlin.Throws(Exception::class)
|
||||
fun foo2() {}
|
||||
|
||||
@kotlin.jvm.Throws(Exception::class)
|
||||
fun foo3() {}
|
||||
|
||||
fun foo5(x: Throws) {}
|
||||
fun foo6(x: kotlin.Throws) {}
|
||||
fun foo7(x: kotlin.jvm.Throws) {}
|
||||
@@ -1,3 +1,4 @@
|
||||
// FIR_IDENTICAL
|
||||
// !DIAGNOSTICS: -UNUSED_PARAMETER
|
||||
// FILE: main1.kt
|
||||
package abc1
|
||||
|
||||
+8
@@ -34,6 +34,7 @@ import org.jetbrains.kotlin.psi.KtElement
|
||||
import org.jetbrains.kotlin.psi.KtExpression
|
||||
import org.jetbrains.kotlin.psi.KtFunction
|
||||
import org.jetbrains.kotlin.psi.KtIfExpression
|
||||
import org.jetbrains.kotlin.psi.KtImportDirective
|
||||
import org.jetbrains.kotlin.psi.KtModifierListOwner
|
||||
import org.jetbrains.kotlin.psi.KtNamedDeclaration
|
||||
import org.jetbrains.kotlin.psi.KtObjectDeclaration
|
||||
@@ -2593,6 +2594,13 @@ internal val KT_DIAGNOSTIC_CONVERTER = KtDiagnosticConverterBuilder.buildConvert
|
||||
token,
|
||||
)
|
||||
}
|
||||
add(FirErrors.CONFLICTING_IMPORT) { firDiagnostic ->
|
||||
ConflictingImportImpl(
|
||||
firDiagnostic.a,
|
||||
firDiagnostic as FirPsiDiagnostic<*>,
|
||||
token,
|
||||
)
|
||||
}
|
||||
add(FirJvmErrors.CONFLICTING_JVM_DECLARATIONS) { firDiagnostic ->
|
||||
ConflictingJvmDeclarationsImpl(
|
||||
firDiagnostic as FirPsiDiagnostic<*>,
|
||||
|
||||
+6
@@ -40,6 +40,7 @@ import org.jetbrains.kotlin.psi.KtElement
|
||||
import org.jetbrains.kotlin.psi.KtExpression
|
||||
import org.jetbrains.kotlin.psi.KtFunction
|
||||
import org.jetbrains.kotlin.psi.KtIfExpression
|
||||
import org.jetbrains.kotlin.psi.KtImportDirective
|
||||
import org.jetbrains.kotlin.psi.KtModifierListOwner
|
||||
import org.jetbrains.kotlin.psi.KtNamedDeclaration
|
||||
import org.jetbrains.kotlin.psi.KtObjectDeclaration
|
||||
@@ -1819,6 +1820,11 @@ sealed class KtFirDiagnostic<PSI: PsiElement> : KtDiagnosticWithPsi<PSI> {
|
||||
abstract val name: Name
|
||||
}
|
||||
|
||||
abstract class ConflictingImport : KtFirDiagnostic<KtImportDirective>() {
|
||||
override val diagnosticClass get() = ConflictingImport::class
|
||||
abstract val name: Name
|
||||
}
|
||||
|
||||
abstract class ConflictingJvmDeclarations : KtFirDiagnostic<PsiElement>() {
|
||||
override val diagnosticClass get() = ConflictingJvmDeclarations::class
|
||||
}
|
||||
|
||||
+9
@@ -42,6 +42,7 @@ import org.jetbrains.kotlin.psi.KtElement
|
||||
import org.jetbrains.kotlin.psi.KtExpression
|
||||
import org.jetbrains.kotlin.psi.KtFunction
|
||||
import org.jetbrains.kotlin.psi.KtIfExpression
|
||||
import org.jetbrains.kotlin.psi.KtImportDirective
|
||||
import org.jetbrains.kotlin.psi.KtModifierListOwner
|
||||
import org.jetbrains.kotlin.psi.KtNamedDeclaration
|
||||
import org.jetbrains.kotlin.psi.KtObjectDeclaration
|
||||
@@ -2951,6 +2952,14 @@ internal class CannotBeImportedImpl(
|
||||
override val firDiagnostic: FirPsiDiagnostic<*> by weakRef(firDiagnostic)
|
||||
}
|
||||
|
||||
internal class ConflictingImportImpl(
|
||||
override val name: Name,
|
||||
firDiagnostic: FirPsiDiagnostic<*>,
|
||||
override val token: ValidityToken,
|
||||
) : KtFirDiagnostic.ConflictingImport(), KtAbstractFirDiagnostic<KtImportDirective> {
|
||||
override val firDiagnostic: FirPsiDiagnostic<*> by weakRef(firDiagnostic)
|
||||
}
|
||||
|
||||
internal class ConflictingJvmDeclarationsImpl(
|
||||
firDiagnostic: FirPsiDiagnostic<*>,
|
||||
override val token: ValidityToken,
|
||||
|
||||
Reference in New Issue
Block a user