Repository: autonomousapps/dependency-analysis-android-gradle-plugin
Issue: KotlinNullPointerException on `antlr` dependency

Reproducer project there: https://github.com/martinbonnin/test-antlr/

Calling `./gradlew buildHealth --stacktrace` fails with:

```
Caused by: kotlin.KotlinNullPointerException
        at com.autonomousapps.internal.ConsoleReport$Companion.from(models.kt:487)
        at com.autonomousapps.tasks.BuildHealthTask.action(BuildHealthTask.kt:46)
        at org.gradle.internal.reflect.JavaMethod.invoke(JavaMethod.java:104)
        at org.gradle.api.internal.project.taskfactory.StandardTaskAction.doExecute(StandardTaskAction.java:49)
```

This only happens on current `main`. The sample projects expects `0.61.1-SNAPSHOT` in mavenLocal. `0.61.0` is ok (although it displays "null" for configurations). I guess it has to do with the fact that the `antlr` plugin adds the dependency automagically. 



Changed files:
src/main/kotlin/com/autonomousapps/internal/models.kt
src/main/kotlin/com/autonomousapps/tasks/ArtifactsReportTask.kt
src/main/kotlin/com/autonomousapps/tasks/LocateDependenciesTask.kt
src/main/kotlin/com/autonomousapps/internal/ConfigurationsToDependenciesTransformer.kt

diff --git a/src/main/kotlin/com/autonomousapps/internal/ConfigurationsToDependenciesTransformer.kt b/src/main/kotlin/com/autonomousapps/internal/ConfigurationsToDependenciesTransformer.kt
index 8bcd2f3e..b2472545 100644
--- a/src/main/kotlin/com/autonomousapps/internal/ConfigurationsToDependenciesTransformer.kt
+++ b/src/main/kotlin/com/autonomousapps/internal/ConfigurationsToDependenciesTransformer.kt
@@ -1,23 +1,20 @@
 package com.autonomousapps.internal
 
-import com.autonomousapps.internal.utils.capitalizeSafely
-import com.autonomousapps.internal.utils.flatMapToMutableSet
-import com.autonomousapps.internal.utils.getLogger
-import com.autonomousapps.internal.utils.toIdentifiers
+import com.autonomousapps.internal.utils.*
 import com.autonomousapps.tasks.LocateDependenciesTask
