Skip to content

GROOVY-12514, GROOVY-12517: qualified this and super in nested classes under static type checking and compilation - #3049

Open
codeconsole wants to merge 19 commits into
apache:masterfrom
codeconsole:stc-qualified-this-super
Open

codeconsole wants to merge 19 commits into
apache:masterfrom
codeconsole:stc-qualified-this-super

Conversation

@codeconsole

@codeconsole codeconsole commented Oct 26, 2026 •

Copy link
Copy Markdown
Contributor

This makes qualified this and super in nested classes (N.this, N.super, Outer.super) work under @TypeChecked and @CompileStatic as in Java. Outer.super from a closure or an inner class goes through the super$ helper methods that statically compiled closures use for super calls, so the two bugs of those helpers that it runs into are fixed first: GROOVY-12516 and GROOVY-12518. Each issue has its own commit (GROOVY-12514 one for this and one for super, GROOVY-12518 two more, for overloaded setters and for extension accessors).

Outer.super without an enclosing instance, in a static nested class or in an anonymous class in a static method, is a compile error in every mode since GROOVY-12512; the support for Outer.super here relies on that check.

Outer.super.x = v, ++ and compound assignments with overloaded setters of the superclass choose the setter as super.x = v does since GROOVY-12521; InnerClassTest.testQualifiedSuperOfOuterClassWithOverloadedSetters covers it in all three modes. Three cases combine this with other open pull requests, and the second of each pair to be merged adds or changes their tests:

class Base { String p() { 'base' } }

@CompileStatic
class Outer extends Base {
    String p() { 'outer' }
    static class N extends Base {
        String p() { 'n' }
        String test() { N.super.p() }      // was "No such property: super"; now 'base' (GROOVY-12514)
    }
    class Inner {
        String test() { Outer.super.p() }  // was "No such property: super"; now 'base', on the enclosing instance (GROOVY-12517)
    }
}

GROOVY-12514: N.this and N.super within a nested class N

N.this, which is this, failed under @TypeChecked and @CompileStatic with "No such property: this", and N.super.m() and N.super.p with "No such property: super", in static nested, inner and enum classes and in closures and lambdas there. Both now compile, as in javac. N.super means super in every mode: dynamic Groovy read N.super.p as N.this.p. In a static context they remain errors. Top.this and Top.super in a top-level class keep their error (GROOVY-4321, StaticThisTest).

GROOVY-12517: Outer.super in a class nested in Outer

In an inner class, an anonymous class, a closure or lambda in one of them, or an inner class of an inner class, Outer.super.m() and Outer.super.p failed under @TypeChecked and @CompileStatic with "No such property: super". They now call the superclass's member on the enclosing Outer instance, as in javac. Under @CompileStatic the calls go through super$ helper methods of Outer, since only Outer may call its superclass's methods non-virtually. Property reads and writes, ++ and compound assignments work too. Dynamic Groovy read and wrote Outer.super.p and Outer.super.@f as Outer.this.p and Outer.this.@f; they now use the superclass's property and field, also when the enclosing instance is of a subclass of Outer. Without an enclosing instance of Outer it stays an error.

GROOVY-12516 and GROOVY-12518: super in closures under @CompileStatic

  • GROOVY-12516: two closures or lambdas of one class calling the same super method generated the super$ helper twice, and the class failed to load with ClassFormatError. The helper is generated once.
  • GROOVY-12518: super.p, super.name and super.name = v in a closure or lambda failed with VerifyError (the superclass accessor was called with invokespecial from the closure class); they now go through super$ helpers, like super method calls. super.n++, ++super.n and super.n += v read the property through a dynamic call that failed with MissingPropertyException when the class declares the same property; they now call the superclass's accessors. When the superclass overloads the setter (setX(int) and setX(String)), the setter is the one the type checker selected for the value. When an accessor is an extension method, such as getText() and setText(String) of a File, super.text += v compiled to a dynamic property access; it now calls the extension methods directly.

Behaviour changes

Code that changes meaning in dynamic Groovy, each time to javac's meaning, described in COMPATIBILITY.md: N.super.p and N.super.@f inside N use the superclass's member instead of N's own (GROOVY-12514); Outer.super.p and Outer.super.@f in a class nested in Outer use the superclass's member instead of Outer.this's (GROOVY-12517). Outer.super.p, Outer.super.@f and Outer.super.m() are resolved against Outer's superclass also when the enclosing instance is of a subclass of Outer; dynamic Groovy resolved them against the instance's own class (for Outer.super.m() since GROOVY-11611).

