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 4d8ac18a7e0 [incubator-kie-issues#6737] Allow declared enums to 
reference other d… (#6738)
4d8ac18a7e0 is described below

commit 4d8ac18a7e0ed6d42687a1f51fe4f126d5cb435f
Author: Daniel Clark <[email protected]>
AuthorDate: Mon Jun 29 06:15:43 2026 +0200

    [incubator-kie-issues#6737] Allow declared enums to reference other d… 
(#6738)
    
    * [incubator-kie-drools-6737] Allow declared enums to reference other 
declared enums
    
    Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
    
    * fast fail on non parseable enum declaration
    
    ---------
    
    Co-authored-by: Claude Opus 4.7 (1M context) <[email protected]>
---
 .../generator/declaredtype/EnumGenerator.java      |  21 ++-
 .../model/codegen/execmodel/DeclaredTypesTest.java |  61 +++++++++
 .../drools/mvel/asm/DefaultBeanClassBuilder.java   |  47 ++++---
 .../drools/mvel/asm/DefaultEnumClassBuilder.java   |  12 +-
 .../org/drools/mvel/asm/EnumLiteralEmitter.java    | 146 +++++++++++++++++++++
 .../org/drools/mvel/integrationtests/EnumTest.java |  32 +++++
 .../drools/mvel/integrationtests/test_Enums.drl    |  42 +++---
 7 files changed, 321 insertions(+), 40 deletions(-)

diff --git 
a/drools-model/drools-model-codegen/src/main/java/org/drools/model/codegen/execmodel/generator/declaredtype/EnumGenerator.java
 
b/drools-model/drools-model-codegen/src/main/java/org/drools/model/codegen/execmodel/generator/declaredtype/EnumGenerator.java
index c5965e8d28e..0cf2404ca95 100644
--- 
a/drools-model/drools-model-codegen/src/main/java/org/drools/model/codegen/execmodel/generator/declaredtype/EnumGenerator.java
+++ 
b/drools-model/drools-model-codegen/src/main/java/org/drools/model/codegen/execmodel/generator/declaredtype/EnumGenerator.java
@@ -24,6 +24,8 @@ import java.util.List;
 import java.util.Map;
 import java.util.stream.Collectors;
 
+import com.github.javaparser.ParseProblemException;
+import com.github.javaparser.StaticJavaParser;
 import com.github.javaparser.ast.Modifier;
 import com.github.javaparser.ast.NodeList;
 import com.github.javaparser.ast.body.EnumConstantDeclaration;
@@ -31,6 +33,7 @@ import com.github.javaparser.ast.body.EnumDeclaration;
 import com.github.javaparser.ast.body.FieldDeclaration;
 import com.github.javaparser.ast.body.MethodDeclaration;
 import com.github.javaparser.ast.body.TypeDeclaration;
+import com.github.javaparser.ast.expr.Expression;
 import com.github.javaparser.ast.expr.NameExpr;
 import com.github.javaparser.ast.stmt.BlockStmt;
 import com.github.javaparser.ast.stmt.ReturnStmt;
@@ -104,8 +107,24 @@ public class EnumGenerator {
     private void addEnumerationValue(EnumLiteralDescr enumLiteral) {
         EnumConstantDeclaration element = new 
EnumConstantDeclaration(enumLiteral.getName());
         for (String constructorArgument : enumLiteral.getConstructorArgs()) {
-            element.addArgument(new NameExpr(constructorArgument));
+            element.addArgument(parseConstructorArgument(enumLiteral, 
constructorArgument));
         }
         enumDeclaration.addEntry(element);
     }
+
+    /**
+     * Parses an enum-constant constructor argument as a Java expression.
+     */
+    private Expression parseConstructorArgument(EnumLiteralDescr enumLiteral,
+                                                String constructorArgument) {
+        try {
+            return StaticJavaParser.parseExpression(constructorArgument);
+        } catch (ParseProblemException e) {
+            throw new IllegalStateException(
+                "Failed to parse constructor argument '" + constructorArgument 
+
+                    "' for enum literal '" + enumLiteral.getName() +
+                    "': " + e.getMessage(),
+                e);
+        }
+    }
 }
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 3a1587b4a91..353caceb53f 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
@@ -515,6 +515,67 @@ public class DeclaredTypesTest extends BaseModelTest {
         assertThat(p.getAge()).isEqualTo(11);
     }
 
+    @ParameterizedTest
+    @MethodSource("parameters")
+    public void testEnumWithMisspelledConstantFailsAtCompileTime(RUN_TYPE 
runType) {
+        // Diagnostic experience: a typo in a cross-enum reference must fail at
+        // compile time (with KieBuilder error messages), not at runtime via a
+        // mysterious ExceptionInInitializerError on first enum access.
+        String str =
+                "package org.example;\n" +
+                "declare enum Color\n" +
+                "    RED, GREEN, BLUE;\n" +
+                "end\n" +
+                "declare enum Light\n" +
+                "    STOP( Color.RED ),\n" +
+                "    GO(   Color.GRENE );\n" +  // typo
+                "    color: Color\n" +
+                "end\n";
+
+        Results results = createKieBuilder(runType, str).getResults();
+        List<Message> errors = results.getMessages(Message.Level.ERROR);
+        assertThat(errors).as("expected a compile error naming the bad 
constant").isNotEmpty();
+        assertThat(errors.toString()).contains("GRENE");
+    }
+
+    @ParameterizedTest
+    @MethodSource("parameters")
+    public void testEnumWithDeclaredEnumField(RUN_TYPE runType) {
+        String str =
+                "package org.example;\n" +
+                "import " + Result.class.getCanonicalName() + ";\n" +
+                "\n" +
+                "declare enum Category\n" +
+                "    TARGET(\"target\"),\n" +
+                "    NON_TARGET(\"non-target\"),\n" +
+                "    NEW_LESION(\"new\");\n" +
+                "\n" +
+                "    code: String\n" +
+                "end\n" +
+                "\n" +
+                "declare enum LesionState\n" +
+                "    TARGET_PRESENT(    org.example.Category.TARGET, 
\"present\" ),\n" +    // fully-qualified
+                "    NON_TARGET_ABSENT( Category.NON_TARGET,          
\"absent\"  ),\n" +   // simple (same package)
+                "    NEW_PRESENT(       Category.NEW_LESION,          
\"present\" );\n" +   // simple (same package)
+                "\n" +
+                "    category: org.example.Category\n" +
+                "    nominal: String\n" +
+                "end\n" +
+                "\n" +
+                "rule \"emit\"\n" +
+                "    when\n" +
+                "    then\n" +
+                "        insert(new 
Result(LesionState.TARGET_PRESENT.getCategory().getCode()));\n" +
+                "        insert(new 
Result(LesionState.NON_TARGET_ABSENT.getCategory() == Category.NON_TARGET));\n" 
+
+                "end\n";
+
+        KieSession ksession = getKieSession(runType, str);
+        ksession.fireAllRules();
+
+        Collection<Result> results = getObjectsIntoList(ksession, 
Result.class);
+        assertThat(results).extracting(Result::getValue).contains("target", 
true);
+    }
+
     @ParameterizedTest
        @MethodSource("parameters")
     public void testDeclaredSlidingWindowOnEventInTypeDeclaration(RUN_TYPE 
runType) throws Exception {
diff --git 
a/drools-mvel/src/main/java/org/drools/mvel/asm/DefaultBeanClassBuilder.java 
b/drools-mvel/src/main/java/org/drools/mvel/asm/DefaultBeanClassBuilder.java
index e7cda9ce63e..998dd75ac2a 100755
--- a/drools-mvel/src/main/java/org/drools/mvel/asm/DefaultBeanClassBuilder.java
+++ b/drools-mvel/src/main/java/org/drools/mvel/asm/DefaultBeanClassBuilder.java
@@ -110,7 +110,7 @@ public class DefaultBeanClassBuilder implements Opcodes, 
BeanClassBuilder, Seria
             buildFieldTMS( cw, classDef );
         }
 
-        buildConstructors( cw, classDef );
+        buildConstructors( cw, classDef, classLoader );
         buildGettersAndSetters( cw, classDef );
         buildEqualityMethods( cw, classDef );
         buildToString( cw, classDef );
@@ -453,11 +453,12 @@ public class DefaultBeanClassBuilder implements Opcodes, 
BeanClassBuilder, Seria
     }
 
 
-    protected void buildConstructors(ClassWriter cw, ClassDefinition classDef) 
{
+    protected void buildConstructors(ClassWriter cw, ClassDefinition classDef, 
ClassLoader classLoader) {
         // Building default constructor
         try {
             this.buildDefaultConstructor( cw,
-                                          classDef );
+                                          classDef,
+                                          classLoader );
         } catch (Exception e) {
             LOG.error("Exception", e);
         }
@@ -466,7 +467,8 @@ public class DefaultBeanClassBuilder implements Opcodes, 
BeanClassBuilder, Seria
         if (classDef.getFieldsDefinitions().size() > 0 && 
classDef.getFieldsDefinitions().size() < 120) {
             this.buildConstructorWithFields( cw,
                                              classDef,
-                                             classDef.getFieldsDefinitions() );
+                                             classDef.getFieldsDefinitions(),
+                                             classLoader );
         }
 
         // Building constructor with key fields only
@@ -479,7 +481,8 @@ public class DefaultBeanClassBuilder implements Opcodes, 
BeanClassBuilder, Seria
         if ( !keys.isEmpty() && keys.size() != 
classDef.getFieldsDefinitions().size() ) {
             this.buildConstructorWithFields( cw,
                                              classDef,
-                                             keys );
+                                             keys,
+                                             classLoader );
         }
     }
 