-import org.gradle.api.Project
+import org.gradle.api.artifacts.ConfigurationContainer
 
 internal class ConfigurationsToDependenciesTransformer(
   private val flavorName: String?,
   private val variantName: String,
-  private val project: Project
+  private val configurations: ConfigurationContainer
 ) {
 
   private val logger = getLogger<LocateDependenciesTask>()
 
   companion object {
     private val DEFAULT_CONFS = listOf(
-      "api", "implementation", "compile", "compileOnly", "runtimeOnly", "antlr"
+      "api", "implementation", "compile", "compileOnly", "runtimeOnly"
     )
     private val DEFAULT_PROC_CONFS = listOf("kapt", "annotationProcessor")
   }
@@ -25,18 +22,23 @@ internal class ConfigurationsToDependenciesTransformer(
   fun dependencyConfigurations(): Set<DependencyConfiguration> {
     val candidateConfNames = buildConfNames() + buildAPConfNames()
 
-    // Filter all configurations for those we care about
-    val interestingConfs = project.configurations.asMap
-      .filter { (name, _) -> candidateConfNames.contains(name) }
-      .map { (_, conf) -> conf }
+    // Partition all configurations into those we care about and those we don't
+    val (interestingConfs, otherConfs) = configurations.partition {
+      candidateConfNames.contains(it.name)
+    }
 
     // TODO combine these into one sink
     val warnings = linkedMapOf<String, MutableSet<String>>()
     val metadataSink = mutableMapOf<String, Boolean>()
 
+    // Get all the interesting confs
     val locations = interestingConfs.flatMapToMutableSet { conf ->
       conf.dependencies.toIdentifiers(metadataSink).map { identifier ->
-        DependencyConfiguration(identifier = identifier, configurationName = conf.name).also {
+        DependencyConfiguration(
+          identifier = identifier,
+          configurationName = conf.name,
+          isInteresting = true
+        ).also {
           // Looking for dependencies stored on multiple configurations
           warnings.merge(it.identifier, mutableSetOf(it.configurationName)) { old, new ->
             old.apply { addAll(new) }
@@ -44,6 +46,18 @@ internal class ConfigurationsToDependenciesTransformer(
         }
       }
     }
+    // Get all the non-interesting confs, too
+    val otherLocations = otherConfs.flatMapToSet { conf ->
+      conf.dependencies.toIdentifiers(metadataSink).map { identifier ->
+        DependencyConfiguration(
+          identifier = identifier,
+          configurationName = conf.name,
+          isInteresting = false
+        )
+      }
+    }
+      // if a dependency happens to be in both sets, prefer locations over otherLocations
+      .filterToSet { !locations.contains(it) }
 
     // Warn if dependency is declared on multiple configurations
     warnings.entries.forEach { (identifier, configurations) ->
@@ -63,7 +77,7 @@ internal class ConfigurationsToDependenciesTransformer(
       }
     }
 
-    return locations
+    return locations + otherLocations
   }
 
   private fun buildConfNames(): Set<String> {
diff --git a/src/main/kotlin/com/autonomousapps/internal/models.kt b/src/main/kotlin/com/autonomousapps/internal/models.kt
index 45acb803..0dbfb513 100644
--- a/src/main/kotlin/com/autonomousapps/internal/models.kt
+++ b/src/main/kotlin/com/autonomousapps/internal/models.kt
@@ -4,13 +4,10 @@ package com.autonomousapps.internal
 
 import com.autonomousapps.advice.*
 import com.autonomousapps.internal.AndroidPublicRes.Line
+import com.autonomousapps.internal.DependencyConfiguration.Companion.findMatch
 import com.autonomousapps.internal.advice.ComputedAdvice
 import com.autonomousapps.internal.asm.Opcodes
 import com.autonomousapps.internal.utils.*
-import com.autonomousapps.internal.utils.asString
-import com.autonomousapps.internal.utils.efficient
-import com.autonomousapps.internal.utils.mapToSet
-import com.autonomousapps.internal.utils.resolvedVersion
 import org.gradle.api.artifacts.component.ComponentIdentifier
 import java.io.File
 import java.io.Serializable
@@ -18,12 +15,34 @@ import java.lang.annotation.RetentionPolicy
 import java.util.regex.Pattern
 
 /**
- * A tuple of an identifier (project or external module) and the name of the configuration on which it is declared.
+ * A tuple of an identifier (project or external module) and the name of the configuration on which
+ * it is declared.
  */
 data class DependencyConfiguration(
   val identifier: String,
-  val configurationName: String
-) : Serializable
+  val configurationName: String,
+  /**
+   * Only a small handful of configurations are "interesting" for dependency analysis. The rest
+   * should be ignored.
+   */
+  val isInteresting: Boolean
+) : Serializable {
+
+  /**
+   * Returns true if this is an interesting location and if the identifiers match.
+   */
+  fun matchesComponentIdentifier(componentIdentifier: ComponentIdentifier): Boolean {
+    return isInteresting && identifier == componentIdentifier.asString()
+  }
+
+  companion object {
+    internal fun Iterable<DependencyConfiguration>.findMatch(
+      componentIdentifier: ComponentIdentifier
+    ): DependencyConfiguration? = find {
+      it.matchesComponentIdentifier(componentIdentifier)
+    }
+  }
+}
 
 /**
  * Primarily used as a pointer to a [file] on disk; a physical artifact.
@@ -51,7 +70,7 @@ data class Artifact(
     dependency = Dependency(
       identifier = componentIdentifier.asString(),
       resolvedVersion = componentIdentifier.resolvedVersion(),
-      configurationName = candidates.find { it.identifier == componentIdentifier.asString() }?.configurationName
+      configurationName = candidates.findMatch(componentIdentifier)?.configurationName
     ),
     file = file
   )
@@ -72,7 +91,7 @@ data class NativeLibDependency(
     dependency = Dependency(
       identifier = componentIdentifier.asString(),
       resolvedVersion = componentIdentifier.resolvedVersion(),
-      configurationName = candidates.find { it.identifier == componentIdentifier.asString() }?.configurationName
+      configurationName = candidates.findMatch(componentIdentifier)?.configurationName
     ),
     fileNames = fileNames
   )
@@ -425,7 +444,7 @@ data class AnnotationProcessor(
     dependency = Dependency(
       identifier = componentIdentifier.asString(),
       resolvedVersion = componentIdentifier.resolvedVersion(),
-      configurationName = candidates.find { it.identifier == componentIdentifier.asString() }?.configurationName
+      configurationName = candidates.findMatch(componentIdentifier)?.configurationName
     )
   )
 
@@ -451,7 +470,7 @@ internal data class ServiceLoader(
     dependency = Dependency(
       identifier = componentIdentifier.asString(),
       resolvedVersion = componentIdentifier.resolvedVersion(),
-      configurationName = candidates.find { it.identifier == componentIdentifier.asString() }?.configurationName
+      configurationName = candidates.findMatch(componentIdentifier)?.configurationName
     )
   )
 
diff --git a/src/main/kotlin/com/autonomousapps/tasks/ArtifactsReportTask.kt b/src/main/kotlin/com/autonomousapps/tasks/ArtifactsReportTask.kt
index df5d33a4..3f8cbd58 100644
--- a/src/main/kotlin/com/autonomousapps/tasks/ArtifactsReportTask.kt
+++ b/src/main/kotlin/com/autonomousapps/tasks/ArtifactsReportTask.kt
@@ -5,10 +5,9 @@ package com.autonomousapps.tasks
 import com.autonomousapps.TASK_GROUP_DEP_INTERNAL
 import com.autonomousapps.internal.Artifact
 import com.autonomousapps.internal.DependencyConfiguration
+import com.autonomousapps.internal.utils.*
 import com.autonomousapps.internal.utils.fromJsonSet
 import com.autonomousapps.internal.utils.getAndDelete
-import com.autonomousapps.internal.utils.toJson
-import com.autonomousapps.internal.utils.toPrettyString
 import org.gradle.api.DefaultTask
 import org.gradle.api.GradleException
 import org.gradle.api.artifacts.ArtifactCollection
@@ -67,6 +66,7 @@ abstract class ArtifactsReportTask : DefaultTask() {
     val reportPrettyFile = outputPretty.getAndDelete()
 
     val candidates = dependencyConfigurations.fromJsonSet<DependencyConfiguration>()
+      .filterToSet { it.isInteresting }
 
     val artifacts = artifacts.mapNotNull {
       try {
diff --git a/src/main/kotlin/com/autonomousapps/tasks/LocateDependenciesTask.kt b/src/main/kotlin/com/autonomousapps/tasks/LocateDependenciesTask.kt
index 756967cc..bb091cd9 100644
--- a/src/main/kotlin/com/autonomousapps/tasks/LocateDependenciesTask.kt
+++ b/src/main/kotlin/com/autonomousapps/tasks/LocateDependenciesTask.kt
@@ -39,7 +39,7 @@ abstract class LocateDependenciesTask : DefaultTask() {
     val locations = ConfigurationsToDependenciesTransformer(
       flavorName = flavorName.orNull,
       variantName = variantName.get(),
-      project = project
+      configurations = project.configurations
     ).dependencyConfigurations()
 
     outputFile.writeText(locations.toJson())