Repository: pantsbuild/pants

Issue:
Java: dependency inference needs to handle ambiguous owners
Similar to Python, Shell, Protobuf, etc, we must warn when you have >1 owner of the same import.


Discussion:
@stuhood I think it is a good use of time for me to do this one? It requires zero knowledge of JVM and I wrote most the original code.

cc @tdyas @patricklaw 
Yea, I think that that would be good. It would be great to lift/generalize that code.
Currently this results in:
```
pants.backend.java.dependency_inference.package_prefix_tree.PackageRootedDependencyMap.ConflictingTypeOwnershipError: Attempted register 'blah/src/main/java/com/example/Client.java' as provider of fully qualified type 'com.example.Client', but it is already provided by 'foo/src/main/java/com/example/Client.java'
```

Resolving patch:
diff --git a/src/python/pants/backend/java/dependency_inference/package_prefix_tree.py b/src/python/pants/backend/java/dependency_inference/package_prefix_tree.py
--- a/src/python/pants/backend/java/dependency_inference/package_prefix_tree.py
+++ b/src/python/pants/backend/java/dependency_inference/package_prefix_tree.py
@@ -9,105 +9,46 @@
 
 
 class PackageRootedDependencyMap:
-    """A utility class for mapping Java FQTs and packages to owning source Addresses.
+    """A utility class for mapping Java FQTs to owning source Addresses.
 
-    This class treats Java packages as logically opaque strings, ignoring the
-    apparent hierarchy (which is itself misleading).  For example, the packages
-    "org.pantsbuild" and "org.pantsbuild.foo" are treated as completely unrelated
-    packages by this mapping implementation.
-
-    We keep track of two things:
-        * Which Address provides a fully qualified symbol
-        * The set of Addresses that provide a given package
+    Keep tracks of which Address provides a fully qualified symbol.
     """
 
-    class ConflictingTypeOwnershipError(Exception):
-        """Raised when a Java FQT appears to be provided by more than one Address."""
-
     def __init__(self):
-        self._type_map: dict[str, Address] = {}
-        self._package_map: dict[str, set[Address]] = defaultdict(set)
+        self._type_map: dict[str, set[Address]] = defaultdict(set)
 
     def add_top_level_type(self, package: str, type_: str, address: Address):
-        """Declare a single Address as the provider of a top level type.
+        """Declare a single Address as a provider of a top level type.
 
-        Raises ConflictingTypeOwnershipError if another address is already the provider
-        of the passed FQT.
-
-        This method also associates the address with the type's package, and there can
-        be more than one address associated with a given package.
+        This method also associates the address with the type's package, and there can be more than
+        one address associated with a given package.
         """
         fqt = ".".join([package, type_])
-        if fqt in self._type_map and self._type_map[fqt] != address:
-            raise PackageRootedDependencyMap.ConflictingTypeOwnershipError(
-                f"Attempted register '{address}' as provider of fully qualified type"
-                f" '{fqt}', but it is already provided by '{self._type_map[fqt]}'"
-            )
-        self._type_map[fqt] = address
-        self._package_map[package].add(address)
+        self._type_map[fqt].add(address)
 
     def add_package(self, package: str, address: Address):
         """Add an address as one of the providers of a package."""
-        self._package_map[package].add(address)
-
-    def addresses_for_symbol(self, symbol: str) -> frozenset[Address]:
-        """Returns the set of addresses that provide the passed symbol.
 
-        `symbol` should be a fully qualified Java type (FQT) (e.g. `foo.bar.Thing`),
-        or a Java package (e.g. `foo.bar`).
+    def addresses_for_type(self, symbol: str) -> frozenset[Address]:
+        """Returns the set of addresses that provide the passed type.
 
