Support loading Java records

^KT-43677 In Progress
This commit is contained in:
Denis.Zharkov
2020-11-23 16:02:26 +03:00
parent f25b7672a7
commit 513f7177ca
45 changed files with 688 additions and 61 deletions
@@ -23,19 +23,18 @@ import org.jetbrains.kotlin.descriptors.SimpleFunctionDescriptor
import org.jetbrains.kotlin.load.java.structure.JavaClass
import org.jetbrains.kotlin.load.java.structure.JavaElement
import org.jetbrains.kotlin.load.java.structure.JavaField
import org.jetbrains.kotlin.load.java.structure.JavaMethod
import org.jetbrains.kotlin.load.java.structure.JavaMember
import org.jetbrains.kotlin.load.java.structure.impl.JavaClassImpl
import org.jetbrains.kotlin.load.java.structure.impl.JavaElementImpl
import org.jetbrains.kotlin.load.java.structure.impl.JavaFieldImpl
import org.jetbrains.kotlin.load.java.structure.impl.JavaMethodImpl
import org.jetbrains.kotlin.resolve.BindingContext.*
import org.jetbrains.kotlin.resolve.BindingContextUtils
import org.jetbrains.kotlin.resolve.lazy.ResolveSession
class LazyResolveBasedCache(resolveSession: ResolveSession) : AbstractJavaResolverCache(resolveSession) {
override fun recordMethod(method: JavaMethod, descriptor: SimpleFunctionDescriptor) {
BindingContextUtils.recordFunctionDeclarationToDescriptor(trace, (method as? JavaMethodImpl)?.psi ?: return, descriptor)
override fun recordMethod(member: JavaMember, descriptor: SimpleFunctionDescriptor) {
BindingContextUtils.recordFunctionDeclarationToDescriptor(trace, (member as? JavaElementImpl<*>)?.psi ?: return, descriptor)
}
override fun recordConstructor(element: JavaElement, descriptor: ConstructorDescriptor) {
@@ -39,7 +39,7 @@ import org.jetbrains.kotlin.name.Name;
@NotNull Kind kind,
@NotNull JavaMethodDescriptor declaration
) {
super(containingDeclaration, original, declaration.getAnnotations(), declaration.getName(), kind, declaration.getSource());
super(containingDeclaration, original, declaration.getAnnotations(), declaration.getName(), kind, declaration.getSource(), false);
this.declaration = declaration;
setParameterNamesStatus(declaration.hasStableParameterNames(), declaration.hasSynthesizedParameterNames());
}
@@ -88,7 +88,8 @@ public class SignaturesPropagationData {
Annotations.Companion.getEMPTY(),
method.getName(),
//TODO: what to do?
SourceElement.NO_SOURCE
SourceElement.NO_SOURCE,
false
);
autoMethodDescriptor.initialize(
null,
@@ -27,6 +27,8 @@ import org.jetbrains.kotlin.incremental.components.LookupTracker
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
import org.jetbrains.kotlin.incremental.record
import org.jetbrains.kotlin.load.java.JvmAbi
import org.jetbrains.kotlin.load.java.descriptors.JavaClassDescriptor
import org.jetbrains.kotlin.load.java.descriptors.JavaMethodDescriptor
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.resolve.DescriptorFactory
import org.jetbrains.kotlin.resolve.DescriptorUtils
@@ -113,20 +115,37 @@ class JavaSyntheticPropertiesScope(storageManager: StorageManager, private val l
}
private fun syntheticPropertyInClassNotCached(ownerClass: ClassDescriptor, name: Name): SyntheticPropertyHolder {
val forBean = syntheticPropertyHolderForBeanConvention(name, ownerClass)
if (forBean.descriptor != null) return forBean
fun result(descriptor: PropertyDescriptor?, getterNames: List<Name>, setterName: Name? = null): SyntheticPropertyHolder {
if (lookupTracker === LookupTracker.DO_NOTHING) {
return if (descriptor == null) SyntheticPropertyHolder.EMPTY else SyntheticPropertyHolder(descriptor, emptyList())
}
if (!ownerClass.isRecord()) return forBean
val names = ArrayList<Name>(getterNames.size + (setterName?.let { 1 } ?: 0))
val propertyForComponent = syntheticPropertyDescriptorForRecordComponent(name, ownerClass)
names.addAll(getterNames)
names.addIfNotNull(setterName)
return createSyntheticPropertyHolder(propertyForComponent, forBean.lookedNames, name)
}
return SyntheticPropertyHolder(descriptor, names)
private fun createSyntheticPropertyHolder(
descriptor: PropertyDescriptor?,
lookedNames: List<Name>,
additionalName: Name? = null
): SyntheticPropertyHolder {
if (lookupTracker === LookupTracker.DO_NOTHING) {
return if (descriptor == null) SyntheticPropertyHolder.EMPTY else SyntheticPropertyHolder(descriptor, emptyList())
}
val names = ArrayList<Name>(lookedNames.size + (additionalName?.let { 1 } ?: 0))
names.addAll(lookedNames)
names.addIfNotNull(additionalName)
return SyntheticPropertyHolder(descriptor, names)
}
private fun syntheticPropertyHolderForBeanConvention(
name: Name,
ownerClass: ClassDescriptor
): SyntheticPropertyHolder {
if (name.isSpecial) return SyntheticPropertyHolder.EMPTY
val identifier = name.identifier
@@ -142,7 +161,7 @@ class JavaSyntheticPropertiesScope(storageManager: StorageManager, private val l
.flatMap { memberScope.getContributedFunctions(it, NoLookupLocation.FROM_SYNTHETIC_SCOPE) }
.singleOrNull {
it.hasJavaOriginInHierarchy() && isGoodGetMethod(it)
} ?: return result(null, possibleGetMethodNames)
} ?: return createSyntheticPropertyHolder(null, possibleGetMethodNames)
val setMethodName = setMethodName(getMethod.name)
@@ -152,7 +171,25 @@ class JavaSyntheticPropertiesScope(storageManager: StorageManager, private val l
val propertyType = getMethod.returnType!!
val descriptor = MyPropertyDescriptor.create(ownerClass, getMethod.original, setMethod?.original, name, propertyType)
return result(descriptor, possibleGetMethodNames, setMethodName)
return createSyntheticPropertyHolder(descriptor, possibleGetMethodNames, setMethodName)
}
private fun syntheticPropertyDescriptorForRecordComponent(
name: Name,
ownerClass: ClassDescriptor
): PropertyDescriptor? {
val componentLikeMethod =
ownerClass.unsubstitutedMemberScope
.getContributedFunctions(name, NoLookupLocation.FROM_SYNTHETIC_SCOPE)
.singleOrNull(this::isGoodGetMethod) ?: return null
if (componentLikeMethod !is JavaMethodDescriptor || !componentLikeMethod.isForRecordComponent) {
return null
}
val propertyType = componentLikeMethod.returnType!!
return MyPropertyDescriptor.create(ownerClass, componentLikeMethod.original, null, name, propertyType)
}
private fun isGoodGetMethod(descriptor: FunctionDescriptor): Boolean {
@@ -249,8 +286,14 @@ class JavaSyntheticPropertiesScope(storageManager: StorageManager, private val l
if (classifier is ClassDescriptor) {
for (descriptor in classifier.unsubstitutedMemberScope.getContributedDescriptors(DescriptorKindFilter.FUNCTIONS)) {
if (descriptor is FunctionDescriptor) {
val propertyName = SyntheticJavaPropertyDescriptor.propertyNameByGetMethodName(descriptor.getName()) ?: continue
addIfNotNull(syntheticPropertyInClass(Pair(classifier, propertyName)).descriptor)
val propertyName = SyntheticJavaPropertyDescriptor.propertyNameByGetMethodName(descriptor.getName())
if (propertyName != null) {
addIfNotNull(syntheticPropertyInClass(Pair(classifier, propertyName)).descriptor)
}
if (classifier.isRecord()) {
addIfNotNull(syntheticPropertyInClass(Pair(classifier, descriptor.name)).descriptor)
}
}
}
} else {
@@ -258,6 +301,9 @@ class JavaSyntheticPropertiesScope(storageManager: StorageManager, private val l
}
}
private fun ClassifierDescriptor.isRecord() =
this is JavaClassDescriptor && isRecord
private fun SmartList<PropertyDescriptor>?.add(property: PropertyDescriptor?): SmartList<PropertyDescriptor>? {
if (property == null) return this
val list = if (this != null) this else SmartList()
@@ -280,15 +326,15 @@ class JavaSyntheticPropertiesScope(storageManager: StorageManager, private val l
}
private class MyPropertyDescriptor(
containingDeclaration: DeclarationDescriptor,
original: PropertyDescriptor?,
annotations: Annotations,
modality: Modality,
visibility: DescriptorVisibility,
isVar: Boolean,
name: Name,
kind: CallableMemberDescriptor.Kind,
source: SourceElement
containingDeclaration: DeclarationDescriptor,
original: PropertyDescriptor?,
annotations: Annotations,
modality: Modality,
visibility: DescriptorVisibility,
isVar: Boolean,
name: Name,
kind: CallableMemberDescriptor.Kind,
source: SourceElement
) : SyntheticJavaPropertyDescriptor, PropertyDescriptorImpl(
containingDeclaration, original, annotations, modality, visibility, isVar, name, kind, source,
/* lateInit = */ false, /* isConst = */ false, /* isExpect = */ false, /* isActual = */ false, /* isExternal = */ false,
@@ -374,13 +420,13 @@ class JavaSyntheticPropertiesScope(storageManager: StorageManager, private val l
}
override fun createSubstitutedCopy(
newOwner: DeclarationDescriptor,
newModality: Modality,
newVisibility: DescriptorVisibility,
original: PropertyDescriptor?,
kind: CallableMemberDescriptor.Kind,
newName: Name,
source: SourceElement
newOwner: DeclarationDescriptor,
newModality: Modality,
newVisibility: DescriptorVisibility,
original: PropertyDescriptor?,
kind: CallableMemberDescriptor.Kind,
newName: Name,
source: SourceElement
): PropertyDescriptorImpl {
return MyPropertyDescriptor(newOwner, this, annotations, newModality, newVisibility, isVar, newName, kind, this.source).apply {
getMethod = this@MyPropertyDescriptor.getMethod
@@ -24,12 +24,12 @@ import org.jetbrains.kotlin.load.java.components.AbstractJavaResolverCache
import org.jetbrains.kotlin.load.java.structure.JavaClass
import org.jetbrains.kotlin.load.java.structure.JavaElement
import org.jetbrains.kotlin.load.java.structure.JavaField
import org.jetbrains.kotlin.load.java.structure.JavaMethod
import org.jetbrains.kotlin.load.java.structure.JavaMember
import org.jetbrains.kotlin.resolve.lazy.ResolveSession
class StubJavaResolverCache(resolveSession: ResolveSession) : AbstractJavaResolverCache(resolveSession) {
override fun recordMethod(method: JavaMethod, descriptor: SimpleFunctionDescriptor) {}
override fun recordMethod(member: JavaMember, descriptor: SimpleFunctionDescriptor) {}
override fun recordConstructor(element: JavaElement, descriptor: ConstructorDescriptor) {}
@@ -37,4 +37,4 @@ class StubJavaResolverCache(resolveSession: ResolveSession) : AbstractJavaResolv
override fun recordClass(javaClass: JavaClass, descriptor: ClassDescriptor) {}
}
}
@@ -157,9 +157,12 @@ class MockKotlinClassifier(override val classId: ClassId,
override val isInterface get() = shouldNotBeCalled()
override val isAnnotationType get() = shouldNotBeCalled()
override val isEnum get() = shouldNotBeCalled()
override val isRecord get() = shouldNotBeCalled()
override val methods get() = shouldNotBeCalled()
override val fields get() = shouldNotBeCalled()
override val constructors get() = shouldNotBeCalled()
override val recordComponents get() = shouldNotBeCalled()
override fun hasDefaultConstructor() = shouldNotBeCalled()
override val annotations get() = shouldNotBeCalled()
override val isDeprecatedInJavaDoc get() = shouldNotBeCalled()
@@ -65,6 +65,9 @@ class FakeSymbolBasedClass(
override val isEnum: Boolean get() = false
override val isRecord: Boolean get() = false
override val recordComponents: Collection<JavaRecordComponent> get() = emptyList()
override val lightClassOriginKind: LightClassOriginKind? get() = null
override val methods: Collection<JavaMethod> get() = emptyList()
@@ -133,6 +133,12 @@ class SymbolBasedClass(
.filter { it.kind == ElementKind.CONSTRUCTOR }
.map { SymbolBasedConstructor(it as ExecutableElement, this, javac) }
override val isRecord: Boolean
get() = false
override val recordComponents: Collection<JavaRecordComponent>
get() = emptyList()
override fun hasDefaultConstructor() = false // default constructors are explicit in symbols
override val innerClassNames: Collection<Name>
@@ -114,6 +114,10 @@ class TreeBasedClass(
override val isEnum: Boolean
get() = tree.modifiers.flags and Flags.ENUM.toLong() != 0L
// TODO: Support
override val isRecord: Boolean
get() = false
override val lightClassOriginKind: LightClassOriginKind?
get() = null
@@ -134,6 +138,9 @@ class TreeBasedClass(
TreeBasedConstructor(constructor as JCTree.JCMethodDecl, compilationUnit, this, javac)
}
override val recordComponents: Collection<JavaRecordComponent>
get() = emptyList()
override fun hasDefaultConstructor() = !isInterface && constructors.isEmpty()
override val innerClassNames: Collection<Name>
@@ -59,6 +59,9 @@ class JavaClassImpl(psiClass: PsiClass) : JavaClassifierImpl<PsiClass>(psiClass)
override val isEnum: Boolean
get() = psi.isEnum
override val isRecord: Boolean
get() = psi.isRecord
override val outerClass: JavaClassImpl?
get() {
val outer = psi.containingClass
@@ -100,6 +103,13 @@ class JavaClassImpl(psiClass: PsiClass) : JavaClassifierImpl<PsiClass>(psiClass)
return constructors(psi.constructors.filter { method -> method.isConstructor })
}
override val recordComponents: Collection<JavaRecordComponent>
get() {
assertNotLightClass()
return psi.recordComponents.convert(::JavaRecordComponentImpl)
}
override fun hasDefaultConstructor() = !isInterface && constructors.isEmpty()
override val isAbstract: Boolean
@@ -24,14 +24,14 @@ import org.jetbrains.kotlin.load.java.NULLABILITY_ANNOTATIONS
import org.jetbrains.kotlin.load.java.structure.*
import org.jetbrains.kotlin.name.Name
private inline fun <Psi, Java> Array<Psi>.convert(factory: (Psi) -> Java): List<Java> =
inline fun <Psi, Java> Array<Psi>.convert(factory: (Psi) -> Java): List<Java> =
when (size) {
0 -> emptyList()
1 -> listOf(factory(first()))
else -> map(factory)
}
private fun <Psi, Java> Collection<Psi>.convert(factory: (Psi) -> Java): List<Java> =
fun <Psi, Java> Collection<Psi>.convert(factory: (Psi) -> Java): List<Java> =
when (size) {
0 -> emptyList()
1 -> listOf(factory(first()))
@@ -0,0 +1,18 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.load.java.structure.impl
import com.intellij.psi.PsiRecordComponent
import org.jetbrains.kotlin.load.java.structure.JavaRecordComponent
import org.jetbrains.kotlin.load.java.structure.JavaType
class JavaRecordComponentImpl(psiRecordComponent: PsiRecordComponent) : JavaMemberImpl<PsiRecordComponent>(psiRecordComponent), JavaRecordComponent {
override val type: JavaType
get() = JavaTypeImpl.create(psi.type)
override val isVararg: Boolean
get() = psi.isVarArgs
}
@@ -47,6 +47,8 @@ class BinaryJavaClass(
override val methods = arrayListOf<JavaMethod>()
override val fields = arrayListOf<JavaField>()
override val constructors = arrayListOf<JavaConstructor>()
override val recordComponents = arrayListOf<JavaRecordComponent>()
override fun hasDefaultConstructor() = false // never: all constructors explicit in bytecode
override val annotationsByFqName by buildLazyValueForMap()
@@ -63,6 +65,9 @@ class BinaryJavaClass(
override val isInterface get() = isSet(Opcodes.ACC_INTERFACE)
override val isAnnotationType get() = isSet(Opcodes.ACC_ANNOTATION)
override val isEnum get() = isSet(Opcodes.ACC_ENUM)
override val isRecord get() = isSet(Opcodes.ACC_RECORD)
override val lightClassOriginKind: LightClassOriginKind? get() = null
override fun isFromSourceCodeInScope(scope: SearchScope): Boolean = false
@@ -184,6 +189,16 @@ class BinaryJavaClass(
}
}
override fun visitRecordComponent(name: String, descriptor: String, signature: String?): RecordComponentVisitor? {
val type = signatureParser.parseTypeString(StringCharacterIterator(signature ?: descriptor), context)
// TODO: Read isVararg properly
val isVararg = false
recordComponents.add(BinaryJavaRecordComponent(Name.identifier(name), this, type, isVararg))
return null
}
/**
* All the int-like values (including Char/Boolean) come in visitor as Integer instances
*/
@@ -64,6 +64,22 @@ class BinaryJavaValueParameter(
}
}
class BinaryJavaRecordComponent(
override val name: Name,
override val containingClass: JavaClass,
override val type: JavaType,
override val isVararg: Boolean,
) : JavaRecordComponent, BinaryJavaModifierListOwner {
override val annotations: Collection<JavaAnnotation>
get() = emptyList()
override val access: Int
get() = 0
override val annotationsByFqName: Map<FqName?, JavaAnnotation>
get() = emptyMap()
}
fun isNotTopLevelClass(classContent: ByteArray): Boolean {
var isNotTopLevelClass = false
ClassReader(classContent).accept(
@@ -0,0 +1,16 @@
// FILE: MyRecord.java
public record MyRecord(int x, CharSequence y) {
}
// FILE: main.kt
fun foo(mr: MyRecord) {
MyRecord(1, "")
mr.x()
mr.y()
mr.x
mr.y
}
@@ -0,0 +1,12 @@
package
public fun foo(/*0*/ mr: MyRecord): kotlin.Unit
/*record*/ public final class MyRecord : java.lang.Record {
public constructor MyRecord(/*0*/ x: kotlin.Int, /*1*/ y: kotlin.CharSequence!)
public abstract override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public abstract override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public abstract override /*1*/ /*fake_override*/ fun toString(): kotlin.String
/*record component*/ public open fun x(): kotlin.Int
/*record component*/ public open fun y(): kotlin.CharSequence!
}
@@ -0,0 +1,11 @@
package test
public final class GenericRecord</*0*/ T : kotlin.Any!, /*1*/ E : kotlin.Any!> : java.lang.Record {
public constructor GenericRecord</*0*/ T : kotlin.Any!, /*1*/ E : kotlin.Any!>(/*0*/ x: T!, /*1*/ y: E!)
private final val x: T!
private final val y: E!
public open fun x(): T!
public open fun y(): E!
public open fun y(/*0*/ p0: E!): E!
public open fun z(): kotlin.Double
}
+11
View File
@@ -0,0 +1,11 @@
package test
/*record*/ public final class GenericRecord</*0*/ T : kotlin.Any!, /*1*/ E : kotlin.Any!> : java.lang.Record {
public constructor GenericRecord</*0*/ T : kotlin.Any!, /*1*/ E : kotlin.Any!>(/*0*/ x: T!, /*1*/ y: E!)
private final val x: T!
private final val y: E!
/*record component*/ public open fun x(): T!
/*record component*/ public open fun y(): E!
public open fun y(/*0*/ p0: E!): E!
public open fun z(): kotlin.Double
}
+6
View File
@@ -0,0 +1,6 @@
package test;
public record GenericRecord<T, E>(T x, E y) {
public E y() { return y; }
public E y(E n) { return y; }
public double z() { return 0.0; }
}
+9
View File
@@ -0,0 +1,9 @@
package test
/*record*/ public final class GenericRecord</*0*/ T : kotlin.Any!, /*1*/ E : kotlin.Any!> : java.lang.Record {
public constructor GenericRecord</*0*/ T : kotlin.Any!, /*1*/ E : kotlin.Any!>(/*0*/ x: T!, /*1*/ y: E!)
/*record component*/ public open fun x(): T!
/*record component*/ public open fun y(): E!
public open fun y(/*0*/ n: E!): E!
public open fun z(): kotlin.Double
}
@@ -0,0 +1,11 @@
package test
public final class SimpleRecord : java.lang.Record {
public constructor SimpleRecord(/*0*/ x: kotlin.Int, /*1*/ y: kotlin.CharSequence!)
private final val x: kotlin.Int
private final val y: kotlin.CharSequence!
public open fun x(): kotlin.Int
public open fun y(): kotlin.CharSequence!
public open fun y(/*0*/ p0: kotlin.Int): kotlin.CharSequence!
public open fun z(): kotlin.Double
}
+11
View File
@@ -0,0 +1,11 @@
package test
/*record*/ public final class SimpleRecord : java.lang.Record {
public constructor SimpleRecord(/*0*/ x: kotlin.Int, /*1*/ y: kotlin.CharSequence!)
private final val x: kotlin.Int
private final val y: kotlin.CharSequence!
/*record component*/ public open fun x(): kotlin.Int
/*record component*/ public open fun y(): kotlin.CharSequence!
public open fun y(/*0*/ p0: kotlin.Int): kotlin.CharSequence!
public open fun z(): kotlin.Double
}
+6
View File
@@ -0,0 +1,6 @@
package test;
public record SimpleRecord(int x, CharSequence y) {
public CharSequence y() { return y; }
public CharSequence y(int n) { return y; }
public double z() { return 0.0; }
}
+9
View File
@@ -0,0 +1,9 @@
package test
/*record*/ public final class SimpleRecord : java.lang.Record {
public constructor SimpleRecord(/*0*/ x: kotlin.Int, /*1*/ y: kotlin.CharSequence!)
/*record component*/ public open fun x(): kotlin.Int
/*record component*/ public open fun y(): kotlin.CharSequence!
public open fun y(/*0*/ n: kotlin.Int): kotlin.CharSequence!
public open fun z(): kotlin.Double
}
@@ -0,0 +1,25 @@
/*
* 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.checkers
import org.jetbrains.kotlin.test.TestJdkKind
abstract class AbstractDiagnosticsWithJdk15Test : AbstractDiagnosticsTest() {
override fun getTestJdkKind(files: List<TestFile>): TestJdkKind {
return TestJdkKind.FULL_JDK_15
}
}
@@ -0,0 +1,18 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.jvm.compiler
import org.jetbrains.kotlin.test.KotlinTestUtils
import org.jetbrains.kotlin.test.TestJdkKind
import java.io.File
abstract class AbstractLoadJava15Test : AbstractLoadJavaTest() {
override fun getJdkKind(): TestJdkKind = TestJdkKind.FULL_JDK_15
override fun getJdkHomeForJavac(): File = KotlinTestUtils.getJdk15Home()
override fun getAdditionalJavacArgs(): List<String> = ADDITIONAL_JAVAC_ARGS_FOR_15
}
val ADDITIONAL_JAVAC_ARGS_FOR_15 = listOf("--release", "15", "--enable-preview")
@@ -0,0 +1,16 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.jvm.compiler
import org.jetbrains.kotlin.test.KotlinTestUtils
import org.jetbrains.kotlin.test.TestJdkKind
import java.io.File
abstract class AbstractLoadJava15WithPsiClassReadingTest : AbstractLoadJavaWithPsiClassReadingTest() {
override fun getJdkKind(): TestJdkKind = TestJdkKind.FULL_JDK_15
override fun getJdkHomeForJavac(): File = KotlinTestUtils.getJdk15Home()
override fun getAdditionalJavacArgs(): List<String> = ADDITIONAL_JAVAC_ARGS_FOR_15
}
@@ -9,6 +9,7 @@ import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.io.FileUtil;
import junit.framework.ComparisonFailure;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.analyzer.AnalysisResult;
import org.jetbrains.kotlin.checkers.CompilerTestLanguageVersionSettingsKt;
import org.jetbrains.kotlin.cli.common.config.ContentRootsKt;
@@ -299,7 +300,19 @@ public abstract class AbstractLoadJavaTest extends TestCaseWithTmpdir {
Pair<PackageViewDescriptor, BindingContext> javaPackageAndContext = compileJavaAndLoadTestPackageAndBindingContextFromBinary(
srcFiles, compiledDir, ConfigurationKind.ALL
);
checkJavaPackage(getExpectedFile(javaFileName.replaceFirst("\\.java$", ".txt")), javaPackageAndContext.first, javaPackageAndContext.second, configuration);
checkJavaPackage(getExpectedFile(
useTxtSuffixIfFileExists(javaFileName.replaceFirst("\\.java$", ".txt"), "compiled")
), javaPackageAndContext.first, javaPackageAndContext.second, configuration);
}
public static String useTxtSuffixIfFileExists(String name, String suffix) {
File differentResultFile = KotlinTestUtils.replaceExtension(new File(name), suffix + ".txt");
if (differentResultFile.exists()) return differentResultFile.getPath();
return name;
}
@NotNull
@@ -308,12 +321,21 @@ public abstract class AbstractLoadJavaTest extends TestCaseWithTmpdir {
@NotNull File outDir,
@NotNull ConfigurationKind configurationKind
) throws IOException {
compileJavaWithAnnotationsJar(javaFiles, outDir);
compileJavaWithAnnotationsJar(javaFiles, outDir, getAdditionalJavacArgs(), getJdkHomeForJavac());
return loadTestPackageAndBindingContextFromJavaRoot(outDir, getTestRootDisposable(), getJdkKind(), configurationKind, true,
usePsiClassFilesReading(), useJavacWrapper(), null,
getExtraClasspath(), this::configureEnvironment);
}
protected List<String> getAdditionalJavacArgs() {
return Collections.emptyList();
}
@Nullable
protected File getJdkHomeForJavac() {
return null;
}
private static void checkJavaPackage(
File txtFile,
PackageViewDescriptor javaPackage,
@@ -142,7 +142,12 @@ public class LoadDescriptorUtil {
return Pair.create(packageView, analysisResult.getBindingContext());
}
public static void compileJavaWithAnnotationsJar(@NotNull Collection<File> javaFiles, @NotNull File outDir) throws IOException {
public static void compileJavaWithAnnotationsJar(
@NotNull Collection<File> javaFiles,
@NotNull File outDir,
@NotNull List<String> additionalArgs,
@Nullable File customJdkHomeForJavac
) throws IOException {
List<String> args = new ArrayList<>(Arrays.asList(
"-sourcepath", "compiler/testData/loadJava/include",
"-d", outDir.getPath())
@@ -170,7 +175,14 @@ public class LoadDescriptorUtil {
args.add("-classpath");
args.add(classpath.stream().map(File::getPath).collect(Collectors.joining(File.pathSeparator)));
KotlinTestUtils.compileJavaFiles(javaFiles, args);
args.addAll(additionalArgs);
if (customJdkHomeForJavac != null) {
KotlinTestUtils.compileJavaFilesExternally(javaFiles, args, customJdkHomeForJavac);
}
else {
KotlinTestUtils.compileJavaFiles(javaFiles, args);
}
}
@NotNull
@@ -680,6 +680,10 @@ public class KotlinTestUtils {
}
public static boolean compileJavaFilesExternallyWithJava9(@NotNull Collection<File> files, @NotNull List<String> options) {
return compileJavaFilesExternally(files, options, getJdk9Home());
}
public static boolean compileJavaFilesExternally(@NotNull Collection<File> files, @NotNull List<String> options, @NotNull File jdkHome) {
List<String> command = new ArrayList<>();
command.add(new File(jdkHome, "bin/javac").getPath());
command.addAll(options);
@@ -27,6 +27,8 @@ import org.jetbrains.kotlin.contracts.description.*;
import org.jetbrains.kotlin.descriptors.*;
import org.jetbrains.kotlin.descriptors.impl.SubpackagesScope;
import org.jetbrains.kotlin.jvm.compiler.ExpectedLoadErrorsUtil;
import org.jetbrains.kotlin.load.java.descriptors.JavaClassDescriptor;
import org.jetbrains.kotlin.load.java.descriptors.JavaMethodDescriptor;
import org.jetbrains.kotlin.name.FqName;
import org.jetbrains.kotlin.platform.TargetPlatformKt;
import org.jetbrains.kotlin.renderer.*;
@@ -193,7 +195,24 @@ public class RecursiveDescriptorComparator {
boolean isPrimaryConstructor = conf.checkPrimaryConstructors &&
descriptor instanceof ConstructorDescriptor && ((ConstructorDescriptor) descriptor).isPrimary();
printer.print(isPrimaryConstructor ? "/*primary*/ " : "", conf.renderer.render(descriptor));
boolean isRecord = descriptor instanceof JavaClassDescriptor && ((JavaClassDescriptor) descriptor).isRecord();
boolean isRecordComponent = descriptor instanceof JavaMethodDescriptor && ((JavaMethodDescriptor) descriptor).isForRecordComponent();
StringBuilder prefix = new StringBuilder();
if (isPrimaryConstructor) {
prefix.append("/*primary*/ ");
}
if (isRecord) {
prefix.append("/*record*/ ");
}
if (isRecordComponent) {
prefix.append("/*record component*/ ");
}
printer.print(prefix.toString(), conf.renderer.render(descriptor));
if (descriptor instanceof FunctionDescriptor && conf.checkFunctionContracts) {
printEffectsIfAny((FunctionDescriptor) descriptor, printer);
@@ -0,0 +1,35 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.checkers;
import com.intellij.testFramework.TestDataPath;
import org.jetbrains.kotlin.test.JUnit3RunnerWithInners;
import org.jetbrains.kotlin.test.KotlinTestUtils;
import org.jetbrains.kotlin.test.TestMetadata;
import org.junit.runner.RunWith;
import java.io.File;
import java.util.regex.Pattern;
/** This class is generated by {@link org.jetbrains.kotlin.generators.tests.TestsPackage}. DO NOT MODIFY MANUALLY */
@SuppressWarnings("all")
@TestMetadata("compiler/testData/diagnostics/testsWithJava15")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public class DiagnosticsWithJdk15TestGenerated extends AbstractDiagnosticsWithJdk15Test {
private void runTest(String testDataFilePath) throws Exception {
KotlinTestUtils.runTest(this::doTest, this, testDataFilePath);
}
public void testAllFilesPresentInTestsWithJava15() throws Exception {
KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/diagnostics/testsWithJava15"), Pattern.compile("^(.+)\\.kt$"), null, true);
}
@TestMetadata("simpleRecords.kt")
public void testSimpleRecords() throws Exception {
runTest("compiler/testData/diagnostics/testsWithJava15/simpleRecords.kt");
}
}
@@ -123,6 +123,10 @@ fun main(args: Array<String>) {
model("diagnostics/testsWithJava9")
}
testClass<AbstractDiagnosticsWithJdk15Test> {
model("diagnostics/testsWithJava15")
}
testClass<AbstractDiagnosticsWithUnsignedTypes> {
model("diagnostics/testsWithUnsignedTypes")
}
@@ -309,6 +313,15 @@ fun main(args: Array<String>) {
model("loadJava/compiledJava", extension = "java", testMethod = "doTestCompiledJava")
}
testClass<AbstractLoadJava15Test> {
model("loadJava15", extension = "java", testMethod = "doTestCompiledJava", testClassName = "CompiledJava")
model("loadJava15", extension = "java", testMethod = "doTestSourceJava", testClassName = "SourceJava")
}
testClass<AbstractLoadJava15WithPsiClassReadingTest> {
model("loadJava15", extension = "java", testMethod = "doTestCompiledJava")
}
testClass<AbstractCompileJavaAgainstKotlinTest> {
model(
"compileJavaAgainstKotlin",
@@ -0,0 +1,66 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.jvm.compiler;
import com.intellij.testFramework.TestDataPath;
import org.jetbrains.kotlin.test.JUnit3RunnerWithInners;
import org.jetbrains.kotlin.test.KotlinTestUtils;
import org.jetbrains.kotlin.test.TestMetadata;
import org.junit.runner.RunWith;
import java.io.File;
import java.util.regex.Pattern;
/** This class is generated by {@link org.jetbrains.kotlin.generators.tests.TestsPackage}. DO NOT MODIFY MANUALLY */
@SuppressWarnings("all")
@RunWith(JUnit3RunnerWithInners.class)
public class LoadJava15TestGenerated extends AbstractLoadJava15Test {
@TestMetadata("compiler/testData/loadJava15")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class CompiledJava extends AbstractLoadJava15Test {
private void runTest(String testDataFilePath) throws Exception {
KotlinTestUtils.runTest(this::doTestCompiledJava, this, testDataFilePath);
}
public void testAllFilesPresentInCompiledJava() throws Exception {
KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/loadJava15"), Pattern.compile("^(.+)\\.java$"), null, true);
}
@TestMetadata("GenericRecord.java")
public void testGenericRecord() throws Exception {
runTest("compiler/testData/loadJava15/GenericRecord.java");
}
@TestMetadata("SimpleRecord.java")
public void testSimpleRecord() throws Exception {
runTest("compiler/testData/loadJava15/SimpleRecord.java");
}
}
@TestMetadata("compiler/testData/loadJava15")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class SourceJava extends AbstractLoadJava15Test {
private void runTest(String testDataFilePath) throws Exception {
KotlinTestUtils.runTest(this::doTestSourceJava, this, testDataFilePath);
}
public void testAllFilesPresentInSourceJava() throws Exception {
KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/loadJava15"), Pattern.compile("^(.+)\\.java$"), null, true);
}
@TestMetadata("GenericRecord.java")
public void testGenericRecord() throws Exception {
runTest("compiler/testData/loadJava15/GenericRecord.java");
}
@TestMetadata("SimpleRecord.java")
public void testSimpleRecord() throws Exception {
runTest("compiler/testData/loadJava15/SimpleRecord.java");
}
}
}
@@ -0,0 +1,40 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.jvm.compiler;
import com.intellij.testFramework.TestDataPath;
import org.jetbrains.kotlin.test.JUnit3RunnerWithInners;
import org.jetbrains.kotlin.test.KotlinTestUtils;
import org.jetbrains.kotlin.test.TestMetadata;
import org.junit.runner.RunWith;
import java.io.File;
import java.util.regex.Pattern;
/** This class is generated by {@link org.jetbrains.kotlin.generators.tests.TestsPackage}. DO NOT MODIFY MANUALLY */
@SuppressWarnings("all")
@TestMetadata("compiler/testData/loadJava15")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public class LoadJava15WithPsiClassReadingTestGenerated extends AbstractLoadJava15WithPsiClassReadingTest {
private void runTest(String testDataFilePath) throws Exception {
KotlinTestUtils.runTest(this::doTestCompiledJava, this, testDataFilePath);
}
public void testAllFilesPresentInLoadJava15() throws Exception {
KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/loadJava15"), Pattern.compile("^(.+)\\.java$"), null, true);
}
@TestMetadata("GenericRecord.java")
public void testGenericRecord() throws Exception {
runTest("compiler/testData/loadJava15/GenericRecord.java");
}
@TestMetadata("SimpleRecord.java")
public void testSimpleRecord() throws Exception {
runTest("compiler/testData/loadJava15/SimpleRecord.java");
}
}