This is an automated email from the ASF dual-hosted git repository.

emilles pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/groovy.git


The following commit(s) were added to refs/heads/master by this push:
     new 3a43a99eac GROOVY-11820: check category getter/setter declaring class 
distance
3a43a99eac is described below

commit 3a43a99eac7a449656cfeb40673bc7b1655b7eb1
Author: Eric Milles <[email protected]>
AuthorDate: Sun Dec 28 12:38:17 2025 -0600

    GROOVY-11820: check category getter/setter declaring class distance
---
 src/main/java/groovy/lang/MetaClassImpl.java      |  24 +-
 src/spec/test/metaprogramming/CategoryTest.groovy |  32 +-
 src/test/groovy/groovy/CategoryTest.groovy        | 538 +++++++++++++---------
 3 files changed, 334 insertions(+), 260 deletions(-)

diff --git a/src/main/java/groovy/lang/MetaClassImpl.java 
b/src/main/java/groovy/lang/MetaClassImpl.java
index 21db1df992..767ad24801 100644
--- a/src/main/java/groovy/lang/MetaClassImpl.java
+++ b/src/main/java/groovy/lang/MetaClassImpl.java
@@ -2113,10 +2113,11 @@ public class MetaClassImpl implements MetaClass, 
MutableMetaClass {
     }
 
     private Tuple2<MetaMethod, MetaProperty> 