-        We first check if the symbol has an exact matching provider address for the FQT.
-        If it does, only that address is returned.  We then check if the symbol is
-        actually a package, in which case we return the set of addresses that provide
-        that package.
-
-        We then chop off the rightmost part of the symbol (e.g. `foo.bar.Thing` becomes
-        `foo.bar`) and repeat the above process until there is nothing left.  If nothing
-        is found, an empty set is returned.
+        `symbol` should be a fully qualified Java type (FQT) (e.g. `foo.bar.Thing`).
         """
-        # TODO: Consider removing as callers may not need it.
-        parts = symbol.split(".")
-        for num_parts in range(len(parts), 0, -1):
-            prefix = ".".join(parts[:num_parts])
-            if prefix in self._type_map:
-                return frozenset([self._type_map[prefix]])
-            if prefix in self._package_map:
-                return frozenset(self._package_map[prefix])
-        return frozenset()
-
-    def addresses_for_exact_symbol(self, symbol: str) -> frozenset[Address]:
-        result = self._type_map.get(symbol)
-        return frozenset({result}) if result else frozenset()
+        return frozenset(self._type_map[symbol])
 
     def merge(self, other: PackageRootedDependencyMap):
-        """Merge 'other' into this dependency map.
-
-        Raises ConflictingTypeOwnershipError if 'other' has an FQT mapped to an address that
-        conflicts with this dep map.
-        """
+        """Merge 'other' into this dependency map."""
 
-        for type_, address in other._type_map.items():
-            if type_ in self._type_map and self._type_map[type_] != address:
-                raise PackageRootedDependencyMap.ConflictingTypeOwnershipError(
-                    'Conflicting ownership of FQT "{type_}": both {self._type_map[type_]}'
-                    " and {address} appear to provide this type."
-                )
-            self._type_map[type_] = address
-        for package, addresses in other._package_map.items():
-            self._package_map[package] |= addresses
+        for type_, addresses in other._type_map.items():
+            self._type_map[type_] |= addresses
 
     def to_json_dict(self):
         return {
-            "type_map": {ty: str(addr) for ty, addr in self._type_map.items()},
-            "package_map": {
-                pkg: [str(addr) for addr in addrs] for pkg, addrs in self._package_map.items()
-            },
+            "type_map": {ty: [str(addr) for addr in addrs] for ty, addrs in self._type_map.items()},
         }
 
     def __repr__(self) -> str:
-        type_map = ", ".join(f"{ty}:{addr}" for ty, addr in self._type_map.items())
-        package_map = ", ".join(
-            f"{pkg}:{', '.join(str(addr) for addr in addrs)}"
-            for pkg, addrs in self._package_map.items()
+        type_map = ", ".join(
+            f"{ty}:{', '.join(str(addr) for addr in addrs)}" for ty, addrs in self._type_map.items()
         )
-        return f"PackageRootedDependencyMap(type_map={type_map}, package_map={package_map})"
+        return f"PackageRootedDependencyMap(type_map={type_map})"
diff --git a/src/python/pants/backend/java/dependency_inference/rules.py b/src/python/pants/backend/java/dependency_inference/rules.py
--- a/src/python/pants/backend/java/dependency_inference/rules.py
+++ b/src/python/pants/backend/java/dependency_inference/rules.py
@@ -1,19 +1,14 @@
 # Copyright 2021 Pants project contributors (see CONTRIBUTORS.md).
 # Licensed under the Apache License, Version 2.0 (see LICENSE).
 
-import itertools
 import logging
 from typing import cast
 
 from pants.backend.java.dependency_inference import import_parser, java_parser, package_mapper
-from pants.backend.java.dependency_inference.import_parser import (
-    ParsedJavaImports,
-    ParseJavaImportsRequest,
-)
 from pants.backend.java.dependency_inference.package_mapper import FirstPartyJavaPackageMapping
 from pants.backend.java.dependency_inference.types import JavaSourceDependencyAnalysis
 from pants.backend.java.target_types import JavaSourceField
-from pants.core.util_rules.source_files import SourceFiles, SourceFilesRequest
+from pants.core.util_rules.source_files import SourceFilesRequest
 from pants.core.util_rules.source_files import rules as source_files_rules
 from pants.engine.addresses import Address
 from pants.engine.rules import Get, MultiGet, collect_rules, rule
@@ -27,6 +22,7 @@
 )
 from pants.engine.unions import UnionRule
 from pants.option.subsystem import Subsystem
+from pants.util.ordered_set import OrderedSet
 
 logger = logging.getLogger(__name__)
 
@@ -42,73 +38,71 @@ def register_options(cls, register):
             "--imports",
             default=True,
             type=bool,
-            help=(
-                "Infer a target's imported dependencies by parsing import statements from sources."
-            ),
+            help=("Infer a target's dependencies by parsing import statements from sources."),
+        )
+        register(
+            "--consumed-types",
+            default=True,
+            type=bool,
+            help=("Infer a target's dependencies by parsing consumed types from sources."),
         )
 
     @property
     def imports(self) -> bool:
         return cast(bool, self.options.imports)
 
+    @property
+    def consumed_types(self) -> bool:
+        return cast(bool, self.options.consumed_types)
 
-class InferJavaImportDependencies(InferDependenciesRequest):
+
+class InferJavaSourceDependencies(InferDependenciesRequest):
     infer_from = JavaSourceField
 
 
 @rule(desc="Inferring Java dependencies by analyzing imports")
 async def infer_java_dependencies_via_imports(
-    request: InferJavaImportDependencies,
+    request: InferJavaSourceDependencies,
     java_infer_subsystem: JavaInferSubsystem,
     first_party_dep_map: FirstPartyJavaPackageMapping,
 ) -> InferredDependencies:
-    if not java_infer_subsystem.imports:
+    if not java_infer_subsystem.imports and not java_infer_subsystem.consumed_types:
         return InferredDependencies([])
 
-    wrapped_tgt = await Get(WrappedTarget, Address, request.sources_field.address)
-    explicitly_provided_deps, detected_imports = await MultiGet(
+    address = request.sources_field.address
+    wrapped_tgt = await Get(WrappedTarget, Address, address)
+    explicitly_provided_deps, analysis = await MultiGet(
         Get(ExplicitlyProvidedDependencies, DependenciesRequest(wrapped_tgt.target[Dependencies])),
-        Get(
-            ParsedJavaImports,
-            ParseJavaImportsRequest(
-                request.sources_field,
-            ),
-        ),
-    )
-    relevant_imports = detected_imports  # TODO: Remove stdlib
-
-    dep_map = first_party_dep_map.package_rooted_dependency_map
-
-    candidate_symbols = list(relevant_imports)
-    return InferredDependencies(
-        dependencies=itertools.chain.from_iterable(
-            dep_map.addresses_for_symbol(imp) for imp in candidate_symbols
-        ),
+        Get(JavaSourceDependencyAnalysis, SourceFilesRequest([request.sources_field])),
     )
 
+    types: OrderedSet[str] = OrderedSet()
+    if java_infer_subsystem.imports:
+        types.update(imp.name for imp in analysis.imports)
+    if java_infer_subsystem.consumed_types:
+        package = analysis.declared_package
+        types.update(
+            f"{package}.{consumed_type}" for consumed_type in analysis.consumed_unqualified_types
+        )
 
-class InferJavaConsumedTypesDependencies(InferDependenciesRequest):
-    infer_from = JavaSourceField
-
+    dep_map = first_party_dep_map.package_rooted_dependency_map
 
-@rule(desc="Inferring Java dependencies by analyzing consumed and top-level types")
-async def infer_java_dependencies_via_consumed_types(
-    request: InferJavaConsumedTypesDependencies,
-    first_party_dep_map: FirstPartyJavaPackageMapping,
-) -> InferredDependencies:
-    source_files = await Get(SourceFiles, SourceFilesRequest([request.sources_field]))
-    analysis = await Get(JavaSourceDependencyAnalysis, SourceFiles, source_files)
+    dependencies: OrderedSet[Address] = OrderedSet()
+    for typ in types:
+        matches = dep_map.addresses_for_type(typ)
+        if not matches:
+            continue
+        explicitly_provided_deps.maybe_warn_of_ambiguous_dependency_inference(
+            matches,
+            address,
+            import_reference="type",
+            context=f"The target {address} imports `{typ}`",
+        )
+        maybe_disambiguated = explicitly_provided_deps.disambiguated(matches)
+        if maybe_disambiguated:
+            dependencies.add(maybe_disambiguated)
 
-    package = analysis.declared_package
-    dep_map = first_party_dep_map.package_rooted_dependency_map
-    candidate_consumed_types = [
-        f"{package}.{consumed_type}" for consumed_type in analysis.consumed_unqualified_types
-    ]
-    return InferredDependencies(
-        dependencies=itertools.chain.from_iterable(
-            dep_map.addresses_for_exact_symbol(imp) for imp in candidate_consumed_types
-        ),
-    )
+    return InferredDependencies(dependencies)
 
 
 def rules():
@@ -118,6 +112,5 @@ def rules():
         *import_parser.rules(),
         *package_mapper.rules(),
         *source_files_rules(),
-        UnionRule(InferDependenciesRequest, InferJavaImportDependencies),
-        UnionRule(InferDependenciesRequest, InferJavaConsumedTypesDependencies),
+        UnionRule(InferDependenciesRequest, InferJavaSourceDependencies),
     ]
diff --git a/src/python/pants/engine/target.py b/src/python/pants/engine/target.py
--- a/src/python/pants/engine/target.py
+++ b/src/python/pants/engine/target.py
@@ -1953,7 +1953,7 @@ class ExplicitlyProvidedDependencies:
     ignores: FrozenOrderedSet[Address]
 
     @memoized_method
-    def any_are_covered_by_includes(self, addresses: Tuple[Address, ...]) -> bool:
+    def any_are_covered_by_includes(self, addresses: Iterable[Address]) -> bool:
         """Return True if every address is in the explicitly provided includes.
 
         Note that if the input addresses are generated targets, they will still be marked as covered
