Repository: square/anvil (kotlin)

Issue:
StackOverflow when calling ClassReference.directSuperClassReferences()
If I have the following structure:
```kotlin
abstract class OuterClass {
    interface InnerInterface
}

class ClassA : OuterClass {
    class ClassB : InnerInterface
}
```

And I call `directSuperClassReferences()` when a code generator is processing  `ClassB` I expected to get a list with just one element, the ClassReference to InnerInterface, but I get a StackOverflow with the following stacktrace:
```
e: java.lang.StackOverflowError
	at kotlin.sequences.SequencesKt___SequencesKt.first(_Sequences.kt:109)
	at com.squareup.anvil.compiler.internal.PsiUtilsKt.requireFqName(PsiUtils.kt:58)
	at com.squareup.anvil.compiler.internal.PsiUtilsKt.requireFqName(PsiUtils.kt:118)
	at com.squareup.anvil.compiler.internal.PsiUtilsKt.requireFqName(PsiUtils.kt:137)
	at com.squareup.anvil.compiler.internal.reference.ClassReference$Psi$directSuperClassReferences$2.invoke(ClassReference.kt:164)
	at com.squareup.anvil.compiler.internal.reference.ClassReference$Psi$directSuperClassReferences$2.invoke(ClassReference.kt:163)
	at kotlin.UnsafeLazyImpl.getValue(Lazy.kt:81)
	at com.squareup.anvil.compiler.internal.reference.ClassReference$Psi.getDirectSuperClassReferences(ClassReference.kt:163)
	at com.squareup.anvil.compiler.internal.reference.ClassReference$Psi.directSuperClassReferences(ClassReference.kt:211)
	at com.squareup.anvil.compiler.internal.reference.ClassReferenceKt$allSuperTypeClassReferences$1.invoke(ClassReference.kt:390)
	at com.squareup.anvil.compiler.internal.reference.ClassReferenceKt$allSuperTypeClassReferences$1.invoke(ClassReference.kt:388)
	at kotlin.sequences.GeneratorSequence$iterator$1.calcNext(Sequences.kt:591)
	at kotlin.sequences.GeneratorSequence$iterator$1.hasNext(Sequences.kt:609)
	at kotlin.sequences.FlatteningSequence$iterator$1.ensureItemIterator(Sequences.kt:311)
	at kotlin.sequences.FlatteningSequence$iterator$1.hasNext(Sequences.kt:303)
	at kotlin.sequences.DistinctIterator.computeNext(Sequences.kt:570)
	at kotlin.collections.AbstractIterator.tryToComputeNext(AbstractIterator.kt:42)
	at kotlin.collections.AbstractIterator.hasNext(AbstractIterator.kt:29)
	at kotlin.sequences.FlatteningSequence$iterator$1.ensureItemIterator(Sequences.kt:307)
	at kotlin.sequences.FlatteningSequence$iterator$1.hasNext(Sequences.kt:303)
	at kotlin.sequences.TransformingSequence$iterator$1.hasNext(Sequences.kt:214)
	at kotlin.sequences.FilteringSequence$iterator$1.calcNext(Sequences.kt:169)
	at kotlin.sequences.FilteringSequence$iterator$1.hasNext(Sequences.kt:194)
	at kotlin.sequences.SequencesKt___SequencesKt.firstOrNull(_Sequences.kt:167)
	at com.squareup.anvil.compiler.internal.PsiUtilsKt.findClassReferenceInSuperTypes(PsiUtils.kt:283)
	at com.squareup.anvil.compiler.internal.PsiUtilsKt.requireFqName(PsiUtils.kt:256)
	at com.squareup.anvil.compiler.internal.PsiUtilsKt.requireFqName(PsiUtils.kt:118)
	at com.squareup.anvil.compiler.internal.PsiUtilsKt.requireFqName(PsiUtils.kt:137)
	at com.squareup.anvil.compiler.internal.reference.ClassReference$Psi$directSuperClassReferences$2.invoke(ClassReference.kt:164)
	at com.squareup.anvil.compiler.internal.reference.ClassReference$Psi$directSuperClassReferences$2.invoke(ClassReference.kt:163)
	at kotlin.UnsafeLazyImpl.getValue(Lazy.kt:81)
	at com.squareup.anvil.compiler.internal.reference.ClassReference$Psi.getDirectSuperClassReferences(ClassReference.kt:163)
	at com.squareup.anvil.compiler.internal.reference.ClassReference$Psi.directSuperClassReferences(ClassReference.kt:211)
	at com.squareup.anvil.compiler.internal.reference.ClassReferenceKt$allSuperTypeClassReferences$1.invoke(ClassReference.kt:390)
	at com.squareup.anvil.compiler.internal.reference.ClassReferenceKt$allSuperTypeClassReferences$1.invoke(ClassReference.kt:388)
	at kotlin.sequences.GeneratorSequence$iterator$1.calcNext(Sequences.kt:591)
	at kotlin.sequences.GeneratorSequence$iterator$1.hasNext(Sequences.kt:609)
	at kotlin.sequences.FlatteningSequence$iterator$1.ensureItemIterator(Sequences.kt:311)
	at kotlin.sequences.FlatteningSequence$iterator$1.hasNext(Sequences.kt:303)
	at kotlin.sequences.DistinctIterator.computeNext(Sequences.kt:570)
	at kotlin.collections.AbstractIterator.tryToComputeNext(AbstractIterator.kt:42)
	at kotlin.collections.AbstractIterator.hasNext(AbstractIterator.kt:29)
	at kotlin.sequences.FlatteningSequence$iterator$1.ensureItemIterator(Sequences.kt:307)
	at kotlin.sequences.FlatteningSequence$iterator$1.hasNext(Sequences.kt:303)
	at kotlin.sequences.TransformingSequence$iterator$1.hasNext(Sequences.kt:214)
	at kotlin.sequences.FilteringSequence$iterator$1.calcNext(Sequences.kt:169)
	at kotlin.sequences.FilteringSequence$iterator$1.hasNext(Sequences.kt:194)
	at kotlin.sequences.SequencesKt___SequencesKt.firstOrNull(_Sequences.kt:167)
	at com.squareup.anvil.compiler.internal.PsiUtilsKt.findClassReferenceInSuperTypes(PsiUtils.kt:283)
	at com.squareup.anvil.compiler.internal.PsiUtilsKt.requireFqName(PsiUtils.kt:256)
```

