You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@groovy.apache.org by em...@apache.org on 2022/05/24 17:00:25 UTC

[groovy] branch GROOVY_4_0_X updated: GROOVY-10143: super trait field access

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

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


The following commit(s) were added to refs/heads/GROOVY_4_0_X by this push:
     new ca8071ca91 GROOVY-10143: super trait field access
ca8071ca91 is described below

commit ca8071ca9187221f4eb38bd8e6d28233fbb9d49f
Author: Eric Milles <er...@thomsonreuters.com>
AuthorDate: Tue May 24 09:52:05 2022 -0500

    GROOVY-10143: super trait field access
---
 .../codehaus/groovy/ast/tools/GenericsUtils.java   |  2 +-
 .../transform/trait/SuperCallTraitTransformer.java | 60 ++++++++--------------
 .../groovy/transform/trait/TraitComposer.java      | 38 ++++++--------
 .../transform/trait/TraitReceiverTransformer.java  | 19 ++++---
 src/test-resources/groovy/bugs/groovyA143/A.groovy | 22 ++++++++
 src/test-resources/groovy/bugs/groovyA143/B.groovy | 33 ++++++++++++
 src/test-resources/groovy/bugs/groovyA143/C.groovy | 26 ++++++++++
 src/test-resources/groovy/bugs/groovyA143/D.groovy | 40 +++++++++++++++
 src/test-resources/groovy/bugs/groovyA143/E.groovy | 26 ++++++++++
 .../groovy/bugs/groovyA143/Main.groovy             | 26 ++++++++++
 src/test/groovy/script/RuntimeResolveTests.groovy  |  1 +
 .../traitx/TraitASTTransformationTest.groovy       | 18 +++++++
 12 files changed, 241 insertions(+), 70 deletions(-)

