1 /*
<lambda>null2  * Copyright 2020 Google LLC
3  * Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  * http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */
17 
18 package com.google.devtools.ksp.symbol.impl.java
19 
20 import com.google.devtools.ksp.KSObjectCache
21 import com.google.devtools.ksp.getClassDeclarationByName
22 import com.google.devtools.ksp.processing.impl.KSNameImpl
23 import com.google.devtools.ksp.processing.impl.ResolverImpl
24 import com.google.devtools.ksp.symbol.*
25 import com.google.devtools.ksp.symbol.impl.binary.getAbsentDefaultArguments
26 import com.google.devtools.ksp.symbol.impl.binary.getDefaultConstructorArguments
27 import com.google.devtools.ksp.symbol.impl.kotlin.KSTypeImpl
28 import com.google.devtools.ksp.symbol.impl.toLocation
29 import com.intellij.lang.jvm.JvmClassKind
30 import com.intellij.psi.*
31 import org.jetbrains.kotlin.descriptors.ClassDescriptor
32 
33 class KSAnnotationJavaImpl private constructor(val psi: PsiAnnotation) : KSAnnotation {
34     companion object : KSObjectCache<PsiAnnotation, KSAnnotationJavaImpl>() {
35         fun getCached(psi: PsiAnnotation) = cache.getOrPut(psi) { KSAnnotationJavaImpl(psi) }
36     }
37 
38     override val origin = Origin.JAVA
39 
40     override val location: Location by lazy {
41         psi.toLocation()
42     }
43 
44     override val parent: KSNode? by lazy {
45         var parentPsi = psi.parent
46         while (true) {
47             when (parentPsi) {
48                 null, is PsiJavaFile, is PsiClass, is PsiMethod, is PsiParameter, is PsiTypeParameter, is PsiType ->
49                     break
50                 else -> parentPsi = parentPsi.parent
51             }
52         }
53         when (parentPsi) {
54             is PsiJavaFile -> KSFileJavaImpl.getCached(parentPsi)
55             is PsiClass -> KSClassDeclarationJavaImpl.getCached(parentPsi)
56             is PsiMethod -> KSFunctionDeclarationJavaImpl.getCached(parentPsi)
57             is PsiParameter -> KSValueParameterJavaImpl.getCached(parentPsi)
58             is PsiTypeParameter -> KSTypeParameterJavaImpl.getCached(parentPsi)
59             is PsiType ->
60                 if (parentPsi.parent is PsiClassType) KSTypeArgumentJavaImpl.getCached(parentPsi, this)
61                 else KSTypeReferenceJavaImpl.getCached(parentPsi, this)
62             else -> null
63         }
64     }
65 
66     override val annotationType: KSTypeReference by lazy {
67         KSTypeReferenceLiteJavaImpl.getCached(psi, this)
68     }
69 
70     override val arguments: List<KSValueArgument> by lazy {
71         val annotationConstructor = (
72             (annotationType.resolve() as? KSTypeImpl)?.kotlinType?.constructor
73                 ?.declarationDescriptor as? ClassDescriptor
74             )?.constructors?.single()
75         val presentValueArguments = psi.parameterList.attributes
76             .mapIndexed { index, it ->
77                 // use the name in the attribute if it is explicitly specified, otherwise, fall back to index.
78                 val name = it.name ?: annotationConstructor?.valueParameters?.getOrNull(index)?.name?.asString()
79                 val value = it.value
80                 val calculatedValue: Any? = if (value is PsiArrayInitializerMemberValue) {
81                     value.initializers.map {
82                         calcValue(it)
83                     }
84                 } else {
85                     calcValue(it.value)
86                 }
87                 KSValueArgumentJavaImpl.getCached(
88                     name = name?.let(KSNameImpl::getCached),
89                     value = calculatedValue,
90                     this
91                 )
92             }
93         val presentValueArgumentNames = presentValueArguments.map { it.name?.asString() ?: "" }
94         val argumentsFromDefault = annotationConstructor?.let {
95             it.getAbsentDefaultArguments(presentValueArgumentNames, this)
96         } ?: emptyList()
97         presentValueArguments.plus(argumentsFromDefault)
98     }
99 
100     override val defaultArguments: List<KSValueArgument> by lazy {
101         val kotlinType = (annotationType.resolve() as? KSTypeImpl)?.kotlinType
102         kotlinType?.getDefaultConstructorArguments(emptyList(), this) ?: emptyList()
103     }
104 
105     private fun calcValue(value: PsiAnnotationMemberValue?): Any? {
106         if (value is PsiAnnotation) {
107             return getCached(value)
108         }
109         val result = when (value) {
110             is PsiReference -> value.resolve()?.let { resolved ->
111                 JavaPsiFacade.getInstance(value.project).constantEvaluationHelper.computeConstantExpression(value)
112                     ?: resolved
113             }
114             else -> value?.let {
115                 JavaPsiFacade.getInstance(value.project).constantEvaluationHelper.computeConstantExpression(value)
116             }
117         }
118         return when (result) {
119             is PsiType -> {
120                 ResolverImpl.instance!!.resolveJavaTypeInAnnotations(result)
121             }
122             is PsiLiteralValue -> {
123                 result.value
124             }
125             is PsiField -> {
126                 // manually handle enums as constant expression evaluator does not seem to be resolving them.
127                 val containingClass = result.containingClass
128                 if (containingClass?.classKind == JvmClassKind.ENUM) {
129                     // this is an enum entry
130                     containingClass.qualifiedName?.let {
131                         ResolverImpl.instance!!.getClassDeclarationByName(it)
132                     }?.declarations?.find {
133                         it is KSClassDeclaration && it.classKind == ClassKind.ENUM_ENTRY &&
134                             it.simpleName.asString() == result.name
135                     }?.let { (it as KSClassDeclaration).asStarProjectedType() }
136                         ?.let {
137                             return it
138                         }
139                 } else {
140                     null
141                 }
142             }
143             else -> result
144         }
145     }
146 
147     override val shortName: KSName by lazy {
148         KSNameImpl.getCached(psi.qualifiedName!!.split(".").last())
149     }
150 
151     override val useSiteTarget: AnnotationUseSiteTarget? = null
152 
153     override fun <D, R> accept(visitor: KSVisitor<D, R>, data: D): R {
154         return visitor.visitAnnotation(this, data)
155     }
156 
157     override fun toString(): String {
158         return "@${shortName.asString()}"
159     }
160 }
161