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.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<String, KaptJavaFileObject>()
@@ -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 == "<init>"
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 <T : JCExpression?> getNotAnonymousType(descriptor: DeclarationDescriptor?, f: () -> T): T {
if (descriptor is CallableDescriptor) {
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);
}
@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");
+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 abstract void abstract(java.lang.String p0, int p1);
public abstract void _abstract_(java.lang.String p0, int p1);
public Outer() {
super();