diff --git a/src/main/java/org/codehaus/groovy/ast/tools/GenericsUtils.java b/src/main/java/org/codehaus/groovy/ast/tools/GenericsUtils.java
index f98a8eca2e..ccede732f1 100644
--- a/src/main/java/org/codehaus/groovy/ast/tools/GenericsUtils.java
+++ b/src/main/java/org/codehaus/groovy/ast/tools/GenericsUtils.java
@@ -64,7 +64,7 @@ import static org.codehaus.groovy.transform.stc.StaticTypeCheckingSupport.implem
  */
 public class GenericsUtils {
     public static final GenericsType[] EMPTY_GENERICS_ARRAY = GenericsType.EMPTY_ARRAY;
-    public static final String JAVA_LANG_OBJECT = "java.lang.Object";
+    public static final String JAVA_LANG_OBJECT = ClassHelper.OBJECT;
 
     /**
      * Given a parameterized type and a generic type information, aligns actual type parameters. For example, if a
diff --git a/src/main/java/org/codehaus/groovy/transform/trait/SuperCallTraitTransformer.java b/src/main/java/org/codehaus/groovy/transform/trait/SuperCallTraitTransformer.java
index c5d22de9eb..578f9dbfb9 100644
--- a/src/main/java/org/codehaus/groovy/transform/trait/SuperCallTraitTransformer.java
+++ b/src/main/java/org/codehaus/groovy/transform/trait/SuperCallTraitTransformer.java
@@ -18,7 +18,6 @@
  */
 package org.codehaus.groovy.transform.trait;
 
-import groovy.lang.MetaProperty;
 import org.codehaus.groovy.ast.ClassCodeExpressionTransformer;
 import org.codehaus.groovy.ast.ClassHelper;
 import org.codehaus.groovy.ast.ClassNode;
@@ -40,11 +39,13 @@ import org.codehaus.groovy.syntax.Types;
 import java.util.List;
 import java.util.function.Function;
 
+import static org.codehaus.groovy.ast.tools.GeneralUtils.args;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.callX;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.classX;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.getGetterName;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.getSetterName;
 import static org.codehaus.groovy.ast.tools.GeneralUtils.thisPropX;
 import static org.codehaus.groovy.ast.tools.GeneralUtils.varX;
-import static org.codehaus.groovy.ast.ClassHelper.isClassType;
-import static org.codehaus.groovy.ast.ClassHelper.isPrimitiveBoolean;
-import static org.codehaus.groovy.ast.ClassHelper.isPrimitiveVoid;
 import static org.objectweb.asm.Opcodes.ACC_ABSTRACT;
 import static org.objectweb.asm.Opcodes.ACC_PUBLIC;
 import static org.objectweb.asm.Opcodes.ACC_STATIC;
@@ -102,19 +103,13 @@ class SuperCallTraitTransformer extends ClassCodeExpressionTransformer {
                     ClassNode helperType = getHelper(traitType);
                     // TraitType.super.foo = ... -> TraitType$Trait$Helper.setFoo(this, ...)
 
-                    String setterName = MetaProperty.getSetterName(leftExpression.getPropertyAsString());
+                    String setterName = getSetterName(leftExpression.getPropertyAsString());
                     for (MethodNode method : helperType.getMethods(setterName)) {
                         Parameter[] parameters = method.getParameters();
                         if (parameters.length == 2 && isSelfType(parameters[0], traitType)) {
-                            MethodCallExpression setterCall = new MethodCallExpression(
-                                    new ClassExpression(helperType),
-                                    setterName,
-                                    new ArgumentListExpression(
-                                            isClassType(parameters[0].getType())
-                                                ? thisPropX(false, "class") : varX("this"),
-                                            bin.getRightExpression()
-                                    )
-                            );
+                            Expression thisExpr = ClassHelper.isClassType(parameters[0].getType()) ? thisPropX(false, "class") : varX("this");
+
+                            MethodCallExpression setterCall = callX(classX(helperType), setterName, args(thisExpr, bin.getRightExpression()));
                             setterCall.getObjectExpression().setSourcePosition(leftExpression.getObjectExpression());
                             setterCall.getMethod().setSourcePosition(leftExpression.getProperty());
                             setterCall.setSpreadSafe(leftExpression.isSpreadSafe());
@@ -145,14 +140,9 @@ class SuperCallTraitTransformer extends ClassCodeExpressionTransformer {
                 // TraitType.super.foo -> TraitType$Trait$Helper.getFoo(this)
 
                 Function<MethodNode, MethodCallExpression> xform = (methodNode) -> {
-                    MethodCallExpression methodCall = new MethodCallExpression(
-                            new ClassExpression(helperType),
-                            methodNode.getName(),
-                            new ArgumentListExpression(
-                                    isClassType(methodNode.getParameters()[0].getType())
-                                        ? thisPropX(false, "class") : varX("this")
-                            )
-                    );
+                    Expression thisExpr = ClassHelper.isClassType(methodNode.getParameters()[0].getType()) ? thisPropX(false, "class") : varX("this");
+
+                    MethodCallExpression methodCall = callX(classX(helperType), methodNode.getName(), args(thisExpr));
                     methodCall.getObjectExpression().setSourcePosition(traitType);
                     methodCall.getMethod().setSourcePosition(exp.getProperty());
                     methodCall.setSpreadSafe(exp.isSpreadSafe());
@@ -161,20 +151,18 @@ class SuperCallTraitTransformer extends ClassCodeExpressionTransformer {
                     return methodCall;
                 };
 
-                String getterName = MetaProperty.getGetterName(exp.getPropertyAsString(), null);
+                String getterName = getGetterName(exp.getPropertyAsString());
                 for (MethodNode method : helperType.getMethods(getterName)) {
-                    if (method.isStatic() && method.getParameters().length == 1
-                            && isSelfType(method.getParameters()[0], traitType)
-                            && !isPrimitiveVoid(method.getReturnType())) {
+                    if (method.isStatic() && !method.isVoidMethod()
+                            && method.getParameters().length == 1 && isSelfType(method.getParameters()[0], traitType)) {
                         return xform.apply(method);
                     }
                 }
 
                 String isserName = "is" + getterName.substring(3);
                 for (MethodNode method : helperType.getMethods(isserName)) {
-                    if (method.isStatic() && method.getParameters().length == 1
-                            && isSelfType(method.getParameters()[0], traitType)
-                            && isPrimitiveBoolean(method.getReturnType())) {
+                    if (method.isStatic() && ClassHelper.isPrimitiveBoolean(method.getReturnType())
+                            && method.getParameters().length == 1 && isSelfType(method.getParameters()[0], traitType)) {
                         return xform.apply(method);
                     }
                 }
@@ -192,10 +180,9 @@ class SuperCallTraitTransformer extends ClassCodeExpressionTransformer {
 
             List<MethodNode> targets = helperType.getMethods(exp.getMethodAsString());
             boolean isStatic = !targets.isEmpty() && targets.stream().map(MethodNode::getParameters)
-                .allMatch(params -> params.length > 0 && isClassType(params[0].getType()));
+                .allMatch(params -> params.length > 0 && ClassHelper.isClassType(params[0].getType()));
 
-            ArgumentListExpression newArgs = new ArgumentListExpression(
-                    isStatic ? thisPropX(false, "class") : varX("this"));
+            ArgumentListExpression newArgs = args(isStatic ? thisPropX(false, "class") : varX("this"));
             Expression arguments = exp.getArguments();
             if (arguments instanceof TupleExpression) {
                 for (Expression expression : (TupleExpression) arguments) {
@@ -205,12 +192,9 @@ class SuperCallTraitTransformer extends ClassCodeExpressionTransformer {
                 newArgs.addExpression(transform(arguments));
             }
 
-            MethodCallExpression newCall = new MethodCallExpression(
-                    new ClassExpression(helperType),
-                    transform(exp.getMethod()),
-                    newArgs
-            );
+            MethodCallExpression newCall = callX(classX(helperType), transform(exp.getMethod()), newArgs);
             newCall.getObjectExpression().setSourcePosition(traitType);
+            newCall.setGenericsTypes(exp.getGenericsTypes());
             newCall.setSpreadSafe(exp.isSpreadSafe());
             newCall.setImplicitThis(false);
             return newCall;
@@ -255,7 +239,7 @@ class SuperCallTraitTransformer extends ClassCodeExpressionTransformer {
     private static boolean isSelfType(final Parameter parameter, final ClassNode traitType) {
         ClassNode paramType = parameter.getType();
         if (paramType.equals(traitType)) return true;
-        return isClassType(paramType)
+        return ClassHelper.isClassType(paramType)
                 && paramType.getGenericsTypes() != null
                 && paramType.getGenericsTypes()[0].getType().equals(traitType);
     }
diff --git a/src/main/java/org/codehaus/groovy/transform/trait/TraitComposer.java b/src/main/java/org/codehaus/groovy/transform/trait/TraitComposer.java
index 1023f82d8a..135e82e31e 100644
--- a/src/main/java/org/codehaus/groovy/transform/trait/TraitComposer.java
+++ b/src/main/java/org/codehaus/groovy/transform/trait/TraitComposer.java
@@ -25,6 +25,7 @@ import org.codehaus.groovy.ast.ClassHelper;
 import org.codehaus.groovy.ast.ClassNode;
 import org.codehaus.groovy.ast.FieldNode;
 import org.codehaus.groovy.ast.GenericsType;
+import org.codehaus.groovy.ast.GroovyClassVisitor;
 import org.codehaus.groovy.ast.MethodNode;
 import org.codehaus.groovy.ast.Parameter;
 import org.codehaus.groovy.ast.PropertyNode;
@@ -92,30 +93,25 @@ public abstract class TraitComposer {
     public static final ClassNode COMPILESTATIC_CLASSNODE = ClassHelper.make(CompileStatic.class);
 
     /**
-     * Given a class node, if this class node implements a trait, then generate all the appropriate
-     * code which delegates calls to the trait. It is safe to call this method on a class node which
-     * does not implement a trait.
-     * @param cNode a class node
-     * @param unit the source unit
+     * Given a class node, if this class node implements a trait, then generate
+     * all the appropriate code which delegates calls to the trait.  It is safe
+     * to call this method on a class node which does not implement a trait.
      */
-    public static void doExtendTraits(final ClassNode cNode, final SourceUnit unit, final CompilationUnit cu) {
-        if (cNode.isInterface()) return;
-        boolean isItselfTrait = Traits.isTrait(cNode);
-        SuperCallTraitTransformer superCallTransformer = new SuperCallTraitTransformer(unit);
-        if (isItselfTrait) {
-            checkTraitAllowed(cNode, unit);
+    public static void doExtendTraits(final ClassNode cn, final SourceUnit su, final CompilationUnit cu) {
+        if (cn.isInterface()) return;
+        if (Traits.isTrait(cn)) {
+            checkTraitAllowed(cn, su);
             return;
         }
-        if (!cNode.getNameWithoutPackage().endsWith(Traits.TRAIT_HELPER)) {
-            List<ClassNode> traits = Traits.findTraits(cNode);
-            for (ClassNode trait : traits) {
-                TraitHelpersTuple helpers = Traits.findHelpers(trait);
-                applyTrait(trait, cNode, helpers, unit);
-                superCallTransformer.visitClass(cNode);
-                if (unit!=null) {
-                    ASTTransformationCollectorCodeVisitor collector = new ASTTransformationCollectorCodeVisitor(unit, cu.getTransformLoader());
-                    collector.visitClass(cNode);
-                }
+        if (!cn.getNameWithoutPackage().endsWith(Traits.TRAIT_HELPER)) {
+            GroovyClassVisitor visitor = new SuperCallTraitTransformer(su);
+            for (ClassNode trait : Traits.findTraits(cn)) {
+                applyTrait(trait, cn, Traits.findHelpers(trait), su);
+                visitor.visitClass(cn);
+            }
+            if (su != null) {
+                visitor = new ASTTransformationCollectorCodeVisitor(su, cu.getTransformLoader());
+                visitor.visitClass(cn);
             }
         }
     }
diff --git a/src/main/java/org/codehaus/groovy/transform/trait/TraitReceiverTransformer.java b/src/main/java/org/codehaus/groovy/transform/trait/TraitReceiverTransformer.java
index c2b6792ce2..51efa1e7ca 100644
--- a/src/main/java/org/codehaus/groovy/transform/trait/TraitReceiverTransformer.java
+++ b/src/main/java/org/codehaus/groovy/transform/trait/TraitReceiverTransformer.java
@@ -43,6 +43,7 @@ import org.codehaus.groovy.ast.expr.VariableExpression;
 import org.codehaus.groovy.control.SourceUnit;
 import org.codehaus.groovy.syntax.SyntaxException;
 import org.codehaus.groovy.syntax.Token;
+import org.codehaus.groovy.syntax.Types;
 
 import java.lang.reflect.Modifier;
 import java.util.Collection;
@@ -100,8 +101,7 @@ class TraitReceiverTransformer extends ClassCodeExpressionTransformer {
             return transformBinaryExpression((BinaryExpression) exp, weavedType);
         } else if (exp instanceof StaticMethodCallExpression) {
             StaticMethodCallExpression call = (StaticMethodCallExpression) exp;
-            ClassNode ownerType = call.getOwnerType();
-            if (traitClass.equals(ownerType)) {
+            if (call.getOwnerType().equals(traitClass)) {
                 MethodCallExpression mce = callX(
                         varX(weaved),
                         call.getMethod(),
@@ -187,9 +187,8 @@ class TraitReceiverTransformer extends ClassCodeExpressionTransformer {
         Expression leftExpression = exp.getLeftExpression();
         Expression rightExpression = exp.getRightExpression();
         Token operation = exp.getOperation();
-        if (operation.getText().equals("=")) {
+        if (operation.getType() == Types.ASSIGN) {
             String leftFieldName = null;
-            // it's an assignment
             if (leftExpression instanceof VariableExpression && ((VariableExpression) leftExpression).getAccessedVariable() instanceof FieldNode) {
                 leftFieldName = ((VariableExpression) leftExpression).getAccessedVariable().getName();
             } else if (leftExpression instanceof FieldExpression) {
@@ -197,14 +196,13 @@ class TraitReceiverTransformer extends ClassCodeExpressionTransformer {
             } else if (leftExpression instanceof PropertyExpression
                     && (((PropertyExpression) leftExpression).isImplicitThis() || "this".equals(((PropertyExpression) leftExpression).getObjectExpression().getText()))) {
                 leftFieldName = ((PropertyExpression) leftExpression).getPropertyAsString();
-                FieldNode fn = tryGetFieldNode(weavedType, leftFieldName);
-                if (fieldHelper == null || fn == null && !fieldHelper.hasPossibleMethod(Traits.helperSetterName(new FieldNode(leftFieldName, 0, ClassHelper.OBJECT_TYPE, weavedType, null)), rightExpression)) {
-                    return binX(propX(varX(weaved), leftFieldName), operation, transform(rightExpression));
-                }
             }
             if (leftFieldName != null) {
-                FieldNode fn = weavedType.getDeclaredField(leftFieldName);
                 FieldNode staticField = tryGetFieldNode(weavedType, leftFieldName);
+                if (fieldHelper == null || staticField == null && !fieldHelper.hasPossibleMethod(Traits.helperSetterName(new FieldNode(leftFieldName, 0, ClassHelper.OBJECT_TYPE, weavedType, null)), rightExpression)) {
+                    return binX(propX(varX(weaved), leftFieldName), operation, transform(rightExpression)); // GROOVY-7342, GROOVY-7456, GROOVY-9739, GROOVY-10143, et al.
+                }
+                FieldNode fn = weavedType.getDeclaredField(leftFieldName);
                 if (fn == null) {
                     fn = new FieldNode(leftFieldName, 0, ClassHelper.OBJECT_TYPE, weavedType, null);
                 }
@@ -217,7 +215,7 @@ class TraitReceiverTransformer extends ClassCodeExpressionTransformer {
                 MethodCallExpression mce = callX(
                         receiver,
                         method,
-                        args(super.transform(rightExpression))
+                        super.transform(rightExpression)
                 );
                 mce.setImplicitThis(false);
                 mce.setSourcePosition(exp);
@@ -225,6 +223,7 @@ class TraitReceiverTransformer extends ClassCodeExpressionTransformer {
                 return mce;
             }
         }
+
         Expression leftTransform = transform(leftExpression);
         Expression rightTransform = transform(rightExpression);
         Expression ret = exp instanceof DeclarationExpression ?
diff --git a/src/test-resources/groovy/bugs/groovyA143/A.groovy b/src/test-resources/groovy/bugs/groovyA143/A.groovy
new file mode 100644
index 0000000000..2c1c3cc6bf
--- /dev/null
+++ b/src/test-resources/groovy/bugs/groovyA143/A.groovy
@@ -0,0 +1,22 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package groovy.bugs.groovyA143
+
+abstract class A {
+}
diff --git a/src/test-resources/groovy/bugs/groovyA143/B.groovy b/src/test-resources/groovy/bugs/groovyA143/B.groovy
new file mode 100644
index 0000000000..42c18897fe
--- /dev/null
+++ b/src/test-resources/groovy/bugs/groovyA143/B.groovy
@@ -0,0 +1,33 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package groovy.bugs.groovyA143
+
+@groovy.transform.SelfType(A)
+trait B {
+	abstract boolean booleanMethod1()
+	abstract boolean booleanMethod2()
+	abstract boolean booleanMethod3()
+	Object getManager() {
+		if (this.managerObject == null) {
+			this.managerObject = new C(this as B)
+		}
+		this.managerObject
+	}
+	C managerObject
+}
diff --git a/src/test-resources/groovy/bugs/groovyA143/C.groovy b/src/test-resources/groovy/bugs/groovyA143/C.groovy
new file mode 100644
index 0000000000..a11da18f39
--- /dev/null
+++ b/src/test-resources/groovy/bugs/groovyA143/C.groovy
@@ -0,0 +1,26 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package groovy.bugs.groovyA143
+
+class C {
+	final B base
+	C(B base) {
+		this.base = base
+	}
+}
diff --git a/src/test-resources/groovy/bugs/groovyA143/D.groovy b/src/test-resources/groovy/bugs/groovyA143/D.groovy
new file mode 100644
index 0000000000..4cb5ae87fc
--- /dev/null
+++ b/src/test-resources/groovy/bugs/groovyA143/D.groovy
@@ -0,0 +1,40 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package groovy.bugs.groovyA143
+
+@groovy.transform.CompileStatic
+@groovy.transform.SelfType([A])
+trait D extends B {
+	boolean booleanMethod1() {
+		true
+	}
+	boolean booleanMethod2() {
+		true
+	}
+	boolean booleanMethod3() {
+		true
+	}
+	@Override
+	Object getManager() {
+		if (managerObject == null) {
+			managerObject = new E(this as B)
+		}
+		managerObject
+	}
+}
diff --git a/src/test-resources/groovy/bugs/groovyA143/E.groovy b/src/test-resources/groovy/bugs/groovyA143/E.groovy
new file mode 100644
index 0000000000..db90e1e913
--- /dev/null
+++ b/src/test-resources/groovy/bugs/groovyA143/E.groovy
@@ -0,0 +1,26 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package groovy.bugs.groovyA143
+
+@groovy.transform.InheritConstructors
+class E extends C {
+	String getStringValue() {
+		'string value'
+	}
+}
diff --git a/src/test-resources/groovy/bugs/groovyA143/Main.groovy b/src/test-resources/groovy/bugs/groovyA143/Main.groovy
new file mode 100644
index 0000000000..bcdcf7e0d5
--- /dev/null
+++ b/src/test-resources/groovy/bugs/groovyA143/Main.groovy
@@ -0,0 +1,26 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package groovy.bugs.groovyA143
+
+class X extends A implements D {
+}
+
+def x = new X()
+assert x.manager.base === x
+assert x.manager.stringValue == 'string value'
diff --git a/src/test/groovy/script/RuntimeResolveTests.groovy b/src/test/groovy/script/RuntimeResolveTests.groovy
index 32acab68c1..c039649c3f 100644
--- a/src/test/groovy/script/RuntimeResolveTests.groovy
+++ b/src/test/groovy/script/RuntimeResolveTests.groovy
@@ -74,6 +74,7 @@ final class RuntimeResolveTests {
 
     @Test
     void testResolvePackagePeersAndCompileTrait() {
+        runScript('/groovy/bugs/groovyA143/Main.groovy')
         runScript('/groovy/bugs/groovyA144/Main.groovy')
     }
 
diff --git a/src/test/org/codehaus/groovy/transform/traitx/TraitASTTransformationTest.groovy b/src/test/org/codehaus/groovy/transform/traitx/TraitASTTransformationTest.groovy
index 16b49df79d..e3b29ce162 100644
--- a/src/test/org/codehaus/groovy/transform/traitx/TraitASTTransformationTest.groovy
+++ b/src/test/org/codehaus/groovy/transform/traitx/TraitASTTransformationTest.groovy
@@ -3482,4 +3482,22 @@ final class TraitASTTransformationTest {
             new AbstractData<TestData>()
         '''
     }
+
+    // GROOVY-10598
+    void testAssignOperators() {
+        assertScript shell, '''
+            trait T {
+            }
+            class C implements T {
+                @TypeChecked test() {
+                    @ASTTest(phase=CANONICALIZATION, value={
+                        // simulates GrailsASTUtils#addApiVariableDeclaration DeclarationExpression
+                        node.operation.@type = org.codehaus.groovy.syntax.Types.ASSIGNMENT_OPERATOR
+                    })
+                    def var = null
+                }
+            }
+            new C().test()
+        '''
+    }
 }