Skip to content

rewrite-python: Unknown declaring-type for unattributed method calls + public SearchResult.found#7631

Merged
jkschneider merged 1 commit intomainfrom
rewrite-python-unknown-declaring-type
May 10, 2026
Merged

rewrite-python: Unknown declaring-type for unattributed method calls + public SearchResult.found#7631
jkschneider merged 1 commit intomainfrom
rewrite-python-unknown-declaring-type

Conversation

@jkschneider
Copy link
Copy Markdown
Member

@jkschneider jkschneider commented May 10, 2026

Summary

  • Two paired changes that together unblock Preconditions.check(uses_method(...), V()) gates from failing wire-side evaluation when Python sources lack receiver-type attribution (e.g. unit-test fixtures that don't import the receiver). The motivating regression: rewrite-migrate-python#54's recipe wraps were silently no-op'ing for inputs like my_array.tostring() once the wire-side HasMethod gate became active.

type_mapping

PythonTypeMapping._get_declaring_type previously returned None when Ty couldn't resolve the receiver and AST inference didn't yield a recognizable type. That left JavaType.Method._declaring_type as None for method invocations like my_array.tostring() in unattributed sources. The Python MethodMatcher.matches explicitly returns False when method_type.declaring_type is None, so the in-process UsesMethod gate fails — and on the wire path TypesInUse.hasMethodUse can't reliably match the method either.

Now _get_declaring_type returns a non-null FullyQualified in every case — falling back to the shared _UNKNOWN singleton — so HasMethod / UsesMethod precondition gates work against unattributed code. The function signature changes from Optional[JavaType.FullyQualified] to JavaType.FullyQualified to document the new invariant. The intermediate resolution paths (Ty type-id lookup, attribute chain inference) that previously returned None now fall through to the Unknown fallback rather than short-circuiting.

SearchResult.found

  • Adds a public SearchResult.found(tree, description=None) static factory mirroring Java's org.openrewrite.marker.SearchResult.found. Returns a new tree carrying a fresh SearchResult marker — the canonical "different tree" sentinel that Check / CompositePrecondition interpret as the gate matching. The native rewrite/python/search/{IsSourceFile,UsesType,UsesMethod} visitors (added in rewrite-python: Preconditions.or_ / and_ / not_ for RPC recipes #7625) were already calling SearchResult.found(tree), which silently AttributeError'd at runtime because the helper didn't exist; this fixes that.

  • Impact on rewrite-migrate-python#54

Locally I rebuilt rewrite-migrate-python's full test suite against this branch's editable install:

Before After
12 failing (test_array_deprecations, test_cgi_parse_deprecations, test_html_parser_deprecations, …) All 415 passing
  • (One more recipe-level fix in ChangeMethodName works only with static methods #54 was also needed — ReplaceReTemplate was gating on uses_method but matches both method and field-access forms of re.template / re.TEMPLATE — that's a small follow-up commit on the recipe side.)

Test plan

  • pytest rewrite-python/rewrite/tests/ — 1198 passed, 11 skipped
  • Direct unit verification: PythonTypeMapping().method_invocation_type(ast.parse("data = my_array.tostring()").body[0].value).declaring_type is _UNKNOWN
  • rewrite-migrate-python full suite against an editable install — 415 passed

…+ public SearchResult.found

Two paired changes that together unblock ``Preconditions.check(uses_method(...), V())``
gates from failing wire-side evaluation when Python sources lack
receiver-type attribution (e.g. unit-test fixtures that don't import
the receiver).

## type_mapping

``PythonTypeMapping._get_declaring_type`` previously returned ``None``
when Ty couldn't resolve the receiver and AST inference didn't yield
a recognizable type. That left ``JavaType.Method._declaring_type`` as
``None`` for method invocations like ``my_array.tostring()`` in
unattributed sources. Java's ``MethodMatcher.matches`` accepts
``JavaType.Unknown`` receivers under wildcard patterns (``*..*``)
but rejects method types whose declaring type is null at the
``TypesInUse.hasMethodUse`` lookup level.

Now ``_get_declaring_type`` returns a non-null ``FullyQualified`` in
every case — falling back to the shared ``_UNKNOWN`` singleton — so
``HasMethod`` / ``UsesMethod`` precondition gates work against
unattributed code. The function signature changes from
``Optional[JavaType.FullyQualified]`` to ``JavaType.FullyQualified``
to document the new invariant. Resolution paths that previously
returned None now fall through to the AST-inference and Unknown
fallbacks.

## SearchResult.found

Adds a public ``SearchResult.found(tree, description=None)`` static
factory mirroring Java's ``org.openrewrite.marker.SearchResult.found``.
Returns a new tree carrying a fresh ``SearchResult`` marker — the
canonical "different tree" sentinel that ``Check`` /
``CompositePrecondition`` interpret as the gate matching. The native
``rewrite/python/search/{IsSourceFile,UsesType,UsesMethod}`` visitors
now call this instead of constructing the Markers/Marker pair by hand.
@github-project-automation github-project-automation Bot moved this to In Progress in OpenRewrite May 10, 2026
@jkschneider jkschneider merged commit cd68590 into main May 10, 2026
1 check failed
@jkschneider jkschneider deleted the rewrite-python-unknown-declaring-type branch May 10, 2026 22:16
@github-project-automation github-project-automation Bot moved this from In Progress to Done in OpenRewrite May 10, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

1 participant