@@ -1966,7 +1966,7 @@ def any_are_covered_by_includes(self, addresses: Tuple[Address, ...]) -> bool:
 
     @memoized_method
     def remaining_after_disambiguation(
-        self, addresses: Tuple[Address, ...], owners_must_be_ancestors: bool
+        self, addresses: Iterable[Address], owners_must_be_ancestors: bool
     ) -> frozenset[Address]:
         """All addresses that remain after ineligible candidates are discarded.
 
@@ -1996,7 +1996,7 @@ def is_valid(addr: Address) -> bool:
 
     def maybe_warn_of_ambiguous_dependency_inference(
         self,
-        ambiguous_addresses: Tuple[Address, ...],
+        ambiguous_addresses: Iterable[Address],
         original_address: Address,
         *,
         context: str,
@@ -2030,7 +2030,7 @@ def maybe_warn_of_ambiguous_dependency_inference(
         )
 
     def disambiguated(
-        self, ambiguous_addresses: Tuple[Address, ...], owners_must_be_ancestors: bool = False
+        self, ambiguous_addresses: Iterable[Address], owners_must_be_ancestors: bool = False
     ) -> Address | None:
         """If exactly one of the input addresses remains after disambiguation, return it.
 


Test patch:
diff --git a/src/python/pants/backend/java/dependency_inference/package_prefix_tree_test.py b/src/python/pants/backend/java/dependency_inference/package_prefix_tree_test.py
--- a/src/python/pants/backend/java/dependency_inference/package_prefix_tree_test.py
+++ b/src/python/pants/backend/java/dependency_inference/package_prefix_tree_test.py
@@ -3,8 +3,6 @@
 
 from __future__ import annotations
 
