Isolate KSP2 processor classpath from KAPT processor JARs (#1670)

When a target has both KAPT and KSP plugins, the KSP2 action was receiving all processor JARs — including KAPT-only processors — on its --processor_classpath. If any of those KAPT JARs contained a META-INF/services/com.google.devtools.ksp.processing.SymbolProcessorProvider registration (e.g. dagger-compiler 2.48+), KSP2 would auto-discover and run them, causing spurious type resolution failures.

- Add `_targets_to_ksp_transitive_runtime_jars` in plugins.bzl that collects processor JARs only from KspPluginInfo targets, excluding JavaPluginInfo/JavaInfo (KAPT) processors
- Pass this KSP-only depset to `_run_ksp_builder_actions` for `--processor_classpath`, while KAPT and KotlinCompile actions continue to receive the full `transitive_runtime_jars`
- Fix `_targets_to_annotation_processors` checking `_KspPluginInfo in targets` (the list) instead of `_KspPluginInfo in t` (the loop variable)
- Add analysis test verifying KSP2 processor classpath excludes KAPT JARs when both plugin types are present
diff --git a/kotlin/internal/jvm/compile.bzl b/kotlin/internal/jvm/compile.bzl
index 7991b73..8c40e0a 100644
--- a/kotlin/internal/jvm/compile.bzl
+++ b/kotlin/internal/jvm/compile.bzl
@@ -778,6 +778,7 @@
     ksp_annotation_processors = _plugin_mappers.targets_to_ksp_annotation_processors(ctx.attr.plugins + ctx.attr.deps)
     ksp_options = _plugin_mappers.targets_to_ksp_options(ctx.attr.plugins + ctx.attr.deps)
     transitive_runtime_jars = _plugin_mappers.targets_to_transitive_runtime_jars(ctx.attr.plugins + ctx.attr.deps)
+    ksp_transitive_runtime_jars = _plugin_mappers.targets_to_ksp_transitive_runtime_jars(ctx.attr.plugins + ctx.attr.deps)
     plugins = _new_plugins_from(ctx.attr.plugins + _exported_plugins(deps = ctx.attr.deps))
 
     deps_artifacts = _deps_artifacts(toolchains, ctx.attr.deps + ctx.attr.associates)
@@ -802,6 +803,7 @@
         ksp_annotation_processors = ksp_annotation_processors,
         ksp_options = ksp_options,
         transitive_runtime_jars = transitive_runtime_jars,
+        ksp_transitive_runtime_jars = ksp_transitive_runtime_jars,
         plugins = plugins,
         compile_jar = compile_jar,
         output_jdeps = output_jdeps,
@@ -907,6 +909,7 @@
         ksp_annotation_processors,
         ksp_options,
         transitive_runtime_jars,
+        ksp_transitive_runtime_jars,
         plugins,
         compile_jar,
         output_jdeps):
@@ -952,7 +955,7 @@
             toolchains = toolchains,
             srcs = srcs,
             compile_deps = compile_deps,
-            transitive_runtime_jars = transitive_runtime_jars,
+            transitive_runtime_jars = ksp_transitive_runtime_jars,
             ksp_options = ksp_options,
         )
         ksp_generated_class_jar = ksp_outputs.ksp_generated_class_jar
diff --git a/kotlin/internal/jvm/plugins.bzl b/kotlin/internal/jvm/plugins.bzl
index ab949a0..9591414 100644
--- a/kotlin/internal/jvm/plugins.bzl
+++ b/kotlin/internal/jvm/plugins.bzl
@@ -35,7 +35,7 @@
 def _targets_to_annotation_processors(targets):
     plugins = []
     for t in targets:
-        if _KspPluginInfo in targets:
+        if _KspPluginInfo in t:
             # KSP plugins are handled by the KSP Kotlinc compiler plugin
             pass
         elif JavaPluginInfo in t:
@@ -81,12 +81,20 @@
             transitive.extend([plugin.plugins.processor_jars for plugin in t[_KspPluginInfo].plugins])
     return depset(transitive = transitive)
 
+def _targets_to_ksp_transitive_runtime_jars(targets):
+    transitive = []
+    for t in targets:
+        if _KspPluginInfo in t:
+            transitive.extend([plugin.plugins.processor_jars for plugin in t[_KspPluginInfo].plugins])
+    return depset(transitive = transitive)
+
 mappers = struct(
     targets_to_annotation_processors = _targets_to_annotation_processors,
     targets_to_ksp_annotation_processors = _targets_to_ksp_annotation_processors,
     targets_to_ksp_options = _targets_to_ksp_options,
     targets_to_annotation_processors_java_plugin_info = _targets_to_annotation_processors_java_plugin_info,
     targets_to_transitive_runtime_jars = _targets_to_transitive_runtime_jars,
+    targets_to_ksp_transitive_runtime_jars = _targets_to_ksp_transitive_runtime_jars,
     kt_plugin_to_processor = _kt_plugin_to_processor,
     kt_plugin_to_processorpath = _kt_plugin_to_processorpath,
 )
diff --git a/src/test/starlark/ksp/BUILD.bazel b/src/test/starlark/ksp/BUILD.bazel
index fcf0cc0..67e2273 100644
--- a/src/test/starlark/ksp/BUILD.bazel
+++ b/src/test/starlark/ksp/BUILD.bazel
@@ -12,9 +12,10 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
+load("@rules_java//java:defs.bzl", "java_plugin")
 load("//kotlin:core.bzl", "kt_ksp_plugin")
 load("//kotlin:jvm.bzl", "kt_jvm_library")
