Fix code generation with leafrefs pointing to optional members 75/106475/6
authorRobert Varga <robert.varga@pantheon.tech>
Tue, 13 Jun 2023 14:57:25 +0000 (16:57 +0200)
committerRobert Varga <nite@hq.sk>
Wed, 14 Jun 2023 15:04:34 +0000 (15:04 +0000)
When we have a typedef which points to a location disabled by
if-feature, we need to accept this fact -- but do not allow that typedef
to be referenced.

JIRA: MDSAL-829
Change-Id: Ibde28faa2f96904dc16fcc6f5a922edc6bcffebb
Signed-off-by: Robert Varga <robert.varga@pantheon.tech>
binding/mdsal-binding-generator/src/main/java/org/opendaylight/mdsal/binding/generator/impl/reactor/AbstractTypeObjectGenerator.java
binding/mdsal-binding-generator/src/main/java/org/opendaylight/mdsal/binding/generator/impl/reactor/TypeReference.java
binding/mdsal-binding-generator/src/test/java/org/opendaylight/mdsal/binding/generator/impl/Bug4621Test.java
binding/mdsal-binding-generator/src/test/java/org/opendaylight/mdsal/binding/generator/impl/DefaultBindingGeneratorTest.java
binding/mdsal-binding-generator/src/test/java/org/opendaylight/mdsal/binding/generator/impl/GeneratedTypesLeafrefTest.java
binding/mdsal-binding-generator/src/test/java/org/opendaylight/mdsal/binding/generator/impl/Mdsal829Test.java [new file with mode: 0644]
binding/mdsal-binding-generator/src/test/resources/mdsal829.yang [new file with mode: 0644]

index 3afca47a8ab44eb5877dcfbd09f0be696da75743..7a5849c36c7bfcc8f7b15da42e71a0ab38255e56 100644 (file)
@@ -372,11 +372,7 @@ abstract class AbstractTypeObjectGenerator<S extends EffectiveStatement<?, ?>, R
                 .map(context::resolveIdentity)
                 .collect(Collectors.toUnmodifiableList()));
         } else if (TypeDefinitions.LEAFREF.equals(arg)) {
-            final AbstractTypeObjectGenerator<?, ?> targetGenerator = context.resolveLeafref(
-                type.findFirstEffectiveSubstatementArgument(PathEffectiveStatement.class).orElseThrow());
-            checkArgument(targetGenerator != this, "Effective model contains self-referencing leaf %s",
-                statement().argument());
-            refType = TypeReference.leafRef(targetGenerator);
+            refType = resolveLeafref(context);
         } else if (TypeDefinitions.UNION.equals(arg)) {
             unionDependencies = new UnionDependencies(type, context);
             LOG.trace("Resolved union {} to dependencies {}", type, unionDependencies);
@@ -391,6 +387,20 @@ abstract class AbstractTypeObjectGenerator<S extends EffectiveStatement<?, ?>, R
         LOG.trace("Resolved derived {} to generator {}", type, refType);
     }
 
+    private @NonNull TypeReference resolveLeafref(final GeneratorContext context) {
+        final AbstractTypeObjectGenerator<?, ?> targetGenerator;
+        try {
+            targetGenerator = context.resolveLeafref(
+                type.findFirstEffectiveSubstatementArgument(PathEffectiveStatement.class).orElseThrow());
+        } catch (IllegalArgumentException e) {
+            return TypeReference.leafRef(e);
+        }
+
+        checkArgument(targetGenerator != this, "Effective model contains self-referencing leaf %s",
+            statement().argument());
+        return TypeReference.leafRef(targetGenerator);
+    }
+
     private static boolean isBuiltinName(final QName typeName) {
         return YangConstants.RFC6020_YANG_MODULE.equals(typeName.getModule());
     }
index cde69a941abc1487d43e28f969d14b3efc21fd37..bbd30ec9db8e659996746ee1e7a64e173ba75d30 100644 (file)
@@ -15,7 +15,7 @@ import org.eclipse.jdt.annotation.Nullable;
 import org.opendaylight.mdsal.binding.model.api.Type;
 import org.opendaylight.mdsal.binding.model.ri.Types;
 
