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)
This commit is contained in:
Yan Zhulanow
2017-01-21 13:59:39 +09:00
parent e28b4dfbc7
commit 72bbf9697b
6 changed files with 72 additions and 5 deletions
@@ -19,6 +19,7 @@ package org.jetbrains.kotlin.kapt3.stubs
import com.sun.tools.javac.code.Flags import com.sun.tools.javac.code.Flags
import com.sun.tools.javac.code.TypeTag import com.sun.tools.javac.code.TypeTag
import com.sun.tools.javac.file.JavacFileManager 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.JCTree.* import com.sun.tools.javac.tree.JCTree.*
import com.sun.tools.javac.tree.TreeMaker 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.*
import org.jetbrains.kotlin.kapt3.javac.KaptTreeMaker import org.jetbrains.kotlin.kapt3.javac.KaptTreeMaker
import org.jetbrains.kotlin.kapt3.javac.KaptJavaFileObject import org.jetbrains.kotlin.kapt3.javac.KaptJavaFileObject
import org.jetbrains.kotlin.kapt3.stubs.Kapt3StubUtils.*
import org.jetbrains.kotlin.kapt3.util.* import org.jetbrains.kotlin.kapt3.util.*
import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.resolve.DescriptorUtils import org.jetbrains.kotlin.resolve.DescriptorUtils
@@ -69,6 +71,12 @@ class ClassFileToSourceStubConverter(
"org.jetbrains.annotations.", // Nullable/NotNull, ReadOnly, Mutable "org.jetbrains.annotations.", // Nullable/NotNull, ReadOnly, Mutable
"kotlin.jvm.", "kotlin.Metadata" // Kotlin annotations from runtime "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<String, KaptJavaFileObject>() private val _bindings = mutableMapOf<String, KaptJavaFileObject>()
@@ -210,7 +218,7 @@ class ClassFileToSourceStubConverter(
return treeMaker.ClassDef( return treeMaker.ClassDef(
modifiers, modifiers,
treeMaker.name(simpleName), treeMaker.name(getNonKeywordName(simpleName)),
genericType.typeParameters, genericType.typeParameters,
if (hasSuperClass) genericType.superClass else null, if (hasSuperClass) genericType.superClass else null,
genericType.interfaces, genericType.interfaces,
@@ -222,7 +230,7 @@ class ClassFileToSourceStubConverter(
val descriptor = kaptContext.origins[field]?.descriptor val descriptor = kaptContext.origins[field]?.descriptor
val modifiers = convertModifiers(field.access, ElementKind.FIELD, packageFqName, field.visibleAnnotations, field.invisibleAnnotations) 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) val type = Type.getType(field.desc)
// Enum type must be an identifier (Javac requirement) // Enum type must be an identifier (Javac requirement)
@@ -260,7 +268,7 @@ class ClassFileToSourceStubConverter(
} }
val isConstructor = method.name == "<init>" val isConstructor = method.name == "<init>"
val name = treeMaker.name(method.name) val name = treeMaker.name(getNonKeywordName(method.name))
val modifiers = convertModifiers( val modifiers = convertModifiers(
if (containingClass.isEnum() && isConstructor) if (containingClass.isEnum() && isConstructor)
@@ -286,7 +294,7 @@ class ClassFileToSourceStubConverter(
info.visibleAnnotations, info.visibleAnnotations,
info.invisibleAnnotations) info.invisibleAnnotations)
val name = treeMaker.name(info.name) val name = treeMaker.name(getNonKeywordName(info.name))
val type = treeMaker.Type(info.type) val type = treeMaker.Type(info.type)
treeMaker.VarDef(modifiers, name, type, null) treeMaker.VarDef(modifiers, name, type, null)
} }
@@ -394,6 +402,12 @@ class ClassFileToSourceStubConverter(
return ifNonError() 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 <T : JCExpression?> getNotAnonymousType(descriptor: DeclarationDescriptor?, f: () -> T): T { private inline fun <T : JCExpression?> getNotAnonymousType(descriptor: DeclarationDescriptor?, f: () -> T): T {
if (descriptor is CallableDescriptor) { if (descriptor is CallableDescriptor) {
val returnTypeDescriptor = descriptor.returnType?.constructor?.declarationDescriptor val returnTypeDescriptor = descriptor.returnType?.constructor?.declarationDescriptor
@@ -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;
}
}
@@ -108,6 +108,12 @@ public class ClassFileToSourceStubConverterTestGenerated extends AbstractClassFi
doTest(fileName); 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") @TestMetadata("jvmOverloads.kt")
public void testJvmOverloads() throws Exception { public void testJvmOverloads() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("plugins/kapt3/testData/converter/jvmOverloads.kt"); String fileName = KotlinTestUtils.navigationMetadata("plugins/kapt3/testData/converter/jvmOverloads.kt");
+4
View File
@@ -0,0 +1,4 @@
class default {
lateinit val extends: String
fun implements(instanceof: Int) {}
}
+14
View File
@@ -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();
}
}
+1 -1
View File
@@ -3,7 +3,7 @@ public final class Outer {
public final void nonAbstract(java.lang.String s, int i) { 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() { public Outer() {
super(); super();