-import pytest
-
 from pants.backend.java.dependency_inference.package_prefix_tree import PackageRootedDependencyMap
 from pants.engine.addresses import Address
 
@@ -15,56 +13,16 @@ def test_package_rooted_dependency_map() -> None:
     a = Address("a")
     dep_map.add_top_level_type(package="org.pantsbuild", type_="Foo", address=a)
     # An exact match yields the exact matching address
-    assert dep_map.addresses_for_symbol("org.pantsbuild.Foo") == frozenset([a])
-    # A miss with a package match yields all providers of the package
-    assert dep_map.addresses_for_symbol("org.pantsbuild.Bar") == frozenset([a])
-    # A miss without a package map returns nothing
-    assert dep_map.addresses_for_symbol("org.Foo") == frozenset()
-    assert dep_map.addresses_for_symbol("org.other.Foo") == frozenset()
+    assert dep_map.addresses_for_type("org.pantsbuild.Foo") == frozenset([a])
+    # A miss returns nothing
+    assert dep_map.addresses_for_type("org.Foo") == frozenset()
+    assert dep_map.addresses_for_type("org.other.Foo") == frozenset()
 
     b = Address("b")
     dep_map.add_top_level_type(package="org.pantsbuild", type_="Baz", address=b)
     # Again, exact matches yield exact providers
