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

tkobayas pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/incubator-kie-drools.git


The following commit(s) were added to refs/heads/main by this push:
     new 11e8da64172 [6779] Exec model: include Java superclass @Position 
fields in genera… (#6780)
11e8da64172 is described below

commit 11e8da64172c2a48a8782aa38ceb261e95124045
Author: Daniel Clark <[email protected]>
AuthorDate: Mon Jul 6 09:47:35 2026 +0200

    [6779] Exec model: include Java superclass @Position fields in genera… 
(#6780)
    
    * [6779] Exec model: include Java superclass @Position fields in generated 
constructor
    
    When a DRL-declared type extends a Java (classpath) class, the executable 
model
    generated a constructor over only the type's own fields, omitting the 
inherited
    fields and the super(...) call. The classic compiler includes them, so rule
    consequents such as `new Sub(inheritedField, ownField)` failed to compile 
under
    the executable model with "The constructor ... is undefined".
    
    DescrTypeDefinition.findInheritedDeclaredFields() now pulls the resolved 
Java
    superclass's @Position fields (ordered by position) when the supertype is a
    classpath class rather than a DRL-declared type, so the generated 
full-argument
    constructor includes them and forwards them to super(...). Falls back to 
plain
    declared-field order when the superclass carries no @Position.
    
    Adds DeclaredTypesTest#testExtendPojoInheritedFieldsConstructor and a 
minimal
    PositionalParent POJO covering the scenario.
    
    Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
    
    * Only inherit @Position superclass fields, else no-arg super()
    
    ---------
    
    Co-authored-by: Claude Opus 4.8 (1M context) <[email protected]>
---
 .../declaredtype/DescrTypeDefinition.java          | 44 ++++++++++++-
 .../model/codegen/execmodel/DeclaredTypesTest.java | 72 ++++++++++++++++++++++
 .../codegen/execmodel/domain/PlainParent.java      | 41 ++++++++++++
 .../codegen/execmodel/domain/PositionalParent.java | 59 ++++++++++++++++++
 4 files changed, 215 insertions(+), 1 deletion(-)

diff --git 
a/drools-model/drools-model-codegen/src/main/java/org/drools/model/codegen/execmodel/generator/declaredtype/DescrTypeDefinition.java
 
b/drools-model/drools-model-codegen/src/main/java/org/drools/model/codegen/execmodel/generator/declaredtype/DescrTypeDefinition.java
index c07b8cf7d29..ef3ae10798a 100644
--- 
a/drools-model/drools-model-codegen/src/main/java/org/drools/model/codegen/execmodel/generator/declaredtype/DescrTypeDefinition.java
+++ 
b/drools-model/drools-model-codegen/src/main/java/org/drools/model/codegen/execmodel/generator/declaredtype/DescrTypeDefinition.java
@@ -18,10 +18,13 @@
  */
 package org.drools.model.codegen.execmodel.generator.declaredtype;
 
+import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
+import java.util.Comparator;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
@@ -42,6 +45,7 @@ import 
org.drools.model.codegen.execmodel.generator.declaredtype.api.FieldDefini
 import 
org.drools.model.codegen.execmodel.generator.declaredtype.api.MethodDefinition;
 import 
org.drools.model.codegen.execmodel.generator.declaredtype.api.TypeDefinition;
 import 
org.drools.model.codegen.execmodel.generator.declaredtype.api.TypeResolver;
+import org.kie.api.definition.type.Position;
 
 import static java.util.Optional.of;
 import static java.util.Optional.ofNullable;
@@ -161,7 +165,45 @@ public class DescrTypeDefinition implements TypeDefinition 
{
 
     @Override
     public List<FieldDefinition> findInheritedDeclaredFields() {
-        return findInheritedDeclaredFields(new ArrayList<>(), 
getSuperType(typeDeclarationDescr, packageDescr));
+        List<FieldDefinition> fields = findInheritedDeclaredFields(new 
ArrayList<>(), getSuperType(typeDeclarationDescr, packageDescr));
+        if (fields.isEmpty()) {
+            abstractClass.ifPresent(superClass -> 
fields.addAll(inheritedFieldsFromSuperClass(superClass)));
+        }
+        return fields;
+    }
+
+    /**
+     * Collects the positional fields of a resolved Java superclass, walking 
the class hierarchy.
+     * A field participates only when it carries {@link Position} (explicit 
opt-in), ordered by its
+     * position value; this deterministically excludes non-positional members 
and keeps the generated
+     * {@code super(...)} call aligned with a positional constructor on the 
superclass. When the
+     * superclass declares no {@link Position} field, the result is empty so 
the generated constructor
+     * uses a no-arg {@code super()} rather than guessing a signature from all 
instance fields (which
+     * would not match any superclass constructor and would fail to compile).
+     */
+    private List<FieldDefinition> inheritedFieldsFromSuperClass(Class<?> 
superClass) {
+        List<Class<?>> hierarchy = new ArrayList<>();
+        for (Class<?> c = superClass; c != null && c != Object.class; c = 
c.getSuperclass()) {
+            hierarchy.add(0, c);
+        }
+
+        List<Field> instanceFields = new ArrayList<>();
+        for (Class<?> c : hierarchy) {
+            for (Field f : c.getDeclaredFields()) {
+                if (!Modifier.isStatic(f.getModifiers())) {
+                    instanceFields.add(f);
+                }
+            }
+        }
+
+        // Only fields explicitly opted-in with @Position are inherited into 
the generated
+        // constructor. When the superclass declares none, return empty (no 
fallback to all instance
+        // fields) so the constructor uses a no-arg super() instead of an 
unmatched super(...) call.
+        return instanceFields.stream()
+                .filter(f -> f.getAnnotation(Position.class) != null)
+                .sorted(Comparator.comparingInt(f -> 
f.getAnnotation(Position.class).value()))
+                .map(f -> (FieldDefinition) new 
DescrFieldDefinition(f.getName(), f.getType().getCanonicalName(), null))
+                .collect(Collectors.toList());
     }
 
     private List<FieldDefinition> 
findInheritedDeclaredFields(List<FieldDefinition> fields, 
Optional<TypeDeclarationDescr> superType) {
diff --git 
a/drools-model/drools-model-codegen/src/test/java/org/drools/model/codegen/execmodel/DeclaredTypesTest.java
 
b/drools-model/drools-model-codegen/src/test/java/org/drools/model/codegen/execmodel/DeclaredTypesTest.java
index 353caceb53f..0bc143692e7 100644
--- 
a/drools-model/drools-model-codegen/src/test/java/org/drools/model/codegen/execmodel/DeclaredTypesTest.java
+++ 
b/drools-model/drools-model-codegen/src/test/java/org/drools/model/codegen/execmodel/DeclaredTypesTest.java
@@ -41,6 +41,8 @@ import org.drools.core.reteoo.EntryPointNode;
 import org.drools.core.reteoo.ObjectTypeNode;
 import org.drools.model.codegen.execmodel.domain.Address;
 import org.drools.model.codegen.execmodel.domain.Person;
+import org.drools.model.codegen.execmodel.domain.PlainParent;
+import org.drools.model.codegen.execmodel.domain.PositionalParent;
 import org.drools.model.codegen.execmodel.domain.Result;
 import org.junit.jupiter.params.ParameterizedTest;
 import org.junit.jupiter.params.provider.MethodSource;
@@ -660,6 +662,76 @@ public class DeclaredTypesTest extends BaseModelTest {
         assertThat(ksession.fireAllRules()).isEqualTo(1);
     }
 
+    @ParameterizedTest
+    @MethodSource("parameters")
+    public void testExtendPojoInheritedFieldsConstructor(RUN_TYPE runType) 
throws Exception {
+        // A declared type extending a Java (classpath) class must generate a 
full-argument
+        // constructor that also includes the superclass @Position fields and 
forwards them to
+        // super(...). PositionalParent has @Position(0) name and @Position(1) 
age, so Child should
+        // expose Child(String name, int age, String dept). The executable 
model previously omitted
+        // the inherited fields, generating only Child(String dept), which 
failed to compile the
+        // 'new Child("Mario", 40, "Sales")' consequent.
+        String str =
+                "package org.test;\n" +
+                "import " + PositionalParent.class.getCanonicalName() + ";\n" +
+                "import " + Result.class.getCanonicalName() + ";\n" +
+                "declare PositionalParent end\n" +
+                "declare Child extends PositionalParent\n" +
+                "    dept : String\n" +
+                "end\n" +
+                "rule Init when\n" +
+                "then\n" +
+                "    insert( new Child(\"Mario\", 40, \"Sales\") );\n" +
+                "end\n" +
+                "rule Check when\n" +
+                "    $c : Child( name == \"Mario\", age == 40, dept == 
\"Sales\" )\n" +
+                "then\n" +
+                "    insert( new Result($c.getName()) );\n" +
+                "end";
+
+        KieSession ksession = getKieSession(runType, str);
+        assertThat(ksession.fireAllRules()).isEqualTo(2);
+
+        Collection<Result> results = getObjectsIntoList(ksession, 
Result.class);
+        assertThat(results).hasSize(1);
+        assertThat(results.iterator().next().getValue()).isEqualTo("Mario");
+    }
+
+    @ParameterizedTest
+    @MethodSource("parametersPatternOnly")
+    public void testExtendPojoWithoutPositionUsesNoArgSuper(RUN_TYPE runType) {
+        // Executable-model regression for #6779: a declared type extending a 
Java class that has NO
+        // @Position fields must not pull the superclass's instance fields 
into the generated
+        // constructor - doing so would emit a super(...) call with no 
matching superclass constructor
+        // and fail to compile ("The constructor Child(String) is undefined"). 
Only the declared type's
+        // own fields are constructor parameters, and a no-arg super() is 
used. PlainParent has a single
+        // (unannotated) field and only a no-arg constructor. (Exec-model 
codegen only; the classic
+        // compiler handles declared-extends-POJO via a different path.)
+        String str =
+                "package org.test;\n" +
+                "import " + PlainParent.class.getCanonicalName() + ";\n" +
+                "import " + Result.class.getCanonicalName() + ";\n" +
+                "declare Child extends PlainParent\n" +
+                "    dept : String\n" +
+                "end\n" +
+                "rule Init when\n" +
+                "then\n" +
+                "    insert( new Child(\"Sales\") );\n" +
+                "end\n" +
+                "rule Check when\n" +
+                "    $c : Child( dept == \"Sales\" )\n" +
+                "then\n" +
+                "    insert( new Result($c.getDept()) );\n" +
+                "end";
+
+        KieSession ksession = getKieSession(runType, str);
+        assertThat(ksession.fireAllRules()).isEqualTo(2);
+
+        Collection<Result> results = getObjectsIntoList(ksession, 
Result.class);
+        assertThat(results).hasSize(1);
+        assertThat(results.iterator().next().getValue()).isEqualTo("Sales");
+    }
+
     @ParameterizedTest
     @MethodSource("parameters")
     void testNonDefinedCustomAnnotation(RUN_TYPE runType) {
diff --git 
a/drools-model/drools-model-codegen/src/test/java/org/drools/model/codegen/execmodel/domain/PlainParent.java
 
b/drools-model/drools-model-codegen/src/test/java/org/drools/model/codegen/execmodel/domain/PlainParent.java
new file mode 100644
index 00000000000..9473559aef7
--- /dev/null
+++ 
b/drools-model/drools-model-codegen/src/test/java/org/drools/model/codegen/execmodel/domain/PlainParent.java
@@ -0,0 +1,41 @@
+/*
+ * 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 org.drools.model.codegen.execmodel.domain;
+
+/**
+ * A plain classpath POJO with NO {@code @Position} fields and only a no-arg 
constructor, used to
+ * verify that a DRL-declared type extending it does not pull the superclass's 
instance fields into
+ * the generated constructor (which would emit an unmatched {@code super(...)} 
call); instead a no-arg
+ * {@code super()} is used.
+ */
+public class PlainParent {
+
+    private String label;
+
+    public PlainParent() {
+    }
+
+    public String getLabel() {
+        return label;
+    }
+
+    public void setLabel(String label) {
+        this.label = label;
+    }
+}
diff --git 
a/drools-model/drools-model-codegen/src/test/java/org/drools/model/codegen/execmodel/domain/PositionalParent.java
 
b/drools-model/drools-model-codegen/src/test/java/org/drools/model/codegen/execmodel/domain/PositionalParent.java
new file mode 100644
index 00000000000..70570848527
--- /dev/null
+++ 
b/drools-model/drools-model-codegen/src/test/java/org/drools/model/codegen/execmodel/domain/PositionalParent.java
@@ -0,0 +1,59 @@
+/*
+ * 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 org.drools.model.codegen.execmodel.domain;
+
+import org.kie.api.definition.type.Position;
+
+/**
+ * Minimal classpath POJO with positional fields, used to verify that a 
DRL-declared type
+ * extending a Java class generates a full-argument constructor that includes 
the inherited
+ * {@code @Position} fields and forwards them to {@code super(...)}.
+ */
+public class PositionalParent {
+
+    @Position(0)
+    private String name;
+
+    @Position(1)
+    private int age;
+
+    public PositionalParent() {
+    }
+
+    public PositionalParent(String name, int age) {
+        this.name = name;
+        this.age = age;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public int getAge() {
+        return age;
+    }
+
+    public void setAge(int age) {
+        this.age = age;
+    }
+}


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to