Skip to content

Commit 319ddff

Browse files
Jeffsetneetopia
authored andcommitted
Provide more hints for error types.
Allow KSErrorType to receive a message as additional piece of information on why a valid type can't be provided. Cover `replace`/`asType` cases with a utility function `errorTypeOnInconsistentArguments` to get consistent error messages across implementations. Tweak KSTypeImpl.isError detection in AA, move TODO there.
1 parent 1e8486e commit 319ddff

File tree

19 files changed

+207
-190
lines changed

19 files changed

+207
-190
lines changed
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
package com.google.devtools.ksp.common
2+
3+
import com.google.devtools.ksp.symbol.KSType
4+
import com.google.devtools.ksp.symbol.KSTypeArgument
5+
6+
inline fun <E> errorTypeOnInconsistentArguments(
7+
arguments: List<KSTypeArgument>,
8+
placeholdersProvider: () -> List<KSTypeArgument>,
9+
withCorrectedArguments: (corrected: List<KSTypeArgument>) -> KSType,
10+
errorType: (name: String, message: String) -> E,
11+
): E? {
12+
if (arguments.isNotEmpty()) {
13+
val placeholders = placeholdersProvider()
14+
val diff = arguments.size - placeholders.size
15+
if (diff > 0) {
16+
val wouldBeType = withCorrectedArguments(arguments.dropLast(diff))
17+
return errorType(wouldBeType.toString(), "Unexpected extra $diff type argument(s)")
18+
} else if (diff < 0) {
19+
val wouldBeType = withCorrectedArguments(arguments + placeholders.drop(arguments.size))
20+
return errorType(wouldBeType.toString(), "Missing ${-diff} type argument(s)")
21+
}
22+
}
23+
return null
24+
}

compiler-plugin/src/main/kotlin/com/google/devtools/ksp/processing/impl/ResolverImpl.kt

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1248,7 +1248,7 @@ class ResolverImpl(
12481248
}
12491249

