Check rc:yang-data template name duplications 59/108259/3
authorRobert Varga <robert.varga@pantheon.tech>
Thu, 5 Oct 2023 16:54:16 +0000 (18:54 +0200)
committerRobert Varga <nite@hq.sk>
Thu, 5 Oct 2023 17:38:36 +0000 (17:38 +0000)
yang-data template names have a global scope, similar to schema tree
namespace. Enforce non-overlap of template names in a particular
reactor.

JIRA: YANGTOOLS-1482
Change-Id: I2da9188d5a95baa591b8ceef585a3e84fa18f531
Signed-off-by: Robert Varga <robert.varga@pantheon.tech>
parser/rfc8040-parser-support/src/main/java/org/opendaylight/yangtools/rfc8040/parser/YangDataStatementSupport.java
parser/rfc8040-parser-support/src/test/java/org/opendaylight/yangtools/rfc8040/parser/AbstractYangDataTest.java
parser/rfc8040-parser-support/src/test/java/org/opendaylight/yangtools/rfc8040/parser/YT1482Test.java [new file with mode: 0644]
parser/rfc8040-parser-support/src/test/resources/yt1482.yang [new file with mode: 0644]
parser/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/impl/DefaultReactors.java

index 319a00ad6f13041b8879e7307836e18b63c23c1a..88e259c64029f844ccb0544ec006bdfb13e735ba 100644 (file)
@@ -13,6 +13,7 @@ import com.google.common.annotations.Beta;
 import com.google.common.annotations.VisibleForTesting;
 import com.google.common.collect.ImmutableList;
 import java.util.stream.Collectors;
+import org.eclipse.jdt.annotation.NonNull;
 import org.opendaylight.yangtools.rfc8040.model.api.YangDataConstants;
 import org.opendaylight.yangtools.rfc8040.model.api.YangDataEffectiveStatement;
 import org.opendaylight.yangtools.rfc8040.model.api.YangDataStatement;
@@ -33,6 +34,8 @@ import org.opendaylight.yangtools.yang.parser.spi.meta.BoundStmtCtx;
 import org.opendaylight.yangtools.yang.parser.spi.meta.EffectiveStmtCtx.Current;
 import org.opendaylight.yangtools.yang.parser.spi.meta.InvalidSubstatementException;
 import org.opendaylight.yangtools.yang.parser.spi.meta.MissingSubstatementException;
+import org.opendaylight.yangtools.yang.parser.spi.meta.NamespaceBehaviour;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ParserNamespace;
 import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
 import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext.Mutable;
 import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContextUtils;
@@ -42,6 +45,13 @@ import org.opendaylight.yangtools.yang.parser.spi.source.SourceException;
 @Beta
 public final class YangDataStatementSupport
         extends AbstractStatementSupport<YangDataName, YangDataStatement, YangDataEffectiveStatement> {
+    private static final @NonNull ParserNamespace<YangDataName,
+        StmtContext<YangDataName, YangDataStatement, YangDataEffectiveStatement>> NAMESPACE =
+        new ParserNamespace<>("yang-data");
+    public static final @NonNull NamespaceBehaviour<YangDataName,
+        StmtContext<YangDataName, YangDataStatement, YangDataEffectiveStatement>> BEHAVIOUR =
+        NamespaceBehaviour.global(NAMESPACE);
+
     // As per RFC8040 page 81:
     //
     //    The substatements of this extension MUST follow the
@@ -88,12 +98,23 @@ public final class YangDataStatementSupport
     }
 
     @Override