Here's a test and a code generator reproducing the issue:
https://github.com/gmarques33/KotlinIncrementalCompilationBug/blob/gmarques/stack-overflow/anvil-plugin/src/test/java/com/gmarques/anvilplugin/MyCodeGeneratorTest.kt

https://github.com/gmarques33/KotlinIncrementalCompilationBug/blob/gmarques/stack-overflow/anvil-plugin/src/main/java/com/gmarques/anvilplugin/MyCodeGenerator.kt

Pull request:
Fixes #587

Patch:
diff --git a/compiler-utils/src/main/java/com/squareup/anvil/compiler/internal/PsiUtils.kt b/compiler-utils/src/main/java/com/squareup/anvil/compiler/internal/PsiUtils.kt
index 44f56be4c..890786270 100644
--- a/compiler-utils/src/main/java/com/squareup/anvil/compiler/internal/PsiUtils.kt
+++ b/compiler-utils/src/main/java/com/squareup/anvil/compiler/internal/PsiUtils.kt
@@ -7,7 +7,6 @@ import com.squareup.anvil.compiler.api.AnvilCompilationException
 import com.squareup.anvil.compiler.internal.reference.ClassReference
 import com.squareup.anvil.compiler.internal.reference.allSuperTypeClassReferences
 import com.squareup.anvil.compiler.internal.reference.canResolveFqName
-import com.squareup.anvil.compiler.internal.reference.toClassReference
 import com.squareup.anvil.compiler.internal.reference.toClassReferenceOrNull
 import org.jetbrains.kotlin.com.intellij.psi.PsiElement
 import org.jetbrains.kotlin.descriptors.ModuleDescriptor
@@ -15,6 +14,7 @@ import org.jetbrains.kotlin.name.FqName
 import org.jetbrains.kotlin.psi.KtAnnotationEntry
 import org.jetbrains.kotlin.psi.KtClassLiteralExpression
 import org.jetbrains.kotlin.psi.KtClassOrObject
+import org.jetbrains.kotlin.psi.KtConstructorCalleeExpression
 import org.jetbrains.kotlin.psi.KtDotQualifiedExpression
 import org.jetbrains.kotlin.psi.KtFunctionType
 import org.jetbrains.kotlin.psi.KtNameReferenceExpression
@@ -28,6 +28,7 @@ import org.jetbrains.kotlin.psi.KtUserType
 import org.jetbrains.kotlin.psi.psiUtil.collectDescendantsOfType
 import org.jetbrains.kotlin.psi.psiUtil.parents
 import org.jetbrains.kotlin.psi.psiUtil.parentsWithSelf
+import org.jetbrains.kotlin.resolve.ImportPath
 
 private val kotlinAnnotations = listOf(jvmSuppressWildcardsFqName, publishedApiFqName)
 