12501250
// Convert type arguments for Java wildcard, recursively.
1251-
private fun KotlinType.toWildcard(mode: TypeMappingMode): KotlinType {
1251+
private fun KotlinType.toWildcard(mode: TypeMappingMode): Result<KotlinType> {
12521252
val parameters = constructor.parameters
12531253
val arguments = arguments
12541254

@@ -1258,10 +1258,11 @@ class ResolverImpl(
12581258
parameter.variance != org.jetbrains.kotlin.types.Variance.INVARIANT &&
12591259
argument.projectionKind != org.jetbrains.kotlin.types.Variance.INVARIANT
12601260
) {
1261-
// conflicting variances
1262-
throw IllegalArgumentException(
1263-
"Conflicting variance: variance '${parameter.variance.label}' vs projection " +
1264-
"'${argument.projectionKind.label}'"
1261+
return Result.failure(
1262+
IllegalArgumentException(
1263+
"Conflicting variance: variance '${parameter.variance.label}' vs projection " +
1264+
"'${argument.projectionKind.label}'"
1265+
)
12651266
)
12661267
}
12671268

@@ -1270,10 +1271,10 @@ class ResolverImpl(
12701271
val genericMode = argMode.toGenericArgumentMode(
12711272
getEffectiveVariance(parameter.variance, argument.projectionKind)
12721273
)
1273-
TypeProjectionImpl(variance, argument.type.toWildcard(genericMode))
1274+
TypeProjectionImpl(variance, argument.type.toWildcard(genericMode).getOrElse { return Result.failure(it) })
12741275
}
12751276

1276-
return replace(wildcardArguments)
1277+
return Result.success(replace(wildcardArguments))
12771278
}
12781279

12791280
private val JVM_SUPPRESS_WILDCARDS_NAME = KSNameImpl.getCached("kotlin.jvm.JvmSuppressWildcards")
@@ -1372,12 +1373,19 @@ class ResolverImpl(
13721373
if (position == RefPosition.SUPER_TYPE &&
13731374
argument.projectionKind != org.jetbrains.kotlin.types.Variance.INVARIANT
13741375
) {
1375-
throw IllegalArgumentException("Type projection isn't allowed in immediate arguments to supertypes")
1376+
val errorType = KSErrorType(
1377+
name = type.toString(),
1378+
message = "Type projection isn't allowed in immediate arguments to supertypes"
1379+
)
1380+
return KSTypeReferenceSyntheticImpl.getCached(errorType, null)
13761381
}
13771382
}
13781383

13791384
val wildcardType = kotlinType.toWildcard(typeMappingMode).let {
1380-
var candidate: KotlinType = it
1385+
var candidate: KotlinType = it.getOrElse { error ->
1386+
val errorType = KSErrorType(name = type.toString(), message = error.message)
1387+
return KSTypeReferenceSyntheticImpl.getCached(errorType, null)
1388+
}
13811389
for (i in indexes.reversed()) {
13821390
candidate = candidate.arguments[i].type
13831391
}

compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/binary/KSClassDeclarationDescriptorImpl.kt

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -151,9 +151,7 @@ class KSClassDeclarationDescriptorImpl private constructor(val descriptor: Class
151151
}
152152

153153
override fun asType(typeArguments: List<KSTypeArgument>): KSType =
154-
descriptor.defaultType.replaceTypeArguments(typeArguments)?.let {
155-
getKSTypeCached(it, typeArguments)
156-
} ?: KSErrorType()
154+
descriptor.defaultType.replaceTypeArguments(typeArguments)
157155

158156
override fun asStarProjectedType(): KSType {
159157
return getKSTypeCached(descriptor.defaultType.replaceArgumentsWithStarProjections())

compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/java/KSClassDeclarationJavaEnumEntryImpl.kt

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717

1818
package com.google.devtools.ksp.symbol.impl.java
1919

20+
import com.google.devtools.ksp.common.errorTypeOnInconsistentArguments
2021
import com.google.devtools.ksp.common.impl.KSNameImpl
2122
import com.google.devtools.ksp.common.toKSModifiers
2223
import com.google.devtools.ksp.processing.impl.KSObjectCache
@@ -96,8 +97,10 @@ class KSClassDeclarationJavaEnumEntryImpl private constructor(val psi: PsiEnumCo
9697

9798
// Enum can't have type parameters.
9899
override fun asType(typeArguments: List<KSTypeArgument>): KSType {
99-
if (typeArguments.isNotEmpty())
100-
return KSErrorType()
100+
errorTypeOnInconsistentArguments(
101+
arguments = typeArguments, placeholdersProvider = ::emptyList,
102+
withCorrectedArguments = ::asType, errorType = ::KSErrorType,
103+
)?.let { error -> return error }
101104
return asStarProjectedType()
102105
}
103106

compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/java/KSClassDeclarationJavaImpl.kt

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -153,17 +153,13 @@ class KSClassDeclarationJavaImpl private constructor(val psi: PsiClass) :
153153
}
154154

155155
override fun asType(typeArguments: List<KSTypeArgument>): KSType {
156-
return descriptor?.let {
157-
it.defaultType.replaceTypeArguments(typeArguments)?.let {
158-
getKSTypeCached(it, typeArguments)
159-
}
160-
} ?: KSErrorType()
156+
return descriptor?.defaultType?.replaceTypeArguments(typeArguments) ?: KSErrorType(psi.qualifiedName)
161157
}
162158

163159
override fun asStarProjectedType(): KSType {
164160
return descriptor?.let {
165161
getKSTypeCached(it.defaultType.replaceArgumentsWithStarProjections())
166-
} ?: KSErrorType()
162+
} ?: KSErrorType(psi.qualifiedName)
167163
}
168164

169165
override fun <D, R> accept(visitor: KSVisitor<D, R>, data: D): R {

compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/kotlin/KSClassDeclarationImpl.kt

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -122,9 +122,7 @@ class KSClassDeclarationImpl private constructor(val ktClassOrObject: KtClassOrO
122122
}
123123

124124
override fun asType(typeArguments: List<KSTypeArgument>): KSType {
125-
return descriptor.defaultType.replaceTypeArguments(typeArguments)?.let {
126-
getKSTypeCached(it, typeArguments)
127-
} ?: KSErrorType()
125+
return descriptor.defaultType.replaceTypeArguments(typeArguments)
128126
}
129127

130128
override fun asStarProjectedType(): KSType {

compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/kotlin/KSErrorType.kt

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,12 @@ import org.jetbrains.kotlin.types.error.ErrorType
2525
import org.jetbrains.kotlin.types.error.ErrorTypeKind
2626

2727
class KSErrorType(
28-
val nameHint: String? = null,
28+
val nameHint: String?,
2929
) : KSType {
30+
constructor(name: String, message: String?) : this(
31+
nameHint = listOfNotNull(name, message).takeIf { it.isNotEmpty() }?.joinToString(" % ")
32+
)
33+
3034
override val annotations: Sequence<KSAnnotation>
3135
get() = emptySequence()
3236

compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/kotlin/KSPropertyDeclarationImpl.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ class KSPropertyDeclarationImpl private constructor(val ktProperty: KtProperty)
9797
KSTypeReferenceDeferredImpl.getCached(this) {
9898
val desc = propertyDescriptor as? VariableDescriptorWithAccessors
9999
if (desc == null) {
100-
KSErrorType()
100+
KSErrorType(null /* no info available */)
101101
} else {
102102
getKSTypeCached(desc.type)
103103
}

compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/kotlin/KSTypeImpl.kt

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -95,9 +95,7 @@ class KSTypeImpl private constructor(
9595
}
9696

9797
override fun replace(arguments: List<KSTypeArgument>): KSType {
98-
return kotlinType.replaceTypeArguments(arguments)?.let {
99-
getKSTypeCached(it, arguments, annotations)
100-
} ?: KSErrorType()
98+
return kotlinType.replaceTypeArguments(arguments, annotations)
10199
}
102100

103101
override fun starProjection(): KSType {

compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/kotlin/KSValueParameterImpl.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,8 @@ class KSValueParameterImpl private constructor(val ktParameter: KtParameter) : K
112112

113113
override val type: KSTypeReference by lazy {
114114
ktParameter.typeReference?.let { KSTypeReferenceImpl.getCached(it) }
115-
?: findPropertyForAccessor()?.type ?: KSTypeReferenceSyntheticImpl.getCached(KSErrorType(), this)
115+
?: findPropertyForAccessor()?.type
116+
?: KSTypeReferenceSyntheticImpl.getCached(KSErrorType(null /* no info available */), this)
116117
}
117118

118119
override val hasDefault: Boolean = ktParameter.hasDefaultValue()

compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/utils.kt

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ package com.google.devtools.ksp.symbol.impl
1818

1919
import com.google.devtools.ksp.ExceptionMessage
2020
import com.google.devtools.ksp.KspExperimental
21+
import com.google.devtools.ksp.common.errorTypeOnInconsistentArguments
2122
import com.google.devtools.ksp.common.impl.KSNameImpl
2223
import com.google.devtools.ksp.processing.impl.ResolverImpl
2324
import com.google.devtools.ksp.symbol.*
@@ -136,10 +137,18 @@ fun org.jetbrains.kotlin.types.Variance.toKSVariance(): Variance {
136137

137138
private fun KSTypeReference.toKotlinType() = (resolve() as? KSTypeImpl)?.kotlinType
138139

139-
// returns null if error
140-
internal fun KotlinType.replaceTypeArguments(newArguments: List<KSTypeArgument>): KotlinType? {
141-
if (newArguments.isNotEmpty() && this.arguments.size != newArguments.size)
142-
return null
140+
// returns KSErrorType if error
141+
internal fun KotlinType.replaceTypeArguments(
142+
newArguments: List<KSTypeArgument>,
143+
annotations: Sequence<KSAnnotation> = emptySequence(),
144+
): KSType {
145+
errorTypeOnInconsistentArguments(
146+
arguments = newArguments,
147+
placeholdersProvider = { arguments.map { KSTypeArgumentDescriptorImpl.getCached(it, Origin.SYNTHETIC, null) } },
148+
withCorrectedArguments = ::replaceTypeArguments,
149+
errorType = ::KSErrorType,
150+
)?.let { error -> return error }
151+
143152
return replace(
144153
newArguments.mapIndexed { index, ksTypeArgument ->
145154
val variance = when (ksTypeArgument.variance) {
@@ -155,11 +164,11 @@ internal fun KotlinType.replaceTypeArguments(newArguments: List<KSTypeArgument>)
155164
else -> throw IllegalStateException(
156165
"Unexpected psi for type argument: ${ksTypeArgument.javaClass}, $ExceptionMessage"
157166
)
158-
}.toKotlinType() ?: return null
167+
}.let { it.toKotlinType() ?: return KSErrorType.fromReferenceBestEffort(it) }
159168

160169
TypeProjectionImpl(variance, type)
161170
}
162-
)
171+
).let { KSTypeImpl.getCached(it, newArguments, annotations) }
163172
}
164173

165174
internal fun FunctionDescriptor.toKSDeclaration(): KSDeclaration {

kotlin-analysis-api/src/main/kotlin/com/google/devtools/ksp/impl/symbol/kotlin/KSClassDeclarationImpl.kt

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
package com.google.devtools.ksp.impl.symbol.kotlin
1919

2020
import com.google.devtools.ksp.common.KSObjectCache
21+
import com.google.devtools.ksp.common.errorTypeOnInconsistentArguments
2122
import com.google.devtools.ksp.common.impl.KSNameImpl
2223
import com.google.devtools.ksp.common.impl.KSTypeReferenceSyntheticImpl
2324
import com.google.devtools.ksp.impl.ResolverAAImpl
@@ -124,9 +125,12 @@ class KSClassDeclarationImpl private constructor(internal val ktClassOrObjectSym
124125
}
125126

126127
override fun asType(typeArguments: List<KSTypeArgument>): KSType {
127-
if (typeArguments.isNotEmpty() && typeArguments.size != asStarProjectedType().arguments.size) {
128-
return KSErrorType()
129-
}
128+
errorTypeOnInconsistentArguments(
129+
arguments = typeArguments,
130+
placeholdersProvider = { asStarProjectedType().arguments },
131+
withCorrectedArguments = ::asType,
132+
errorType = ::KSErrorType,
133+
)?.let { error -> return error }
130134
return analyze {
131135
if (typeArguments.isEmpty()) {
132136
typeParameters.map { buildTypeParameterType((it as KSTypeParameterImpl).ktTypeParameterSymbol) }

kotlin-analysis-api/src/main/kotlin/com/google/devtools/ksp/impl/symbol/kotlin/KSErrorType.kt

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,12 @@ package com.google.devtools.ksp.impl.symbol.kotlin
1919
import com.google.devtools.ksp.symbol.*
2020

2121
class KSErrorType(
22-
private val hint: String? = null,
22+
private val hint: String?,
2323
) : KSType {
24+
constructor(name: String, message: String?) : this(
25+
hint = listOfNotNull(name, message).takeIf { it.isNotEmpty() }?.joinToString(" % ")
26+
)
27+
2428
override val declaration: KSDeclaration
2529
get() = KSErrorTypeClassDeclaration(this)
2630

kotlin-analysis-api/src/main/kotlin/com/google/devtools/ksp/impl/symbol/kotlin/KSErrorTypeClassDeclaration.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,7 @@ class KSErrorTypeClassDeclaration(
114114
return this === other || other is KSErrorTypeClassDeclaration && other.type == type
115115
}
116116

117-
override fun hashCode(): Int = type.hashCode()
117+
override fun hashCode(): Int = type.hashCode() * 2
118118

119119
override val docString
120120
get() = null

kotlin-analysis-api/src/main/kotlin/com/google/devtools/ksp/impl/symbol/kotlin/KSTypeImpl.kt

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ package com.google.devtools.ksp.impl.symbol.kotlin
1919

2020
import com.google.devtools.ksp.common.IdKeyPair
2121
import com.google.devtools.ksp.common.KSObjectCache
22+
import com.google.devtools.ksp.common.errorTypeOnInconsistentArguments
2223
import com.google.devtools.ksp.impl.ResolverAAImpl
2324
import com.google.devtools.ksp.impl.recordLookupWithSupertypes
2425
import com.google.devtools.ksp.impl.symbol.kotlin.resolved.KSTypeArgumentResolvedImpl
@@ -110,12 +111,17 @@ class KSTypeImpl private constructor(internal val type: KtType) : KSType {
110111
}
111112

112113
override fun replace(arguments: List<KSTypeArgument>): KSType {
113-
return type.replace(arguments.map { it.toKtTypeProjection() })?.let { getCached(it) } ?: KSErrorType()
114+
errorTypeOnInconsistentArguments(
115+
arguments = arguments,
116+
placeholdersProvider = { type.typeArguments().map { KSTypeArgumentResolvedImpl.getCached(it) } },
117+
withCorrectedArguments = ::replace,
118+
errorType = ::KSErrorType,
119+
)?.let { error -> return error }
120+
return getCached(type.replace(arguments.map { it.toKtTypeProjection() }))
114121
}
115122

116123
override fun starProjection(): KSType {
117-
return type.replace(List(type.typeArguments().size) { KtStarTypeProjection(type.token) })
118-
?.let { getCached(it) } ?: KSErrorType()
124+
return getCached(type.replace(List(type.typeArguments().size) { KtStarTypeProjection(type.token) }))
119125
}
120126

121127
override fun makeNullable(): KSType {
@@ -134,7 +140,8 @@ class KSTypeImpl private constructor(internal val type: KtType) : KSType {
134140
get() = type.nullability == KtTypeNullability.NULLABLE
135141

136142
override val isError: Boolean
137-
get() = type is KtErrorType
143+
// TODO: non exist type returns KtNonErrorClassType, check upstream for KtClassErrorType usage.
144+
get() = type is KtErrorType || type.classifierSymbol() == null
138145

139146
override val isFunctionType: Boolean
140147
get() = type is KtFunctionalType && !type.isSuspend

kotlin-analysis-api/src/main/kotlin/com/google/devtools/ksp/impl/symbol/kotlin/resolved/KSTypeReferenceResolvedImpl.kt

Lines changed: 1 addition & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -22,14 +22,11 @@ import com.google.devtools.ksp.common.KSObjectCache
2222
import com.google.devtools.ksp.impl.recordLookup
2323
import com.google.devtools.ksp.impl.symbol.kotlin.Deferrable
2424
import com.google.devtools.ksp.impl.symbol.kotlin.KSClassDeclarationImpl
25-
import com.google.devtools.ksp.impl.symbol.kotlin.KSErrorType
2625
import com.google.devtools.ksp.impl.symbol.kotlin.KSTypeImpl
2726
import com.google.devtools.ksp.impl.symbol.kotlin.KSTypeParameterImpl
2827
import com.google.devtools.ksp.impl.symbol.kotlin.Restorable
2928
import com.google.devtools.ksp.impl.symbol.kotlin.analyze
3029
import com.google.devtools.ksp.impl.symbol.kotlin.annotations
31-
import com.google.devtools.ksp.impl.symbol.kotlin.classifierSymbol
32-
import com.google.devtools.ksp.impl.symbol.kotlin.getNameHint
3330
import com.google.devtools.ksp.impl.symbol.kotlin.render
3431
import com.google.devtools.ksp.impl.symbol.kotlin.toClassifierReference
3532
import com.google.devtools.ksp.impl.symbol.kotlin.toLocation
@@ -61,22 +58,7 @@ class KSTypeReferenceResolvedImpl private constructor(
6158

6259
override fun resolve(): KSType {
6360
analyze { recordLookup(ktType, parent) }
64-
// TODO: non exist type returns KtNonErrorClassType, check upstream for KtClassErrorType usage.
65-
return if (
66-
analyze {
67-
ktType is KtClassErrorType || (ktType.classifierSymbol() == null)
68-
}
69-
) {
70-
KSErrorType(
71-
when (ktType) {
72-
is KtClassErrorType -> ktType.getNameHint()
73-
is KtTypeErrorType -> null // No info available
74-
else -> ktType.render()
75-
}
76-
)
77-
} else {
78-
KSTypeImpl.getCached(ktType)
79-
}
61+
return KSTypeImpl.getCached(ktType)
8062
}
8163

8264
override val annotations: Sequence<KSAnnotation> by lazy {

0 commit comments

Comments
 (0)