Skip to content

Commit 1315700

Browse files
committed
Kapt3: Generate import statements in stubs
1 parent 69a063b commit 1315700

File tree

10 files changed

+138
-5
lines changed

10 files changed

+138
-5
lines changed

plugins/kapt3/src/org/jetbrains/kotlin/kapt3/KaptContext.kt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ class KaptContext(
4040
val compiler: KaptJavaCompiler
4141
val fileManager: JavacFileManager
4242
val options: Options
43+
val javaLog: KaptJavaLog
4344

4445
init {
4546
KaptJavaLog.preRegister(context, logger.messageCollector)
@@ -50,6 +51,8 @@ class KaptContext(
5051
fileManager = context.get(JavaFileManager::class.java) as JavacFileManager
5152
compiler = JavaCompiler.instance(context) as KaptJavaCompiler
5253

54+
javaLog = compiler.log as KaptJavaLog
55+
5356
options = Options.instance(context)
5457
for ((key, value) in processorOptions) {
5558
val option = if (value.isEmpty()) "-A$key" else "-A$key=$value"

plugins/kapt3/src/org/jetbrains/kotlin/kapt3/annotationProcessing.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ fun KaptContext.doAnnotationProcessing(
6565
val parsedJavaFiles = compiler.parseFiles(javaFileObjects)
6666

6767
compilerAfterAP = try {
68+
javaLog.interceptorData.files = parsedJavaFiles.map { it.sourceFile to it }.toMap()
6869
val analyzedFiles = compiler.stopIfErrorOccurred(
6970
CompileStates.CompileState.PARSE, compiler.enterTrees(parsedJavaFiles + additionalSources))
7071
compiler.processAnnotations(analyzedFiles)

plugins/kapt3/src/org/jetbrains/kotlin/kapt3/javac/KaptJavaLog.kt

Lines changed: 47 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
package org.jetbrains.kotlin.kapt3.javac
1818

19+
import com.sun.tools.javac.tree.JCTree
1920
import com.sun.tools.javac.util.Context
2021
import com.sun.tools.javac.util.JCDiagnostic
2122
import com.sun.tools.javac.util.Log
@@ -24,12 +25,14 @@ import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity.*
2425
import org.jetbrains.kotlin.cli.common.messages.MessageCollector
2526
import org.jetbrains.kotlin.kapt3.util.MessageCollectorBackedWriter
2627
import java.io.PrintWriter
28+
import javax.tools.JavaFileObject
2729

2830
class KaptJavaLog(
2931
context: Context,
3032
errWriter: PrintWriter,
3133
warnWriter: PrintWriter,
32-
noticeWriter: PrintWriter
34+
noticeWriter: PrintWriter,
35+
val interceptorData: DiagnosticInterceptorData
3336
) : Log(context, errWriter, warnWriter, noticeWriter) {
3437
init {
3538
context.put(Log.outKey, noticeWriter)
@@ -40,26 +43,67 @@ class KaptJavaLog(
4043
return
4144
}
4245

46+
val targetElement = diagnostic.diagnosticPosition
47+
if (diagnostic.code.contains("err.cant.resolve") && targetElement != null) {
48+
val sourceFile = interceptorData.files[diagnostic.source]
49+
if (sourceFile != null) {
50+
val insideImports = targetElement.tree in sourceFile.imports
51+
// Ignore resolve errors in import statements
52+
if (insideImports) return
53+
}
54+
}
55+
4356
super.report(diagnostic)
4457
}
4558

59+
private operator fun <T : JCTree> Iterable<T>.contains(element: JCTree?): Boolean {
60+
if (element == null) {
61+
return false
62+
}
63+
64+
var found = false
65+
val visitor = object : JCTree.Visitor() {
66+
override fun visitImport(that: JCTree.JCImport) {
67+
super.visitImport(that)
68+
if (!found) that.qualid.accept(this)
69+
}
70+
71+
override fun visitSelect(that: JCTree.JCFieldAccess) {
72+
super.visitSelect(that)
73+
if (!found) that.selected.accept(this)
74+
}
75+
76+
override fun visitTree(that: JCTree) {
77+
if (!found && element == that) found = true
78+
}
79+
}
80+
this.forEach { if (!found) it.accept(visitor) }
81+
return found
82+
}
83+
4684
companion object {
4785
private val IGNORED_DIAGNOSTICS = setOf(
4886
"compiler.err.name.clash.same.erasure",
4987
"compiler.err.name.clash.same.erasure.no.override",
5088
"compiler.err.name.clash.same.erasure.no.override.1",
5189
"compiler.err.name.clash.same.erasure.no.hide",
5290
"compiler.err.already.defined",
53-
"compiler.err.annotation.type.not.applicable")
91+
"compiler.err.annotation.type.not.applicable",
92+
"compiler.err.doesnt.exist")
5493

5594
internal fun preRegister(context: Context, messageCollector: MessageCollector) {
95+
val interceptorData = DiagnosticInterceptorData()
5696
context.put(Log.logKey, Context.Factory<Log> {
5797
fun makeWriter(severity: CompilerMessageSeverity) = PrintWriter(MessageCollectorBackedWriter(messageCollector, severity))
5898
val errWriter = makeWriter(ERROR)
5999
val warnWriter = makeWriter(STRONG_WARNING)
60100
val noticeWriter = makeWriter(INFO)
61-
KaptJavaLog(it, errWriter, warnWriter, noticeWriter)
101+
KaptJavaLog(it, errWriter, warnWriter, noticeWriter, interceptorData)
62102
})
63103
}
64104
}
105+
106+
class DiagnosticInterceptorData {
107+
var files: Map<JavaFileObject, JCTree.JCCompilationUnit> = emptyMap()
108+
}
65109
}

plugins/kapt3/src/org/jetbrains/kotlin/kapt3/javac/KaptTreeMaker.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ import org.jetbrains.org.objectweb.asm.Type
2626
import org.jetbrains.org.objectweb.asm.Type.*
2727

2828
class KaptTreeMaker(context: Context) : TreeMaker(context) {
29-
private val nameTable = Names.instance(context).table
29+
val nameTable = Names.instance(context).table
3030

3131
fun Type(type: Type): JCTree.JCExpression {
3232
convertBuiltinType(type)?.let { return it }

plugins/kapt3/src/org/jetbrains/kotlin/kapt3/stubs/ClassFileToSourceStubConverter.kt

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,9 @@ import org.jetbrains.kotlin.kapt3.*
2929
import org.jetbrains.kotlin.kapt3.javac.KaptTreeMaker
3030
import org.jetbrains.kotlin.kapt3.javac.KaptJavaFileObject
3131
import org.jetbrains.kotlin.kapt3.util.*
32+
import org.jetbrains.kotlin.name.FqName
3233
import org.jetbrains.kotlin.psi.*
34+
import org.jetbrains.kotlin.resolve.BindingContext
3335
import org.jetbrains.kotlin.resolve.DescriptorUtils
3436
import org.jetbrains.kotlin.resolve.descriptorUtil.getSuperClassNotAny
3537
import org.jetbrains.kotlin.resolve.descriptorUtil.getSuperClassOrAny
@@ -131,7 +133,7 @@ class ClassFileToSourceStubConverter(
131133

132134
val classDeclaration = convertClass(clazz, packageName, true) ?: return null
133135

134-
val imports = JavacList.nil<JCTree>()
136+
val imports = if (correctErrorTypes) convertImports(ktFile, classDeclaration) else JavacList.nil()
135137
val classes = JavacList.of<JCTree>(classDeclaration)
136138

137139
val topLevel = treeMaker.TopLevel(packageAnnotations, packageClause, imports + classes)
@@ -144,6 +146,44 @@ class ClassFileToSourceStubConverter(
144146
return topLevel
145147
}
146148

149+
private fun convertImports(file: KtFile, classDeclaration: JCClassDecl): JavacList<JCTree> {
150+
val imports = mutableListOf<JCTree>()
151+
152+
for (importDirective in file.importDirectives) {
153+
// Qualified name should be valid Java fq-name
154+
val importedFqName = importDirective.importedFqName ?: continue
155+
if (!isValidQualifiedName(importedFqName)) continue
156+
157+
val shortName = importedFqName.shortName()
158+
if (shortName.asString() == classDeclaration.simpleName.toString()) continue
159+
160+
// If alias is specified, it also should be valid Java name
161+
val aliasName = importDirective.aliasName
162+
if (aliasName != null /*TODO support aliases */ /*&& getValidIdentifierName(aliasName) == null*/) continue
163+
164+
val importedReference = getReferenceExpression(importDirective.importedReference)
165+
?.let { kaptContext.bindingContext[BindingContext.REFERENCE_TARGET, it] }
166+
167+
if (importedReference is CallableDescriptor) continue
168+
169+
val importedExpr = treeMaker.FqName(importedFqName.asString())
170+
171+
imports += if (importDirective.isAllUnder) {
172+
treeMaker.Import(treeMaker.Select(importedExpr, treeMaker.nameTable.names.asterisk), false)
173+
} else {
174+
treeMaker.Import(importedExpr, false)
175+
}
176+
}
177+
178+
return JavacList.from(imports)
179+
}
180+
181+
private tailrec fun getReferenceExpression(expression: KtExpression?): KtReferenceExpression? = when (expression) {
182+
is KtReferenceExpression -> expression
183+
is KtQualifiedExpression -> getReferenceExpression(expression.selectorExpression)
184+
else -> null
185+
}
186+
147187
/**
148188
* Returns false for the inner classes or if the origin for the class was not found.
149189
*/
@@ -456,6 +496,10 @@ class ClassFileToSourceStubConverter(
456496
return ifNonError()
457497
}
458498

499+
private fun isValidQualifiedName(name: FqName): Boolean {
500+
return name.pathSegments().all { getValidIdentifierName(it.asString(), false) != null }
501+
}
502+
459503
fun getValidIdentifierName(name: String, canBeConstructor: Boolean = false): String? {
460504
if (canBeConstructor && name == "<init>") {
461505
return name

plugins/kapt3/test/org/jetbrains/kotlin/kapt3/test/AbstractKotlinKapt3Test.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,7 @@ abstract class AbstractClassFileToSourceStubConverterTest : AbstractKotlinKapt3T
9696

9797
val javaFiles = convert(kaptRunner, typeMapper, generateNonExistentClass, correctErrorTypes)
9898

99+
kaptRunner.javaLog.interceptorData.files = javaFiles.map { it.sourceFile to it }.toMap()
99100
if (validate) kaptRunner.compiler.enterTrees(javaFiles)
100101

101102
val actualRaw = javaFiles.sortedBy { it.sourceFile.name }.joinToString (FILE_SEPARATOR)

plugins/kapt3/test/org/jetbrains/kotlin/kapt3/test/ClassFileToSourceStubConverterTestGenerated.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,12 @@ public void testGenericSimple() throws Exception {
102102
doTest(fileName);
103103
}
104104

105+
@TestMetadata("importsForErrorTypes.kt")
106+
public void testImportsForErrorTypes() throws Exception {
107+
String fileName = KotlinTestUtils.navigationMetadata("plugins/kapt3/testData/converter/importsForErrorTypes.kt");
108+
doTest(fileName);
109+
}
110+
105111
@TestMetadata("inheritanceSimple.kt")
106112
public void testInheritanceSimple() throws Exception {
107113
String fileName = KotlinTestUtils.navigationMetadata("plugins/kapt3/testData/converter/inheritanceSimple.kt");
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
// CORRECT_ERROR_TYPES
2+
3+
import java.util.zip.*
4+
import java.util.Date
5+
import java.util.concurrent.TimeUnit
6+
import java.util.concurrent.TimeUnit.MICROSECONDS
7+
import java.util.concurrent.TimeUnit.*
8+
import java.util.Calendar.*
9+
10+
fun test(): Any? {
11+
return null
12+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import java.util.zip.*;
2+
import java.util.Date;
3+
import java.util.concurrent.TimeUnit;
4+
import java.util.concurrent.TimeUnit.MICROSECONDS;
5+
import java.util.concurrent.TimeUnit.*;
6+
import java.util.Calendar.*;
7+
8+
public final class ImportsForErrorTypesKt {
9+
10+
public ImportsForErrorTypesKt() {
11+
super();
12+
}
13+
14+
@org.jetbrains.annotations.Nullable()
15+
public static final java.lang.Object test() {
16+
return null;
17+
}
18+
}

plugins/kapt3/testData/converter/nonExistentClassTypesConversion.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import java.util.Calendar;
2+
13
public final class MyType<T extends java.lang.Object> {
24

35
public MyType() {
@@ -8,6 +10,8 @@ public final class MyType<T extends java.lang.Object> {
810
////////////////////
911

1012

13+
import java.util.Calendar;
14+
1115
@kotlin.Suppress(names = {"UNRESOLVED_REFERENCE"})
1216
public final class Test {
1317
@org.jetbrains.annotations.NotNull()

0 commit comments

Comments
 (0)