-    assert dep_map.addresses_for_symbol("org.pantsbuild.Foo") == frozenset([a])
-    assert dep_map.addresses_for_symbol("org.pantsbuild.Baz") == frozenset([b])
-    # But package-only match yields all providers of that package.
-    assert dep_map.addresses_for_symbol("org.pantsbuild.Bar") == frozenset([a, b])
-    # And total misses result in nothing.
-    assert dep_map.addresses_for_symbol("org.Foo") == frozenset()
-    assert dep_map.addresses_for_symbol("org.other.Foo") == frozenset()
-
-    c = Address("c")
-    # We can also add a package provider manually.
-    dep_map.add_package("org.pantsbuild", c)
-    # It will be included in the event of a package-only match:
-    assert dep_map.addresses_for_symbol("org.pantsbuild.Bar") == frozenset([a, b, c])
-
-    # Package matching also works if the package alone is passed as a symbol:
-    assert dep_map.addresses_for_symbol("org.pantsbuild") == frozenset([a, b, c])
-    # But note that we can't distinguish a FQT from a package:
-    assert dep_map.addresses_for_symbol("org.pantsbuild.other.package") == frozenset([a, b, c])
-
-    d = Address("d")
-    # But if we know about org.pantsbuild.other.package, the above doesn't happen:
-    dep_map.add_package("org.pantsbuild.other.package", d)
-    assert dep_map.addresses_for_symbol("org.pantsbuild.other.package") == frozenset([d])
-
-
-def test_package_rooted_dependency_map_errors() -> None:
-    dep_map = PackageRootedDependencyMap()
-    a = Address("a")
-    b = Address("b")
-
-    dep_map.add_top_level_type(package="org.pantsbuild", type_="Foo", address=a)
-    # Adding this type again is fine as long as the address is the same
-    dep_map.add_top_level_type(package="org.pantsbuild", type_="Foo", address=a)
-    # But trying to associate it to a different address is an error:
-    with pytest.raises(PackageRootedDependencyMap.ConflictingTypeOwnershipError):
-        dep_map.add_top_level_type(package="org.pantsbuild", type_="Foo", address=b)
-
-    other_map = PackageRootedDependencyMap()
-    other_map.add_top_level_type(package="org.pantsbuild", type_="Foo", address=b)
-
-    # Trying to merge in a conflict causes the same error:
-    with pytest.raises(PackageRootedDependencyMap.ConflictingTypeOwnershipError):
-        dep_map.merge(other_map)
+    assert dep_map.addresses_for_type("org.pantsbuild.Foo") == frozenset([a])
+    assert dep_map.addresses_for_type("org.pantsbuild.Baz") == frozenset([b])
+    # Misses result in nothing.
+    assert dep_map.addresses_for_type("org.Foo") == frozenset()
+    assert dep_map.addresses_for_type("org.other.Foo") == frozenset()
diff --git a/src/python/pants/backend/java/dependency_inference/rules_test.py b/src/python/pants/backend/java/dependency_inference/rules_test.py
--- a/src/python/pants/backend/java/dependency_inference/rules_test.py
+++ b/src/python/pants/backend/java/dependency_inference/rules_test.py
@@ -10,7 +10,7 @@
 from pants.backend.java.dependency_inference.java_parser_launcher import (
     rules as java_parser_launcher_rules,
 )
-from pants.backend.java.dependency_inference.rules import InferJavaImportDependencies
+from pants.backend.java.dependency_inference.rules import InferJavaSourceDependencies
 from pants.backend.java.dependency_inference.rules import rules as dep_inference_rules
 from pants.backend.java.target_types import (
     JavaSourceField,
@@ -57,7 +57,7 @@ def rule_runner() -> RuleRunner:
             *util_rules(),
             QueryRule(Addresses, [DependenciesRequest]),
             QueryRule(ExplicitlyProvidedDependencies, [DependenciesRequest]),
-            QueryRule(InferredDependencies, [InferJavaImportDependencies]),
+            QueryRule(InferredDependencies, [InferJavaSourceDependencies]),
             QueryRule(Targets, [UnparsedAddressInputs]),
         ],
         target_types=[JavaSourcesGeneratorTarget, JunitTestsGeneratorTarget],
@@ -96,7 +96,7 @@ def test_infer_java_imports_same_target(rule_runner: RuleRunner) -> None:
     assert (
         rule_runner.request(
             InferredDependencies,
-            [InferJavaImportDependencies(target_a[JavaSourceField])],
+            [InferJavaSourceDependencies(target_a[JavaSourceField])],
         )
         == InferredDependencies(dependencies=[])
     )
@@ -104,7 +104,7 @@ def test_infer_java_imports_same_target(rule_runner: RuleRunner) -> None:
     assert (
         rule_runner.request(
             InferredDependencies,
-            [InferJavaImportDependencies(target_b[JavaSourceField])],
+            [InferJavaSourceDependencies(target_b[JavaSourceField])],
         )
         == InferredDependencies(dependencies=[])
     )
@@ -146,11 +146,11 @@ def test_infer_java_imports(rule_runner: RuleRunner) -> None:
     target_b = rule_runner.get_target(Address("sub", target_name="b", relative_file_path="B.java"))
 
     assert rule_runner.request(
-        InferredDependencies, [InferJavaImportDependencies(target_a[JavaSourceField])]
+        InferredDependencies, [InferJavaSourceDependencies(target_a[JavaSourceField])]
     ) == InferredDependencies(dependencies=[target_b.address])
 
     assert rule_runner.request(
-        InferredDependencies, [InferJavaImportDependencies(target_b[JavaSourceField])]
+        InferredDependencies, [InferJavaSourceDependencies(target_b[JavaSourceField])]
     ) == InferredDependencies(dependencies=[])
 
 
