Match nested type references by either . or $ in UsesType - #8430
Merged
knutwannheden merged 1 commit intoAug 9, 2026
Conversation
`UsesType`'s `Reference.Matcher` for `SourceFileWithReferences` compared the requested name to the reference value with `String.equals`, so a nested type written in dotted form never matched a Spring XML/properties/YAML reference in binary form. Recipes gated on `UsesType` — including `FindTypes`, whose own `TypeMatcher` accepts both forms — short-circuited to zero results. Comparing with `TypeUtils.fullyQualifiedNamesAreEqual` brings the matcher in line with `TypeNameMatcher`, which every other matching path in these recipes already uses.
knutwannheden
deleted the
usestype-exact-match-misses-dotted-nested-types
branch
August 9, 2026 05:58
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Motivation
UsesTypeis used as a precondition by a large fraction of search and migration recipes, so anything it rejects is invisible to the recipe behind it — the caller gets zero results rather than an error, and cannot tell "no usages" from "your type name was rejected". ItsReference.MatcherforSourceFileWithReferences(Spring XML, properties, YAML) compared the requested type name to the reference value withString.equals, while every other matching path in the same recipes goes throughTypeNameMatcher, which treats.and$as equivalent at a nested-type boundary. A nested type written the way a developer writes it —a.b.Outer.Inner— therefore never matched a reference stored in binary form, andFindTypeswas gated to zero by its own precondition even though itsTypeMatcherwould have matched.Note that the Java source path was already correct:
TypesInUse.hasTypecanonicalizes the query and the trie carries a canonical alias for every$-containing FQN, and the legacy fallback compares viaTypeUtils.isAssignableTo(String, JavaType). This PR closes the one remaining path where the comparison was literal.Summary
UsesType.ExactMatch.matchesReferencecompares withTypeUtils.fullyQualifiedNamesAreEqualinstead ofString.equals, so a nested type matches a reference written in either form.UsesTypeTestcoverage for nested types in dotted and binary form (single and double nesting, including the mixeda.b.Outer.Mid$Inner), a top-level miss, an uppercase package segment that must not be treated as a nesting boundary, and the XML reference case.FindTypesTestcoverage asserting both forms produce the same result, on Java sources and on a Spring XML bean class.The trie lookup is untouched, so the
O(1)fast path for exact fully qualified names is unchanged.Test plan
./gradlew :rewrite-java-test:test --tests "org.openrewrite.java.search.UsesTypeTest" --tests "org.openrewrite.java.search.FindTypesTest"passes.UsesTypechange reverted,FindTypesTest > nestedTypeInSpringXml [1] type = "a.b.Outer.Inner"is the single failure — the new tests fail for the intended reason../gradlew :rewrite-java-test:test :rewrite-java:testpasses in full.