Skip to content

Commit

Permalink
Fix generic types caching (#2619)
Browse files Browse the repository at this point in the history
  • Loading branch information
vmishenev committed Aug 18, 2022
1 parent e9e95f6 commit df8d987
Show file tree
Hide file tree
Showing 2 changed files with 75 additions and 19 deletions.
Expand Up @@ -502,18 +502,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 @@ -522,33 +533,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

0 comments on commit df8d987

Please sign in to comment.