Code that failed to compile and now compiles: GROOVY-12514 and GROOVY-12517. Code that failed at run time and now runs under @CompileStatic: GROOVY-12516 and GROOVY-12518.

After merging master (GROOVY-12533, where this.name in an inner subclass reads the enclosing instance's private field), static compilation still treated Type.this.name differently from this.name when Type is the current inner subclass: with z private in Base and an inner Sub extends Base, Sub.this.z and Sub.this.z = v (also in closures and lambdas) used Sub's own instance under @CompileStatic, while this.z and dynamic Groovy use the enclosing Base instance. PropertyExpressionTransformer now gives Type.this for the class being compiled the same handling as a bare this. InnerClassTest.testQualifiedThisOfCurrentInnerSubclass covers reads, writes, closures, lambdas, public fields and properties in dynamic, @TypeChecked and @CompileStatic modes.

Tests

Each commit has tests that fail without it, marked with its JIRA key: gls.innerClass.InnerClassTest, ClosuresSTCTest (and ClosuresStaticCompileTest), FieldsAndPropertiesSTCTest (and FieldsAndPropertiesStaticCompileTest) and, for the overloaded setters and the extension accessors, FieldsAndPropertiesStaticCompileTest alone. 13,655 tests in the groovy, gls, bugs, org.codehaus.groovy.classgen, transform and control packages pass, apart from bugs.Groovy12363, which fails on master too when it runs after bugs.Groovy12142 in the same JVM. The Checkstyle and CodeNarc gates pass.

Backport

All commits cherry-pick cleanly onto GROOVY_6_0_X, and the inner-class, closure and fields-and-properties tests pass there. The COMPATIBILITY.md entry for the changed dynamic meaning of N.super and Outer.super then goes after the 6.0.1 entries, as for GROOVY-12521.

@codecov-commenter

codecov-commenter commented Oct 26, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 79.43262% with 29 lines in your changes missing coverage. Please review.
✅ Project coverage is 74.1160%. Comparing base (ef76fcb) to head (412c44a).

Files with missing lines Patch % Lines
...ovy/classgen/asm/sc/StaticTypesCallSiteWriter.java 74.6479% 4 Missing and 14 partials ⚠️
...va/org/codehaus/groovy/control/ResolveVisitor.java 66.6667% 0 Missing and 3 partials ⚠️
...roovy/transform/stc/StaticTypeCheckingVisitor.java 86.3636% 0 Missing and 3 partials ⚠️
.../sc/transformers/StaticCompilationTransformer.java 0.0000% 0 Missing and 2 partials ⚠️
...a/org/apache/groovy/ast/tools/ExpressionUtils.java 50.0000% 0 Missing and 1 partial ⚠️
...taticTypesBinaryExpressionMultiTypeDispatcher.java 50.0000% 0 Missing and 1 partial ⚠️
...m/sc/transformers/BinaryExpressionTransformer.java 83.3333% 0 Missing and 1 partial ⚠️
Additional details and impacted files

Impacted file tree graph

@@                Coverage Diff                 @@
##               master      #3049        +/-   ##
==================================================
+ Coverage     74.1012%   74.1160%   +0.0148%     
- Complexity      43600      43671        +71     
==================================================
  Files            1692       1692                
  Lines          149254     149374       +120     
  Branches        28286      28332        +46     
==================================================
+ Hits           110599     110710       +111     
+ Misses          28475      28466         -9     
- Partials        10180      10198        +18     
Files with missing lines Coverage Δ
...rg/codehaus/groovy/classgen/AsmClassGenerator.java 85.7759% <100.0000%> (+0.0102%) ⬆️
...codehaus/groovy/classgen/asm/InvocationWriter.java 86.1804% <100.0000%> (-0.2441%) ⬇️
...vy/classgen/asm/sc/StaticCompilationMopWriter.java 100.0000% <100.0000%> (ø)
.../transformers/MethodCallExpressionTransformer.java 89.1892% <100.0000%> (-1.2870%) ⬇️
...sc/transformers/PropertyExpressionTransformer.java 100.0000% <100.0000%> (ø)
...a/org/apache/groovy/ast/tools/ExpressionUtils.java 79.7619% <50.0000%> (+0.2438%) ⬆️
...taticTypesBinaryExpressionMultiTypeDispatcher.java 87.0370% <50.0000%> (+0.7016%) ⬆️
...m/sc/transformers/BinaryExpressionTransformer.java 94.1667% <83.3333%> (-0.1836%) ⬇️
.../sc/transformers/StaticCompilationTransformer.java 93.1818% <0.0000%> (-2.1670%) ⬇️
...va/org/codehaus/groovy/control/ResolveVisitor.java 90.9817% <66.6667%> (+0.0936%) ⬆️
... and 2 more

... and 13 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@codeconsole
codeconsole marked this pull request as draft October 26, 2026 16:03
…ed class

N.this inside a nested class N is this, as in Java. The type checker
rejected it with "No such property: this for Class or static property
for class: Box$N", under @TypeChecked and @CompileStatic, for a static
nested, inner or enum class, and also inside a closure or lambda there;
dynamic Groovy ran it, and javac accepts it.

The type checker now types N.this inside N, outside a static context,
as N. In a static context ResolveVisitor still reports its error.
Top.this in a top-level class keeps its error in every mode (see
StaticThisTest, GROOVY-4321).
…ling the same super method

Under @CompileStatic a call to super.m() in a closure or lambda calls
a generated method super$2$m of the enclosing class. The type checker
records the target for every such call, and StaticCompilationMopWriter
generated one method per record, so two closures or lambdas calling
the same super method produced the method twice and the class failed
to load with "ClassFormatError: Duplicate method name super$2$m".
Dynamic Groovy and @TypeChecked run the same code.

The writer now generates one method per name and descriptor; overloads
of the same name keep one method each.
…lass

N.super inside a nested class N is super, as in Java (JLS 15.11.2,
15.12.1). Under @TypeChecked and @CompileStatic, N.super.m() and
N.super.p failed with "No such property: super for Class or static
property for class: Box$N". Dynamic Groovy ran N.super.m(), but read
N.super.p and N.super.q as N.this.p and N.this.q, the subclass's
properties; javac reads the superclass's.

ResolveVisitor now replaces N.super inside a nested class N with
super, so every mode gives it the meaning of super: a call or property
of the superclass, also inside a closure or lambda, for static nested,
inner and enum classes. Under @CompileStatic a closure or lambda then
calls the superclass method through the super$ helper (see
GROOVY-12516). In a static context ResolveVisitor still reports its
error. Top.super in a top-level class keeps its error in every mode
(see StaticThisTest, GROOVY-4321), and Outer.super, naming an outer
class, is not changed here.

In dynamic Groovy, N.super.p inside N now reads the superclass's
property instead of N.this.p.
…es, increments and compound assignments

Under static compilation a property read or written through super inside
a closure or lambda called the superclass accessor with invokespecial from
the closure class, which fails verification. A super method call in a
closure already goes through a super$ helper of the enclosing class; the
type checker now asks for the same helper for the getter and setter of a
super property (also for a property whose accessors are generated later),
and the property access and assignment transformers call it.

super.name++, ++super.name and super.name += value read the property
through ScriptBytecodeAdapter.getPropertyOnSuper (and ++ also wrote it
through setPropertyOnSuper), which throws MissingPropertyException when the
class declares the same property. The call site writer now reads and
writes such a super property through the superclass accessor, directly
with invokespecial, or through the super$ helpers in a closure or lambda.
In a class nested in Outer that has an enclosing instance of Outer (an
inner class, an anonymous class, a closure or lambda in one of them, or an
inner class of an inner class), Outer.super names the superclass of that
instance (JLS 15.11.2, 15.12.1). The type checker rejected it ("No such
property: super"); it now types Outer.super as the superclass of Outer.
Where the class has no enclosing instance of Outer it stays an error.

Under static compilation, a method or property of Outer.super is called
through a super$ helper of Outer, as a super call in a closure is: the
JVM allows invokespecial on Outer's superclass only from within Outer.
Property reads and writes, increments and compound assignments use the
helpers for the accessors.

Dynamic Groovy read and wrote a property of Outer.super as a property of
Outer.this; it now goes to the superclass property, as Outer.super.m()
already went to the superclass method.
…name++ and compound assignments

super.name++, ++super.name, super.name op= value and super.name ?= value
wrote the property through the first one-argument setter the
superclass declares, so with overloads such as setX(int) and
setX(String) the value could go to the wrong one: a GroovyCastException,
a character passed as an int, or, in a closure, a lambda or for
Outer.super.name, the class's own property written instead. The call
site writer now calls the setter the type checker selected for the
value, which is also the one it asked the super$ helper for.

The lookup of a property node whose accessors "are generated later" is
removed: the accessors of every class of the compilation unit exist
before bytecode is generated, so it only ran for a property with a
non-default accessor name, and there it ignored static and private
properties.
…and compound assignments

super.name++, super.name op= value and their kin compiled to dynamic
getPropertyOnSuper and setPropertyOnSuper calls when an accessor of the
property is an extension method, such as getText() and setText(String)
of a File, also in a closure, a lambda and for Outer.super.name. The
call site writer now calls the extension methods directly: the setter
the type checker selected for the value, and the getter it selects
when the superclass declares none.
…uperclass

Outer.super.x = v, ++ and compound assignments choose among the setters
of the superclass, as super.x = v does since GROOVY-12521, in dynamic
Groovy, under @TypeChecked and under @CompileStatic.
@codeconsole
codeconsole force-pushed the stc-qualified-this-super branch from cd35874 to cdb685d Compare October 29, 2026 20:05
@codeconsole
codeconsole marked this pull request as ready for review October 29, 2026 20:05

@daniellansun daniellansun left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the careful multi-ticket stack on qualified this/super — tip cdb685d.

The shape is right: ResolveVisitor rewrites N.super to super (GROOVY-12514) after the existing Class.this/Class.super checks; STC recognizes N.this and types Outer.super as the enclosing type’s superclass (GROOVY-12517); SC sends Outer.super (and super-from-closure) through Outer’s super$ helpers, with InvocationWriter’s existing Outer.this rewrite for the receiver; StaticCompilationMopWriter dedupes super$ by name+descriptor (GROOVY-12516); and the 12518 property / ++ / += / overloaded-setter / extension-accessor paths are backed by strong parameterized tests. CI is green on this tip.

A few items before I would approve:

  1. COMPATIBILITY.md for the dynamic meaning change
    N.super.p and Outer.super.p / Outer.super.f used to read the subclass / enclosing instance’s own member in dynamic Groovy; they now follow javac (superclass member). That is the same class of silent change we documented for GROOVY-12521. The PR description is clear; please add a short COMPATIBILITY section (Who is affected) before merge — also needed if we cherry-pick to GROOVY_6_0_X.

  2. Outer.super.@field asymmetry
    visitPropertyExpression now uses isSuperOrOuterSuper → get/setPropertyOnSuper, but visitAttributeExpression still keys only on isSuperExpression. Outer.super.@q therefore still hits Outer’s own field. Please mirror the property fix (and test Outer.super.@x / N.super.@x), or file a follow-up JIRA and mention it explicitly so we do not leave the @ form silently wrong. makeSuperPropertySite’s AttributeExpression skip is the SC counterpart of the same gap.

  3. Landing next to #3071 (GROOVY-12526)
    Complementary, not competing: #3071 rejects non-direct Iface.super; this PR enables class N.super / Outer.super. Expect a small ResolveVisitor / STC existsProperty conflict. Keep the interface check as an early path and the N.super rewrite afterward for non-interface nested classes.

  4. Nit: isSuperOrOuterSuper vs InvocationWriter.isOuterClassSuper
    Same predicate in two places (and !isInterface() matters next to #3071). A shared helper would help; not a merge blocker.

  5. Cherry-pick
    After COMPATIBILITY: full tip to GROOVY_6_0_X is appropriate (12516/12518 are pure fixes; 12514/12517 fix STC/SC hard failures and intentionally align dynamic Groovy with javac). Without COMPATIBILITY I would only be comfortable cherry-picking the 12516+12518 commits.

Happy to re-review quickly once COMPATIBILITY is in and the attribute path is either fixed or explicitly deferred with a ticket.

MethodCallerMultiAdapter adapter;
if (controller.getCompileStack().isLHS()) {
adapter = isSuperExpression(objectExpression) ? setPropertyOnSuper : isGroovyObject(expression) ? setGroovyObjectProperty : setProperty;
adapter = isSuperOrOuterSuper(objectExpression) ? setPropertyOnSuper : isGroovyObject(expression) ? setGroovyObjectProperty : setProperty;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Property form now goes through isSuperOrOuterSuper → get/setPropertyOnSuper, but the attribute adapters in visitAttributeExpression (tip ~1550) still key only on isSuperExpression. Outer.super.@q will load Outer.this and then getField without useSuper, i.e. Outer's own field — the same bug shape this PR fixes for Outer.super.q.

Please mirror isSuperOrOuterSuper there (and cover Outer.super.@x / N.super.@x in InnerClassTest), or open a follow-up ticket and call it out in the PR so the @ form is not left silently wrong.

* Determines if an expression is "super" or "Outer.super" for a class: a
* property of it is a property of the superclass of the instance.
*/
private static boolean isSuperOrOuterSuper(final Expression expression) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

isSuperOrOuterSuper matches InvocationWriter.isOuterClassSuper / isClassWithSuper (GROOVY-11611). Fine as a local copy for this fix, but consider one shared ExpressionUtils (or package) helper so the !isInterface() guard stays aligned with GROOVY-12526 (#3071).

}

checkThisAndSuperAsPropertyAccess(xe);
if (isSuperOfCurrentNestedClass(xe)) { // "Type.super" within nested class Type is "super"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good place for the rewrite (after checkThisAndSuperAsPropertyAccess). When rebasing onto or against #3071 (GROOVY-12526), keep the interface-super rejection as an early path for ClassExpression + interface + "super", and keep this rewrite only for non-interface nested classes — the interface early-return must not swallow N.super.

return true;
}

private static MethodNode findSuperPropertyAccessor(final PropertyExpression expression, final ClassNode superType, final String name, final boolean setter) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

findSuperPropertyAccessor looks up isX then getX on the superclass, while the setter path trusts DIRECT_METHOD_CALL_TARGET. That matches Verifier-generated boolean properties and the new tests. Optional hardening: for getters, prefer DIRECT_METHOD_CALL_TARGET when present (as setters do) so a synthetic isX recorded on SUPER_MOP_METHOD_REQUIRED cannot disagree with a lone getX on the ClassNode.

@codeconsole

Copy link
Copy Markdown
Contributor Author

@daniellansun thanks. New commits on top of cdb685d, after merging master:

  1. COMPATIBILITY. 1b7a08c adds "Groovy 7 — N.super.name and Outer.super.name reach the member of the superclass (GROOVY-12514, GROOVY-12517)". Each claim was checked against master with scripts: in dynamic Groovy, N.super.p/.f and Outer.super.p/.f read and wrote the instance's own member, as did the .@ forms, and with an enclosing instance of a subclass of Outer they reached the subclass's member; N.super.m() and Outer.super.m() already called the superclass's method; under @TypeChecked and @CompileStatic all the property and field forms were "No such property: super" compile errors.

  2. Outer.super.@field. Fixed in 585caf6: AsmClassGenerator.visitAttributeExpression picks get/setFieldOnSuper with isSuperOrOuterSuper, as visitPropertyExpression does. N.super.@x was already right on this branch, since ResolveVisitor rewrites N.super to super (master read N's own field). Under SC Outer.super.@x was already a direct GETFIELD/PUTFIELD of the superclass's field, because the type checker types Outer.super as Outer's superclass; with the dynamic fix it would have gone through getFieldOnSuper, so StaticTypesCallSiteWriter.fallbackAttributeOrPropertySite now keeps it a direct field access, next to makeSuperPropertySite.

    Testing it found one more case: InvocationWriter.makeCall passed the nested class as the sender, so dynamic Outer.super was resolved against the class of the enclosing instance at run time; with an instance of Sub extends Outer, Outer.super.q read Outer's q and Outer.super.m() called Outer's m (the latter on master too). 2fd3cbe passes Outer as the sender.

    InnerClassTest.testQualifiedSuperField (read, write, ++, +=, closure and lambda for Outer.super.@x and N.super.@x, with same-named fields in the superclass and the subclass, all three modes) and testQualifiedSuperOfOuterClassWithInstanceOfSubclass fail without their changes in dynamic and @TypeChecked code; FieldsAndPropertiesStaticCompileTest.testOuterSuperFieldIsAccessedDirectly checks the SC bytecode and fails without the SC part.

  3. GROOVY-12526: reject qualified super unless the interface is direct #3071. git merge-tree with GROOVY-12526: reject qualified super unless the interface is direct #3071's head reports no conflicting files, and the merge has the order you describe: the interface check returns early in checkThisAndSuperAsPropertyAccess, the N.super rewrite runs after it only for a non-interface nested class, and in existsProperty the Outer.super branch follows the interface branch and requires a non-interface type. On the merge, Groovy12526.testEnclosingClassSuperIsOutsideTheInterfaceCheck fails: it expects D.super.value() from D.B to be "No such property: super", which is what GROOVY-12517 removes. Whichever is merged second should make it an assertScript returning 'C'; the description lists it. InnerClassTest, ThisAndSuperTest and Groovy12526pt2 pass on the merge.

  4. Shared helper. da0bf45 adds ExpressionUtils.isOuterSuperExpression (Type.super with Type not an interface), used by AsmClassGenerator.isSuperOrOuterSuper, InvocationWriter.writeInvokeMethod (replacing isOuterClassSuper), StaticTypeCheckingVisitor.checkSuperCallFromClosure and StaticTypesCallSiteWriter.findSuperPropertyExtension, which had the check without the interface guard. InvocationWriter.isClassWithSuper and StaticInvocationWriter.isClassWithSuper stay as they are, since they match interfaces on purpose (INVOKESPECIAL for Iface.super.m()).

  5. Cherry-pick. All code and test commits cherry-pick cleanly onto GROOVY_6_0_X, and InnerClassTest, ClosuresSTCTest, FieldsAndPropertiesSTCTest and FieldsAndPropertiesStaticCompileTest pass there. The COMPATIBILITY.md commit conflicts only because 6.0.x has no Groovy 7 sections; there it goes after the 6.0.1 entries, as for GROOVY-12521.

StaticTypesCallSiteWriter:900, the getter from DIRECT_METHOD_CALL_TARGET: not done. For ++ and compound assignments the target on that expression is the setter, so the getter is looked up by name anyway. And where the two can differ, the metadata is the odd one out: PropertyNode.getGetterNameOrDefault prefers a declared getX to isX, while plain super.f, dynamic Groovy and findSuperPropertyAccessor pick isX. Taking the metadata would make { -> super.f } call getF while super.f calls isF; that disagreement belongs in the type checker's choice of getter name.

gls, groovy.transform.stc, org.codehaus.groovy.classgen.asm.sc and groovy.ThisAndSuperTest pass (5,535 tests), as do the Checkstyle and CodeNarc gates.

@codeconsole

Copy link
Copy Markdown
Contributor Author

This also fixes GROOVY-12566 (filed by @paulk-asert): super.x in a statically compiled closure or lambda goes through a super$ helper instead of an invokespecial from the closure class. As the JIRA asks, 4936199 adds @CompileStatic to Groovy10133.testGetterVersusIsser10 and ThisAndSuperTest.testSuperDotPropertyWithOverloadedSettersInClosure; both fail without this PR and pass with it.

# Conflicts:
#	COMPATIBILITY.md
#	src/test/groovy/groovy/transform/stc/ClosuresSTCTest.groovy
…ery mode and calls the method of D's superclass
@codeconsole

Copy link
Copy Markdown
Contributor Author

Merged master now that #3071 (GROOVY-12526) has landed. The conflicts were textual: COMPATIBILITY.md keeps master's GROOVY-12488 section ahead of the GROOVY-12514/12517 one, and ClosuresSTCTest keeps both new tests. Since this PR merges second, Groovy12526.testEnclosingClassSuperIsOutsideTheInterfaceCheck (7b7060c) no longer expects "No such property: super": it asserts that D.super.value() from an inner class of D returns 'C', the method of D's superclass, in dynamic, @TypeChecked and @CompileStatic code. The order fits #3071: ResolveVisitor checks the direct-superinterface rule for Iface.super first and rewrites N.super to super only after that, for a nested class that is not an interface; in existsProperty the interface branch also runs before the Outer.super branch.

MethodReferenceTest.testMethodReferencesInStaticAndDefaultMethodsOfInterface fails on master itself (ClassFormatError: ... illegal modifiers: 0x101A for the GROOVY-12536 GString adapter generated private final in an interface), so CI here will show it too.

…nclosing instance's private field, like this.name

PropertyExpressionTransformer gave only a bare this receiver the
GROOVY-12533 treatment, so Sub.this.z (in methods, closures, lambdas and
assignments) read and wrote the private field z of this instance while
this.z and dynamic Groovy use the enclosing instance. Treat Type.this
for the class being rewritten like this.
@testlens-app

testlens-app Bot commented Oct 31, 2026

Copy link
Copy Markdown

✅ All tests passed ✅

🏷️ Commit: 412c44a
▶️ Tests: 123354 executed
⚪️ Checks: 26/26 completed


Learn more about TestLens at testlens.app/docs.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants