From 72bbf9697bf8fc52de0208742ed6a9f7c32d60b3 Mon Sep 17 00:00:00 2001 From: Yan Zhulanow Date: Sat, 21 Jan 2017 13:59:39 +0900 Subject: [PATCH] Kapt3: As javac now reads stubs from Java files, not from AST, we should provide the valid names for all identifiers (at least names should not clash with Java keywords). Rename identifiers if it clashes with Java keywords. (KT-15838) --- .../stubs/ClassFileToSourceStubConverter.kt | 22 +++++++++++--- .../kotlin/kapt3/stubs/Kapt3StubUtils.java | 29 +++++++++++++++++++ ...ileToSourceStubConverterTestGenerated.java | 6 ++++ .../kapt3/testData/converter/javaKeywords.kt | 4 +++ .../kapt3/testData/converter/javaKeywords.txt | 14 +++++++++ plugins/kapt3/testData/converter/kt14998.txt | 2 +- 6 files changed, 72 insertions(+), 5 deletions(-) create mode 100644 plugins/kapt3/src/org/jetbrains/kotlin/kapt3/stubs/Kapt3StubUtils.java create mode 100644 plugins/kapt3/testData/converter/javaKeywords.kt create mode 100644 plugins/kapt3/testData/converter/javaKeywords.txt diff --git a/plugins/kapt3/src/org/jetbrains/kotlin/kapt3/stubs/ClassFileToSourceStubConverter.kt b/plugins/kapt3/src/org/jetbrains/kotlin/kapt3/stubs/ClassFileToSourceStubConverter.kt index 42823f59e2b..aa3f839c4ea 100644 --- a/plugins/kapt3/src/org/jetbrains/kotlin/kapt3/stubs/ClassFileToSourceStubConverter.kt +++ b/plugins/kapt3/src/org/jetbrains/kotlin/kapt3/stubs/ClassFileToSourceStubConverter.kt @@ -19,6 +19,7 @@ package org.jetbrains.kotlin.kapt3.stubs import com.sun.tools.javac.code.Flags import com.sun.tools.javac.code.TypeTag import com.sun.tools.javac.file.JavacFileManager +import com.sun.tools.javac.parser.Tokens import com.sun.tools.javac.tree.JCTree import com.sun.tools.javac.tree.JCTree.* import com.sun.tools.javac.tree.TreeMaker @@ -27,6 +28,7 @@ import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.kapt3.* import org.jetbrains.kotlin.kapt3.javac.KaptTreeMaker import org.jetbrains.kotlin.kapt3.javac.KaptJavaFileObject +import org.jetbrains.kotlin.kapt3.stubs.Kapt3StubUtils.* import org.jetbrains.kotlin.kapt3.util.* import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.resolve.DescriptorUtils @@ -69,6 +71,12 @@ class ClassFileToSourceStubConverter( "org.jetbrains.annotations.", // Nullable/NotNull, ReadOnly, Mutable "kotlin.jvm.", "kotlin.Metadata" // Kotlin annotations from runtime ) + + private val JAVA_KEYWORD_FILTER_REGEX = "[a-z]+".toRegex() + + private val JAVA_KEYWORDS = Tokens.TokenKind.values() + .filter { JAVA_KEYWORD_FILTER_REGEX.matches(getTokenName(it)) } + .mapTo(hashSetOf(), ::getTokenName) } private val _bindings = mutableMapOf() @@ -210,7 +218,7 @@ class ClassFileToSourceStubConverter( return treeMaker.ClassDef( modifiers, - treeMaker.name(simpleName), + treeMaker.name(getNonKeywordName(simpleName)), genericType.typeParameters, if (hasSuperClass) genericType.superClass else null, genericType.interfaces, @@ -222,7 +230,7 @@ class ClassFileToSourceStubConverter( val descriptor = kaptContext.origins[field]?.descriptor val modifiers = convertModifiers(field.access, ElementKind.FIELD, packageFqName, field.visibleAnnotations, field.invisibleAnnotations) - val name = treeMaker.name(field.name) + val name = treeMaker.name(getNonKeywordName(field.name)) val type = Type.getType(field.desc) // Enum type must be an identifier (Javac requirement) @@ -260,7 +268,7 @@ class ClassFileToSourceStubConverter( } val isConstructor = method.name == "" - val name = treeMaker.name(method.name) + val name = treeMaker.name(getNonKeywordName(method.name)) val modifiers = convertModifiers( if (containingClass.isEnum() && isConstructor) @@ -286,7 +294,7 @@ class ClassFileToSourceStubConverter( info.visibleAnnotations, info.invisibleAnnotations) - val name = treeMaker.name(info.name) + val name = treeMaker.name(getNonKeywordName(info.name)) val type = treeMaker.Type(info.type) treeMaker.VarDef(modifiers, name, type, null) } @@ -394,6 +402,12 @@ class ClassFileToSourceStubConverter( return ifNonError() } + private fun getNonKeywordName(name: String): String { + // In theory, this could lead to member/parameter name clashes, though it's supposed to be extremely rare. + if (name in JAVA_KEYWORDS) return '_' + name + '_' + return name + } + private inline fun getNotAnonymousType(descriptor: DeclarationDescriptor?, f: () -> T): T { if (descriptor is CallableDescriptor) { val returnTypeDescriptor = descriptor.returnType?.constructor?.declarationDescriptor diff --git a/plugins/kapt3/src/org/jetbrains/kotlin/kapt3/stubs/Kapt3StubUtils.java b/plugins/kapt3/src/org/jetbrains/kotlin/kapt3/stubs/Kapt3StubUtils.java new file mode 100644 index 00000000000..52fc163e8dc --- /dev/null +++ b/plugins/kapt3/src/org/jetbrains/kotlin/kapt3/stubs/Kapt3StubUtils.java @@ -0,0 +1,29 @@ +/* + * Copyright 2010-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.kapt3.stubs; + +import com.sun.tools.javac.parser.Tokens; +import org.jetbrains.annotations.NotNull; + +public class Kapt3StubUtils { + @NotNull + public static String getTokenName(@NotNull Tokens.TokenKind kind) { + // This is a work-around. `name` can't be called from Kotlin, as it clashes with the `name` enum property. + String name = kind.name; + return (name == null) ? "" : name; + } +} diff --git a/plugins/kapt3/test/org/jetbrains/kotlin/kapt3/test/ClassFileToSourceStubConverterTestGenerated.java b/plugins/kapt3/test/org/jetbrains/kotlin/kapt3/test/ClassFileToSourceStubConverterTestGenerated.java index fee967332dc..5093ec4d662 100644 --- a/plugins/kapt3/test/org/jetbrains/kotlin/kapt3/test/ClassFileToSourceStubConverterTestGenerated.java +++ b/plugins/kapt3/test/org/jetbrains/kotlin/kapt3/test/ClassFileToSourceStubConverterTestGenerated.java @@ -108,6 +108,12 @@ public class ClassFileToSourceStubConverterTestGenerated extends AbstractClassFi doTest(fileName); } + @TestMetadata("javaKeywords.kt") + public void testJavaKeywords() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("plugins/kapt3/testData/converter/javaKeywords.kt"); + doTest(fileName); + } + @TestMetadata("jvmOverloads.kt") public void testJvmOverloads() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("plugins/kapt3/testData/converter/jvmOverloads.kt"); diff --git a/plugins/kapt3/testData/converter/javaKeywords.kt b/plugins/kapt3/testData/converter/javaKeywords.kt new file mode 100644 index 00000000000..d380d3ded25 --- /dev/null +++ b/plugins/kapt3/testData/converter/javaKeywords.kt @@ -0,0 +1,4 @@ +class default { + lateinit val extends: String + fun implements(instanceof: Int) {} +} \ No newline at end of file diff --git a/plugins/kapt3/testData/converter/javaKeywords.txt b/plugins/kapt3/testData/converter/javaKeywords.txt new file mode 100644 index 00000000000..c5755b6b92d --- /dev/null +++ b/plugins/kapt3/testData/converter/javaKeywords.txt @@ -0,0 +1,14 @@ +public final class _default_ { + public java.lang.String _extends_; + + public final java.lang.String getExtends() { + return null; + } + + public final void _implements_(int _instanceof_) { + } + + public _default_() { + super(); + } +} diff --git a/plugins/kapt3/testData/converter/kt14998.txt b/plugins/kapt3/testData/converter/kt14998.txt index e2767d2a172..1692663ba7c 100644 --- a/plugins/kapt3/testData/converter/kt14998.txt +++ b/plugins/kapt3/testData/converter/kt14998.txt @@ -3,7 +3,7 @@ public final class Outer { public final void nonAbstract(java.lang.String s, int i) { } - public abstract void abstract(java.lang.String p0, int p1); + public abstract void _abstract_(java.lang.String p0, int p1); public Outer() { super();