Repository navigation
fix(play): look up Class.method among the class's own members, not the whole file - #1950
danusha2345 wants to merge 1 commit into
Conversation
3402117 to
3aff60e
Compare
d3c326f to
31b830b
Compare
fe7fc84 to
4be61aa
Compare
…rst same-named method in its file The Play resolver claims every `Class.method` reference in a Play project (route handlers and ordinary Java/Scala calls alike) at 0.9 confidence, and picked the first method with that name anywhere in the class's FILE. A file holding several classes (Java nested classes, a Scala class + companion + siblings) sent e.g. `AuthorityPort.parse` / `MediaType.parse` to a sibling's `parse`, and Scala `HttpEntity.Strict(...)` to a Java constructor. Only a method whose qualifiedName is `<class qn>::<method>` counts now (a Scala companion shares its class's qualifiedName, so it is covered). An inherited member is not looked up; the resolver declines to the generic resolver instead of guessing. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
4be61aa to
4289186
Compare
…names The Play resolver claims every Class.method reference in a Play project, in routes files and in ordinary Java and Scala code alike, and took the first method of that name anywhere in the class's file. In a file holding several classes (Java nested classes, a Scala class with its companion and siblings) that was usually another class's method: MediaType.parse(...) went to Cookie.parse, a route controllers.Application.index to a nested Helper.index, and Scala RequestAuthority.parse(...) to AuthorityPort.parse. A method now counts only when its qualified name is the named type's own, cls::method. A Scala object is an owner too, so a companion's own members are found through it, and for a call in code so are a Java interface's and enum's static methods. For a call written in code, the types of the caller's language are tried first: Play declares RequestAuthority both as a Scala case class and as a Java record, and each language's callers mean their own; the other language's types still follow, since Play's Java API calls its Scala classes. Among same-named types the caller's own file comes first (Scala names carry no package), an overloaded member gives the overload the call's arguments fit, and a Java file's import of the name from outside the project leaves the call to the generic resolver. A route keeps the plain order and takes a class or object. An inherited member is left to the generic resolver, which finds it through declared supertypes. Validation: - frameworks-integration.test.ts: the PR's case (nested Http.Cookie / MediaType, Scala sibling objects, a route beside a nested Helper.index), red on main; ours, Scala and Java callers of a RequestAuthority declared in both languages (red on main and with the PR's own resolver) and an inherited route handler; and a guard for overloads, same-named objects in two packages and a Java interface's static method. - A/B against main cde4cd7: playframework +113 / -257, 510 retargeted; CMAK 47 retargeted, all first-in-file picks corrected; play-samples -2; click and gin identical. The 50 Scala parse calls that records sent to the Java record reach their Scala type again. - Cost: playframework process CPU, median of 3 interleaved fresh indexes, 63.3 s on main and 62.8 s here. Known costs, left as follow-ups: - 177 right edges main reached only through its first-in-file rule are lost: 171 Assets.resourceNameAt calls in AssetsSpec through a local val Assets = new AssetsBuilder(...), which the generic resolver drops because it reads Scala's private[controllers] as file-private, and 6 Writeable.writeableOf_* calls inherited by object Writeable extends DefaultWriteables, an object's supertypes the generic resolver does not follow. - 8 calls in Scala files that import Play's Java EventSource, Comet, Environment or (in a nested scope) Accumulator reach the same-named Scala object: no Scala import is read. Original PR #1950 by @danusha2345. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Thank you @danusha2345! This landed as #2875 with your authorship. Play's Since Java records are now indexed (#2868), Play's |
Problem
In a Play project, the Play resolver handles every
A.breference, both in route files and in ordinary Java/Scala calls, at 0.9. For a method, it takes the first method with that name anywhere in the class's file, not in the class. So in a file with several classes, such asHttp.javaor a Scala file with sibling objects, the first same-named method wins, whichever class declares it:AuthorityPort.parse(...)→RequestAuthority::parseor another sibling'sparseHttpEntity.Strict(...)from Scala → the JavaHttpEntity::Strictconstructorcontrollers.Foo.index→ a nestedFoo::Helper::indexdeclared first in the fileFix
A method counts only if its qualified name is exactly
<class qn>::<method>. Nested classes follow from that, and a Scala companion object shares its class's qualified name. Inherited members aren't looked up, because supertypes aren't known on the first pass, so the resolver declines instead of guessing and the generic resolver handles the reference.Measured on playframework (
mainba3c21e vs this branch; every edge checked by hand)AuthorityPort::parse;HttpEntity.Strict/Streamed/ChunkedJava constructors reached from Scala;F.Tupleconstructors;ApplicationLoader/Context.createcrossing Java and Scala;Assets.versioned/resourceNameAt(the object extendsAssetsBuilder) andHelpers.await/call/flash/session. The old first-in-file rule reached these by accident. Recovering them needs a supertype walk in a later pass.RequestAuthority::parse/parseOrThrow(~36), theF::Tuplefactory, the rightApplicationLoader.createowners,FormController::index,Clients::show, and others.Tests
__tests__/frameworks-integration.test.tsgets three cases: a Java nested class (Http.Cookie/MediaType.parse), Scala sibling objects (Scheme/AuthorityPort.parse), and a route whose file declares a nestedHelper.indexfirst. Each failed onmainand passes now. The full suite passes.🤖 Generated with Claude Code