Skip to content

Commit

Permalink
google#1232: Include type hint into KSErrorType.
Browse files Browse the repository at this point in the history
This CL makes `KSErrorType` a class with a string
"hint", that by convention should be a "simpleName"
of an unresolved type.

`KSErrorTypeClassDeclaration` is also no longer a singleton
and references a corresponding `KSErrorType`.

The implementation consists of extracting the available
info on the unresolved type on the best effort basis.
  • Loading branch information
Jeffset committed Apr 18, 2024
1 parent cf3b401 commit fc18135
Show file tree
Hide file tree
Showing 31 changed files with 335 additions and 164 deletions.
Expand Up @@ -691,7 +691,8 @@ class ResolverImpl(
builtIns.arrayType.replace(typeArgs)
}
else -> {
getClassDeclarationByName(psiType.canonicalText)?.asStarProjectedType() ?: KSErrorType
getClassDeclarationByName(psiType.canonicalText)?.asStarProjectedType()
?: KSErrorType(psiType.canonicalText)
}
}
}
Expand Down Expand Up @@ -1053,7 +1054,7 @@ class ResolverImpl(
}
}
// if substitution fails, fallback to the type from the property
return KSErrorType
return KSErrorType.fromReferenceBestEffort(property.type)
}

internal fun asMemberOf(
Expand Down Expand Up @@ -1246,7 +1247,7 @@ class ResolverImpl(
}

// Convert type arguments for Java wildcard, recursively.
private fun KotlinType.toWildcard(mode: TypeMappingMode): KotlinType? {
private fun KotlinType.toWildcard(mode: TypeMappingMode): KotlinType {
val parameters = constructor.parameters
val arguments = arguments

Expand All @@ -1257,16 +1258,18 @@ class ResolverImpl(
argument.projectionKind != org.jetbrains.kotlin.types.Variance.INVARIANT
) {
// conflicting variances
// TODO: error message
return null
throw IllegalArgumentException(
"Conflicting variance: variance '${parameter.variance.label}' vs projection " +
"'${argument.projectionKind.label}'"
)
}

val argMode = mode.updateFromAnnotations(argument.type)
val variance = KotlinTypeMapper.getVarianceForWildcard(parameter, argument, argMode)
val genericMode = argMode.toGenericArgumentMode(
getEffectiveVariance(parameter.variance, argument.projectionKind)
)
TypeProjectionImpl(variance, argument.type.toWildcard(genericMode) ?: return null)
TypeProjectionImpl(variance, argument.type.toWildcard(genericMode))
}

return replace(wildcardArguments)
Expand Down Expand Up @@ -1368,19 +1371,17 @@ class ResolverImpl(
if (position == RefPosition.SUPER_TYPE &&
argument.projectionKind != org.jetbrains.kotlin.types.Variance.INVARIANT
) {
// Type projection isn't allowed in immediate arguments to supertypes.
// TODO: error message
return KSTypeReferenceSyntheticImpl.getCached(KSErrorType, null)
throw IllegalArgumentException("Type projection isn't allowed in immediate arguments to supertypes")
}
}

val wildcardType = kotlinType.toWildcard(typeMappingMode)?.let {
val wildcardType = kotlinType.toWildcard(typeMappingMode).let {
var candidate: KotlinType = it
for (i in indexes.reversed()) {
candidate = candidate.arguments[i].type
}
getKSTypeCached(candidate)
} ?: KSErrorType
}

return KSTypeReferenceSyntheticImpl.getCached(wildcardType, null)
}
Expand Down
Expand Up @@ -62,7 +62,9 @@ import org.jetbrains.kotlin.load.kotlin.getContainingKotlinJvmBinaryClass
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.psi.KtClassLiteralExpression
import org.jetbrains.kotlin.psi.KtCollectionLiteralExpression
import org.jetbrains.kotlin.psi.KtExpression
import org.jetbrains.kotlin.psi.KtParameter
import org.jetbrains.kotlin.resolve.AnnotationResolverImpl
import org.jetbrains.kotlin.resolve.BindingContext
Expand Down Expand Up @@ -161,7 +163,7 @@ private fun <T> ConstantValue<T>.toValue(parent: KSNode): Any? = when (this) {
} else classValue.classId.findKSType()
is KClassValue.Value.LocalClass -> getKSTypeCached(classValue.type)
}
is ErrorValue -> KSErrorType
is ErrorValue -> KSErrorType(toString())
is NullValue -> null
else -> value
}
Expand Down Expand Up @@ -211,7 +213,7 @@ fun LazyAnnotationDescriptor.getValueArguments(): Map<Name, ConstantValue<*>> {
} else if (resolvedArgument is DefaultValueArgument) {
valueParameter.name to DefaultConstantValue
} else {
c.annotationResolver.getAnnotationArgumentValue(c.trace, valueParameter, resolvedArgument)?.let { value ->
c.annotationResolver.getAnnotationArgumentValue(c.trace, valueParameter, resolvedArgument).let { value ->
val argExp = resolvedArgument.arguments.lastOrNull()?.getArgumentExpression()
// When some elements are not available, the expected and actual size of an array argument will
// be different. In such case, we need to reconstruct the array.
Expand All @@ -224,17 +226,22 @@ fun LazyAnnotationDescriptor.getValueArguments(): Map<Name, ConstantValue<*>> {
val bc = ResolverImpl.instance!!.bindingTrace.bindingContext
val args = argExp.innerExpressions.map {
bc.get(BindingContext.COMPILE_TIME_VALUE, it)?.toConstantValue(value.type)
?: ErrorValue.create("<ERROR VALUE>")
?: it.asErrorValue()
}
valueParameter.name to TypedArrayValue(args, value.type)
} else {
valueParameter.name to value
valueParameter.name to (value ?: argExp?.asErrorValue() ?: ErrorValue.create("ERROR VALUE"))
}
} ?: (valueParameter.name to ErrorValue.create("<ERROR VALUE>"))
}
}
}.toMap()
}

