Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix generic types caching #2619

Merged
merged 2 commits into from Aug 18, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Expand Up @@ -501,18 +501,29 @@ class DefaultPsiToDocumentableTranslator(
} ?: PropertyContainer.empty()

private fun getBound(type: PsiType): Bound {
fun bound() = when (type) {
//We would like to cache most of the bounds since it is not common to annotate them,
//but if this is the case, we treat them as 'one of'
fun PsiType.cacheBoundIfHasNoAnnotation(f: (List<Annotations.Annotation>) -> Bound): Bound {
val annotations = this.annotations.toList().toListOfAnnotations()
return if (annotations.isNotEmpty()) f(annotations)
else cachedBounds.getOrPut(canonicalText) {
f(annotations)
}
}

return when (type) {
is PsiClassReferenceType ->
type.resolve()?.let { resolved ->
when {
resolved.qualifiedName == "java.lang.Object" -> JavaObject(type.annotations())
resolved.qualifiedName == "java.lang.Object" -> type.cacheBoundIfHasNoAnnotation { annotations -> JavaObject(annotations.annotations()) }
resolved is PsiTypeParameter -> {
TypeParameter(
dri = DRI.from(resolved),
name = resolved.name.orEmpty(),
extra = type.annotations()
)
}

Regex("kotlin\\.jvm\\.functions\\.Function.*").matches(resolved.qualifiedName ?: "") ||
Regex("java\\.util\\.function\\.Function.*").matches(
resolved.qualifiedName ?: ""
Expand All @@ -521,33 +532,41 @@ class DefaultPsiToDocumentableTranslator(
type.parameters.map { getProjection(it) },
extra = type.annotations()
)
else -> GenericTypeConstructor(
DRI.from(resolved),
type.parameters.map { getProjection(it) },
extra = type.annotations()
)

else -> {
// cache types that have no annotation and no type parameter
// since we cache only by name and type parameters depend on context
val typeParameters = type.parameters.map { getProjection(it) }
if (typeParameters.isEmpty())
type.cacheBoundIfHasNoAnnotation { annotations ->
GenericTypeConstructor(
DRI.from(resolved),
typeParameters,
extra = annotations.annotations()
)
}
else
GenericTypeConstructor(
DRI.from(resolved),
typeParameters,
extra = type.annotations()
)
}
}
} ?: UnresolvedBound(type.presentableText, type.annotations())

is PsiArrayType -> GenericTypeConstructor(
DRI("kotlin", "Array"),
listOf(getProjection(type.componentType)),
extra = type.annotations()
)

is PsiPrimitiveType -> if (type.name == "void") Void
else PrimitiveJavaType(type.name, type.annotations())
is PsiImmediateClassType -> JavaObject(type.annotations())
else type.cacheBoundIfHasNoAnnotation { annotations -> PrimitiveJavaType(type.name, annotations.annotations()) }
is PsiImmediateClassType ->
type.cacheBoundIfHasNoAnnotation { annotations -> JavaObject(annotations.annotations()) }
else -> throw IllegalStateException("${type.presentableText} is not supported by PSI parser")
}

//We would like to cache most of the bounds since it is not common to annotate them,
//but if this is the case, we treat them as 'one of'
return if (type.annotations.toList().toListOfAnnotations().isEmpty()) {
cachedBounds.getOrPut(type.canonicalText) {
bound()
}
} else {
bound()
}
}


Expand Down
37 changes: 37 additions & 0 deletions plugins/base/src/test/kotlin/model/JavaTest.kt
Expand Up @@ -143,6 +143,43 @@ class JavaTest : AbstractModelTest("/src/main/kotlin/java/Test.java", "java") {
) {
with((this / "java" / "Foo").cast<DClass>()) {
generics counts 1
generics[0].dri.classNames equals "Foo"
(functions[0].type as? TypeParameter)?.dri?.run {
packageName equals "java"
name equals "Foo"
callable?.name equals "foo"
}
}
}
}

@Test
fun typeParameterIntoDifferentClasses2596() {
inlineModelTest(
"""
|class GenericDocument { }
|public interface DocumentClassFactory<T> {
| String getSchemaName();
| GenericDocument toGenericDocument(T document);
| T fromGenericDocument(GenericDocument genericDoc);
|}
|
|public final class DocumentClassFactoryRegistry {
| public <T> DocumentClassFactory<T> getOrCreateFactory(T documentClass) {
| return null;
| }
|}
""", configuration = configuration
) {
with((this / "java" / "DocumentClassFactory").cast<DInterface>()) {
generics counts 1
generics[0].dri.classNames equals "DocumentClassFactory"
}
with((this / "java" / "DocumentClassFactoryRegistry").cast<DClass>()) {
functions.forEach {
(it.type as GenericTypeConstructor).dri.classNames equals "DocumentClassFactory"
((it.type as GenericTypeConstructor).projections[0] as TypeParameter).dri.classNames equals "DocumentClassFactoryRegistry"
}
}
}
}
Expand Down