K2: add JVM checker for NO_REFLECTION_IN_CLASS_PATH warning
It's based on the existing K1 checker `JvmReflectionAPICallChecker`. #KT-60587 Fixed
This commit is contained in:
committed by
Space Team
parent
bd46bb02c1
commit
7fc3e60854
+6
@@ -5055,6 +5055,12 @@ internal val KT_DIAGNOSTIC_CONVERTER = KtDiagnosticConverterBuilder.buildConvert
|
|||||||
token,
|
token,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
add(FirJvmErrors.NO_REFLECTION_IN_CLASS_PATH) { firDiagnostic ->
|
||||||
|
NoReflectionInClassPathImpl(
|
||||||
|
firDiagnostic as KtPsiDiagnostic,
|
||||||
|
token,
|
||||||
|
)
|
||||||
|
}
|
||||||
add(FirJsErrors.IMPLEMENTING_FUNCTION_INTERFACE) { firDiagnostic ->
|
add(FirJsErrors.IMPLEMENTING_FUNCTION_INTERFACE) { firDiagnostic ->
|
||||||
ImplementingFunctionInterfaceImpl(
|
ImplementingFunctionInterfaceImpl(
|
||||||
firDiagnostic as KtPsiDiagnostic,
|
firDiagnostic as KtPsiDiagnostic,
|
||||||
|
|||||||
+4
@@ -3523,6 +3523,10 @@ sealed interface KtFirDiagnostic<PSI : PsiElement> : KtDiagnosticWithPsi<PSI> {
|
|||||||
override val diagnosticClass get() = JavaSamInterfaceConstructorReference::class
|
override val diagnosticClass get() = JavaSamInterfaceConstructorReference::class
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface NoReflectionInClassPath : KtFirDiagnostic<PsiElement> {
|
||||||
|
override val diagnosticClass get() = NoReflectionInClassPath::class
|
||||||
|
}
|
||||||
|
|
||||||
interface ImplementingFunctionInterface : KtFirDiagnostic<KtClassOrObject> {
|
interface ImplementingFunctionInterface : KtFirDiagnostic<KtClassOrObject> {
|
||||||
override val diagnosticClass get() = ImplementingFunctionInterface::class
|
override val diagnosticClass get() = ImplementingFunctionInterface::class
|
||||||
}
|
}
|
||||||
|
|||||||
+5
@@ -4257,6 +4257,11 @@ internal class JavaSamInterfaceConstructorReferenceImpl(
|
|||||||
token: KtLifetimeToken,
|
token: KtLifetimeToken,
|
||||||
) : KtAbstractFirDiagnostic<PsiElement>(firDiagnostic, token), KtFirDiagnostic.JavaSamInterfaceConstructorReference
|
) : KtAbstractFirDiagnostic<PsiElement>(firDiagnostic, token), KtFirDiagnostic.JavaSamInterfaceConstructorReference
|
||||||
|
|
||||||
|
internal class NoReflectionInClassPathImpl(
|
||||||
|
firDiagnostic: KtPsiDiagnostic,
|
||||||
|
token: KtLifetimeToken,
|
||||||
|
) : KtAbstractFirDiagnostic<PsiElement>(firDiagnostic, token), KtFirDiagnostic.NoReflectionInClassPath
|
||||||
|
|
||||||
internal class ImplementingFunctionInterfaceImpl(
|
internal class ImplementingFunctionInterfaceImpl(
|
||||||
firDiagnostic: KtPsiDiagnostic,
|
firDiagnostic: KtPsiDiagnostic,
|
||||||
token: KtLifetimeToken,
|
token: KtLifetimeToken,
|
||||||
|
|||||||
+1
@@ -173,5 +173,6 @@ object JVM_DIAGNOSTICS_LIST : DiagnosticList("FirJvmErrors") {
|
|||||||
PositioningStrategy.SPREAD_OPERATOR
|
PositioningStrategy.SPREAD_OPERATOR
|
||||||
)
|
)
|
||||||
val JAVA_SAM_INTERFACE_CONSTRUCTOR_REFERENCE by error<PsiElement>()
|
val JAVA_SAM_INTERFACE_CONSTRUCTOR_REFERENCE by error<PsiElement>()
|
||||||
|
val NO_REFLECTION_IN_CLASS_PATH by warning<PsiElement>()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+1
@@ -119,6 +119,7 @@ object FirJvmErrors {
|
|||||||
val CONCURRENT_HASH_MAP_CONTAINS_OPERATOR by deprecationError0<PsiElement>(ProhibitConcurrentHashMapContains)
|
val CONCURRENT_HASH_MAP_CONTAINS_OPERATOR by deprecationError0<PsiElement>(ProhibitConcurrentHashMapContains)
|
||||||
val SPREAD_ON_SIGNATURE_POLYMORPHIC_CALL by deprecationError0<PsiElement>(ProhibitSpreadOnSignaturePolymorphicCall, SourceElementPositioningStrategies.SPREAD_OPERATOR)
|
val SPREAD_ON_SIGNATURE_POLYMORPHIC_CALL by deprecationError0<PsiElement>(ProhibitSpreadOnSignaturePolymorphicCall, SourceElementPositioningStrategies.SPREAD_OPERATOR)
|
||||||
val JAVA_SAM_INTERFACE_CONSTRUCTOR_REFERENCE by error0<PsiElement>()
|
val JAVA_SAM_INTERFACE_CONSTRUCTOR_REFERENCE by error0<PsiElement>()
|
||||||
|
val NO_REFLECTION_IN_CLASS_PATH by warning0<PsiElement>()
|
||||||
|
|
||||||
init {
|
init {
|
||||||
RootDiagnosticRendererFactory.registerFactory(FirJvmErrorsDefaultMessages)
|
RootDiagnosticRendererFactory.registerFactory(FirJvmErrorsDefaultMessages)
|
||||||
|
|||||||
+6
@@ -52,6 +52,7 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.jvm.FirJvmErrors.LOCAL_JVM_
|
|||||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.jvm.FirJvmErrors.NON_DATA_CLASS_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
|
import org.jetbrains.kotlin.fir.analysis.diagnostics.jvm.FirJvmErrors.NON_FINAL_JVM_RECORD
|
||||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.jvm.FirJvmErrors.NON_SOURCE_REPEATED_ANNOTATION
|
import org.jetbrains.kotlin.fir.analysis.diagnostics.jvm.FirJvmErrors.NON_SOURCE_REPEATED_ANNOTATION
|
||||||
|
import org.jetbrains.kotlin.fir.analysis.diagnostics.jvm.FirJvmErrors.NO_REFLECTION_IN_CLASS_PATH
|
||||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.jvm.FirJvmErrors.OVERLOADS_ABSTRACT
|
import org.jetbrains.kotlin.fir.analysis.diagnostics.jvm.FirJvmErrors.OVERLOADS_ABSTRACT
|
||||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.jvm.FirJvmErrors.OVERLOADS_ANNOTATION_CLASS_CONSTRUCTOR
|
import org.jetbrains.kotlin.fir.analysis.diagnostics.jvm.FirJvmErrors.OVERLOADS_ANNOTATION_CLASS_CONSTRUCTOR
|
||||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.jvm.FirJvmErrors.OVERLOADS_INTERFACE
|
import org.jetbrains.kotlin.fir.analysis.diagnostics.jvm.FirJvmErrors.OVERLOADS_INTERFACE
|
||||||
@@ -257,5 +258,10 @@ object FirJvmErrorsDefaultMessages : BaseDiagnosticRendererFactory() {
|
|||||||
TO_STRING,
|
TO_STRING,
|
||||||
TO_STRING,
|
TO_STRING,
|
||||||
)
|
)
|
||||||
|
map.put(
|
||||||
|
NO_REFLECTION_IN_CLASS_PATH,
|
||||||
|
"Call uses reflection API which is not found in compilation classpath. " +
|
||||||
|
"Make sure you have kotlin-reflect.jar in the classpath"
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-1
@@ -11,7 +11,8 @@ import org.jetbrains.kotlin.fir.analysis.jvm.checkers.expression.*
|
|||||||
object JvmExpressionCheckers : ExpressionCheckers() {
|
object JvmExpressionCheckers : ExpressionCheckers() {
|
||||||
override val basicExpressionCheckers: Set<FirBasicExpressionChecker>
|
override val basicExpressionCheckers: Set<FirBasicExpressionChecker>
|
||||||
get() = setOf(
|
get() = setOf(
|
||||||
FirJvmProtectedInSuperClassCompanionCallChecker
|
FirJvmProtectedInSuperClassCompanionCallChecker,
|
||||||
|
FirJvmReflectionApiCallChecker,
|
||||||
)
|
)
|
||||||
|
|
||||||
override val qualifiedAccessExpressionCheckers: Set<FirQualifiedAccessExpressionChecker>
|
override val qualifiedAccessExpressionCheckers: Set<FirQualifiedAccessExpressionChecker>
|
||||||
|
|||||||
+24
@@ -0,0 +1,24 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||||
|
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jetbrains.kotlin.fir.analysis.jvm.checkers.expression
|
||||||
|
|
||||||
|
import org.jetbrains.kotlin.KtSourceElement
|
||||||
|
import org.jetbrains.kotlin.diagnostics.DiagnosticReporter
|
||||||
|
import org.jetbrains.kotlin.diagnostics.reportOn
|
||||||
|
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
|
||||||
|
import org.jetbrains.kotlin.fir.analysis.checkers.expression.AbstractFirReflectionApiCallChecker
|
||||||
|
import org.jetbrains.kotlin.fir.analysis.diagnostics.jvm.FirJvmErrors
|
||||||
|
import org.jetbrains.kotlin.fir.resolve.providers.symbolProvider
|
||||||
|
import org.jetbrains.kotlin.load.java.JvmAbi
|
||||||
|
|
||||||
|
object FirJvmReflectionApiCallChecker : AbstractFirReflectionApiCallChecker() {
|
||||||
|
override fun isWholeReflectionApiAvailable(context: CheckerContext): Boolean =
|
||||||
|
context.session.symbolProvider.getClassLikeSymbolByClassId(JvmAbi.REFLECTION_FACTORY_IMPL) != null
|
||||||
|
|
||||||
|
override fun report(source: KtSourceElement?, context: CheckerContext, reporter: DiagnosticReporter) {
|
||||||
|
reporter.reportOn(source, FirJvmErrors.NO_REFLECTION_IN_CLASS_PATH, context)
|
||||||
|
}
|
||||||
|
}
|
||||||
+89
@@ -0,0 +1,89 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||||
|
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jetbrains.kotlin.fir.analysis.checkers.expression
|
||||||
|
|
||||||
|
import org.jetbrains.kotlin.KtSourceElement
|
||||||
|
import org.jetbrains.kotlin.builtins.StandardNames
|
||||||
|
import org.jetbrains.kotlin.config.AnalysisFlags
|
||||||
|
import org.jetbrains.kotlin.diagnostics.DiagnosticReporter
|
||||||
|
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
|
||||||
|
import org.jetbrains.kotlin.fir.analysis.checkers.toClassLikeSymbol
|
||||||
|
import org.jetbrains.kotlin.fir.expressions.FirQualifiedAccessExpression
|
||||||
|
import org.jetbrains.kotlin.fir.expressions.FirStatement
|
||||||
|
import org.jetbrains.kotlin.fir.expressions.calleeReference
|
||||||
|
import org.jetbrains.kotlin.fir.packageFqName
|
||||||
|
import org.jetbrains.kotlin.fir.references.resolved
|
||||||
|
import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol
|
||||||
|
import org.jetbrains.kotlin.name.ClassId
|
||||||
|
import org.jetbrains.kotlin.name.FqName
|
||||||
|
import org.jetbrains.kotlin.name.Name
|
||||||
|
|
||||||
|
// TODO (KT-60899): implement this checker for JS, similarly to K1's JsReflectionAPICallChecker.
|
||||||
|
abstract class AbstractFirReflectionApiCallChecker : FirBasicExpressionChecker() {
|
||||||
|
protected abstract fun isWholeReflectionApiAvailable(context: CheckerContext): Boolean
|
||||||
|
protected abstract fun report(source: KtSourceElement?, context: CheckerContext, reporter: DiagnosticReporter)
|
||||||
|
|
||||||
|
protected open fun isAllowedKClassMember(name: Name, context: CheckerContext): Boolean = when (name) {
|
||||||
|
K_CLASS_SIMPLE_NAME, K_CLASS_IS_INSTANCE -> true
|
||||||
|
K_CLASS_QUALIFIED_NAME -> context.languageVersionSettings.getFlag(AnalysisFlags.allowFullyQualifiedNameInKClass)
|
||||||
|
else -> false
|
||||||
|
}
|
||||||
|
|
||||||
|
final override fun check(expression: FirStatement, context: CheckerContext, reporter: DiagnosticReporter) {
|
||||||
|
if (isWholeReflectionApiAvailable(context)) return
|
||||||
|
|
||||||
|
// Do not report the diagnostic on kotlin-reflect sources.
|
||||||
|
if (isReflectionSource(context)) return
|
||||||
|
|
||||||
|
val resolvedReference = expression.calleeReference?.resolved ?: return
|
||||||
|
val referencedSymbol = resolvedReference.resolvedSymbol as? FirCallableSymbol ?: return
|
||||||
|
|
||||||
|
val containingClassId =
|
||||||
|
(expression as? FirQualifiedAccessExpression)?.dispatchReceiver?.typeRef?.toClassLikeSymbol(context.session)?.classId
|
||||||
|
if (containingClassId == null || containingClassId.packageFqName != StandardNames.KOTLIN_REFLECT_FQ_NAME) return
|
||||||
|
|
||||||
|
if (!isAllowedReflectionApi(referencedSymbol.name, containingClassId, context)) {
|
||||||
|
report(resolvedReference.source, context, reporter)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun isAllowedReflectionApi(name: Name, containingClassId: ClassId, context: CheckerContext): Boolean =
|
||||||
|
name in ALLOWED_MEMBER_NAMES ||
|
||||||
|
containingClassId == K_CLASS && isAllowedKClassMember(name, context) ||
|
||||||
|
(name.asString() == "get" || name.asString() == "set") && containingClassId in K_PROPERTY_CLASSES ||
|
||||||
|
containingClassId in ALLOWED_CLASSES
|
||||||
|
|
||||||
|
private fun isReflectionSource(context: CheckerContext): Boolean {
|
||||||
|
val containingFile = context.containingFile
|
||||||
|
return containingFile != null && containingFile.packageFqName.startsWith(StandardNames.KOTLIN_REFLECT_FQ_NAME)
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private val K_CLASS = ClassId.topLevel(StandardNames.FqNames.kClass.toSafe())
|
||||||
|
|
||||||
|
private val K_CLASS_SIMPLE_NAME = Name.identifier("simpleName")
|
||||||
|
private val K_CLASS_IS_INSTANCE = Name.identifier("isInstance")
|
||||||
|
private val K_CLASS_QUALIFIED_NAME = Name.identifier("qualifiedName")
|
||||||
|
|
||||||
|
private val K_PROPERTY_CLASSES: Set<ClassId> =
|
||||||
|
listOf(
|
||||||
|
StandardNames.FqNames.kProperty0,
|
||||||
|
StandardNames.FqNames.kProperty1,
|
||||||
|
StandardNames.FqNames.kProperty2,
|
||||||
|
StandardNames.FqNames.kMutableProperty0,
|
||||||
|
StandardNames.FqNames.kMutableProperty1,
|
||||||
|
StandardNames.FqNames.kMutableProperty2,
|
||||||
|
).mapTo(HashSet()) { ClassId.topLevel(it.toSafe()) }
|
||||||
|
|
||||||
|
private val ALLOWED_MEMBER_NAMES: Set<Name> =
|
||||||
|
listOf("equals", "hashCode", "toString", "invoke", "name").mapTo(HashSet(), Name::identifier)
|
||||||
|
|
||||||
|
private val ALLOWED_CLASSES: Set<ClassId> =
|
||||||
|
listOf("KType", "KTypeParameter", "KTypeProjection", "KTypeProjection.Companion", "KVariance").mapTo(HashSet()) {
|
||||||
|
ClassId(StandardNames.KOTLIN_REFLECT_FQ_NAME, FqName(it), false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+2
-3
@@ -17,8 +17,8 @@
|
|||||||
package org.jetbrains.kotlin.resolve.calls.checkers
|
package org.jetbrains.kotlin.resolve.calls.checkers
|
||||||
|
|
||||||
import com.intellij.psi.PsiElement
|
import com.intellij.psi.PsiElement
|
||||||
import org.jetbrains.kotlin.builtins.StandardNames.KOTLIN_REFLECT_FQ_NAME
|
|
||||||
import org.jetbrains.kotlin.builtins.ReflectionTypes
|
import org.jetbrains.kotlin.builtins.ReflectionTypes
|
||||||
|
import org.jetbrains.kotlin.builtins.StandardNames.KOTLIN_REFLECT_FQ_NAME
|
||||||
import org.jetbrains.kotlin.config.AnalysisFlags.allowFullyQualifiedNameInKClass
|
import org.jetbrains.kotlin.config.AnalysisFlags.allowFullyQualifiedNameInKClass
|
||||||
import org.jetbrains.kotlin.descriptors.CallableDescriptor
|
import org.jetbrains.kotlin.descriptors.CallableDescriptor
|
||||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||||
@@ -96,7 +96,6 @@ abstract class AbstractReflectionApiCallChecker(
|
|||||||
|
|
||||||
private fun isReflectionSource(reportOn: PsiElement): Boolean {
|
private fun isReflectionSource(reportOn: PsiElement): Boolean {
|
||||||
val file = reportOn.containingFile as? KtFile ?: return false
|
val file = reportOn.containingFile as? KtFile ?: return false
|
||||||
val fqName = file.packageFqName.toUnsafe()
|
return file.packageFqName.startsWith(KOTLIN_REFLECT_FQ_NAME)
|
||||||
return fqName == KOTLIN_REFLECT_FQ_NAME.toUnsafe() || fqName.asString().startsWith(KOTLIN_REFLECT_FQ_NAME.asString() + ".")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
-62
@@ -1,62 +0,0 @@
|
|||||||
import kotlin.reflect.*
|
|
||||||
|
|
||||||
class Foo(val prop: Any) {
|
|
||||||
fun func() {}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun n01() = Foo::prop
|
|
||||||
fun n02() = Foo::func
|
|
||||||
fun n03() = Foo::class
|
|
||||||
fun n04(p: KProperty0<Int>) = p.get()
|
|
||||||
fun n05(p: KMutableProperty0<String>) = p.set("")
|
|
||||||
fun n07(p: KFunction<String>) = p.name
|
|
||||||
fun n08(p: KProperty1<String, Int>) = p.get("")
|
|
||||||
fun n09(p: KProperty2<String, String, Int>) = p.get("", "")
|
|
||||||
fun n10() = (Foo::func).invoke(Foo(""))
|
|
||||||
fun n11() = (Foo::func)(Foo(""))
|
|
||||||
|
|
||||||
fun y01() = Foo::prop.getter
|
|
||||||
fun y02() = Foo::class.members
|
|
||||||
fun y03() = Foo::class.simpleName
|
|
||||||
fun y04() = Foo::class.<!UNRESOLVED_REFERENCE!>properties<!>
|
|
||||||
|
|
||||||
fun <T : Any> kclass(k: KClass<*>, kt: KClass<T>) {
|
|
||||||
k.simpleName
|
|
||||||
k.qualifiedName
|
|
||||||
k.members
|
|
||||||
k.constructors
|
|
||||||
k.nestedClasses
|
|
||||||
k.objectInstance
|
|
||||||
k.typeParameters
|
|
||||||
k.supertypes
|
|
||||||
k.visibility
|
|
||||||
k.isFinal
|
|
||||||
k.isOpen
|
|
||||||
k.isAbstract
|
|
||||||
k.isSealed
|
|
||||||
k.isData
|
|
||||||
k.isInner
|
|
||||||
k.isCompanion
|
|
||||||
|
|
||||||
k.annotations
|
|
||||||
k.isInstance(42)
|
|
||||||
|
|
||||||
k == kt
|
|
||||||
k.hashCode()
|
|
||||||
k.toString()
|
|
||||||
}
|
|
||||||
|
|
||||||
fun ktype(t: KType, t2: KType) {
|
|
||||||
t.classifier
|
|
||||||
t.arguments
|
|
||||||
t.isMarkedNullable
|
|
||||||
t.annotations
|
|
||||||
|
|
||||||
t == t2
|
|
||||||
t.hashCode()
|
|
||||||
t.toString()
|
|
||||||
|
|
||||||
KTypeProjection.Companion.covariant(t)
|
|
||||||
KTypeProjection.STAR
|
|
||||||
KTypeProjection(KVariance.IN, t)
|
|
||||||
}
|
|
||||||
+2
@@ -1,3 +1,5 @@
|
|||||||
|
// FIR_IDENTICAL
|
||||||
|
|
||||||
import kotlin.reflect.*
|
import kotlin.reflect.*
|
||||||
|
|
||||||
class Foo(val prop: Any) {
|
class Foo(val prop: Any) {
|
||||||
|
|||||||
+1
-1
@@ -1,5 +1,5 @@
|
|||||||
// FIR_IDENTICAL
|
// FIR_IDENTICAL
|
||||||
// !DIAGNOSTICS: -UNUSED_VARIABLE
|
// !DIAGNOSTICS: -UNUSED_VARIABLE -NO_REFLECTION_IN_CLASS_PATH
|
||||||
// !OPT_IN: kotlin.contracts.ExperimentalContracts
|
// !OPT_IN: kotlin.contracts.ExperimentalContracts
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|||||||
@@ -102,6 +102,10 @@ public final class FqName {
|
|||||||
return fqName.startsWith(segment);
|
return fqName.startsWith(segment);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean startsWith(@NotNull FqName other) {
|
||||||
|
return fqName.startsWith(other.fqName);
|
||||||
|
}
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
public static FqName topLevel(@NotNull Name shortName) {
|
public static FqName topLevel(@NotNull Name shortName) {
|
||||||
return new FqName(FqNameUnsafe.topLevel(shortName));
|
return new FqName(FqNameUnsafe.topLevel(shortName));
|
||||||
|
|||||||
@@ -167,6 +167,17 @@ public final class FqNameUnsafe {
|
|||||||
return fqName.regionMatches(0, segmentAsString, 0, firstDot == -1 ? Math.max(fqName.length(), segmentAsString.length()) : firstDot);
|
return fqName.regionMatches(0, segmentAsString, 0, firstDot == -1 ? Math.max(fqName.length(), segmentAsString.length()) : firstDot);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean startsWith(@NotNull FqNameUnsafe other) {
|
||||||
|
if (isRoot()) return false;
|
||||||
|
|
||||||
|
int thisLength = fqName.length();
|
||||||
|
int otherLength = other.fqName.length();
|
||||||
|
if (thisLength < otherLength) return false;
|
||||||
|
|
||||||
|
return (thisLength == otherLength || fqName.charAt(otherLength) == '.') &&
|
||||||
|
fqName.regionMatches(0, other.fqName, 0, otherLength);
|
||||||
|
}
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
public static FqNameUnsafe topLevel(@NotNull Name shortName) {
|
public static FqNameUnsafe topLevel(@NotNull Name shortName) {
|
||||||
return new FqNameUnsafe(shortName.asString(), FqName.ROOT.toUnsafe(), shortName);
|
return new FqNameUnsafe(shortName.asString(), FqName.ROOT.toUnsafe(), shortName);
|
||||||
|
|||||||
Reference in New Issue
Block a user