private fun KtExpression.asErrorValue(): ErrorValue {
val reprExpr = (this as? KtClassLiteralExpression)?.receiverExpression ?: this
return ErrorValue.create(reprExpr.text)
}

fun AnnotationDescriptor.createKSValueArguments(ownerAnnotation: KSAnnotation): List<KSValueArgument> {
val allValueArgs = if (this is LazyAnnotationDescriptor) {
this.getValueArguments()
Expand Down Expand Up @@ -419,14 +426,14 @@ fun ValueParameterDescriptor.getDefaultValue(ownerAnnotation: KSAnnotation): Any
if (!this.type.isError) {
defaultValue?.convert(this.type)?.toValue(ownerAnnotation)
} else {
KSErrorType
KSErrorType.fromKtErrorType(type)
}
}
}
is KtParameter -> if (!this.type.isError) {
ResolverImpl.instance!!.evaluateConstant(psi.defaultValue, this.type)?.toValue(ownerAnnotation)
} else {
KSErrorType
KSErrorType.fromKtErrorType(type)
}
is PsiAnnotationMethod -> {
when (psi.defaultValue) {
Expand Down
Expand Up @@ -153,7 +153,7 @@ class KSClassDeclarationDescriptorImpl private constructor(val descriptor: Class
override fun asType(typeArguments: List<KSTypeArgument>): KSType =
descriptor.defaultType.replaceTypeArguments(typeArguments)?.let {
getKSTypeCached(it, typeArguments)
} ?: KSErrorType
} ?: KSErrorType()

override fun asStarProjectedType(): KSType {
return getKSTypeCached(descriptor.defaultType.replaceArgumentsWithStarProjections())
Expand Down
Expand Up @@ -97,7 +97,7 @@ class KSClassDeclarationJavaEnumEntryImpl private constructor(val psi: PsiEnumCo
// Enum can't have type parameters.
override fun asType(typeArguments: List<KSTypeArgument>): KSType {
if (typeArguments.isNotEmpty())
return KSErrorType
return KSErrorType()
return asStarProjectedType()
}

Expand Down
Expand Up @@ -157,13 +157,13 @@ class KSClassDeclarationJavaImpl private constructor(val psi: PsiClass) :
it.defaultType.replaceTypeArguments(typeArguments)?.let {
getKSTypeCached(it, typeArguments)
}
} ?: KSErrorType
} ?: KSErrorType()
}

override fun asStarProjectedType(): KSType {
return descriptor?.let {
getKSTypeCached(it.defaultType.replaceArgumentsWithStarProjections())
} ?: KSErrorType
} ?: KSErrorType()
}

