Skip to content

Commit

Permalink
#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.
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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)
}
}
}
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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.