Conversation
Resolving one type name looks up many candidate names -- one per enclosing type declaration, one per import, one per on-demand import, one in the same package, one in `java.lang`, and one for the name as a fully-qualified name. Most of those names name no type, and a client that resolves many names looks up the same names over and over. Add an overload of `resolveTypeName` that takes a cache, and route every lookup through it, so that a client that resolves many names pays for each distinct name only once. The cache records a lookup that finds no type, which is the common case. The existing one-argument overload is unchanged from a caller's point of view; it allocates a cache that lives for the one call. This is a performance change; it does not change any result. A cache must not be reused across annotation processing rounds or across `Elements` instances, because a name that names no type in one round might name a generated type in a later round; the Javadoc says so. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
`JavaParserUtil.resolveTypeName` did not model the scope of a local class or of an anonymous class, so a name that such a class declares or inherits resolved to a same-named type that is declared outside it. If that type is irrelevant, inference silently discarded a correct annotation. `TypeDeclaration.getFullyQualifiedName()` is part of the problem: for a local class `Foo` in `Outer`, it returns "Outer.Foo", which `Elements` resolves to a member type of `Outer`. Model these scopes: * A local class shadows every type of the same name, and `Elements` cannot look up a local class, so return null (that is, be conservative). * Likewise for a member type of a class that `Elements` cannot look up: a local class, an anonymous class (including the body of an enum constant), or a class nested within one. * Such a class also inherits its supertype's member types. Resolve the supertype and search it, which is precise rather than conservative. The supertype names are excluded from the class's own body scope, both because that is the Java rule and because it bounds the recursion. Add `nameableFullyQualifiedName`, which returns a fully-qualified name only when `Elements` can look it up, in place of `TypeDeclaration.getFullyQualifiedName()`. Resolving a supertype's name multiplies the number of name lookups, so pass a single cache from the ajava writer to every call, rather than letting each call allocate one that it discards. Add tests for a local class, an anonymous class, an enum constant's body, and a member type inherited into a local or anonymous class. Each test loses an annotation if its part of this fix is reverted. `InheritedTypeShadows` also shows that the supertype search is precise: an annotation on an inherited member type that is irrelevant is still omitted. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…nst-branch-ajava-omit-irrelevant into resolve-type-name-local-anonymous
…nst-branch-ajava-omit-irrelevant into resolve-type-name-local-anonymous
…lve-type-name-memoize
…-mernst-branch-resolve-type-name-memoize into resolve-type-name-local-anonymous
…nst-branch-ajava-omit-irrelevant into resolve-type-name-local-anonymous
…nst-branch-ajava-omit-irrelevant into resolve-type-name-local-anonymous
…nst-branch-ajava-omit-irrelevant into resolve-type-name-local-anonymous
…nst-branch-ajava-omit-irrelevant into resolve-type-name-local-anonymous
A class's member types are in scope in its body, but not in its header:
its annotations, its type parameter bounds, and its supertype names.
Likewise, the member types of an anonymous class are in scope only in
its body, not in the arguments to its constructor. `resolveTypeName`
now tests whether the name appears in the body, rather than testing only
whether it is one of the supertype names.
Other fixes:
* In `outer.new Inner() { ... }`, `Inner` is a member of the type of
`outer` rather than a name that is resolved in the scope of the
expression, so the anonymous class's supertype cannot be determined.
Return null (conservative) rather than resolving `Inner` lexically.
* An enum inherits the member type `EnumDesc` from its implicit
supertype `java.lang.Enum`. Search `java.lang.Enum` when the enum is
unnameable, rather than letting the name fall through to an enclosing
scope.
* Pass `typeElementCache` to `typeToTypeMirror`, which otherwise
allocated a fresh cache per call, so the field was never read.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
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.
Based on #8184.
JavaParserUtil.resolveTypeNamedid not model the scope of a local class or of an anonymous class, so a name that such a class declares or inherits resolved to a same-named type declared outside it. If that type is irrelevant, inference silently discarded a correct annotation.TypeDeclaration.getFullyQualifiedName()is part of the problem: for a local classFooinOuterit returns"Outer.Foo", whichElementsresolves to a member type ofOuter.Model these scopes:
Elementscannot look up a local class, so return null (be conservative).Elementscannot look up: a local class, an anonymous class (including the body of an enum constant), or a class nested within one.Adds
nameableFullyQualifiedName, which returns a fully-qualified name only whenElementscan look it up.Adds tests for a local class, an anonymous class, an enum constant's body, and a member type inherited into a local or anonymous class. Each test loses an annotation if its part of this fix is reverted.
🤖 Generated with Claude Code