override fun <D, R> accept(visitor: KSVisitor<D, R>, data: D): R {
Expand Down
Expand Up @@ -124,11 +124,13 @@ class KSTypeReferenceJavaImpl private constructor(val psi: PsiType, override val
.mapNotNull {
(it.annotationType.resolve() as? KSTypeImpl)?.kotlinType?.constructor?.declarationDescriptor?.fqNameSafe
}
val resolved = if ((resolvedType.declaration as? KSClassDeclarationDescriptorImpl)
?.descriptor is NotFoundClasses.MockClassDescriptor
) {
KSErrorType
} else resolvedType
val resolved = when (val declaration = resolvedType.declaration) {
is KSClassDeclarationDescriptorImpl -> when (val descriptor = declaration.descriptor) {
is NotFoundClasses.MockClassDescriptor -> KSErrorType(descriptor.name.asString())
else -> resolvedType
}
else -> resolvedType
}
val hasNotNull = relatedAnnotations.any { it in NOT_NULL_ANNOTATIONS }
val hasNullable = relatedAnnotations.any { it in NULLABLE_ANNOTATIONS }
return if (hasNullable && !hasNotNull) {
Expand Down
Expand Up @@ -47,13 +47,14 @@ class KSTypeReferenceLiteJavaImpl private constructor(val psiElement: PsiElement
val type: KSType by lazy {
when (psiElement) {
is PsiAnnotation -> {
val psiClass = psiElement.nameReferenceElement!!.resolve() as? PsiClass
val nameReferenceElement = psiElement.nameReferenceElement!!
val psiClass = nameReferenceElement.resolve() as? PsiClass
psiClass?.let {
(psiElement.containingFile as? PsiJavaFile)?.let {
ResolverImpl.instance!!.incrementalContext.recordLookup(it, psiClass.qualifiedName!!)
}
KSClassDeclarationJavaImpl.getCached(psiClass).asStarProjectedType()
} ?: KSErrorType
} ?: KSErrorType(nameReferenceElement.text)
}
is PsiMethod -> {
KSClassDeclarationJavaImpl.getCached(psiElement.containingClass!!).asStarProjectedType()
Expand Down
Expand Up @@ -124,7 +124,7 @@ class KSClassDeclarationImpl private constructor(val ktClassOrObject: KtClassOrO
override fun asType(typeArguments: List<KSTypeArgument>): KSType {
return descriptor.defaultType.replaceTypeArguments(typeArguments)?.let {
getKSTypeCached(it, typeArguments)
} ?: KSErrorType
} ?: KSErrorType()
}

override fun asStarProjectedType(): KSType {
Expand Down
Expand Up @@ -19,17 +19,28 @@ package com.google.devtools.ksp.symbol.impl.kotlin

import com.google.devtools.ksp.symbol.*
import com.google.devtools.ksp.symbol.impl.synthetic.KSErrorTypeClassDeclaration
import org.jetbrains.kotlin.types.FlexibleType
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.error.ErrorType
import org.jetbrains.kotlin.types.error.ErrorTypeKind

object KSErrorType : KSType {
override val annotations: Sequence<KSAnnotation> = emptySequence()
class KSErrorType(
val nameHint: String? = null,
) : KSType {
override val annotations: Sequence<KSAnnotation>
get() = emptySequence()

override val arguments: List<KSTypeArgument> = emptyList()
override val arguments: List<KSTypeArgument>
get() = emptyList()

override val declaration: KSDeclaration = KSErrorTypeClassDeclaration
override val declaration: KSDeclaration
get() = KSErrorTypeClassDeclaration(this)

override val isError: Boolean = true
override val isError: Boolean
get() = true

override val nullability: Nullability = Nullability.NULLABLE
override val nullability: Nullability
get() = Nullability.NULLABLE

override fun isAssignableFrom(that: KSType): Boolean {
return false
Expand All @@ -51,7 +62,8 @@ object KSErrorType : KSType {
return this
}

override val isMarkedNullable: Boolean = false
override val isMarkedNullable: Boolean
get() = false

override fun replace(arguments: List<KSTypeArgument>): KSType {
return this
Expand All @@ -61,11 +73,51 @@ object KSErrorType : KSType {
return this
}

override fun toString(): String {
return "<ERROR TYPE>"
}
override fun toString(): String = nameHint?.let { "<ERROR TYPE: $it>" } ?: "<ERROR TYPE>"

override val isFunctionType: Boolean
get() = false

override val isSuspendFunctionType: Boolean
get() = false

override val isFunctionType: Boolean = false
override fun hashCode() = nameHint.hashCode()

override val isSuspendFunctionType: Boolean = false
override fun equals(other: Any?): Boolean {
return this === other || other is KSErrorType && other.nameHint == nameHint
}

companion object {
fun fromReferenceBestEffort(reference: KSTypeReference?): KSErrorType {
return when (val type = reference?.resolve()) {
is KSErrorType -> type
null -> KSErrorType(reference?.element?.toString())
else -> KSErrorType(type.toString())
}
}

fun fromKtErrorType(ktType: KotlinType): KSErrorType {
// Logic is in sync with `KotlinType.isError`
val errorType: ErrorType = when (val unwrapped = ktType.unwrap()) {
is ErrorType -> unwrapped
is FlexibleType -> unwrapped.delegate as? ErrorType
else -> null
} ?: throw IllegalArgumentException("Not an error type: $ktType")

val hint = when (errorType.kind) {
// Handle "Unresolved types" group
ErrorTypeKind.UNRESOLVED_TYPE,
ErrorTypeKind.UNRESOLVED_CLASS_TYPE,
ErrorTypeKind.UNRESOLVED_JAVA_CLASS,
ErrorTypeKind.UNRESOLVED_DECLARATION,
ErrorTypeKind.UNRESOLVED_KCLASS_CONSTANT_VALUE,
ErrorTypeKind.UNRESOLVED_TYPE_ALIAS -> errorType.formatParams.first()

// TODO: Handle more ErrorTypeKinds where it's possible to extract a name for the error type.
else -> errorType.debugMessage
}

return KSErrorType(hint)
}
}
}
Expand Up @@ -29,18 +29,19 @@ class KSFunctionErrorImpl(
) : KSFunction {
override val isError: Boolean = true

override val returnType: KSType = KSErrorType
override val returnType: KSType
get() = KSErrorType.fromReferenceBestEffort(declaration.returnType)

override val parameterTypes: List<KSType?>
get() = declaration.parameters.map {
KSErrorType
KSErrorType.fromReferenceBestEffort(it.type)
}
override val typeParameters: List<KSTypeParameter>
get() = emptyList()

override val extensionReceiverType: KSType?
get() = declaration.extensionReceiver?.let {
KSErrorType
KSErrorType.fromReferenceBestEffort(it)
}

override fun equals(other: Any?): Boolean {
Expand Down
Expand Up @@ -97,7 +97,7 @@ class KSPropertyDeclarationImpl private constructor(val ktProperty: KtProperty)
KSTypeReferenceDeferredImpl.getCached(this) {
val desc = propertyDescriptor as? VariableDescriptorWithAccessors
if (desc == null) {
KSErrorType
KSErrorType()
} else {
getKSTypeCached(desc.type)
}
Expand Down
Expand Up @@ -97,7 +97,7 @@ class KSTypeImpl private constructor(
override fun replace(arguments: List<KSTypeArgument>): KSType {
return kotlinType.replaceTypeArguments(arguments)?.let {
getKSTypeCached(it, arguments, annotations)
} ?: KSErrorType
} ?: KSErrorType()
}

override fun starProjection(): KSType {
Expand Down Expand Up @@ -136,10 +136,12 @@ fun getKSTypeCached(
ksTypeArguments: List<KSTypeArgument>? = null,
annotations: Sequence<KSAnnotation> = sequenceOf()
): KSType {
return if (kotlinType.isError ||
kotlinType.constructor.declarationDescriptor is NotFoundClasses.MockClassDescriptor
) {
KSErrorType
if (kotlinType.isError) {
return KSErrorType.fromKtErrorType(kotlinType)
}
val descriptor = kotlinType.constructor.declarationDescriptor
return if (descriptor is NotFoundClasses.MockClassDescriptor) {
KSErrorType(descriptor.name.asString())
} else {
KSTypeImpl.getCached(
kotlinType,
Expand Down
Expand Up @@ -112,7 +112,7 @@ class KSValueParameterImpl private constructor(val ktParameter: KtParameter) : K

override val type: KSTypeReference by lazy {
ktParameter.typeReference?.let { KSTypeReferenceImpl.getCached(it) }
?: findPropertyForAccessor()?.type ?: KSTypeReferenceSyntheticImpl.getCached(KSErrorType, this)
?: findPropertyForAccessor()?.type ?: KSTypeReferenceSyntheticImpl.getCached(KSErrorType(), this)
}

override val hasDefault: Boolean = ktParameter.hasDefaultValue()
Expand Down

0 comments on commit fc18135

Please sign in to comment.