-abstract class TypeReference {
+abstract sealed class TypeReference {
     private static final class Identityref extends TypeReference {
         private final List<IdentityGenerator> referencedGenerators;
 
@@ -43,7 +43,7 @@ abstract class TypeReference {
     }
 
     // Note: this is exposed only for legacy naming handling
-    abstract static class Leafref extends TypeReference {
+    abstract static sealed class Leafref extends TypeReference {
         private Leafref() {
             // Hidden on purpose
         }
@@ -75,10 +75,27 @@ abstract class TypeReference {
         }
     }
 
+    private static final class FailedLeafref extends Leafref {
+        private final IllegalArgumentException cause;
+
+        FailedLeafref(final IllegalArgumentException cause) {
+            this.cause = requireNonNull(cause);
+        }
+
+        @Override
+        Type methodReturnType(final TypeBuilderFactory builderFactory) {
+            throw new UnsupportedOperationException("Cannot ascertain type", cause);
+        }
+    }
+
     static @NonNull TypeReference leafRef(final @Nullable AbstractTypeObjectGenerator<?, ?> referencedGenerator) {
         return referencedGenerator == null ? UnresolvedLeafref.INSTANCE : new ResolvedLeafref(referencedGenerator);
     }
 
+    static @NonNull TypeReference leafRef(final @NonNull IllegalArgumentException cause) {
+        return new FailedLeafref(cause);
+    }
+
     static @NonNull TypeReference identityRef(final List<IdentityGenerator> referencedGenerators) {
         return new Identityref(referencedGenerators);
     }
index 4572f4dc9c46c40f1ad61d0970ed39c447c9c7e4..a45221113386f6cee3d5dac4645273544ab739fa 100644 (file)
@@ -7,19 +7,23 @@
  */
 package org.opendaylight.mdsal.binding.generator.impl;
 
+import static org.hamcrest.CoreMatchers.instanceOf;
+import static org.hamcrest.MatcherAssert.assertThat;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertThrows;
 
 import org.junit.Test;
-import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
 import org.opendaylight.yangtools.yang.test.util.YangParserTestUtils;
 
 public class Bug4621Test {
     @Test
     public void testMissingLeafrefTarget() {
-        final EffectiveModelContext context = YangParserTestUtils.parseYangResource("/bug4621.yang");
-        final IllegalArgumentException ex = assertThrows(IllegalArgumentException.class,
+        final var context = YangParserTestUtils.parseYangResource("/bug4621.yang");
+        final var uoe = assertThrows(UnsupportedOperationException.class,
             () -> DefaultBindingGenerator.generateFor(context));
-        assertEquals("Failed to find leafref target /foo:neighbor/foo:mystring1", ex.getMessage());
+        assertEquals("Cannot ascertain type", uoe.getMessage());
+        final var cause = uoe.getCause();
+        assertThat(cause, instanceOf(IllegalArgumentException.class));
+        assertEquals("Failed to find leafref target /foo:neighbor/foo:mystring1", cause.getMessage());
     }
 }
\ No newline at end of file
index d05186818d5515560b1feb6bb5cfae5fb4f27c89..dae49bfa429b3ef6581097be7e20df5b08fa3aa8 100644 (file)
@@ -213,7 +213,12 @@ public class DefaultBindingGeneratorTest {
     @Test
     public void javaTypeForSchemaDefinitionInvalidLeafrefPathTest() {
         final var ctx = YangParserTestUtils.parseYangResource("/unresolvable-leafref.yang");
-        final var ex = assertThrows(IllegalArgumentException.class, () -> DefaultBindingGenerator.generateFor(ctx));
+
+        final var uoe = assertThrows(UnsupportedOperationException.class,
+            () -> DefaultBindingGenerator.generateFor(ctx));
+        assertEquals("Cannot ascertain type", uoe.getMessage());
+        final var ex = uoe.getCause();
+        assertThat(ex, instanceOf(IllegalArgumentException.class));
         assertEquals("Failed to find leafref target /somewhere/i/belong", ex.getMessage());
         final var cause = ex.getCause();
         assertThat(cause, instanceOf(IllegalArgumentException.class));
index e0175d363efd38bd89fb7b0294a90ad321fe9fe6..3d9bbd01f99bcc60dc891931203753226dde13ab 100644 (file)
@@ -8,6 +8,7 @@
 package org.opendaylight.mdsal.binding.generator.impl;
 
 import static org.hamcrest.CoreMatchers.containsString;
+import static org.hamcrest.CoreMatchers.instanceOf;
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
@@ -22,7 +23,6 @@ import org.opendaylight.mdsal.binding.model.api.GeneratedType;
 import org.opendaylight.mdsal.binding.model.api.MethodSignature;
 import org.opendaylight.mdsal.binding.model.api.Type;
 import org.opendaylight.yangtools.yang.binding.contract.Naming;
-import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
 import org.opendaylight.yangtools.yang.test.util.YangParserTestUtils;
 
 public class GeneratedTypesLeafrefTest {
@@ -212,12 +212,15 @@ public class GeneratedTypesLeafrefTest {
 
     @Test
     public void testLeafrefInvalidPathResolving() {
-        final EffectiveModelContext context =  YangParserTestUtils.parseYangResource(
+        final var context =  YangParserTestUtils.parseYangResource(
             "/leafref-test-invalid-model/foo.yang");
         assertEquals(1, context.getModules().size());
 
-        IllegalArgumentException ex = assertThrows(IllegalArgumentException.class,
+        final var uoe = assertThrows(UnsupportedOperationException.class,
             () -> DefaultBindingGenerator.generateFor(context));
-        assertThat(ex.getMessage(), containsString("Failed to find leafref target"));
+        assertEquals("Cannot ascertain type", uoe.getMessage());
+        final var cause = uoe.getCause();
+        assertThat(cause, instanceOf(IllegalArgumentException.class));
+        assertThat(cause.getMessage(), containsString("Failed to find leafref target"));
     }
 }
\ No newline at end of file
diff --git a/binding/mdsal-binding-generator/src/test/java/org/opendaylight/mdsal/binding/generator/impl/Mdsal829Test.java b/binding/mdsal-binding-generator/src/test/java/org/opendaylight/mdsal/binding/generator/impl/Mdsal829Test.java
new file mode 100644 (file)
index 0000000..9c77304
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2023 PANTHEON.tech, s.r.o. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.mdsal.binding.generator.impl;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertSame;
+
+import java.util.Set;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.opendaylight.mdsal.binding.model.api.JavaTypeName;
+import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
+import org.opendaylight.yangtools.yang.test.util.YangParserTestUtils;
+
+public class Mdsal829Test {
+    private static EffectiveModelContext CONTEXT;
+
+    @BeforeClass
+    public static void beforeClass() {
+        CONTEXT = YangParserTestUtils.parseYangResource("/mdsal829.yang", Set.of());
+    }
+
+    @Test
+    public void testCompileTimeTypes() {
+        assertEquals(1, DefaultBindingGenerator.generateFor(CONTEXT).size());
+    }
+
+    @Test
+    public void testRunTimeTypes() {
+        final var types = BindingRuntimeTypesFactory.createTypes(CONTEXT);
+        assertSame(CONTEXT, types.getEffectiveModelContext());
+        final var schema = types.findSchema(
+            JavaTypeName.create("org.opendaylight.yang.gen.v1.mdsal829.norev", "Mdsal829Data")).orElseThrow();
+        assertNotNull(schema);
+    }
+}
diff --git a/binding/mdsal-binding-generator/src/test/resources/mdsal829.yang b/binding/mdsal-binding-generator/src/test/resources/mdsal829.yang
new file mode 100644 (file)
index 0000000..f883127
--- /dev/null
@@ -0,0 +1,17 @@
+module mdsal829 {
+  namespace mdsal829;
+  prefix mdsal829;
+
+  feature feat;
+
+  typedef foo {
+    type leafref {
+      path /mdsal829:bar;
+    }
+  }
+
+  leaf bar {
+    if-feature feat;
+    type string;
+  }
+}