@@ -253,7 +254,7 @@ public fun PsiElement.requireFqName(
     .takeIf { it.canResolveFqName(module) }
     ?.let { return it }
 
-  findClassReferenceInSuperTypes(module, classReference)
+  findClassReferenceInSuperTypes(classReference, importPaths, module)
     ?.let { return it.fqName }
 
   // Check if it's a named import.
@@ -270,15 +271,50 @@ public fun PsiElement.requireFqName(
 }
 
 private fun PsiElement.findClassReferenceInSuperTypes(
+  className: String,
+  importPaths: List<ImportPath>,
   module: ModuleDescriptor,
-  className: String
 ): ClassReference? {
   return parents
+    // Get all outer classes, because the type could be from any inner class of them. These
+    // inner classes can be referenced without an import, e.g.
+    //
+    //   class ClassA : SomeType {
+    //     class ClassB : InnerInterfaceOfSomeType
+    //   }
     .filterIsInstance<KtClassOrObject>()
-    .map { it.toClassReference(module) }
+    .flatMap { ktClass ->
+      ktClass.superTypeListEntries
+        .map { superType ->
+          val superTypeName = (
+            // If it's a super constructor call, then we need to strip the argument list, e.g.
+            // ClassA() -> ClassA
+            //
+            // Otherwise it's an interface and we don't need to do anything.
+            superType.children
+              .filterIsInstance<KtConstructorCalleeExpression>()
+              .singleOrNull()?.typeReference?.text
+              ?: superType.typeReference?.text
+            // It might be a generic type, e.g. ClassA<Abc>. Remove the generic type.
+            )?.substringBefore('<')
+            ?: throw AnvilCompilationException(
+              "Couldn't get the super type for ${superType.text}."
+            )
+
+          // Find the FqName for our super type. We check below if it's actually correct.
+          importPaths
+            .singleOrNull {
+              it.alias?.asString() == superTypeName || it.fqName.asString().endsWith(superTypeName)
+            }
+            ?.fqName
+            // No import? Must be in the same package.
+            ?: superType.containingKtFile.packageFqName.descendant(superTypeName)
+        }
+    }
+    .mapNotNull { it.toClassReferenceOrNull(module) }
+    // Now go through all other super types and check if they contain the inner class.
     .flatMap { it.allSuperTypeClassReferences(includeSelf = true) }
-    .mapNotNull { clazz ->
+    .firstNotNullOfOrNull { clazz ->
       clazz.fqName.descendant(className).toClassReferenceOrNull(module)
     }
-    .firstOrNull()
 }


Test patch:
diff --git a/compiler/src/test/java/com/squareup/anvil/compiler/internal/reference/ClassReferenceTest.kt b/compiler/src/test/java/com/squareup/anvil/compiler/internal/reference/ClassReferenceTest.kt
index 0ebfbcdb8..4115b6718 100644
--- a/compiler/src/test/java/com/squareup/anvil/compiler/internal/reference/ClassReferenceTest.kt
+++ b/compiler/src/test/java/com/squareup/anvil/compiler/internal/reference/ClassReferenceTest.kt
@@ -427,6 +427,23 @@ class ClassReferenceTest {
       class ClassA : OuterClass() {
         class ClassB : InnerInterface
       }
+
+      abstract class OuterClass2<T> {
+        interface InnerInterface2<S>
+      }
+
+      class ClassE : OuterClass2<String>() {
+        class ClassF : InnerInterface2<Int>
+      }
+      """,
+      """
+      package com.squareup.test2
+      
+      import com.squareup.test.OuterClass
+        
+      class ClassC : OuterClass() {
+        class ClassD : InnerInterface
+      }  
       """,
       allWarningsAsErrors = false,
       codeGenerators = listOf(
@@ -435,15 +452,43 @@ class ClassReferenceTest {
             when (psiRef.shortName) {
               "OuterClass" -> {
                 assertThat(ref.directSuperTypeReferences()).isEmpty()
+                assertThat(ref.allSuperTypeClassReferences().toList()).isEmpty()
               }
               "InnerInterface" -> {
                 assertThat(ref.directSuperTypeReferences()).isEmpty()
+                assertThat(ref.allSuperTypeClassReferences().toList()).isEmpty()
               }
               "ClassA" -> {
                 assertThat(ref.directSuperTypeReferences()).hasSize(1)
+                assertThat(ref.allSuperTypeClassReferences().toList()).hasSize(1)
               }
               "ClassB" -> {
                 assertThat(ref.directSuperTypeReferences()).hasSize(1)
+                assertThat(ref.allSuperTypeClassReferences().toList()).hasSize(1)
+              }
+              "ClassC" -> {
+                assertThat(ref.directSuperTypeReferences()).hasSize(1)
+                assertThat(ref.allSuperTypeClassReferences().toList()).hasSize(1)
+              }
+              "ClassD" -> {
+                assertThat(ref.directSuperTypeReferences()).hasSize(1)
+                assertThat(ref.allSuperTypeClassReferences().toList()).hasSize(1)
+              }
+              "OuterClass2" -> {
+                assertThat(ref.directSuperTypeReferences()).isEmpty()
+                assertThat(ref.allSuperTypeClassReferences().toList()).isEmpty()
+              }
+              "InnerInterface2" -> {
+                assertThat(ref.directSuperTypeReferences()).isEmpty()
+                assertThat(ref.allSuperTypeClassReferences().toList()).isEmpty()
+              }
+              "ClassE" -> {
+                assertThat(ref.directSuperTypeReferences()).hasSize(1)
+                assertThat(ref.allSuperTypeClassReferences().toList()).hasSize(1)
+              }
+              "ClassF" -> {
+                assertThat(ref.directSuperTypeReferences()).hasSize(1)
+                assertThat(ref.allSuperTypeClassReferences().toList()).hasSize(1)
               }
               else -> throw NotImplementedError(psiRef.shortName)
             }