From 1a3773a681c42e59d953f980d71a466d74de2453 Mon Sep 17 00:00:00 2001 From: Tom Dyas Date: Tue, 30 Nov 2021 21:33:10 -0500 Subject: [PATCH] scala: infer dependency based on consumed symbols provided by intermediate scopes [ci skip-rust] [ci skip-build-wheels] --- .../scala/dependency_inference/rules_test.py | 34 +++++++++++++++++++ .../dependency_inference/scala_parser.py | 16 ++------- .../dependency_inference/scala_parser_test.py | 21 ++++++++++-- 3 files changed, 56 insertions(+), 15 deletions(-) diff --git a/src/python/pants/backend/scala/dependency_inference/rules_test.py b/src/python/pants/backend/scala/dependency_inference/rules_test.py index b7b33eb7348..823942f8314 100644 --- a/src/python/pants/backend/scala/dependency_inference/rules_test.py +++ b/src/python/pants/backend/scala/dependency_inference/rules_test.py @@ -214,3 +214,37 @@ class C {} assert rule_runner.request( InferredDependencies, [InferScalaSourceDependencies(target_c[ScalaSourceField])] ) == InferredDependencies(dependencies=[Address("a_one", relative_file_path="A.scala")]) + + +def test_infer_unqualified_symbol_from_intermediate_scope(rule_runner: RuleRunner) -> None: + rule_runner.write_files( + { + "foo/BUILD": "scala_sources()", + "foo/A.scala": dedent( + """\ + package org.pantsbuild.outer + package intermediate + + object A { + def main(args: Array[String]): Unit = { + println(B.Foo) + } + } + """ + ), + "bar/BUILD": "scala_sources()", + "bar/B.scala": dedent( + """\ + package org.pantsbuild.outer + object B { + val Foo = 3 + } + """ + ), + } + ) + tgt = rule_runner.get_target(Address("foo", relative_file_path="A.scala")) + deps = rule_runner.request( + InferredDependencies, [InferScalaSourceDependencies(tgt[ScalaSourceField])] + ) + assert deps == InferredDependencies([Address("bar", relative_file_path="B.scala")]) diff --git a/src/python/pants/backend/scala/dependency_inference/scala_parser.py b/src/python/pants/backend/scala/dependency_inference/scala_parser.py index 7ed556b5b37..22c9bf4f595 100644 --- a/src/python/pants/backend/scala/dependency_inference/scala_parser.py +++ b/src/python/pants/backend/scala/dependency_inference/scala_parser.py @@ -130,16 +130,6 @@ def fully_qualified_consumed_symbols(self) -> Iterator[str]: have been provided by any wildcard import in scope, as well as being declared in the current package. """ - # TODO: We compute "the package" as the "shortest scope in the file" to handle the most - # common case of consuming a type from within your package. But this doesn't account - # for the fact that any of the intermediate scopes might be relevant as well. Solving that - # would require resolving the type recursively upward. - package = min( - (*self.imports_by_scope.keys(), *self.consumed_symbols_by_scope.keys()), - key=len, - default="", - ) - # Collect all wildcard imports. wildcard_imports_by_scope = {} for scope, imports in self.imports_by_scope.items(): @@ -155,9 +145,9 @@ def fully_qualified_consumed_symbols(self) -> Iterator[str]: if scope.startswith(s) } for symbol in consumed_symbols: - if package: - yield f"{package}.{symbol}" - if not package or "." in symbol: + for scope in self.scopes: + yield f"{scope}.{symbol}" + if not self.scopes or "." in symbol: # TODO: Similar to #13545: we assume that a symbol containing a dot might already # be fully qualified. yield symbol diff --git a/src/python/pants/backend/scala/dependency_inference/scala_parser_test.py b/src/python/pants/backend/scala/dependency_inference/scala_parser_test.py index 150e1fd3861..41b2e79291b 100644 --- a/src/python/pants/backend/scala/dependency_inference/scala_parser_test.py +++ b/src/python/pants/backend/scala/dependency_inference/scala_parser_test.py @@ -21,7 +21,7 @@ from pants.jvm.resolve.coursier_fetch import rules as coursier_fetch_rules from pants.jvm.resolve.coursier_setup import rules as coursier_setup_rules from pants.jvm.target_types import JvmDependencyLockfile -from pants.testutil.rule_runner import PYTHON_BOOTSTRAP_ENV, QueryRule, RuleRunner, logging +from pants.testutil.rule_runner import PYTHON_BOOTSTRAP_ENV, QueryRule, RuleRunner from pants.util.frozendict import FrozenDict from pants.util.ordered_set import FrozenOrderedSet @@ -48,7 +48,6 @@ def rule_runner() -> RuleRunner: return rule_runner -@logging def test_parser_simple(rule_runner: RuleRunner) -> None: rule_runner.write_files( { @@ -304,6 +303,24 @@ def this(bar: SomeTypeInSecondaryConstructor) { "org.pantsbuild.example.LambdaTypeArg2", "org.pantsbuild.example.TupleTypeArg1", "org.pantsbuild.example.TupleTypeArg2", + "org.pantsbuild.+", + "org.pantsbuild.ABaseClass", + "org.pantsbuild.AParameterType", + "org.pantsbuild.ATrait1", + "org.pantsbuild.ATrait2.Nested", + "org.pantsbuild.BaseWithConstructor", + "org.pantsbuild.Integer", + "org.pantsbuild.LambdaReturnType", + "org.pantsbuild.LambdaTypeArg1", + "org.pantsbuild.LambdaTypeArg2", + "org.pantsbuild.OuterObject.NestedVal", + "org.pantsbuild.SomeTypeInSecondaryConstructor", + "org.pantsbuild.String", + "org.pantsbuild.TupleTypeArg1", + "org.pantsbuild.TupleTypeArg2", + "org.pantsbuild.Unit", + "org.pantsbuild.bar", + "org.pantsbuild.foo", }