createMetaMethodAndMetaProperty(final Class<?> sender, final String name, final 
boolean useSuper, final boolean isStatic) {
-        MetaMethod method = null;
+        MetaMethod   mm = null;
         MetaProperty mp = getMetaProperty(sender, name, useSuper, isStatic);
+
         if ((mp == null || mp instanceof CachedField) && !name.isEmpty() && 
isUpperCase(name.charAt(0)) && (name.length() < 2 || 
!isUpperCase(name.charAt(1))) && !"Class".equals(name) && 
!"MetaClass".equals(name)) {
-            // GROOVY-9618 adjust because capitalised properties aren't stored 
as meta bean props
+            // GROOVY-9618: adjust because capitalised properties aren't 
stored as meta bean props
             MetaProperty saved = mp;
             mp = getMetaProperty(sender, BeanUtils.decapitalize(name), 
useSuper, isStatic);
             if (mp == null || (saved != null && mp instanceof CachedField)) {
@@ -2125,23 +2126,23 @@ public class MetaClassImpl implements MetaClass, 
MutableMetaClass {
             }
         }
 
-        if (mp instanceof MetaBeanProperty) {
-            MetaBeanProperty mbp = (MetaBeanProperty) mp;
-            method = mbp.getGetter();
+        if (mp instanceof MetaBeanProperty mbp) {
+            mm = mbp.getGetter();
             mp = mbp.getField();
         }
 
         // check for a category method named like a getter
         if (!useSuper && !isStatic && 
GroovyCategorySupport.hasCategoryInCurrentThread()) {
-            String getterName = 
GroovyCategorySupport.getPropertyCategoryGetterName(name);
+            var getterName = 
GroovyCategorySupport.getPropertyCategoryGetterName(name);
             if (getterName != null) {
                 MetaMethod categoryMethod = getCategoryMethodGetter(theClass, 
getterName, false);
-                if (categoryMethod != null)
-                    method = categoryMethod;
+                if (categoryMethod != null && (mm == null || 
Boolean.TRUE.equals(getMatchKindForCategory(mm, categoryMethod)))) { // 
GROOVY-11820
+                    mm = categoryMethod;
+                }
             }
         }
 
-        return tuple(method, mp);
+        return tuple(mm, mp);
     }
 
     private static CategoryMethod getCategoryMethodMissing(final Class<?> 
sender) {
@@ -2704,8 +2705,7 @@ public class MetaClassImpl implements MetaClass, 
MutableMetaClass {
         MetaProperty mp = getMetaProperty(sender, name, useSuper, isStatic);
         MetaProperty field = null;
         if (mp != null) {
-            if (mp instanceof MetaBeanProperty) {
-                MetaBeanProperty mbp = (MetaBeanProperty) mp;
+            if (mp instanceof MetaBeanProperty mbp) {
                 method = mbp.getSetter();
                 MetaProperty f = mbp.getField();
                 if (method != null || (f != null && !f.isFinal())) {
@@ -2722,7 +2722,7 @@ public class MetaClassImpl implements MetaClass, 
MutableMetaClass {
             var setterName = 
GroovyCategorySupport.getPropertyCategorySetterName(name);
             if (setterName != null) {
                 MetaMethod categoryMethod = getCategoryMethodSetter(theClass, 
setterName, false);
-                if (categoryMethod != null) {
+                if (categoryMethod != null && (method == null || 
Boolean.TRUE.equals(getMatchKindForCategory(method, categoryMethod)))) { // 
GROOVY-11820
                     method = categoryMethod;
                     arguments = new Object[]{newValue};
                 }
diff --git a/src/spec/test/metaprogramming/CategoryTest.groovy 
b/src/spec/test/metaprogramming/CategoryTest.groovy
index 1d528c6bbc..6a19dd106a 100644
--- a/src/spec/test/metaprogramming/CategoryTest.groovy
+++ b/src/spec/test/metaprogramming/CategoryTest.groovy
@@ -18,14 +18,17 @@
  */
 package metaprogramming
 
-import groovy.test.GroovyTestCase
 import groovy.time.TimeCategory
+import org.junit.jupiter.api.Test
 
-class CategoryTest extends GroovyTestCase {
+import static groovy.test.GroovyAssert.assertScript
 
+final class CategoryTest {
+
+    @Test
     void testApplyTimeCategory() {
         // tag::time_category[]
-        use(TimeCategory)  {
+        use(TimeCategory) {
             println 1.minute.from.now       // <1>
             println 10.hours.ago
 
@@ -35,6 +38,7 @@ class CategoryTest extends GroovyTestCase {
         // end::time_category[]
     }
 
+    @Test
     void testCategoryAnnotation() {
         assertScript '''
             // tag::time_category_anno[]
@@ -50,30 +54,10 @@ class CategoryTest extends GroovyTestCase {
                 }
             }
 
-            use (NumberCategory)  {
+            use(NumberCategory) {
                 assert 42.meters.toString() == '42m'
             }
             // end::time_category_anno[]
         '''
     }
-
-    // GROOVY-8433
-    void testCategoryAnnotationAndAIC() {
-        assertScript '''
-            @Category(Number)
-            class NumberCategory {
-                def m() {
-                    String variable = 'works'
-                    new Object() { // "Cannot cast object '1' with class 
'java.lang.Integer' to class 'NumberCategory'" due to implicit "this"
-                        String toString() { variable }
-                    }
-                }
-            }
-
-            use (NumberCategory) {
-                String result = 1.m()
-                assert result == 'works'
-            }
-        '''
-    }
 }
diff --git a/src/test/groovy/groovy/CategoryTest.groovy 
b/src/test/groovy/groovy/CategoryTest.groovy
index 0723180c13..9584918242 100644
--- a/src/test/groovy/groovy/CategoryTest.groovy
+++ b/src/test/groovy/groovy/CategoryTest.groovy
@@ -18,71 +18,95 @@
  */
 package groovy
 
-import groovy.test.GroovyTestCase
+import org.junit.jupiter.api.Test
 
-final class CategoryTest extends GroovyTestCase {
+import static groovy.test.GroovyAssert.assertScript
+import static org.junit.jupiter.api.Assertions.assertThrows
 
-    @Override
-    protected void setUp() {
-        def dummy = null
-        CategoryTestPropertyCategory.setSomething(dummy, 'hello')
-        CategoryTestHelperPropertyReplacer.setaProperty(dummy, 'anotherValue')
+final class CategoryTest {
+
+    static class StringCategory {
+        static String lower(String s) {
+            s.toLowerCase()
+        }
+    }
+
+    static class IntegerCategory {
+        static Integer inc(Integer i) {
+            i + 1
+        }
     }
 
-    void testCategories() {
-        use (StringCategory) {
-            assert "Sam".lower() == "sam";
-            use (IntegerCategory.class) {
-                assert "Sam".lower() == "sam";
-                assert 1.inc() == 2;
+    @Test
+    void testNestingOfCategories() {
+        use(StringCategory) {
+            assert 'Sam'.lower() == 'sam'
+            use(IntegerCategory) {
+                assert 9.inc() == 10
+                assert 'Sam'.lower() == 'sam'
             }
-            shouldFail(MissingMethodException, { 1.inc() });
+            assertThrows(MissingMethodException) { 1.inc() }
         }
-        shouldFail(MissingMethodException, { "Sam".lower() });
+        assertThrows(MissingMethodException) { 'Sam'.lower() }
     }
 
+    @Test
     void testReturnValueWithUseClass() {
         def returnValue = use(StringCategory) {
-            "Green Eggs And Ham".lower()
+            'Green Eggs And Ham'.lower()
         }
-        assert "green eggs and ham" == returnValue
+        assert returnValue == 'green eggs and ham'
     }
 
-    void testReturnValueWithUseList() {
+    @Test
+    void testReturnValueWithUseClassList() {
         def returnValue = use([StringCategory, IntegerCategory]) {
-            "Green Eggs And Ham".lower() + 5.inc()
+            'Green Eggs And Ham'.lower() + 5.inc()
         }
-        assert "green eggs and ham6" == returnValue
+        assert returnValue == 'green eggs and ham6'
     }
 
+    
//--------------------------------------------------------------------------
+
+    @Test
     void testCategoryDefinedProperties() {
-        use(CategoryTestPropertyCategory) {
-            assert getSomething() == "hello"
-            assert something == "hello"
-            something = "nihao"
-            assert something == "nihao"
-        }
+        assertScript '''
+            class CategoryTestPropertyCategory {
+                private static aVal = 'hello'
+                static getSomething(Object self) { return aVal }
+                static void setSomething(Object self, newValue) { aVal = 
newValue }
+            }
 
-        // test the new value again in a new block
-        use(CategoryTestPropertyCategory) {
-            assert something == "nihao"
-        }
+            use(CategoryTestPropertyCategory) {
+                assert getSomething() == 'hello'
+                assert something == 'hello'
+                something = 'nihao'
+                assert something == 'nihao'
+            }
+            // test the new value again in a new block
+            use(CategoryTestPropertyCategory) {
+                assert something == 'nihao'
+            }
+        '''
     }
 
     // GROOVY-10133
+    @Test
     void testCategoryDefinedProperties2() {
         assertScript '''
             class Cat {
                 static boolean isAbc(self) { true }
                 static boolean getAbc(self) { false }
             }
-            use (Cat) {
-                assert abc // should select "isAbc()"
+
+            use(Cat) {
+                assert abc // should select isAbc()
             }
         '''
     }
 
     // GROOVY-5245
+    @Test
     void testCategoryDefinedProperties3() {
         assertScript '''
             class Isser {
@@ -92,7 +116,8 @@ final class CategoryTest extends GroovyTestCase {
                 static boolean getWorking2(Isser b) { true }
                 static boolean isNotWorking(Isser b) { true }
             }
-            use (IsserCat) {
+
+            use(IsserCat) {
                 assert new Isser().working
                 assert new Isser().working2
                 assert new Isser().notWorking // MissingPropertyException
@@ -100,161 +125,225 @@ final class CategoryTest extends GroovyTestCase {
         '''
     }
 
-    void testCategoryReplacedPropertyAccessMethod() {
-        def cth = new CategoryTestHelper()
-        cth.aProperty = "aValue"
-        assert cth.aProperty == "aValue"
-        use (CategoryTestHelperPropertyReplacer) {
-            assert cth.aProperty == "anotherValue"
-            cth.aProperty = "this is boring"
-            assert cth.aProperty == "this is boring"
-        }
-        assert cth.aProperty == "aValue"
+    @Test
+    void testCategoryMethodReplacesPropertyAccessMethod() {
+        assertScript '''
+            class CategoryTestHelper {
+                def aProperty = 'aValue'
+            }
+            class CategoryTestHelperPropertyReplacer {
+                private static aVal = 'anotherValue'
+                static getaProperty(CategoryTestHelper self) { return aVal }
+                static void setaProperty(CategoryTestHelper self, newValue) { 
aVal = newValue }
+            }
+
+            def cth = new CategoryTestHelper()
+            cth.aProperty = 'aValue'
+            assert cth.aProperty == 'aValue'
+            use(CategoryTestHelperPropertyReplacer) {
+                assert cth.aProperty == 'anotherValue'
+                cth.aProperty = 'this is boring'
+                assert cth.aProperty == 'this is boring'
+            }
+            assert cth.aProperty == 'aValue'
+        '''
+    }
+
+    // GROOVY-11820
+    @Test
+    void testCategoryMethodHiddenByPropertyAccessMethod() {
+        assertScript '''import java.lang.reflect.Field
+            class Pogo {
+                public float field
+            }
+
+            Field field = Pogo.fields.first()
+            assert field.getType() == float
+            assert field.type == float
+
+            @Category(Field)
+            class FieldCat {
+                def getType() { 'override' }
+            }
+
+            use(FieldCat) {
+                assert field.getType() == 'override'
+                assert field.type == 'override'
+            }
+
+            @Category(Object)
+            class ObjectCat {
+                def getType() { 'override' }
+            }
+
+            use(ObjectCat) { // class method is closer than category method
+                assert field.getType() == float
+                assert field.type == float
+            }
+        '''
     }
 
+    @Test
     void testCategoryHiddenByClassMethod() {
-      assertScript """
-         class A{}
-         class B extends A{def m(){1}}
-         class Category{ static m(A a) {2}}
-         def b = new B()
-         use (Category) {
-           assert b.m() == 1
-         }
-      """
+        assertScript '''
+            class A {
+            }
+            class B extends A {
+                def m() { 1 }
+            }
+            class C {
+                static m(A a) { 2 }
+            }
+
+            def b = new B()
+            use(C) {
+                assert b.m() == 1
+            }
+        '''
     }
 
+    @Test
     void testCategoryOverridingClassMethod() {
-      assertScript """
-         class A {def m(){1}}
-         class Category{ static m(A a) {2}}
-         def a = new A()
-         use (Category) {
-           assert a.m() == 2
-         }
-      """
-      assertScript """
-         class A {def m(){1}}
-         class B extends A{}
-         class Category{ static m(A a) {2}}
-         def a = new B()
-         use (Category) {
-           assert a.m() == 2
-         }
-      """
+        assertScript '''
+            class A {
+                def m() { 1 }
+            }
+            class C {
+                static m(A a) { 2 }
+            }
+
+            def a = new A()
+            use(C) {
+                assert a.m() == 2
+            }
+        '''
     }
 
+    @Test
+    void testCategoryOverridingClassMethod2() {
+        assertScript '''
+            class A {
+                def m() { 1 }
+            }
+            class B extends A {
+            }
+            class C {
+                static m(A a) { 2 }
+            }
+
+            def a = new B()
+            use(C) {
+                assert a.m() == 2
+            }
+        '''
+    }
+
+    @Test
     void testCategoryWithMixedOverriding() {
-      assertScript """
-         class A{def m(){0}}
-         class B extends A{def m(){1}}
-         class Category{ static m(A a) {2}}
-         def b = new B()
-         use (Category) {
-           assert b.m() == 1
-         }
-      """
+        assertScript '''
+            class A {
+                def m() { 0 }
+            }
+            class B extends A {
+                def m() { 1 }
+            }
+            class C {
+                static m(A a) { 2 }
+            }
+
+            def b = new B()
+            use(C) {
+                assert b.m() == 1
+            }
+        '''
     }
 
+    @Test
     void testCategoryInheritance() {
-      assertScript """
-        public class Foo {
-          static Object foo(Object obj) {
-            "Foo.foo()"
-          }
-        }
-
-        public class Bar extends Foo{
-          static Object bar(Object obj) {
-            "Bar.bar()"
-          }
-        }
+        assertScript '''
+            class Foo {
+                static Object foo(Object obj) {
+                    'Foo.foo()'
+                }
+            }
+            class Bar extends Foo {
+                static Object bar(Object obj) {
+                    'Bar.bar()'
+                }
+            }
 
-        def obj = new Object()
+            def obj = new Object()
+            use(Foo) {
+                assert obj.foo() == 'Foo.foo()'
+            }
+            use(Bar) {
+                assert obj.bar() == 'Bar.bar()'
+                assert obj.foo() == 'Foo.foo()'
+            }
+        '''
+    }
 
-        use(Foo){
-          assert obj.foo() == "Foo.foo()"
-        }
+    // GROOVY-8433
+    @Test
+    void testCategoryAnnotationAndAIC() {
+        assertScript '''
+            @Category(Number)
+            class NumberCategory {
+                def m() {
+                    String variable = 'works'
+                    new Object() { // "Cannot cast object '1' with class 
'java.lang.Integer' to class 'NumberCategory'" due to implicit "this"
+                        String toString() { variable }
+                    }
+                }
+            }
 
-        use(Bar){
-          assert obj.bar() == "Bar.bar()"
-          assert obj.foo() == "Foo.foo()"
-        }
-      """
+            use(NumberCategory) {
+                String result = 1.m()
+                assert result == 'works'
+            }
+        '''
     }
 
+    // GROOVY-5248
+    @Test
     void testNullReceiverChangeForPOJO() {
-        // GROOVY-5248
         // this test will call a method using a POJO while a category is active
         // in call site caching this triggers the usage of POJOMetaClassSite,
         // which was missing a null check for the receiver. The last foo call
         // uses null to exactly check that path. I use multiple calls with 
foo(1)
         // before to ensure for example indy will do the right things as well,
         // since indy may need more than one call here.
-        assertScript """
-            class Cat {
-              public static findAll(Integer x, Closure cl) {1}
-            }
-
-             def foo(x) {
-                 x.findAll {}
-             }
-
-             use (Cat) {
-                 assert foo(1) == 1
-                 assert foo(1) == 1
-                 assert foo(1) == 1
-                 assert foo(null) == []
-                 assert foo(1) == 1
-                 assert foo(1) == 1
-                 assert foo(1) == 1
-             }
-        """
-    }
-
-    def foo(x){x.bar()}
-
-    void testMethodHiding1() {
-        def x = new X()
-        assert foo(x) == 1
-        use (XCat) {
-            assert foo(x) == 2
-            def t = Thread.start {assert foo(x)==1}
-            t.join()
-        }
-        assert foo(x) == 1
-        def t = Thread.start {use (XCat2){assert foo(x)==3}}
-        t.join()
-        assert foo(x) == 1
-    }
+        assertScript '''
+            class C {
+                static findAll(Integer x, Closure c) { 1 }
+            }
+            def foo(x) {
+                x.findAll { -> }
+            }
 
-    void testMethodHiding2() {
-        def x = new X()
-        assert foo(x) == 1
-        use (XCat) {
-            assert foo(x) == 2
-            def t = Thread.start {use (XCat2){assert foo(x)==3}}
-            t.join()
-            assert foo(x) == 2
-            t = Thread.start {assert foo(x)==1}
-            t.join()
-        }
-        assert foo(x) == 1
-        def t = Thread.start {use (XCat2){assert foo(x)==3}}
-        t.join()
-        assert foo(x) == 1
+            use(C) {
+                assert foo(1) == 1
+                assert foo(1) == 1
+                assert foo(1) == 1
+                assert foo() == []
+                assert foo(1) == 1
+                assert foo(1) == 1
+                assert foo(1) == 1
+            }
+        '''
     }
 
+    @Test
     void testCallToPrivateMethod1() {
         assertScript '''
             class A {
                 private foo() { 1 }
                 def baz() { foo() }
             }
-
-            class B extends A {}
-
-            class C {}
+            class B extends A {
+            }
+            class C {
+            }
 
             use(C) {
                 assert new B().baz() == 1
@@ -263,16 +352,17 @@ final class CategoryTest extends GroovyTestCase {
     }
 
     // GROOVY-6263
+    @Test
     void testCallToPrivateMethod2() {
         assertScript '''
             class A {
                 private foo(a) { 1 }
                 def baz() { foo() }
             }
-
-            class B extends A {}
-
-            class C {}
+            class B extends A {
+            }
+            class C {
+            }
 
             use(C) {
                 assert new B().baz() == 1
@@ -281,13 +371,15 @@ final class CategoryTest extends GroovyTestCase {
     }
 
     // GROOVY-5453
+    @Test
     void testOverloadedGetterMethod1() {
         assertScript '''
-            class Cat {
-                static getFoo(String s) {'String'}
-                static getFoo(CharSequence s) {'CharSequence'}
+            class C {
+                static getFoo(String s) { 'String' }
+                static getFoo(CharSequence s) { 'CharSequence' }
             }
-            use (Cat) {
+
+            use(C) {
                 assert 'abc'.getFoo() == 'String'
                 assert 'abc'.foo      == 'String'
             }
@@ -295,6 +387,7 @@ final class CategoryTest extends GroovyTestCase {
     }
 
     // GROOVY-10214
+    @Test
     void testOverloadedGetterMethod2() {
         assertScript '''
             class Cat {
@@ -320,7 +413,8 @@ final class CategoryTest extends GroovyTestCase {
                     'Double'
                 }
             }
-            use (Cat) {
+
+            use(Cat) {
                 assert 123.foo == 'Integer'
                 assert 4.5d.foo == 'Double'
             }
@@ -328,6 +422,7 @@ final class CategoryTest extends GroovyTestCase {
     }
 
     // GROOVY-10743
+    @Test
     void testStaticMethodOnInterface() {
         assertScript '''
             use(java.util.stream.Stream) {
@@ -337,96 +432,91 @@ final class CategoryTest extends GroovyTestCase {
         '''
     }
 
-    // GROOVY-3867
-    void testPropertyMissing() {
-        def x = new X()
+    
//--------------------------------------------------------------------------
 
-        shouldFail(MissingPropertyException) {
-            assert x.baz != "works" // accessing x.baz should throw MPE
-        }
+    static class X     { def bar() {1} }
+    static class XCat  { static bar(X x) {2} }
+    static class XCat2 { static bar(X x) {3} }
+    static class XCat3 { static methodMissing(X x, String name, args) {4} }
+    static class XCat4 { static propertyMissing(X x, String name) {'works'} }
 
-        use(XCat4) {
-            assert x.baz == "works"
-        }
+    def foo(x) { x.bar() }
 
-        shouldFail(MissingPropertyException) {
-            assert x.baz != "works" // accessing x.baz should throw MPE
+    @Test
+    void testMethodHiding1() {
+        def x = new X()
+        assert foo(x) == 1
+        use(XCat) {
+            assert foo(x) == 2
+            def t = Thread.start {assert foo(x)==1}
+            t.join()
         }
+        assert foo(x) == 1
+        def t = Thread.start {use(XCat2){assert foo(x)==3}}
+        t.join()
+        assert foo(x) == 1
     }
 
-    // GROOVY-10783
-    void testPropertyMissing2() {
-        assertScript '''\
-            class X{ def bar(){1}}
-            class XCat4{ static propertyMissing(X x, String name) {"works"}}
-
-            def x = new X()
-
-            use(XCat4) {
-                assert x.baz == "works"
-            }
-        '''
+    @Test
+    void testMethodHiding2() {
+        def x = new X()
+        assert foo(x) == 1
+        use(XCat) {
+            assert foo(x) == 2
+            def t = Thread.start {use(XCat2){assert foo(x)==3}}
+            t.join()
+            assert foo(x) == 2
+            t = Thread.start {assert foo(x)==1}
+            t.join()
+        }
+        assert foo(x) == 1
+        def t = Thread.start {use(XCat2){assert foo(x)==3}}
+        t.join()
+        assert foo(x) == 1
     }
 
     // GROOVY-3867
+    @Test
     void testMethodMissing() {
         def x = new X()
         assert foo(x) == 1
-        use (XCat3) {
+        use(XCat3) {
             assert foo(x) == 1 // regular foo() is not affected by 
methodMissing in category
             assert x.baz() == 4 // XCat3.methodMissing is called
         }
         assert foo(x) == 1
-        def t = Thread.start {use (XCat3){assert x.baz()==4}}
+        def t = Thread.start {use(XCat3){assert x.baz()==4}}
         t.join()
         assert foo(x) == 1
-        shouldFail(MissingMethodException) {
+        assertThrows(MissingMethodException) {
             x.baz()
         }
     }
 
     // GROOVY-3867
+    @Test
     void testMethodMissingNoStatic() {
         def x = new X()
-        use (XCat3) {
+        use(XCat3) {
             assert x.baz() == 4 // XCat3.methodMissing is called for instance
-            shouldFail(MissingMethodException) {
+            assertThrows(MissingMethodException) {
                 assert X.baz() != 4 // XCat3.methodMissing should not be 
called for static method of X
             }
         }
     }
-}
-
-class X{ def bar(){1}}
-class XCat{ static bar(X x){2}}
-class XCat2{ static bar(X x){3}}
-class XCat3{ static methodMissing(X x, String name, args) {4}}
-class XCat4{ static propertyMissing(X x, String name) {"works"}}
-
-class StringCategory {
-    static String lower(String string) {
-        return string.toLowerCase();
-    }
-}
 
-class IntegerCategory {
-    static Integer inc(Integer i) {
-        return i + 1;
+    // GROOVY-3867, GROOVY-10783
+    @Test
+    void testPropertyMissing() {
+        def x = new X()
+        assertThrows(MissingPropertyException) {
+            assert x.baz != 'works' // accessing x.baz should throw MPE
+        }
+        use(XCat4) {
+            assert x.baz == 'works'
+        }
+        assertThrows(MissingPropertyException) {
+            assert x.baz != 'works' // accessing x.baz should throw MPE
+        }
     }
 }
-
-class CategoryTestPropertyCategory {
-    private static aVal = "hello"
-    static getSomething(Object self) { return aVal }
-    static void setSomething(Object self, newValue) { aVal = newValue }
-}
-
-class CategoryTestHelper {
-    def aProperty = "aValue"
-}
-
-class CategoryTestHelperPropertyReplacer {
-    private static aVal = "anotherValue"
-    static getaProperty(CategoryTestHelper self) { return aVal }
-    static void setaProperty(CategoryTestHelper self, newValue) { aVal = 
newValue }
-}

Reply via email to