1 /*
2 * Copyright 2022 Google LLC
3 * Copyright 2010-2022 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
19
20 import com.google.devtools.ksp.symbol.*
21 import com.intellij.lang.jvm.JvmModifier
22 import com.intellij.openapi.project.Project
23 import com.intellij.psi.PsiComment
24 import com.intellij.psi.PsiDocumentManager
25 import com.intellij.psi.PsiElement
26 import com.intellij.psi.PsiFile
27 import com.intellij.psi.PsiModifierListOwner
28 import org.jetbrains.kotlin.lexer.KtTokens
29 import org.jetbrains.kotlin.psi.KtClass
30 import org.jetbrains.kotlin.psi.KtClassOrObject
31 import org.jetbrains.kotlin.psi.KtEnumEntry
32 import org.jetbrains.kotlin.psi.KtModifierList
33 import org.jetbrains.kotlin.psi.KtModifierListOwner
34 import org.jetbrains.kotlin.psi.KtObjectDeclaration
35 import org.jetbrains.kotlin.psi.psiUtil.siblings
36
37 val jvmModifierMap = mapOf(
38 JvmModifier.PUBLIC to Modifier.PUBLIC,
39 JvmModifier.PRIVATE to Modifier.PRIVATE,
40 JvmModifier.ABSTRACT to Modifier.ABSTRACT,
41 JvmModifier.FINAL to Modifier.FINAL,
42 JvmModifier.PROTECTED to Modifier.PROTECTED,
43 JvmModifier.STATIC to Modifier.JAVA_STATIC,
44 JvmModifier.STRICTFP to Modifier.JAVA_STRICT,
45 JvmModifier.NATIVE to Modifier.JAVA_NATIVE,
46 JvmModifier.SYNCHRONIZED to Modifier.JAVA_SYNCHRONIZED,
47 JvmModifier.TRANSIENT to Modifier.JAVA_TRANSIENT,
48 JvmModifier.VOLATILE to Modifier.JAVA_VOLATILE
49 )
50
51 val javaModifiers = setOf(
52 Modifier.ABSTRACT,
53 Modifier.FINAL,
54 Modifier.JAVA_DEFAULT,
55 Modifier.JAVA_NATIVE,
56 Modifier.JAVA_STATIC,
57 Modifier.JAVA_STRICT,
58 Modifier.JAVA_SYNCHRONIZED,
59 Modifier.JAVA_TRANSIENT,
60 Modifier.JAVA_VOLATILE,
61 Modifier.PRIVATE,
62 Modifier.PROTECTED,
63 Modifier.PUBLIC,
64 )
65
66 val modifierMap = mapOf(
67 KtTokens.PUBLIC_KEYWORD to Modifier.PUBLIC,
68 KtTokens.PRIVATE_KEYWORD to Modifier.PRIVATE,
69 KtTokens.INTERNAL_KEYWORD to Modifier.INTERNAL,
70 KtTokens.PROTECTED_KEYWORD to Modifier.PROTECTED,
71 KtTokens.IN_KEYWORD to Modifier.IN,
72 KtTokens.OUT_KEYWORD to Modifier.OUT,
73 KtTokens.OVERRIDE_KEYWORD to Modifier.OVERRIDE,
74 KtTokens.LATEINIT_KEYWORD to Modifier.LATEINIT,
75 KtTokens.ENUM_KEYWORD to Modifier.ENUM,
76 KtTokens.SEALED_KEYWORD to Modifier.SEALED,
77 KtTokens.ANNOTATION_KEYWORD to Modifier.ANNOTATION,
78 KtTokens.DATA_KEYWORD to Modifier.DATA,
79 KtTokens.INNER_KEYWORD to Modifier.INNER,
80 KtTokens.FUN_KEYWORD to Modifier.FUN,
81 KtTokens.VALUE_KEYWORD to Modifier.VALUE,
82 KtTokens.SUSPEND_KEYWORD to Modifier.SUSPEND,
83 KtTokens.TAILREC_KEYWORD to Modifier.TAILREC,
84 KtTokens.OPERATOR_KEYWORD to Modifier.OPERATOR,
85 KtTokens.INFIX_KEYWORD to Modifier.INFIX,
86 KtTokens.INLINE_KEYWORD to Modifier.INLINE,
87 KtTokens.EXTERNAL_KEYWORD to Modifier.EXTERNAL,
88 KtTokens.ABSTRACT_KEYWORD to Modifier.ABSTRACT,
89 KtTokens.FINAL_KEYWORD to Modifier.FINAL,
90 KtTokens.OPEN_KEYWORD to Modifier.OPEN,
91 KtTokens.VARARG_KEYWORD to Modifier.VARARG,
92 KtTokens.NOINLINE_KEYWORD to Modifier.NOINLINE,
93 KtTokens.CROSSINLINE_KEYWORD to Modifier.CROSSINLINE,
94 KtTokens.REIFIED_KEYWORD to Modifier.REIFIED,
95 KtTokens.EXPECT_KEYWORD to Modifier.EXPECT,
96 KtTokens.ACTUAL_KEYWORD to Modifier.ACTUAL,
97 KtTokens.CONST_KEYWORD to Modifier.CONST
98 )
99
toKSModifiersnull100 fun KtModifierList?.toKSModifiers(): Set<Modifier> {
101 if (this == null)
102 return emptySet()
103 val modifiers = mutableSetOf<Modifier>()
104 modifiers.addAll(
105 modifierMap.entries
106 .filter { hasModifier(it.key) }
107 .map { it.value }
108 )
109 return modifiers
110 }
111
KtModifierListOwnernull112 fun KtModifierListOwner.toKSModifiers(): Set<Modifier> {
113 val modifierList = this.modifierList
114 return modifierList.toKSModifiers()
115 }
116
toKSModifiersnull117 fun PsiModifierListOwner.toKSModifiers(): Set<Modifier> {
118 val modifiers = mutableSetOf<Modifier>()
119 modifiers.addAll(
120 jvmModifierMap.entries.filter { this.hasModifier(it.key) }
121 .map { it.value }
122 .toSet()
123 )
124 if (this.modifierList?.hasExplicitModifier("default") == true) {
125 modifiers.add(Modifier.JAVA_DEFAULT)
126 }
127 return modifiers
128 }
129
Projectnull130 fun Project.findLocationString(file: PsiFile, offset: Int): String {
131 val psiDocumentManager = PsiDocumentManager.getInstance(this)
132 val document = psiDocumentManager.getDocument(file) ?: return "<unknown>"
133 val lineNumber = document.getLineNumber(offset)
134 val offsetInLine = offset - document.getLineStartOffset(lineNumber)
135 return "${file.virtualFile.path}: (${lineNumber + 1}, ${offsetInLine + 1})"
136 }
137
parseDocStringnull138 private fun parseDocString(raw: String): String? {
139 val t1 = raw.trim()
140 if (!t1.startsWith("/**") || !t1.endsWith("*/"))
141 return null
142 val lineSep = t1.findAnyOf(listOf("\r\n", "\n", "\r"))?.second ?: ""
143 return t1.trim('/').trim('*').lines().joinToString(lineSep) {
144 it.trimStart().trimStart('*')
145 }
146 }
147
getDocStringnull148 fun PsiElement.getDocString(): String? =
149 this.firstChild.siblings().firstOrNull { it is PsiComment }?.let {
150 parseDocString(it.text)
151 }
152
KtClassOrObjectnull153 fun KtClassOrObject.getClassType(): ClassKind {
154 return when (this) {
155 is KtObjectDeclaration -> ClassKind.OBJECT
156 is KtEnumEntry -> ClassKind.ENUM_ENTRY
157 is KtClass -> when {
158 this.isEnum() -> ClassKind.ENUM_CLASS
159 this.isInterface() -> ClassKind.INTERFACE
160 this.isAnnotation() -> ClassKind.ANNOTATION_CLASS
161 else -> ClassKind.CLASS
162 }
163 else -> throw IllegalStateException("Unexpected psi type ${this.javaClass}, $ExceptionMessage")
164 }
165 }
166
findParentOfTypenull167 inline fun <reified T> PsiElement.findParentOfType(): T? {
168 var parent = this.parent
169 while (parent != null && parent !is T) {
170 parent = parent.parent
171 }
172 return parent as? T
173 }
174
memoizednull175 fun <T> Sequence<T>.memoized() = MemoizedSequence(this)
176