-load(":ksp_test.bzl", "ksp_action_test", "ksp_conflicting_options_test", "ksp_javac_excludes_srcjars_test", "ksp_javac_includes_srcjars_test", "ksp_options_action_test", "ksp_outputs_test", "ksp_plugin_empty_options_provider_test", "ksp_plugin_options_provider_test", "ksp_single_action_test")
+load(":ksp_test.bzl", "ksp_action_test", "ksp_conflicting_options_test", "ksp_javac_excludes_srcjars_test", "ksp_javac_includes_srcjars_test", "ksp_options_action_test", "ksp_outputs_test", "ksp_plugin_empty_options_provider_test", "ksp_plugin_options_provider_test", "ksp_processor_classpath_isolation_test", "ksp_single_action_test")
 
 # Simple KSP plugin for testing (uses moshi which generates Kotlin code)
 kt_ksp_plugin(
@@ -158,6 +159,14 @@
     ],
 )
 
+# KAPT processor plugin for testing classpath isolation
+java_plugin(
+    name = "autovalue_plugin",
+    generates_api = 1,
+    processor_class = "com.google.auto.value.processor.AutoValueProcessor",
+    deps = ["@kotlin_rules_maven//:com_google_auto_value_auto_value"],
+)
+
 # Mixed Java+Kotlin with KSP (generates_java=False) — KSP srcjars must NOT go to javac
 kt_jvm_library(
     name = "ksp_mixed_no_java_gen",
@@ -186,6 +195,22 @@
     ],
 )
 
+# Target with both KAPT (autovalue) and KSP (moshi) plugins — verifies
+# that KAPT processor JARs do not leak onto the KSP2 processor classpath
+kt_jvm_library(
+    name = "ksp_test_lib_with_kapt",
+    srcs = ["TestModel.kt"],
+    plugins = [
+        ":autovalue_plugin",
+        ":moshi_plugin",
+    ],
+    deps = [
+        "@kotlin_rules_maven//:com_google_auto_value_auto_value_annotations",
+        "@kotlin_rules_maven//:com_squareup_moshi_moshi",
+        "@kotlin_rules_maven//:com_squareup_moshi_moshi_kotlin",
+    ],
+)
+
 # Test that KSP srcjars are excluded from javac when generates_java=False
 ksp_javac_excludes_srcjars_test(
     name = "ksp_javac_excludes_srcjars_test",
@@ -198,6 +223,12 @@
     target_under_test = ":ksp_mixed_with_java_gen",
 )
 
+# Test that KSP2 processor classpath only contains KSP processor JARs
+ksp_processor_classpath_isolation_test(
+    name = "ksp_processor_classpath_isolation_test",
+    target_under_test = ":ksp_test_lib_with_kapt",
+)
+
 test_suite(
     name = "ksp_tests",
     tests = [
@@ -209,6 +240,7 @@
         ":ksp_outputs_test",
         ":ksp_plugin_empty_options_provider_test",
         ":ksp_plugin_options_provider_test",
+        ":ksp_processor_classpath_isolation_test",
         ":ksp_single_action_test",
     ],
 )
diff --git a/src/test/starlark/ksp/ksp_test.bzl b/src/test/starlark/ksp/ksp_test.bzl
index 724eba6..9c6199f 100644
--- a/src/test/starlark/ksp/ksp_test.bzl
+++ b/src/test/starlark/ksp/ksp_test.bzl
@@ -256,6 +256,49 @@
     _ksp_javac_includes_srcjars_when_generating_java_test_impl,
 )
 
+def _ksp_processor_classpath_isolation_test_impl(ctx):
+    """Verify KSP2 processor classpath excludes KAPT processor JARs."""
+    env = analysistest.begin(ctx)
+
+    actions = analysistest.target_actions(env)
+    ksp2_actions = [a for a in actions if a.mnemonic == "KotlinKsp2"]
+
+    asserts.equals(env, 1, len(ksp2_actions), "Should have exactly one KotlinKsp2 action")
+
+    argv = ksp2_actions[0].argv
+
+    # Collect all --processor_classpath values from argv
+    processor_classpath_entries = []
+    collecting = False
+    for arg in argv:
+        if arg == "--processor_classpath":
+            collecting = True
+        elif collecting and arg.startswith("--"):
+            collecting = False
+        elif collecting:
+            processor_classpath_entries.append(arg)
+
+    # KAPT processor JARs (autovalue) must NOT be on KSP2 processor classpath
+    kapt_jars_on_ksp = [e for e in processor_classpath_entries if "auto_value" in e or "auto-value" in e]
+    asserts.equals(
+        env,
+        0,
+        len(kapt_jars_on_ksp),
+        "KAPT processor JARs should not leak onto KSP2 --processor_classpath, found: %s" % kapt_jars_on_ksp,
+    )
+
+    # KSP processor JARs (moshi) SHOULD be present
+    ksp_jars = [e for e in processor_classpath_entries if "moshi" in e]
+    asserts.true(
+        env,
+        len(ksp_jars) > 0,
+        "KSP processor JARs (moshi) should be on KSP2 --processor_classpath",
+    )
+
+    return analysistest.end(env)
+
+ksp_processor_classpath_isolation_test = analysistest.make(_ksp_processor_classpath_isolation_test_impl)
+
 def ksp_test_suite(name):
     """Create test suite for KSP2 integration tests.
 
@@ -276,5 +319,6 @@
             ":ksp_options_action_test",
             ":ksp_javac_excludes_srcjars_test",
             ":ksp_javac_includes_srcjars_test",
+            ":ksp_processor_classpath_isolation_test",
         ],
     )