@@ -193,14 +193,71 @@ def test_infer_java_imports_with_cycle(rule_runner: RuleRunner) -> None:
     target_b = rule_runner.get_target(Address("sub", target_name="b", relative_file_path="B.java"))
 
     assert rule_runner.request(
-        InferredDependencies, [InferJavaImportDependencies(target_a[JavaSourceField])]
+        InferredDependencies, [InferJavaSourceDependencies(target_a[JavaSourceField])]
     ) == InferredDependencies(dependencies=[target_b.address])
 
     assert rule_runner.request(
-        InferredDependencies, [InferJavaImportDependencies(target_b[JavaSourceField])]
+        InferredDependencies, [InferJavaSourceDependencies(target_b[JavaSourceField])]
     ) == InferredDependencies(dependencies=[target_a.address])
 
 
+@maybe_skip_jdk_test
+def test_infer_java_imports_ambiguous(rule_runner: RuleRunner, caplog) -> None:
+    ambiguous_source = dedent(
+        """\
+                package org.pantsbuild.a;
+                public class A {}
+                """
+    )
+    rule_runner.write_files(
+        {
+            "a_one/BUILD": "java_sources()",
+            "a_one/A.java": ambiguous_source,
+            "a_two/BUILD": "java_sources()",
+            "a_two/A.java": ambiguous_source,
+            "b/BUILD": "java_sources()",
+            "b/B.java": dedent(
+                """\
+                package org.pantsbuild.b;
+                import org.pantsbuild.a.A;
+                public class B {}
+                """
+            ),
+            "c/BUILD": dedent(
+                """\
+                java_sources(
+                  dependencies=["!a_two/A.java"],
+                )
+                """
+            ),
+            "c/C.java": dedent(
+                """\
+                package org.pantsbuild.c;
+                import org.pantsbuild.a.A;
+                public class C {}
+                """
+            ),
+        }
+    )
+    target_b = rule_runner.get_target(Address("b", relative_file_path="B.java"))
+    target_c = rule_runner.get_target(Address("c", relative_file_path="C.java"))
+
+    # Because there are two sources of `org.pantsbuild.a.A`, neither should be inferred for B. But C
+    # disambiguates with a `!`, and so gets the appropriate version.
+    caplog.clear()
+    assert rule_runner.request(
+        InferredDependencies, [InferJavaSourceDependencies(target_b[JavaSourceField])]
+    ) == InferredDependencies(dependencies=[])
+    assert len(caplog.records) == 1
+    assert (
+        "The target b/B.java imports `org.pantsbuild.a.A`, but Pants cannot safely" in caplog.text
+    )
+
+    assert rule_runner.request(
+        InferredDependencies, [InferJavaSourceDependencies(target_c[JavaSourceField])]
+    ) == InferredDependencies(dependencies=[Address("a_one", relative_file_path="A.java")])
+
+
 @maybe_skip_jdk_test
 def test_infer_java_imports_unnamed_package(rule_runner: RuleRunner) -> None:
     # A source file without a package declaration lives in the "unnamed package", and does not
@@ -222,7 +279,7 @@ def test_infer_java_imports_unnamed_package(rule_runner: RuleRunner) -> None:
     target_a = rule_runner.get_target(Address("", target_name="a", relative_file_path="Main.java"))
 
     assert rule_runner.request(
-        InferredDependencies, [InferJavaImportDependencies(target_a[JavaSourceField])]
+        InferredDependencies, [InferJavaSourceDependencies(target_a[JavaSourceField])]
     ) == InferredDependencies(dependencies=[])
 
 
@@ -260,11 +317,11 @@ def test_infer_java_imports_same_target_with_cycle(rule_runner: RuleRunner) -> N
     target_b = rule_runner.get_target(Address("", target_name="t", relative_file_path="B.java"))
 
     assert rule_runner.request(
-        InferredDependencies, [InferJavaImportDependencies(target_a[JavaSourceField])]
+        InferredDependencies, [InferJavaSourceDependencies(target_a[JavaSourceField])]
     ) == InferredDependencies(dependencies=[target_b.address])
 
     assert rule_runner.request(
-        InferredDependencies, [InferJavaImportDependencies(target_b[JavaSourceField])]
+        InferredDependencies, [InferJavaSourceDependencies(target_b[JavaSourceField])]
     ) == InferredDependencies(dependencies=[target_a.address])