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 6cb1692312e Incubator kie drools 6782 declared extends cross package 
(#6785)
6cb1692312e is described below

commit 6cb1692312e1cd58d39fc525c4e3fb755caf3e50
Author: Daniel Clark <[email protected]>
AuthorDate: Fri Jul 17 09:13:25 2026 +0200

    Incubator kie drools 6782 declared extends cross package (#6785)
    
    [incubator-kie-drools-6782] Include inherited fields when a declared type 
extends a declared type in another package
    
    Generalizes inherited-field resolution to follow the declared supertype 
chain
    across packages (searching all package descriptors of the build and using 
each
    declaring package's own type resolver), and at the end of the chain into a
    resolvable Java superclass. Plumbs the build's package descriptors and the
    PackageRegistryManager through POJOGenerator and 
DeclaredTypeCompilationPhase so
    DescrTypeDefinition can resolve supertypes declared in other packages.
    
    Stacked on #6780 (incubator-kie-drools-6779): the new inheritedFieldsOf 
subsumes
    the Java-superclass logic introduced there and adds the cross-package case.
    
    Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
    
    * Resolve cross-package declared supertype on explicit codegen path; 
disambiguate by import
    
    Follow-up on #6785 addressing Copilot review feedback:
    
    - ExplicitCanonicalModelCompiler now passes the multi-package context
      (packages, pkgRegistryManager) to POJOGenerator, so a declared type that
      extends a declared type in another package also gets a correct full-arg
      constructor on the build-time codegen path (kie-maven-plugin / Quarkus /
      SpringBoot), not just the KnowledgeBuilder path.
    
    - DescrTypeDefinition no longer resolves a declared supertype by first match
      on simple name across all packages. It prefers a local declaration, then 
an
      explicit qualifier or single-type import to pin the declaring package, and
      falls back to a namespace-ordered (deterministic) scan - so a 
same-simple-name
      type in an unrelated package is never picked and resolution no longer 
depends
      on package iteration order. Also threads the correct per-package 
TypeResolver
      through the supertype chain.
    
    - Tests: RuleCodegenTest#generateCrossPackageDeclaredInheritanceConstructor
      covers the ExplicitCanonicalModelCompiler path;
      DeclaredTypesTest#testDeclaredExtendsAmbiguousSimpleNameResolvedByImport
      covers same-simple-name resolution by import (both engines).
    
    Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
    
    ---------
    
    Co-authored-by: Claude Opus 4.8 (1M context) <[email protected]>
---
 .../declaredtype/DescrTypeDefinition.java          | 166 +++++++++++++++++++--
 .../generator/declaredtype/POJOGenerator.java      |  21 ++-
 .../processors/DeclaredTypeCompilationPhase.java   |   2 +-
 .../tool/ExplicitCanonicalModelCompiler.java       |   2 +-
 .../model/codegen/execmodel/DeclaredTypesTest.java |  66 ++++++++
 .../model/codegen/project/RuleCodegenTest.java     |  33 ++++
 .../model/project/codegen/crosspkg/child.drl       |  31 ++++
 .../model/project/codegen/crosspkg/parent.drl      |  24 +++
 8 files changed, 324 insertions(+), 21 deletions(-)

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 ef3ae10798a..a2b748ad78f 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
@@ -32,10 +32,13 @@ import java.util.Optional;
 import java.util.stream.Collectors;
 import java.util.stream.Stream;
 
+import org.drools.compiler.builder.PackageRegistryManager;
 import org.drools.compiler.compiler.AnnotationDeclarationError;
+import org.drools.compiler.compiler.PackageRegistry;
 import org.drools.drl.parser.DroolsError;
 import org.drools.compiler.compiler.TypeDeclarationError;
 import org.drools.drl.ast.descr.AnnotationDescr;
+import org.drools.drl.ast.descr.ImportDescr;
 import org.drools.drl.ast.descr.PackageDescr;
 import org.drools.drl.ast.descr.QualifiedName;
 import org.drools.drl.ast.descr.TypeDeclarationDescr;
@@ -67,6 +70,12 @@ public class DescrTypeDefinition implements TypeDefinition {
 
     private final TypeResolver typeResolver;
 
+    // Optional cross-package context: all package descriptors in the build, 
and the registry manager
+    // used to obtain a declaring package's own type resolver. Null when not 
running a multi-package
+    // build (e.g. tooling), in which case only the current package is 
consulted.
+    private final Collection<? extends PackageDescr> allPackages;
+    private final PackageRegistryManager pkgRegistryManager;
+
     private List<DroolsError> errors = new ArrayList<>();
     private Map<String, Object> classMetaData = new HashMap<>();
 
@@ -76,9 +85,16 @@ public class DescrTypeDefinition implements TypeDefinition {
     private List<String> interfaceNames = new ArrayList<>();
 
     public DescrTypeDefinition(PackageDescr packageDescr, TypeDeclarationDescr 
typeDeclarationDescr, TypeResolver typeResolver) {
+        this(packageDescr, typeDeclarationDescr, typeResolver, null, null);
+    }
+
+    public DescrTypeDefinition(PackageDescr packageDescr, TypeDeclarationDescr 
typeDeclarationDescr, TypeResolver typeResolver,
+                               Collection<? extends PackageDescr> allPackages, 
PackageRegistryManager pkgRegistryManager) {
         this.packageDescr = packageDescr;
         this.typeDeclarationDescr = typeDeclarationDescr;
         this.typeResolver = typeResolver;
+        this.allPackages = allPackages;
+        this.pkgRegistryManager = pkgRegistryManager;
         this.fieldDefinition = processFields();
 
         processSuperTypes();
@@ -165,13 +181,143 @@ public class DescrTypeDefinition implements 
TypeDefinition {
 
     @Override
     public List<FieldDefinition> findInheritedDeclaredFields() {
-        List<FieldDefinition> fields = findInheritedDeclaredFields(new 
ArrayList<>(), getSuperType(typeDeclarationDescr, packageDescr));
-        if (fields.isEmpty()) {
-            abstractClass.ifPresent(superClass -> 
fields.addAll(inheritedFieldsFromSuperClass(superClass)));
+        return inheritedFieldsOf(typeDeclarationDescr, packageDescr, 
typeResolver);
+    }
+
+    /**
+     * Computes the inherited (supertype) fields of a declared type, 
base-first, following the
+     * supertype chain across packages and, at the end of the chain, into a 
resolvable Java
+     * superclass. This covers a declared type extending another declared type 
in the same OR a
+     * different package, and a declared type (possibly several levels up) 
extending a Java class on
+     * the classpath. A field-less re-declaration of a classpath class 
("declare X end") does not
+     * hide that class's {@link Position} fields. See
+     * DeclaredTypesTest#testExtendPojoInheritedFieldsConstructor and the 
cross-package inheritance
+     * cases.
+     */
+    private List<FieldDefinition> inheritedFieldsOf(TypeDeclarationDescr td, 
PackageDescr pkgDescr, TypeResolver resolver) {
+        List<FieldDefinition> fields = new ArrayList<>();
+        String superName = td.getSuperTypeName();
+        if (superName == null) {
+            return fields;
         }
+
+        Optional<DeclaredSuperType> declaredSuper = 
findDeclaredSuperType(superName, td.getSuperTypeNamespace(), pkgDescr, 
resolver);
+        if (declaredSuper.isPresent()) {
+            DeclaredSuperType st = declaredSuper.get();
+            // the super's own inherited fields first, then the super's own 
declared fields
+            fields.addAll(inheritedFieldsOf(st.descr, st.packageDescr, 
st.resolver));
+            
st.descr.getFields().values().stream().map(DescrFieldDefinition::new).forEach(fields::add);
+            if (!fields.isEmpty()) {
+                return fields;
+            }
+            // The declared supertype chain contributed no fields: it may be a 
field-less
+            // re-declaration of a Java class on the classpath ("declare X 
end" over an imported
+            // class), so fall through to that class's @Position fields.
+        }
+
+        // The supertype is (or re-declares) a Java class. Resolve it in the 
declaring package's
+        // scope (its imports) and contribute its @Position fields.
+        resolver.resolveType(superName)
+                .filter(c -> !c.isInterface())
+                .ifPresent(c -> 
fields.addAll(inheritedFieldsFromSuperClass(c)));
         return fields;
     }
 
+    /**
+     * Locates a declared supertype, disambiguating cross-package matches by 
the namespace the current
+     * package resolves {@code superName} to. A supertype declared in the 
current package always wins
+     * (a local declaration shadows imports), unless it was explicitly 
qualified to another package
+     * ({@code extends a.b.Super}). Otherwise the declaring package is taken 
from an explicit qualifier
+     * or an explicit single-type import, so a same-simple-name type declared 
in an unrelated package is
+     * never picked. When no such qualifier/import narrows it (a bare name 
with only a wildcard import,
+     * say), the remaining packages are scanned in a deterministic order (by 
namespace) so resolution
+     * does not depend on {@code allPackages} iteration order. The returned 
{@link DeclaredSuperType}
+     * carries the declaring package's own descriptor and type resolver, 
needed to resolve that
+     * package's supertypes and imports further up the chain.
+     */
+    private Optional<DeclaredSuperType> findDeclaredSuperType(String 
superName, String superNamespace,
+                                                              PackageDescr 
currentPkgDescr, TypeResolver currentResolver) {
+        boolean explicitlyOtherPackage = superNamespace != null && 
!superNamespace.equals(namespaceOf(currentPkgDescr));
+        if (!explicitlyOtherPackage) {
+            Optional<TypeDeclarationDescr> inCurrent = 
findTypeDeclaration(currentPkgDescr, superName);
+            if (inCurrent.isPresent()) {
+                return Optional.of(new DeclaredSuperType(inCurrent.get(), 
currentPkgDescr, currentResolver));
+            }
+        }
+
+        if (allPackages == null) {
+            return Optional.empty();
+        }
+
+        // Resolve the declaring namespace from an explicit qualifier or the 
current package's explicit
+        // single-type import of superName; when known, the supertype is 
looked up strictly within it.
+        String targetNamespace = superNamespace != null ? superNamespace : 
importedNamespaceFor(superName, currentPkgDescr);
+        if (targetNamespace != null) {
+            return allPackages.stream()
+                    .filter(pkg -> targetNamespace.equals(namespaceOf(pkg)))
+                    .map(pkg -> findTypeDeclaration(pkg, superName)
+                            .map(td -> new DeclaredSuperType(td, pkg, 
resolverForPackage(pkg.getNamespace()))))
+                    .filter(Optional::isPresent).map(Optional::get)
+                    .findFirst();
+        }
+
+        // Bare name with nothing to disambiguate it: scan the other packages 
in a deterministic order
+        // (by namespace) so a same-simple-name collision resolves 
reproducibly across builds.
+        return allPackages.stream()
+                .sorted(Comparator.comparing(DescrTypeDefinition::namespaceOf))
+                .map(pkg -> findTypeDeclaration(pkg, superName)
+                        .map(td -> new DeclaredSuperType(td, pkg, 
resolverForPackage(pkg.getNamespace()))))
+                .filter(Optional::isPresent).map(Optional::get)
+                .findFirst();
+    }
+
+    private static String namespaceOf(PackageDescr pkg) {
+        return pkg.getNamespace() == null ? "" : pkg.getNamespace();
+    }
+
+    private static Optional<TypeDeclarationDescr> 
findTypeDeclaration(PackageDescr pkg, String simpleName) {
+        return pkg.getTypeDeclarations().stream()
+                .filter(td -> td.getTypeName().equals(simpleName))
+                .findFirst();
+    }
+
+    /**
+     * Returns the namespace an explicit single-type import binds {@code 
simpleName} to in the given
+     * package, or {@code null} when there is no such import (a bare name, or 
only a wildcard import) so
+     * the caller falls back to a deterministic cross-package scan.
+     */
+    private static String importedNamespaceFor(String simpleName, PackageDescr 
pkgDescr) {
+        String suffix = "." + simpleName;
+        return pkgDescr.getImports().stream()
+                .map(ImportDescr::getTarget)
+                .filter(target -> target != null && target.endsWith(suffix))
+                .map(target -> target.substring(0, target.length() - 
suffix.length()))
+                .findFirst()
+                .orElse(null);
+    }
+
+    private TypeResolver resolverForPackage(String packageName) {
+        if (pkgRegistryManager != null && packageName != null) {
+            PackageRegistry reg = 
pkgRegistryManager.getPackageRegistry(packageName);
+            if (reg != null) {
+                return new 
POJOGenerator.SafeTypeResolver(reg.getTypeResolver());
+            }
+        }
+        return typeResolver;
+    }
+
+    private static final class DeclaredSuperType {
+        final TypeDeclarationDescr descr;
+        final PackageDescr packageDescr;
+        final TypeResolver resolver;
+
+        DeclaredSuperType(TypeDeclarationDescr descr, PackageDescr 
packageDescr, TypeResolver resolver) {
+            this.descr = descr;
+            this.packageDescr = packageDescr;
+            this.resolver = resolver;
+        }
+    }
+
     /**
      * 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
@@ -206,18 +352,6 @@ public class DescrTypeDefinition implements TypeDefinition 
{
                 .collect(Collectors.toList());
     }
 
-    private List<FieldDefinition> 
findInheritedDeclaredFields(List<FieldDefinition> fields, 
Optional<TypeDeclarationDescr> superType) {
-        superType.ifPresent(st -> {
-            findInheritedDeclaredFields(fields, getSuperType(st, 
packageDescr));
-            st.getFields()
-                    .values()
-                    .stream()
-                    .map(DescrFieldDefinition::new)
-                    .forEach(fields::add);
-        });
-        return fields;
-    }
-
     private List<TypeFieldDescr> 
typeFieldsSortedByPosition(List<FieldDefinition> inheritedFields) {
         Collection<TypeFieldDescr> typeFields = 
typeDeclarationDescr.getFields().values().stream()
                 .filter( f -> inheritedFields.stream().map( 
FieldDefinition::getFieldName ).noneMatch( name -> name.equals( 
f.getFieldName() ) ) )
@@ -267,7 +401,7 @@ public class DescrTypeDefinition implements TypeDefinition {
 
         Stream<FieldDefinition> superTypeKieFields =
                 optionalToStream(getSuperType(this.typeDeclarationDescr, 
packageDescr)
-                                         .map(superType -> new 
DescrTypeDefinition(packageDescr, superType, typeResolver)))
+                                         .map(superType -> new 
DescrTypeDefinition(packageDescr, superType, typeResolver, allPackages, 
pkgRegistryManager)))
                         .flatMap(t -> t.getKeyFields().stream());
 
         return Stream.concat(keyFields, superTypeKieFields).collect(toList());
diff --git 
a/drools-model/drools-model-codegen/src/main/java/org/drools/model/codegen/execmodel/generator/declaredtype/POJOGenerator.java
 
b/drools-model/drools-model-codegen/src/main/java/org/drools/model/codegen/execmodel/generator/declaredtype/POJOGenerator.java
index 157a386b22b..ca7ade7f46a 100644
--- 
a/drools-model/drools-model-codegen/src/main/java/org/drools/model/codegen/execmodel/generator/declaredtype/POJOGenerator.java
+++ 
b/drools-model/drools-model-codegen/src/main/java/org/drools/model/codegen/execmodel/generator/declaredtype/POJOGenerator.java
@@ -29,6 +29,7 @@ import java.util.Set;
 import com.github.javaparser.ast.body.ClassOrInterfaceDeclaration;
 import com.github.javaparser.ast.body.TypeDeclaration;
 import com.github.javaparser.ast.expr.MethodCallExpr;
+import org.drools.compiler.builder.PackageRegistryManager;
 import org.drools.compiler.builder.impl.BuildResultCollector;
 import org.drools.compiler.builder.impl.BuildResultCollectorImpl;
 import org.drools.compiler.builder.impl.processors.CompilationPhase;
@@ -64,19 +65,33 @@ public class POJOGenerator implements CompilationPhase {
     private InternalKnowledgePackage pkg;
     private PackageDescr packageDescr;
     private PackageModel packageModel;
+    private final Collection<? extends PackageDescr> allPackages;
+    private final PackageRegistryManager pkgRegistryManager;
 
     private static final List<String> exprAnnotations = 
Arrays.asList("duration", "timestamp");
 
-    public POJOGenerator(BuildResultCollector builder, 
InternalKnowledgePackage pkg, PackageDescr packageDescr, PackageModel 
packageModel) {
+    public POJOGenerator(BuildResultCollector builder, 
InternalKnowledgePackage pkg, PackageDescr packageDescr, PackageModel 
packageModel,
+                         Collection<? extends PackageDescr> allPackages, 
PackageRegistryManager pkgRegistryManager) {
         this.builder = builder;
         this.pkg = pkg;
         this.packageDescr = packageDescr;
         this.packageModel = packageModel;
+        this.allPackages = allPackages;
+        this.pkgRegistryManager = pkgRegistryManager;
         packageModel.addImports(pkg.getTypeResolver().getImports());
     }
 
+    public POJOGenerator(BuildResultCollector builder, 
InternalKnowledgePackage pkg, PackageDescr packageDescr, PackageModel 
packageModel) {
+        this(builder, pkg, packageDescr, packageModel, null, null);
+    }
+
     public POJOGenerator(InternalKnowledgePackage pkg, PackageDescr 
packageDescr, PackageModel packageModel) {
-        this(new BuildResultCollectorImpl(), pkg, packageDescr, packageModel);
+        this(new BuildResultCollectorImpl(), pkg, packageDescr, packageModel, 
null, null);
+    }
+
+    public POJOGenerator(InternalKnowledgePackage pkg, PackageDescr 
packageDescr, PackageModel packageModel,
+                         Collection<? extends PackageDescr> allPackages, 
PackageRegistryManager pkgRegistryManager) {
+        this(new BuildResultCollectorImpl(), pkg, packageDescr, packageModel, 
allPackages, pkgRegistryManager);
     }
 
     public static Map<String, Class<?>> compileType(BuildResultCollector 
resultAccumulator,
@@ -145,7 +160,7 @@ public class POJOGenerator implements CompilationPhase {
 
     private void createPOJO(TypeDeclarationDescr typeDescr) {
         SafeTypeResolver typeResolver = new 
SafeTypeResolver(pkg.getTypeResolver());
-        DescrTypeDefinition descrDeclaredTypeDefinition = new 
DescrTypeDefinition(packageDescr, typeDescr, typeResolver);
+        DescrTypeDefinition descrDeclaredTypeDefinition = new 
DescrTypeDefinition(packageDescr, typeDescr, typeResolver, allPackages, 
pkgRegistryManager);
         
descrDeclaredTypeDefinition.getErrors().forEach(builder::addBuilderResult);
 
         // Implemented types should be probably in
diff --git 
a/drools-model/drools-model-codegen/src/main/java/org/drools/model/codegen/execmodel/processors/DeclaredTypeCompilationPhase.java
 
b/drools-model/drools-model-codegen/src/main/java/org/drools/model/codegen/execmodel/processors/DeclaredTypeCompilationPhase.java
index 1dccfbc2962..e72d2405b3a 100644
--- 
a/drools-model/drools-model-codegen/src/main/java/org/drools/model/codegen/execmodel/processors/DeclaredTypeCompilationPhase.java
+++ 
b/drools-model/drools-model-codegen/src/main/java/org/drools/model/codegen/execmodel/processors/DeclaredTypeCompilationPhase.java
@@ -61,7 +61,7 @@ public class DeclaredTypeCompilationPhase implements 
CompilationPhase {
         List<CompilationPhase> phases = asList(
                 iteratingPhase((reg, acc) -> new 
DeclaredTypeRegistrationPhase(reg, acc, pkgRegistryManager)),
                 iteratingPhase((reg, acc) ->
-                        new POJOGenerator(reg.getPackage(), acc, 
packageModelManager.getPackageModel(acc, reg, reg.getPackage().getName()))),
+                        new POJOGenerator(reg.getPackage(), acc, 
packageModelManager.getPackageModel(acc, reg, reg.getPackage().getName()), 
packages, pkgRegistryManager)),
                 new GeneratedPojoCompilationPhase(
                         packageModelManager, buildContext, 
buildConfiguration.getClassLoader()),
                 new PojoStoragePhase(buildContext, pkgRegistryManager, 
packages)
diff --git 
a/drools-model/drools-model-codegen/src/main/java/org/drools/model/codegen/tool/ExplicitCanonicalModelCompiler.java
 
b/drools-model/drools-model-codegen/src/main/java/org/drools/model/codegen/tool/ExplicitCanonicalModelCompiler.java
index b3a22e0d866..6fdbc70abf0 100644
--- 
a/drools-model/drools-model-codegen/src/main/java/org/drools/model/codegen/tool/ExplicitCanonicalModelCompiler.java
+++ 
b/drools-model/drools-model-codegen/src/main/java/org/drools/model/codegen/tool/ExplicitCanonicalModelCompiler.java
@@ -131,7 +131,7 @@ public class ExplicitCanonicalModelCompiler<T extends 
PackageSources> {
 
         phases.add(iteratingPhase((reg, acc) -> new 
DeclaredTypeRegistrationPhase(reg, acc, pkgRegistryManager)));
         phases.add(iteratingPhase((reg, acc) ->
-                new POJOGenerator(reg.getPackage(), acc, 
packageModelManager.getPackageModel(acc, reg, reg.getPackage().getName()))));
+                new POJOGenerator(reg.getPackage(), acc, 
packageModelManager.getPackageModel(acc, reg, reg.getPackage().getName()), 
packages, pkgRegistryManager)));
         phases.add(new GeneratedPojoCompilationPhase(
                 packageModelManager, buildContext, 
configuration.getClassLoader()));
         phases.add(new PojoStoragePhase(buildContext, pkgRegistryManager, 
packages));
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 ea5d487cead..a599fad156b 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
@@ -699,6 +699,72 @@ public class DeclaredTypesTest extends BaseModelTest {
         assertThat(results.iterator().next().getValue()).isEqualTo("Mario");
     }
 
+    @ParameterizedTest
+    @MethodSource("parameters")
+    public void 
testDeclaredTypeExtendingDeclaredTypeInDifferentPackage(RUN_TYPE runType) {
+        // A declared type that extends a declared type in a DIFFERENT package 
must generate a
+        // full-argument constructor that includes the inherited fields and 
forwards them to super(...).
+        // Here Child (com.test.childpkg) extends Parent (com.test.parentpkg), 
so the consequent
+        // 'new Child("Mario", 40, "Sales")' must compile and the inherited 
name/age must be populated.
+        String parentDrl =
+                "package com.test.parentpkg;\n" +
+                "declare Parent\n" +
+                "  name : String\n" +
+                "  age : int\n" +
+                "end\n";
+        String childDrl =
+                "package com.test.childpkg;\n" +
+                "import com.test.parentpkg.Parent;\n" +
+                "import " + Result.class.getCanonicalName() + ";\n" +
+                "declare Child extends Parent\n" +
+                "  dept : String\n" +
+                "end\n" +
+                "rule Init when\n" +
+                "then insert( new Child(\"Mario\", 40, \"Sales\") ); end\n" +
+                "rule Chk when\n" +
+                "  Child( name == \"Mario\", age == 40, dept == \"Sales\" )\n" 
+
+                "then insert( new Result(\"ok\") ); end";
+        KieSession ksession = getKieSession(runType, parentDrl, childDrl);
+        assertThat(ksession.fireAllRules()).isEqualTo(2);
+    }
+
+    @ParameterizedTest
+    @MethodSource("parameters")
+    public void 
testDeclaredExtendsAmbiguousSimpleNameResolvedByImport(RUN_TYPE runType) {
+        // Two packages declare a type with the same simple name (Base). The 
child explicitly imports
+        // one of them, so its 'extends Base' must resolve to that package's 
Base - not to another
+        // package's same-named declaration. Here Sub imports b.pkg.Base, 
whose only field is 'city',
+        // so the constructor is Sub(String city, String dept). Resolving to 
a.pkg.Base (name/age)
+        // instead would make the consequent 'new Sub("NYC", "Sales")' fail to 
compile. Runs under both
+        // engines: the legacy path already resolved this correctly; 
exec-model (#6782) previously
+        // matched the first same-named declaration found while scanning 
packages.
+        String aBaseDrl =
+                "package a.pkg;\n" +
+                "declare Base\n" +
+                "  name : String\n" +
+                "  age : int\n" +
+                "end\n";
+        String bBaseDrl =
+                "package b.pkg;\n" +
+                "declare Base\n" +
+                "  city : String\n" +
+                "end\n";
+        String subDrl =
+                "package sub;\n" +
+                "import b.pkg.Base;\n" +
+                "import " + Result.class.getCanonicalName() + ";\n" +
+                "declare Sub extends Base\n" +
+                "  dept : String\n" +
+                "end\n" +
+                "rule Init when\n" +
+                "then insert( new Sub(\"NYC\", \"Sales\") ); end\n" +
+                "rule Chk when\n" +
+                "  Sub( city == \"NYC\", dept == \"Sales\" )\n" +
+                "then insert( new Result(\"ok\") ); end";
+        KieSession ksession = getKieSession(runType, aBaseDrl, bBaseDrl, 
subDrl);
+        assertThat(ksession.fireAllRules()).isEqualTo(2);
+    }
+
     @ParameterizedTest
     @MethodSource("parametersPatternOnly")
     public void testExtendPojoWithoutPositionUsesNoArgSuper(RUN_TYPE runType) {
diff --git 
a/drools-model/drools-model-codegen/src/test/java/org/drools/model/codegen/project/RuleCodegenTest.java
 
b/drools-model/drools-model-codegen/src/test/java/org/drools/model/codegen/project/RuleCodegenTest.java
index ca24fdbce98..622831056b5 100644
--- 
a/drools-model/drools-model-codegen/src/test/java/org/drools/model/codegen/project/RuleCodegenTest.java
+++ 
b/drools-model/drools-model-codegen/src/test/java/org/drools/model/codegen/project/RuleCodegenTest.java
@@ -19,12 +19,14 @@
 package org.drools.model.codegen.project;
 
 import java.io.File;
+import java.nio.charset.StandardCharsets;
 import java.nio.file.Path;
 import java.nio.file.Paths;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
+import java.util.stream.Collectors;
 import java.util.stream.Stream;
 
 import org.drools.codegen.common.DroolsModelBuildContext;
@@ -165,6 +167,37 @@ public class RuleCodegenTest {
     }
 
 
+    @ParameterizedTest
+    
@MethodSource("org.drools.model.codegen.project.RuleCodegenTest#contextBuilders")
+    public void 
generateCrossPackageDeclaredInheritanceConstructor(DroolsModelBuildContext.Builder
 contextBuilder) {
+        // #6782 regression on the static codegen path (RuleCodegen -> 
DroolsModelBuilder ->
+        // ExplicitCanonicalModelCompiler). A declared type extending a 
declared type in a DIFFERENT
+        // package must generate a full-argument constructor that includes the 
inherited fields and
+        // forwards them to super(...). This path does not go through 
DeclaredTypeCompilationPhase, so
+        // it must also receive the multi-package context to resolve the 
cross-package supertype;
+        // without the fix the generated Child(...) constructor omits the 
inherited name/age.
+        withLegacyApi(contextBuilder);
+
+        RuleCodegen ruleCodegen = getRuleCodegenFromFiles(
+                contextBuilder,
+                new File(RESOURCE_PATH + 
"/org/drools/model/project/codegen/crosspkg").listFiles());
+
+        Collection<GeneratedFile> generatedFiles = 
ruleCodegen.withHotReloadMode().generate();
+
+        String childSource = generatedFiles.stream()
+                .filter(f -> f.relativePath().endsWith("Child.java"))
+                .map(f -> new String(f.contents(), StandardCharsets.UTF_8))
+                .findFirst()
+                .orElseThrow(() -> new AssertionError("Generated Child.java 
not found among " +
+                        
generatedFiles.stream().map(GeneratedFile::relativePath).collect(Collectors.toList())));
+
+        // The inherited fields (name, age) are not declared on Child, so they 
only appear in Child.java
+        // via the generated full-arg constructor that forwards them to 
super(...). Their presence is the
+        // signal that the cross-package supertype was resolved on this 
codegen path.
+        assertThat(childSource).contains("age");
+        assertThat(childSource).contains("super(");
+    }
+
     private void assertHasLegacyApiFiles(Collection<GeneratedFile> 
generatedFiles) {
         assertThat(generatedFiles.stream()).anyMatch(f -> 
f.relativePath().endsWith("/ProjectModel.java"));
         assertThat(generatedFiles.stream()).anyMatch(f -> 
f.relativePath().endsWith("/ProjectRuntime.java"));
diff --git 
a/drools-model/drools-model-codegen/src/test/resources/org/drools/model/project/codegen/crosspkg/child.drl
 
b/drools-model/drools-model-codegen/src/test/resources/org/drools/model/project/codegen/crosspkg/child.drl
new file mode 100644
index 00000000000..50cb26542d0
--- /dev/null
+++ 
b/drools-model/drools-model-codegen/src/test/resources/org/drools/model/project/codegen/crosspkg/child.drl
@@ -0,0 +1,31 @@
+/**
+ * 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 com.test.childpkg
+
+import com.test.parentpkg.Parent
+
+declare Child extends Parent
+    dept : String
+end
+
+rule ChildRule
+when
+    Child( name == "Mario", age == 40, dept == "Sales" )
+then
+end
diff --git 
a/drools-model/drools-model-codegen/src/test/resources/org/drools/model/project/codegen/crosspkg/parent.drl
 
b/drools-model/drools-model-codegen/src/test/resources/org/drools/model/project/codegen/crosspkg/parent.drl
new file mode 100644
index 00000000000..b19dc71554d
--- /dev/null
+++ 
b/drools-model/drools-model-codegen/src/test/resources/org/drools/model/project/codegen/crosspkg/parent.drl
@@ -0,0 +1,24 @@
+/**
+ * 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 com.test.parentpkg
+
+declare Parent
+    name : String
+    age : int
+end


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

Reply via email to