-    public void onStatementAdded(final Mutable<YangDataName, YangDataStatement, YangDataEffectiveStatement> ctx) {
+    public void onStatementAdded(final Mutable<YangDataName, YangDataStatement, YangDataEffectiveStatement> stmt) {
         // as per https://www.rfc-editor.org/rfc/rfc8040#section-8,
         // yang-data is ignored unless it appears as a top-level statement
-        if (ctx.coerceParentContext().getParentContext() != null) {
-            ctx.setUnsupported();
+        final var parent = stmt.coerceParentContext();
+        if (parent.getParentContext() != null) {
+            stmt.setUnsupported();
+            return;
+        }
+
+        final var name = stmt.argument();
+        final var prev = parent.namespaceItem(NAMESPACE, name);
+        if (prev != null) {
+            throw new SourceException(stmt,
+                "Error in module '%s': cannot add '%s'. Node name collision: '%s' already declared at %s",
+                stmt.getRoot().rawArgument(), name, prev.argument(), prev.sourceReference());
         }
+        parent.addToNs(NAMESPACE, stmt.argument(), stmt);
     }
 
     @Override
index 420625ae2210ec86a3da6512dc96336070a0cb77..705dd41855122ef7cca1eefc1af73a5b67e63924 100644 (file)
@@ -30,6 +30,7 @@ abstract class AbstractYangDataTest {
         REACTOR = RFC7950Reactors.vanillaReactorBuilder()
                 .addStatementSupport(ModelProcessingPhase.FULL_DECLARATION,
                     new YangDataStatementSupport(YangParserConfiguration.DEFAULT))
+                .addNamespaceSupport(ModelProcessingPhase.FULL_DECLARATION, YangDataStatementSupport.BEHAVIOUR)
                 .build();
     }
 
diff --git a/parser/rfc8040-parser-support/src/test/java/org/opendaylight/yangtools/rfc8040/parser/YT1482Test.java b/parser/rfc8040-parser-support/src/test/java/org/opendaylight/yangtools/rfc8040/parser/YT1482Test.java
new file mode 100644 (file)
index 0000000..912acb1
--- /dev/null
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2022 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.yangtools.rfc8040.parser;
+
+import static org.hamcrest.CoreMatchers.startsWith;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.jupiter.api.Assertions.assertInstanceOf;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+import org.junit.jupiter.api.Test;
+import org.opendaylight.yangtools.yang.parser.spi.meta.SomeModifiersUnresolvedException;
+import org.opendaylight.yangtools.yang.parser.spi.source.SourceException;
+
+class YT1482Test extends AbstractYangDataTest {
+    @Test
+    void duplicateNamesAreRejected() throws Exception {
+        final var action = REACTOR.newBuild().addSources(IETF_RESTCONF_MODULE, sourceForResource("/yt1482.yang"));
+
+        final var ex = assertThrows(SomeModifiersUnresolvedException.class, action::buildEffective);
+        final var cause = assertInstanceOf(SourceException.class, ex.getCause());
+        assertThat(cause.getMessage(), startsWith("""
+            Error in module 'yt1482': cannot add 'YangDataName[module=QNameModule{ns=yt1482}, name=some]'. Node name \
+            collision: 'YangDataName[module=QNameModule{ns=yt1482}, name=some]' already declared at """));
+    }
+}
diff --git a/parser/rfc8040-parser-support/src/test/resources/yt1482.yang b/parser/rfc8040-parser-support/src/test/resources/yt1482.yang
new file mode 100644 (file)
index 0000000..5137515
--- /dev/null
@@ -0,0 +1,14 @@
+module yt1482 {
+  namespace yt1482;
+  prefix yt1482;
+
+  import ietf-restconf { prefix rc; }
+
+  rc:yang-data some {
+    container foo;
+  }
+
+  rc:yang-data some {
+    container bar;
+  }
+}
index d4c68034197ac7c367f6e0c32db43f5dcc27840c..cf2032b723298b01ee2c8419daee607c5c4511be 100644 (file)
@@ -150,6 +150,7 @@ public final class DefaultReactors {
 
             // RFC8040 yang-data support
             .addStatementSupport(ModelProcessingPhase.FULL_DECLARATION, new YangDataStatementSupport(config))
+            .addNamespaceSupport(ModelProcessingPhase.FULL_DECLARATION, YangDataStatementSupport.BEHAVIOUR)
 
             // RFC8528 mount-point support
             .addStatementSupport(ModelProcessingPhase.FULL_DECLARATION, new MountPointStatementSupport(config))