public void test() throws Exception {
final SchemaContext schemaContext = YangParserTestUtils.parseYangResource("/schema-utils-test/foo.yang");
assertTrue(SchemaUtils.findDataParentSchemaOnPath(schemaContext,
- SchemaPath.create(true, qN("my-name"), qN("my-name"))) instanceof ContainerSchemaNode);
+ SchemaPath.create(true, qN("my-name"), qN("my-name-a"))) instanceof ContainerSchemaNode);
assertTrue(SchemaUtils.findDataParentSchemaOnPath(schemaContext,
- SchemaPath.create(true, qN("my-name-2"), qN("my-name"))) instanceof NotificationDefinition);
+ SchemaPath.create(true, qN("my-name-2"), qN("my-name-b"))) instanceof NotificationDefinition);
assertTrue(SchemaUtils.findDataParentSchemaOnPath(schemaContext,
- SchemaPath.create(true, qN("my-name-2"), qN("my-name-2"))) instanceof ActionDefinition);
+ SchemaPath.create(true, qN("my-name-2"), qN("my-name-2-b"))) instanceof ActionDefinition);
}
@Test
.parseYangResource("/schema-utils-test/name-conflicts.yang");
// test my-name conflicts
assertEquals(8, SchemaUtils.findParentSchemaNodesOnPath(schemaContext,
- SchemaPath.create(true, qN("my-name"), qN("my-name"), qN("my-name"))).size());
+ SchemaPath.create(true, qN("my-name"), qN("my-name-nested"), qN("my-name-nested2"))).size());
// test target container
final Collection<SchemaNode> target = SchemaUtils.findParentSchemaNodesOnPath(schemaContext,
- SchemaPath.create(true, qN("my-name-2"), qN("my-name-2"), qN("target")));
+ SchemaPath.create(true, qN("my-name-2"), qN("my-name-nested"), qN("target")));
assertEquals(1, target.size());
assertTrue(target.iterator().next() instanceof ContainerSchemaNode);
}
notification my-name {
- grouping my-name {
+ grouping my-name-a {
}
- container my-name {
+ container my-name-a {
}
}
container my-name-2 {
- grouping my-name {
+ grouping my-name-b {
}
- notification my-name {
+ notification my-name-b {
}
- grouping my-name-2 {
+ grouping my-name-2-b {
}
- action my-name-2 {
+ action my-name-2-b {
}
}
}
prefix nc;
grouping my-name {
- grouping my-name {
- grouping my-name {
+ grouping my-name-nested {
+ grouping my-name-nested2 {
}
- container my-name {
+ container my-name-nested2 {
}
}
- container my-name {
- grouping my-name {
+ container my-name-nested {
+ grouping my-name-nested2 {
}
- container my-name {
+ container my-name-nested2 {
}
}
}
container my-name {
- grouping my-name {
- grouping my-name {
+ grouping my-name-nested {
+ grouping my-name-nested2 {
}
- container my-name {
+ container my-name-nested2 {
}
}
- container my-name {
- grouping my-name {
+ container my-name-nested {
+ grouping my-name-nested2 {
}
- container my-name {
+ container my-name-nested2 {
}
}
}
grouping my-name-2 {
- grouping my-name-2 {
- grouping my-name-2 {
+ grouping my-name-nested {
+ grouping my-name-nested2 {
}
- container my-name-2 {
+ container my-name-nested2 {
}
}
container my-name-2 {
- grouping my-name-2 {
+ grouping my-name-nested2 {
}
- container my-name-2 {
+ container my-name-nested2 {
}
}
}
container my-name-2 {
- grouping my-name-2 {
- grouping my-name-2 {
+ grouping my-name-nested {
+ grouping my-name-nested2 {
}
container target {
}
}
- container my-name-2 {
- grouping my-name-2 {
+ container my-name-nested {
+ grouping my-name-nested2 {
}
- container my-name-2 {
+ container my-name-nested2 {
}
}
}
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;
+import org.opendaylight.yangtools.yang.parser.spi.source.SourceException;
abstract class AbstractGroupingStatementSupport
extends AbstractQNameStatementSupport<GroupingStatement, EffectiveStatement<QName, GroupingStatement>> {
@Override
public final GroupingStatement createDeclared(final StmtContext<QName, GroupingStatement, ?> ctx) {
+ // Shadowing check: make sure grandparent does not see a conflicting definition. This is required to ensure
+ // that a grouping in child scope does not shadow a grouping in parent scope which occurs later in the text.
+ final StmtContext<?, ?, ?> parent = ctx.getParentContext();
+ if (parent != null) {
+ final StmtContext<?, ?, ?> grandParent = parent.getParentContext();
+ if (grandParent != null) {
+ checkConflict(grandParent, ctx);
+ }
+ }
+
return new GroupingStatementImpl(ctx);
}
EffectiveStatement<QName, GroupingStatement>> stmt) {
super.onFullDefinitionDeclared(stmt);
- if (stmt != null && stmt.getParentContext() != null) {
- stmt.getParentContext().addContext(GroupingNamespace.class, stmt.getStatementArgument(), stmt);
+ if (stmt != null) {
+ final Mutable<?, ?, ?> parent = stmt.getParentContext();
+ if (parent != null) {
+ // Shadowing check: make sure we do not trample on pre-existing definitions. This catches sibling
+ // declarations and parent declarations which have already been declared.
+ checkConflict(parent, stmt);
+ parent.addContext(GroupingNamespace.class, stmt.getStatementArgument(), stmt);
+ }
}
}
+
+ private static void checkConflict(final StmtContext<?, ?, ?> parent, final StmtContext<QName, ?, ?> stmt) {
+ final QName arg = stmt.getStatementArgument();
+ final StmtContext<?, ?, ?> existing = parent.getFromNamespace(GroupingNamespace.class, arg);
+ SourceException.throwIf(existing != null, stmt.getStatementSourceReference(), "Duplicate name for grouping %s",
+ arg);
+ }
}
\ No newline at end of file
import org.opendaylight.yangtools.yang.common.QName;
import org.opendaylight.yangtools.yang.model.api.YangStmtMapping;
import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
-import org.opendaylight.yangtools.yang.model.api.stmt.TypedefEffectiveStatement;
import org.opendaylight.yangtools.yang.model.api.stmt.TypedefStatement;
import org.opendaylight.yangtools.yang.parser.spi.TypeNamespace;
import org.opendaylight.yangtools.yang.parser.spi.meta.AbstractQNameStatementSupport;
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;
import org.opendaylight.yangtools.yang.parser.spi.meta.SubstatementValidator;
import org.opendaylight.yangtools.yang.parser.spi.source.SourceException;
@Override
public TypedefStatement createDeclared(final StmtContext<QName, TypedefStatement, ?> ctx) {
+ // Shadowing check: make sure grandparent does not see a conflicting definition. This is required to ensure
+ // that a typedef in child scope does not shadow a typedef in parent scope which occurs later in the text.
+ final StmtContext<?, ?, ?> parent = ctx.getParentContext();
+ if (parent != null) {
+ final StmtContext<?, ?, ?> grandParent = parent.getParentContext();
+ if (grandParent != null) {
+ checkConflict(grandParent, ctx);
+ }
+ }
+
return new TypedefStatementImpl(ctx);
}
}
@Override
- public void onFullDefinitionDeclared(final StmtContext.Mutable<QName, TypedefStatement,
+ public void onFullDefinitionDeclared(final Mutable<QName, TypedefStatement,
EffectiveStatement<QName, TypedefStatement>> stmt) {
super.onFullDefinitionDeclared(stmt);
- if (stmt != null && stmt.getParentContext() != null) {
- final StmtContext<?, TypedefStatement, TypedefEffectiveStatement> existing = stmt.getParentContext()
- .getFromNamespace(TypeNamespace.class, stmt.getStatementArgument());
- SourceException.throwIf(existing != null, stmt.getStatementSourceReference(),
- "Duplicate name for typedef %s", stmt.getStatementArgument());
-
- stmt.getParentContext().addContext(TypeNamespace.class, stmt.getStatementArgument(), stmt);
+ if (stmt != null) {
+ final Mutable<?, ?, ?> parent = stmt.getParentContext();
+ if (parent != null) {
+ // Shadowing check: make sure we do not trample on pre-existing definitions. This catches sibling
+ // declarations and parent declarations which have already been declared.
+ checkConflict(parent, stmt);
+ parent.addContext(TypeNamespace.class, stmt.getStatementArgument(), stmt);
+ }
}
}
protected SubstatementValidator getSubstatementValidator() {
return SUBSTATEMENT_VALIDATOR;
}
+
+ private static void checkConflict(final StmtContext<?, ?, ?> parent, final StmtContext<QName, ?, ?> stmt) {
+ final QName arg = stmt.getStatementArgument();
+ final StmtContext<?, ?, ?> existing = parent.getFromNamespace(TypeNamespace.class, arg);
+ // RFC7950 sections 5.5 and 6.2.1: identifiers must not be shadowed
+ SourceException.throwIf(existing != null, stmt.getStatementSourceReference(), "Duplicate name for typedef %s",
+ arg);
+ }
}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (c) 2017 Pantheon Technologies, 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.yang.stmt;
+
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ReactorException;
+import org.opendaylight.yangtools.yang.parser.spi.source.SourceException;
+
+public class YT838Test {
+ @Test
+ public void testGroupingShadowing() throws Exception {
+ testGrouping("grouping.yang");
+ }
+
+ @Test
+ public void testGroupingPostShadowing() throws Exception {
+ testGrouping("grouping-post.yang");
+ }
+
+ @Test
+ public void testTypedefShadowing() throws Exception {
+ testTypedef("typedef.yang");
+ }
+
+ @Test
+ public void testTypedefPostShadowing() throws Exception {
+ testTypedef("typedef-post.yang");
+ }
+
+ private static void testGrouping(final String model) throws Exception {
+ try {
+ StmtTestUtils.parseYangSource("/bugs/YT838/" + model);
+ fail("Expected failure due to grouping identifier shadowing");
+ } catch (ReactorException e) {
+ final Throwable cause = e.getCause();
+ assertTrue(cause instanceof SourceException);
+ assertTrue(cause.getMessage().startsWith(
+ "Duplicate name for grouping (grouping?revision=2017-12-20)foo [at "));
+ }
+ }
+
+ private static void testTypedef(final String model) throws Exception {
+ try {
+ StmtTestUtils.parseYangSource("/bugs/YT838/" + model);
+ fail("Expected failure due to type identifier shadowing");
+ } catch (ReactorException e) {
+ final Throwable cause = e.getCause();
+ assertTrue(cause instanceof SourceException);
+ assertTrue(cause.getMessage().startsWith(
+ "Duplicate name for typedef (typedef?revision=2017-12-20)foo [at "));
+ }
+ }
+}
--- /dev/null
+module grouping {
+ namespace grouping;
+ prefix grp;
+
+ revision "2017-12-20";
+
+ container top {
+ container nested {
+ grouping foo {
+
+ }
+ }
+
+ grouping foo {
+
+ }
+ }
+}
--- /dev/null
+module grouping {
+ namespace grouping;
+ prefix grp;
+
+ revision "2017-12-20";
+
+ container top {
+ grouping foo {
+
+ }
+
+ container nested {
+ grouping foo {
+
+ }
+ }
+ }
+}
--- /dev/null
+module typedef {
+ namespace typedef;
+ prefix tdef;
+
+ revision "2017-12-20";
+
+ container top {
+ container nested {
+ typedef foo {
+ type string;
+ }
+ }
+
+ typedef foo {
+ type string;
+ }
+ }
+}
--- /dev/null
+module typedef {
+ namespace typedef;
+ prefix tdef;
+
+ revision "2017-12-20";
+
+ container top {
+ typedef foo {
+ type string;
+ }
+
+ container nested {
+ typedef foo {
+ type string;
+ }
+ }
+ }
+}