@@ -925,7 +928,8 @@ public class DefaultBeanClassBuilder implements Opcodes, 
BeanClassBuilder, Seria
      * @param cw
      */
     protected void buildDefaultConstructor(ClassVisitor cw,
-                                           ClassDefinition classDef) {
+                                           ClassDefinition classDef,
+                                           ClassLoader classLoader) {
 
 
         MethodVisitor mv = cw.visitMethod( Opcodes.ACC_PUBLIC,
@@ -942,7 +946,7 @@ public class DefaultBeanClassBuilder implements Opcodes, 
BeanClassBuilder, Seria
             mv.visitLabel( l0 );
         }
 
-        boolean hasObjects = defaultConstructorStart( mv, classDef );
+        boolean hasObjects = defaultConstructorStart( mv, classDef, 
classLoader );
 
         mv.visitInsn(Opcodes.RETURN);
         Label l1;
@@ -962,7 +966,7 @@ public class DefaultBeanClassBuilder implements Opcodes, 
BeanClassBuilder, Seria
     }
 
 
-    protected boolean defaultConstructorStart( MethodVisitor mv, 
ClassDefinition classDef ) {
+    protected boolean defaultConstructorStart( MethodVisitor mv, 
ClassDefinition classDef, ClassLoader classLoader ) {
         // Building default constructor
 
         mv.visitVarInsn( Opcodes.ALOAD,
@@ -983,7 +987,7 @@ public class DefaultBeanClassBuilder implements Opcodes, 
BeanClassBuilder, Seria
         boolean hasObjects = false;
         for (FieldDefinition field : classDef.getFieldsDefinitions()) {
 
-            hasObjects = hasObjects || initFieldWithDefaultValue( mv, 
classDef, field );
+            hasObjects = hasObjects || initFieldWithDefaultValue( mv, 
classDef, field, classLoader );
         }
 
 
@@ -995,7 +999,7 @@ public class DefaultBeanClassBuilder implements Opcodes, 
BeanClassBuilder, Seria
     }
 
 
-    protected boolean initFieldWithDefaultValue( MethodVisitor mv, 
ClassDefinition classDef, FieldDefinition field ) {
+    protected boolean initFieldWithDefaultValue( MethodVisitor mv, 
ClassDefinition classDef, FieldDefinition field, ClassLoader classLoader ) {
         if ( field.getInitExpr() == null && field.isInherited() ) {
             return false;
         }
@@ -1030,12 +1034,14 @@ public class DefaultBeanClassBuilder implements 
Opcodes, BeanClassBuilder, Seria
             // there's a complex init expression
             if ( field.getInitExpr() != null ) {
                 mv.visitVarInsn( ALOAD, 0 );
-                mv.visitLdcInsn( field.getInitExpr() );
-                mv.visitMethodInsn( INVOKESTATIC,
-                                    "org/mvel2/MVEL",
-                                    "eval",
-                                    "(Ljava/lang/String;)Ljava/lang/Object;");
-                mv.visitTypeInsn( CHECKCAST, BuildUtils.getInternalType( 
field.getTypeName() ) );
+                if ( !EnumLiteralEmitter.tryEmit( mv, field.getInitExpr(), 
field.getTypeName(), classLoader ) ) {
+                    mv.visitLdcInsn( field.getInitExpr() );
+                    mv.visitMethodInsn( INVOKESTATIC,
+                                        "org/mvel2/MVEL",
+                                        "eval",
+                                        
"(Ljava/lang/String;)Ljava/lang/Object;");
+                    mv.visitTypeInsn( CHECKCAST, BuildUtils.getInternalType( 
field.getTypeName() ) );
+                }
                 val = field.getInitExpr();
             }
         }
@@ -1120,7 +1126,8 @@ public class DefaultBeanClassBuilder implements Opcodes, 
BeanClassBuilder, Seria
      */
     protected void buildConstructorWithFields(ClassVisitor cw,
                                               ClassDefinition classDef,
-                                              Collection<FieldDefinition> 
fieldDefs) {
+                                              Collection<FieldDefinition> 
fieldDefs,
+                                              ClassLoader classLoader) {
 
 
         Type[] params = new Type[fieldDefs.size()];
@@ -1142,7 +1149,7 @@ public class DefaultBeanClassBuilder implements Opcodes, 
BeanClassBuilder, Seria
             mv.visitLabel( l0 );
         }
 
-        fieldConstructorStart( mv, classDef, fieldDefs );
+        fieldConstructorStart( mv, classDef, fieldDefs, classLoader );
 
         mv.visitInsn( Opcodes.RETURN );
         Label l1;
@@ -1172,7 +1179,7 @@ public class DefaultBeanClassBuilder implements Opcodes, 
BeanClassBuilder, Seria
 
     }
 
-    protected void fieldConstructorStart(MethodVisitor mv, ClassDefinition 
classDef, Collection<FieldDefinition> fieldDefs) {
+    protected void fieldConstructorStart(MethodVisitor mv, ClassDefinition 
classDef, Collection<FieldDefinition> fieldDefs, ClassLoader classLoader) {
         mv.visitVarInsn( Opcodes.ALOAD,
                          0 );
 
@@ -1224,7 +1231,7 @@ public class DefaultBeanClassBuilder implements Opcodes, 
BeanClassBuilder, Seria
         for ( FieldDefinition field : classDef.getFieldsDefinitions() ) {
             if ( ! fieldDefs.contains( field ) && field.getInitExpr() != null 
&& ! "".equals( field.getInitExpr().trim() ) ) {
 
-                initFieldWithDefaultValue( mv, classDef, field );
+                initFieldWithDefaultValue( mv, classDef, field, classLoader );
 
             }
         }
diff --git 
a/drools-mvel/src/main/java/org/drools/mvel/asm/DefaultEnumClassBuilder.java 
b/drools-mvel/src/main/java/org/drools/mvel/asm/DefaultEnumClassBuilder.java
index 3695be04d05..57cca3aa86d 100755
--- a/drools-mvel/src/main/java/org/drools/mvel/asm/DefaultEnumClassBuilder.java
+++ b/drools-mvel/src/main/java/org/drools/mvel/asm/DefaultEnumClassBuilder.java
@@ -75,7 +75,8 @@ public class DefaultEnumClassBuilder implements Opcodes, 
EnumClassBuilder, Seria
 
 
         this.buildConstructors( cw,
-                                edef );
+                                edef,
+                                classLoader );
 
         this.buildGettersAndSetters( cw,
                                      edef );
@@ -140,7 +141,7 @@ public class DefaultEnumClassBuilder implements Opcodes, 
EnumClassBuilder, Seria
     }
 
 
-    protected void buildConstructors(ClassWriter cw, EnumClassDefinition 
classDef) {
+    protected void buildConstructors(ClassWriter cw, EnumClassDefinition 
classDef, ClassLoader classLoader) {
         MethodVisitor mv;
         final StringBuilder argTypesBuilder = new StringBuilder();
         int size = 0;
@@ -205,8 +206,13 @@ public class DefaultEnumClassBuilder implements Opcodes, 
EnumClassBuilder, Seria
                 List<String> args = lit.getConstructorArgs();
                 for ( int k = 0; k < args.size(); k++ ) {
                     String argType = classDef.getField( k ).getTypeName();
+                    String argValue = args.get( k );
 
-                    mv.visitLdcInsn( args.get( k ) );
+                    if ( EnumLiteralEmitter.tryEmit( mv, argValue, argType, 
classLoader ) ) {
+                        continue;
+                    }
+
+                    mv.visitLdcInsn( argValue );
                     mv.visitMethodInsn( INVOKESTATIC,
                                         "org/mvel2/MVEL",
                                         "eval",
diff --git 
a/drools-mvel/src/main/java/org/drools/mvel/asm/EnumLiteralEmitter.java 
b/drools-mvel/src/main/java/org/drools/mvel/asm/EnumLiteralEmitter.java
new file mode 100644
index 00000000000..5d2a318c9bc
--- /dev/null
+++ b/drools-mvel/src/main/java/org/drools/mvel/asm/EnumLiteralEmitter.java
@@ -0,0 +1,146 @@
+/*
+ * 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.mvel.asm;
+
+import java.lang.reflect.Field;
+
+import org.drools.compiler.builder.impl.classbuilder.BuildUtils;
+import org.mvel2.asm.MethodVisitor;
+import org.mvel2.asm.Opcodes;
+
+/**
+ * Compile-time detection and ASM emission of fully-qualified enum-constant
+ * references in declared-type field initializers and declared-enum constructor
+ * arguments.
+ */
+final class EnumLiteralEmitter {
+
+    private EnumLiteralEmitter() { }
+
+    /**
+     * If {@code expression} is a reference to an enum constant whose declaring
+     * class equals {@code expectedTypeName}, emit a direct {@code GETSTATIC}
+     * on {@code mv} and return {@code true}. Both fully-qualified
+     * ({@code "com.example.Category.TARGET"}) and simple
+     * ({@code "Category.TARGET"}) forms are accepted; the simple form is
+     * unambiguous because {@code expectedTypeName} has already been resolved
+     * by the type resolver against the DRL's imports and same-package types.
+     * Returns {@code false} for primitive targets, references that don't
+     * match the expected type's name, or anything that doesn't resolve to an
+     * enum constant via {@code classLoader}.
+     */
+    static boolean tryEmit(MethodVisitor mv,
+                           String expression,
+                           String expectedTypeName,
+                           ClassLoader classLoader) {
+        if (expression == null || expectedTypeName == null) {
+            return false;
+        }
+        if (BuildUtils.isPrimitive(expectedTypeName) || 
expectedTypeName.indexOf('.') < 0) {
+            return false;
+        }
+        String trimmed = expression.trim();
+        String constantName = stripExpectedTypePrefix(trimmed, 
expectedTypeName);
+        if (constantName == null || constantName.isEmpty() || 
!isJavaIdentifier(constantName)) {
+            return false;
+        }
+        Class<?> argClass;
+        try {
+            argClass = Class.forName(expectedTypeName, false, classLoader);
+        } catch (ClassNotFoundException | LinkageError e) {
+            return false;
+        }
+        if (!argClass.isEnum()) {
+            return false;
+        }
+        // From this point on, the user has clearly written the argument as an
+        // enum-constant reference of the expected enum type. Any failure is a
+        // user-facing error: surface it at compile time instead of letting
+        // MVEL.eval fail mysteriously at class load.
+        Field field;
+        try {
+            field = argClass.getField(constantName);
+        } catch (NoSuchFieldException e) {
+            throw new IllegalStateException(
+                "Enum constant '" + constantName + "' does not exist on '" + 
expectedTypeName
+                    + "'. Available constants: " + 
listEnumConstantNames(argClass));
+        }
+        if (!field.isEnumConstant()) {
+            throw new IllegalStateException(
+                "Field '" + constantName + "' on '" + expectedTypeName
+                    + "' is not an enum constant.");
+        }
+        mv.visitFieldInsn(Opcodes.GETSTATIC,
+                          BuildUtils.getInternalType(expectedTypeName),
+                          constantName,
+                          BuildUtils.getTypeDescriptor(expectedTypeName));
+        return true;
+    }
+
+    private static String listEnumConstantNames(Class<?> enumClass) {
+        Object[] constants = enumClass.getEnumConstants();
+        if (constants == null) {
+            return "[]";
+        }
+        StringBuilder sb = new StringBuilder("[");
+        for (int i = 0; i < constants.length; i++) {
+            if (i > 0) {
+                sb.append(", ");
+            }
+            sb.append(((Enum<?>) constants[i]).name());
+        }
+        return sb.append("]").toString();
+    }
+
+    /**
+     * Returns the constant name suffix if {@code expression} starts with 
either
+     * the FQN of {@code expectedTypeName} followed by a dot, or with the
+     * expected type's simple name followed by a dot. Returns {@code null}
+     * otherwise. The FQN form is tried first so a fully-qualified reference
+     * always wins when the simple name happens to be a prefix of something
+     * else.
+     */
+    private static String stripExpectedTypePrefix(String expression, String 
expectedTypeName) {
+        String fqnPrefix = expectedTypeName + ".";
+        if (expression.startsWith(fqnPrefix)) {
+            return expression.substring(fqnPrefix.length());
+        }
+        int lastDot = expectedTypeName.lastIndexOf('.');
+        if (lastDot < 0) {
+            return null;
+        }
+        String simplePrefix = expectedTypeName.substring(lastDot + 1) + ".";
+        if (expression.startsWith(simplePrefix)) {
+            return expression.substring(simplePrefix.length());
+        }
+        return null;
+    }
+
+    private static boolean isJavaIdentifier(String s) {
+        if (s.isEmpty() || !Character.isJavaIdentifierStart(s.charAt(0))) {
+            return false;
+        }
+        for (int i = 1; i < s.length(); i++) {
+            if (!Character.isJavaIdentifierPart(s.charAt(i))) {
+                return false;
+            }
+        }
+        return true;
+    }
+}
diff --git 
a/drools-test-coverage/test-compiler-integration/src/test/java/org/drools/mvel/integrationtests/EnumTest.java
 
b/drools-test-coverage/test-compiler-integration/src/test/java/org/drools/mvel/integrationtests/EnumTest.java
index 9d0e288c512..959cc00b58e 100644
--- 
a/drools-test-coverage/test-compiler-integration/src/test/java/org/drools/mvel/integrationtests/EnumTest.java
+++ 
b/drools-test-coverage/test-compiler-integration/src/test/java/org/drools/mvel/integrationtests/EnumTest.java
@@ -60,10 +60,42 @@ public class EnumTest {
         assertThat(list.contains(4)).isTrue();
         assertThat(list.contains(5.976e+24)).isTrue();
         assertThat(list.contains("Mercury")).isTrue();
+        assertThat(list.contains("TUESDAY")).isTrue();
+        assertThat(list.contains(true)).isTrue();
+        assertThat(list.contains("MONDAY")).isTrue();
 
         ksession.dispose();
     }
 
+    @ParameterizedTest(name = "KieBase type={0}")
+    @MethodSource("parameters")
+    public void 
testCrossEnumReferenceWithMisspelledConstant(KieBaseTestConfiguration 
kieBaseTestConfiguration) {
+        // Regression test for the diagnostic experience around the
+        // declared-enum cross-reference feature: a misspelled constant name
+        // must fail at compile time with a message that names the bad
+        // constant, not at runtime via a buried ExceptionInInitializerError
+        // on first enum access. The legacy ASM path additionally enriches the
+        // message with the list of available constants; the executable-model
+        // path relies on the underlying Java compiler (ECJ), whose default
+        // "X cannot be resolved or is not a field" wording satisfies the
+        // shared contract.
+        final String drl =
+                "package org.test;\n" +
+                "declare enum Color\n" +
+                "    RED, GREEN, BLUE;\n" +
+                "end\n" +
+                "declare enum Light\n" +
+                "    STOP( Color.RED ),\n" +
+                "    GO(   Color.GRENE );\n" +  // typo: GRENE → GREEN
+                "    color: Color\n" +
+                "end\n";
+
+        KieBuilder kieBuilder = 
KieUtil.getKieBuilderFromDrls(kieBaseTestConfiguration, false, drl);
+        List<Message> errors = 
kieBuilder.getResults().getMessages(Message.Level.ERROR);
+        assertThat(errors).as("expected a compile error naming the bad 
constant").isNotEmpty();
+        assertThat(errors.toString()).contains("GRENE");
+    }
+
     @ParameterizedTest(name = "KieBase type={0}")
     @MethodSource("parameters")
     public void testEnumsWithCompositeBuildingProcess(KieBaseTestConfiguration 
kieBaseTestConfiguration) throws Exception {
diff --git 
a/drools-test-coverage/test-compiler-integration/src/test/resources/org/drools/mvel/integrationtests/test_Enums.drl
 
b/drools-test-coverage/test-compiler-integration/src/test/resources/org/drools/mvel/integrationtests/test_Enums.drl
index 8ccacfc764e..29c082956ff 100644
--- 
a/drools-test-coverage/test-compiler-integration/src/test/resources/org/drools/mvel/integrationtests/test_Enums.drl
+++ 
b/drools-test-coverage/test-compiler-integration/src/test/resources/org/drools/mvel/integrationtests/test_Enums.drl
@@ -26,14 +26,14 @@ declare enum DaysOfWeek
 end
 
 declare enum Planets
-    MERCURY ( 3.303e+23, 2.4397e6, new String("Mer") + new 
String("cury").substring(0) ),
-    VENUS   ( 4.869e+24, 6.0518e6, "Venus" ),
+    MERCURY ( 3.303e+23, 2.4397e6,  new String("Mer") + new 
String("cury").substring(0) ),
+    VENUS   ( 4.869e+24, 6.0518e6,  "Venus" ),
     EARTH   ( 5.976e+24, 6.37814e6, "Earth" ),
-    MARS    ( 6.421e+23, 3.3972e6, "Mars" ),
-    JUPITER ( 1.9e+27,   7.1492e7, "Jupiter" ),
-    SATURN  ( 5.688e+26, 6.0268e7, "Saturn" ),
-    URANUS  ( 8.686e+25, 2.5559e7, "Uranus" ),
-    NEPTUNE ( 1.024e+26, 2.4746e7, "Neptune" );
+    MARS    ( 6.421e+23, 3.3972e6,  "Mars" ),
+    JUPITER ( 1.9e+27,   7.1492e7,  "Jupiter" ),
+    SATURN  ( 5.688e+26, 6.0268e7,  "Saturn" ),
+    URANUS  ( 8.686e+25, 2.5559e7,  "Uranus" ),
+    NEPTUNE ( 1.024e+26, 2.4746e7,  "Neptune" );
 
     mass    : double
     radius  : double
@@ -43,15 +43,11 @@ end
 
 
 declare enum WorkLoad
-//    LIGHT( x.DaysOfWeek.TUESDAY, 4 ),
-//    MEDIUM( x.DaysOfWeek.MONDAY, 8 ),
-//    HEAVY( x.DaysOfWeek.SUNDAY, 12 );
+    LIGHT(  x.DaysOfWeek.TUESDAY, 4  ),     // fully-qualified
+    MEDIUM( DaysOfWeek.MONDAY,    8  ),     // simple name (in scope: same 
package)
+    HEAVY(  DaysOfWeek.SUNDAY,    12 );     // simple name
 
-    LIGHT( 4 ),
-    MEDIUM( 8 ),
-    HEAVY( 12 );
-
-//    day     : DaysOfWeek
+    day     : DaysOfWeek
     hours   : int
 end
 
@@ -64,7 +60,7 @@ end
 declare Test
     load    : WorkLoad
     planet  : Planets
-    day     : DaysOfWeek    //= DaysOfWeek.MONDAY
+    day     : DaysOfWeek    = x.DaysOfWeek.MONDAY
     num     : int           = 111
     pers    : Person        //= new Person("xyz",18)
 end
@@ -90,4 +86,18 @@ when
     Test( WorkLoad.LIGHT, Planets.MARS, DaysOfWeek.MONDAY ; $h : load.hours )
 then
     list.add( $h );
+end
+
+rule "Test4_CrossEnumField"
+when
+then
+    list.add( WorkLoad.LIGHT.getDay().name() );
+    list.add( WorkLoad.HEAVY.getDay() == DaysOfWeek.SUNDAY );
+end
+
+rule "Test5_DeclaredTypeEnumDefault"
+when
+then
+    Test t = new Test();
+    list.add( t.getDay().name() );
 end
\ No newline at end of file


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

Reply via email to