1 /*
<lambda>null2  * Copyright (C) 2021 Square, Inc.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * https://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package com.squareup.kotlinpoet.ksp
17 
18 import com.google.devtools.ksp.symbol.KSType
19 import com.google.devtools.ksp.symbol.KSTypeParameter
20 import com.squareup.kotlinpoet.TypeVariableName
21 
22 /**
23  * A resolver for enclosing declarations' type parameters. Parent declarations can be anything with
24  * generics that child nodes declare as defined by [KSType.arguments].
25  *
26  * This is important for resolving inherited generics on child declarations, as KSP interop
27  * otherwise can't resolve them.
28  *
29  * In general, you want to retrieve an instance of this via [toTypeParameterResolver].
30  *
31  * @see toTypeParameterResolver
32  */
33 public interface TypeParameterResolver {
34   public val parametersMap: Map<String, TypeVariableName>
35   public operator fun get(index: String): TypeVariableName
36 
37   public companion object {
38     /**
39      * An empty instance of [TypeParameterResolver], only should be used if enclosing declarations
40      * are known to not have arguments, such as top-level classes.
41      */
42     public val EMPTY: TypeParameterResolver = object : TypeParameterResolver {
43       override val parametersMap: Map<String, TypeVariableName> = emptyMap()
44 
45       override fun get(index: String): TypeVariableName = throw NoSuchElementException("No TypeParameter found for index $index")
46     }
47   }
48 }
49 
50 /**
51  * Returns a [TypeParameterResolver] for this list of [KSTypeParameters][KSTypeParameter] for use
52  * with enclosed declarations.
53  *
54  * @param parent the optional parent resolver, if any. An example of this is cases where you might
55  *               create a resolver for a [KSFunction] and supply a parent resolved from the
56  *               enclosing [KSClassDeclaration].
57  * @param sourceTypeHint an optional hint for error messages. Unresolvable parameter IDs will
58  *                       include this hint in the thrown error's message.
59  */
toTypeParameterResolvernull60 public fun List<KSTypeParameter>.toTypeParameterResolver(
61   parent: TypeParameterResolver? = null,
62   sourceTypeHint: String = "<unknown>",
63 ): TypeParameterResolver {
64   val parametersMap = LinkedHashMap<String, TypeVariableName>()
65   val typeParamResolver = { id: String ->
66     parametersMap[id]
67       ?: parent?.get(id)
68       ?: throw IllegalStateException(
69         "No type argument found for $id! Analyzed $sourceTypeHint with known parameters " +
70           "${parametersMap.keys}",
71       )
72   }
73 
74   val resolver = object : TypeParameterResolver {
75     override val parametersMap: Map<String, TypeVariableName> = parametersMap
76 
77     override operator fun get(index: String): TypeVariableName = typeParamResolver(index)
78   }
79 
80   // Fill the parametersMap. Need to do sequentially and allow for referencing previously defined params
81   for (typeVar in this) {
82     // Put the simple typevar in first, then it can be referenced in the full toTypeVariable()
83     // replacement later that may add bounds referencing this.
84     val id = typeVar.name.getShortName()
85     parametersMap[id] = TypeVariableName(id)
86   }
87 
88   for (typeVar in this) {
89     val id = typeVar.name.getShortName()
90     // Now replace it with the full version.
91     parametersMap[id] = typeVar.toTypeVariableName(resolver)
92   }
93 
94   return resolver
95 }
96