submodule submodule {
+ yang-version 1.1;
+
belongs-to include {
prefix incl;
}
<submodule name="submodule" xmlns="urn:ietf:params:xml:ns:yang:yin:1"
xmlns:incl="include">
+ <yang-version value="1.1"/>
<belongs-to module="include">
<prefix value="incl" />
</belongs-to>
import ietf-restconf {
prefix rc;
- revision-date 2017-01-26;
}
revision 2017-06-01;
container another-top-level-cont {}
}
}
-}
\ No newline at end of file
+}
import ietf-restconf {
prefix rc;
- revision-date 2017-01-26;
}
revision 2017-06-01;
config false;
}
}
-}
\ No newline at end of file
+}
import ietf-restconf {
prefix rc;
- revision-date 2017-01-26;
}
revision 2017-06-01;
rc:yang-data "my-yang-data" {
// missing top level container = should throw an exception
}
-}
\ No newline at end of file
+}
import ietf-restconf {
prefix rc;
- revision-date 2017-01-26;
}
revision 2017-06-01;
grouping grp-2 {
container cont-2 {}
}
-}
\ No newline at end of file
+}
import ietf-restconf {
prefix rc;
- revision-date 2017-01-26;
}
revision 2017-06-01;
type string;
}
}
-}
\ No newline at end of file
+}
import ietf-restconf {
prefix rc;
- revision-date 2017-01-26;
}
revision 2017-06-01;
grouping grp {
container grp-cont {}
}
-}
\ No newline at end of file
+}
import ietf-restconf {
prefix rc;
- revision-date 2017-01-26;
}
revision 2017-06-01;
assertReference(baz, YangStmtMapping.SUBMODULE, 1, 1);
final Iterator<? extends DeclaredStatement<?>> it = baz.declaredSubstatements().iterator();
- assertReference(it.next(), YangStmtMapping.BELONGS_TO, 2, 3);
- assertReference(it.next(), YangStmtMapping.EXTENSION, 6, 3);
+ assertReference(it.next(), YangStmtMapping.YANG_VERSION, 2, 3);
+ assertReference(it.next(), YangStmtMapping.BELONGS_TO, 4, 3);
+ assertReference(it.next(), YangStmtMapping.EXTENSION, 8, 3);
assertFalse(it.hasNext());
}
submodule baz {
+ yang-version 1.1;
+
belongs-to bar {
prefix bar;
}
import org.opendaylight.yangtools.openconfig.model.api.OpenConfigStatements;
import org.opendaylight.yangtools.yang.common.Empty;
import org.opendaylight.yangtools.yang.common.XMLNamespace;
+import org.opendaylight.yangtools.yang.common.YangVersion;
import org.opendaylight.yangtools.yang.model.api.YangStmtMapping;
import org.opendaylight.yangtools.yang.model.api.meta.DeclarationReference;
import org.opendaylight.yangtools.yang.model.api.meta.DeclaredStatement;
import org.opendaylight.yangtools.yang.model.api.stmt.ImportEffectiveStatement;
import org.opendaylight.yangtools.yang.model.api.stmt.ImportStatement;
import org.opendaylight.yangtools.yang.model.api.stmt.PrefixStatement;
+import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier;
import org.opendaylight.yangtools.yang.model.ri.stmt.DeclaredStatementDecorators;
import org.opendaylight.yangtools.yang.model.ri.stmt.DeclaredStatements;
import org.opendaylight.yangtools.yang.model.ri.stmt.EffectiveStatements;
import org.opendaylight.yangtools.yang.parser.spi.source.ImpPrefixToNamespace;
import org.opendaylight.yangtools.yang.parser.spi.source.ModuleNameToNamespace;
import org.opendaylight.yangtools.yang.parser.spi.source.SourceException;
+import org.opendaylight.yangtools.yang.parser.spi.source.YangVersionLinkageException;
@Beta
public final class ImportStatementSupport
* Based on this information, required modules are searched from library
* sources.
*/
- stmt.addRequiredSource(RevisionImport.getImportedSourceIdentifier(stmt));
+ final SourceIdentifier importId = RevisionImport.getImportedSourceIdentifier(stmt);
+ stmt.addRequiredSource(importId);
final String moduleName = stmt.getArgument();
final ModelActionBuilder importAction = stmt.newInferenceAction(SOURCE_PRE_LINKAGE);
final Prerequisite<StmtContext<?, ?, ?>> imported = importAction.requiresCtx(stmt,
PreLinkageModuleNamespace.class, moduleName, SOURCE_PRE_LINKAGE);
- importAction.mutatesCtx(stmt.getRoot(), SOURCE_PRE_LINKAGE);
+ final Prerequisite<Mutable<?, ?, ?>> rootPrereq = importAction.mutatesCtx(stmt.getRoot(), SOURCE_PRE_LINKAGE);
importAction.apply(new InferenceAction() {
@Override
firstAttributeOf(stmt.declaredSubstatements(), PrefixStatement.class), stmt,
"Missing prefix statement");
+ final Mutable<?, ?, ?> root = rootPrereq.resolve(ctx);
+ // Version 1 sources must not import-by-revision Version 1.1 modules
+ if (importId.getRevision().isPresent() && root.yangVersion() == YangVersion.VERSION_1) {
+ final YangVersion importedVersion = importedModuleContext.yangVersion();
+ if (importedVersion != YangVersion.VERSION_1) {
+ throw new YangVersionLinkageException(stmt,
+ "Cannot import by revision version %s module %s", importedVersion, moduleName);
+ }
+ }
+
stmt.addToNs(ImpPrefixToNamespace.class, impPrefix, importedModuleNamespace);
}
import java.util.Optional;
import org.eclipse.jdt.annotation.NonNull;
import org.opendaylight.yangtools.yang.common.Revision;
+import org.opendaylight.yangtools.yang.common.YangVersion;
import org.opendaylight.yangtools.yang.model.api.YangStmtMapping;
import org.opendaylight.yangtools.yang.model.api.meta.DeclarationReference;
import org.opendaylight.yangtools.yang.model.api.meta.DeclaredStatement;
import org.opendaylight.yangtools.yang.parser.spi.meta.SubstatementValidator;
import org.opendaylight.yangtools.yang.parser.spi.source.IncludedModuleContext;
import org.opendaylight.yangtools.yang.parser.spi.source.IncludedSubmoduleNameToModuleCtx;
+import org.opendaylight.yangtools.yang.parser.spi.source.YangVersionLinkageException;
@Beta
public final class IncludeStatementSupport
@Override
public void apply(final InferenceContext ctx) {
final StmtContext<?, ?, ?> includedSubModuleContext = requiresCtxPrerequisite.resolve(ctx);
+ final YangVersion modVersion = stmt.getRoot().yangVersion();
+ final YangVersion subVersion = includedSubModuleContext.yangVersion();
+ if (subVersion != modVersion) {
+ throw new YangVersionLinkageException(stmt,
+ "Cannot include a version %s submodule in a version %s module", subVersion, modVersion);
+ }
stmt.addToNs(IncludedModuleContext.class, revision != null
? RevisionSourceIdentifier.create(submoduleName, revision.argument())
--- /dev/null
+/*
+ * Copyright (c) 2021 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.yang.stmt;
+
+import static org.hamcrest.CoreMatchers.instanceOf;
+import static org.hamcrest.CoreMatchers.startsWith;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertThrows;
+
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.common.YangVersion;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ReactorException;
+import org.opendaylight.yangtools.yang.parser.spi.source.YangVersionLinkageException;
+
+/**
+ * Tests for {@code MUST NOT} statements around include/import interop of RFC6020 and RFC7950, as per
+ * <a href="https://datatracker.ietf.org/doc/html/rfc7950#section-12">RFC7950 section 12</a>.
+ */
+public class YT1339Test {
+ @Test
+ public void testInclude() {
+ // A YANG version 1.1 module MUST NOT include a YANG version 1 submodule,
+ assertFailedInclude("old-sub", YangVersion.VERSION_1, YangVersion.VERSION_1_1);
+ // ... and a YANG version 1 module MUST NOT include a YANG version 1.1 submodule
+ assertFailedInclude("new-sub", YangVersion.VERSION_1_1, YangVersion.VERSION_1);
+ }
+
+ @Test
+ public void testImportNewByRev() throws Exception {
+ // A YANG version 1 module or submodule MUST NOT import a YANG version 1.1 module by revision.
+ assertFailedImport("import-rev");
+ assertFailedImport("import-rev-sub");
+ }
+
+ @Test
+ public void testImportOldByRev() throws Exception {
+ // A YANG version 1.1 module or submodule MAY import a YANG version 1 module by revision.
+ compile("import");
+ }
+
+ @Test
+ public void testImportNoRev() throws Exception {
+ // no language forbidding imports without revision
+ compile("import-norev");
+ }
+
+ private static void assertFailedImport(final String subdir) {
+ assertThat(assertYangVersionLinkageException(subdir),
+ startsWith("Cannot import by revision version 1.1 module new [at "));
+ }
+
+ private static void assertFailedInclude(final String subdir, final YangVersion subVer, final YangVersion modVer) {
+ assertThat(assertYangVersionLinkageException(subdir),
+ startsWith("Cannot include a version " + subVer + " submodule in a version " + modVer + " module [at "));
+ }
+
+ private static String assertYangVersionLinkageException(final String subdir) {
+ final var cause = assertThrows(ReactorException.class, () -> compile(subdir)).getCause();
+ assertThat(cause, instanceOf(YangVersionLinkageException.class));
+ return cause.getMessage();
+ }
+
+ private static void compile(final String subdir) throws Exception {
+ StmtTestUtils.parseYangSources("/bugs/YT1339/" + subdir);
+ }
+}
submodule bar {
+ yang-version 1.1;
+
belongs-to foo {
prefix foo;
}
--- /dev/null
+module new {
+ namespace new;
+ prefix new;
+ yang-version 1.1;
+ revision 2021-10-01;
+}
--- /dev/null
+module old {
+ namespace old;
+ prefix old;
+
+ import new {
+ prefix new;
+ }
+
+ include sub;
+}
--- /dev/null
+submodule sub {
+ belongs-to old {
+ prefix old;
+ }
+
+ import new {
+ prefix new;
+ }
+}
--- /dev/null
+module new {
+ namespace new;
+ prefix new;
+ yang-version 1.1;
+ revision 2021-10-01;
+}
--- /dev/null
+module old {
+ namespace old;
+ prefix old;
+
+ include sub;
+}
--- /dev/null
+submodule sub {
+ belongs-to old {
+ prefix old;
+ }
+
+ import new {
+ prefix new;
+ revision-date 2021-10-01;
+ }
+}
--- /dev/null
+module new {
+ namespace new;
+ prefix new;
+ yang-version 1.1;
+ revision 2021-10-01;
+}
--- /dev/null
+module old {
+ namespace old;
+ prefix old;
+
+ import new {
+ prefix new;
+ revision-date 2021-10-01;
+ }
+}
--- /dev/null
+module new {
+ namespace new;
+ prefix new;
+ yang-version 1.1;
+
+ import old {
+ prefix old;
+ revision-date 2021-10-01;
+ }
+
+ include sub;
+}
--- /dev/null
+module old {
+ namespace old;
+ prefix old;
+ revision 2021-10-01;
+
+}
--- /dev/null
+submodule sub {
+ yang-version 1.1;
+
+ belongs-to new {
+ prefix new;
+ }
+
+ import old {
+ prefix old;
+ revision-date 2021-10-01;
+ }
+}
--- /dev/null
+module mod {
+ namespace mod;
+ prefix mod;
+
+ include sub;
+}
--- /dev/null
+submodule sub {
+ yang-version 1.1;
+
+ belongs-to mod {
+ prefix mod;
+ }
+}
--- /dev/null
+module mod {
+ namespace mod;
+ prefix mod;
+ yang-version 1.1;
+
+ include sub;
+}
--- /dev/null
+submodule sub {
+ belongs-to mod {
+ prefix mod;
+ }
+}
prefix foo;
yang-version 1;
- import foo-imp-1 { prefix foo-imp-1; revision-date 2017-01-23; }
+ import foo-imp-1 { prefix foo-imp-1; }
import foo-imp-2 { prefix foo-imp-2; revision-date 2017-01-23; }
include sub-foo {
revision-date 2017-01-20;
namespace lib-1;
prefix lib-1;
- import lib-2 { prefix lib-2; revision-date 2000-01-01; }
+ import lib-2 { prefix lib-2; }
import missing-lib { prefix missing; revision-date 2000-01-01; }
revision 2000-01-01;
import bar {
prefix bar;
- revision-date 2017-01-20;
}
revision 2017-01-20;
units "weeks";
}
}
-}
\ No newline at end of file
+}
import import-test-imported {
prefix "imported-module";
- revision-date "2016-12-12";
description "Yang 1.1: Allow description and reference in include and import.";
reference "https://tools.ietf.org/html/rfc7950 section-7.1.5/6";
}
-}
\ No newline at end of file
+}
--- /dev/null
+/*
+ * Copyright (c) 2021 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.yang.parser.spi.source;
+
+import com.google.common.annotations.Beta;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.parser.spi.meta.CommonStmtCtx;
+
+/**
+ * An exception indicating source-level problem across multiple YANG sources relating to how they are allowed to be
+ * linked between YANG versions. This typically indicates a direct violation of
+ * <a href="https://datatracker.ietf.org/doc/html/rfc7950#section-12">RFC7950 section 12</a>.
+ */
+@Beta
+public class YangVersionLinkageException extends SourceException {
+ private static final long serialVersionUID = 1L;
+
+ public YangVersionLinkageException(final @NonNull StatementSourceReference source, final @NonNull String format,
+ final Object... args) {
+ super(source, format, args);
+ }
+
+ public YangVersionLinkageException(final @NonNull CommonStmtCtx stmt, final @NonNull String format,
+ final Object... args) {
+ this(stmt.sourceReference(), format, args);
+ }
+}