Hook if-feature statements onto FeatureNamespace 51/80751/1
authorRobert Varga <robert.varga@pantheon.tech>
Fri, 8 Mar 2019 17:10:07 +0000 (18:10 +0100)
committerRobert Varga <robert.varga@pantheon.tech>
Sun, 10 Mar 2019 10:16:59 +0000 (11:16 +0100)
IfFeatureStatementSupport should perform a check against FeatureNamespace,
so that invalid feature references are properly caught and reported.

JIRA: YANGTOOLS-964
Change-Id: If9fe8da3235533a702a34ceac26d62d91acc03c3
Signed-off-by: Robert Varga <robert.varga@pantheon.tech>
(cherry picked from commit 1e6da8dcec386c56a626d7982e20a67e35d8a4f7)
(cherry picked from commit 3e66f45b8e6dd5f6bf2014c2cba3adc61eed6c89)

24 files changed:
yang/rfc8040-parser-support/src/test/resources/yang-data-extension-test/foobar.yang
yang/yang-data-codec-xml/src/test/resources/anyxml-support/params/ietf-netconf@2011-06-01.yang
yang/yang-model-export/src/test/resources/bugs/bug2444/yang/action.yang
yang/yang-model-export/src/test/resources/bugs/bug2444/yang/anydata.yang
yang/yang-model-export/src/test/resources/bugs/bug2444/yin/action.yin
yang/yang-model-export/src/test/resources/bugs/bug2444/yin/anydata.yin
yang/yang-parser-impl/src/test/resources/if-feature-resolution-test/shared-schema-repository/foobar.yang
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/if_feature/IfFeatureStatementSupport.java
yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/DeclaredStatementsTest.java
yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/YangParserTest.java
yang/yang-parser-rfc7950/src/test/resources/declared-statements-test/anyxml-declared-test.yang
yang/yang-parser-rfc7950/src/test/resources/declared-statements-test/choice-declared-test.yang
yang/yang-parser-rfc7950/src/test/resources/declared-statements-test/container-declared-test.yang
yang/yang-parser-rfc7950/src/test/resources/declared-statements-test/root-module-declared-test.yang
yang/yang-parser-rfc7950/src/test/resources/if-feature-resolution-test/bar.yang
yang/yang-parser-rfc7950/src/test/resources/if-feature-resolution-test/foo.yang
yang/yang-parser-rfc7950/src/test/resources/model-new/baz.yang
yang/yang-parser-rfc7950/src/test/resources/model/baz.yang
yang/yang-parser-rfc7950/src/test/resources/rfc7950/bug6869/foo.yang
yang/yang-parser-rfc7950/src/test/resources/rfc7950/list-keys-test/correct-list-keys-test.yang
yang/yang-parser-rfc7950/src/test/resources/rfc7950/model/baz.yang
yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/model/baz.yang
yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/simple-nodes-semantic.yang
yang/yang-parser-rfc7950/src/test/resources/simple-test/simple-nodes.yang

index 1b68a848dea99dd4fdd14ca29815343c9c114f2a..8d34f0724012c149471e808c40a5e606a66bbff9 100644 (file)
@@ -9,6 +9,8 @@ module foobar {
 
     revision 2017-06-01;
 
+    feature feat;
+
     rc:yang-data "my-yang-data" {
         container cont {
             if-feature feat;
@@ -26,4 +28,4 @@ module foobar {
     grouping grp {
         container grp-cont {}
     }
-}
\ No newline at end of file
+}
index 816f1aeeef1837ad04742428961137d46d2cee55..36225168ba79662e29dfa84c97646a8061d12fc0 100644 (file)
@@ -50,6 +50,12 @@ module ietf-netconf {
       "RFC 6241: Network Configuration Protocol";
   }
 
+  // NETCONF capabilities defined as features
+  feature writable-running;
+  feature candidate;
+  feature validate;
+  feature url;
+
   // NETCONF Simple Types
 
   typedef edit-operation-type {
index 92a8a92d28d644c08c1126eb48fc439d22e1a5cb..ee7882e87c05b35ac0b95ad8762b734650688c8a 100644 (file)
@@ -3,6 +3,9 @@ module action {
     prefix ac;
     yang-version 1.1;
 
+    feature my-feature-1;
+    feature my-feature-2;
+
     container root {
         action a {
             if-feature "my-feature-1 or my-feature-2";
index f9d87082813e97c7e2f985dc0ca5af2c2862a75c..a08088f8bd352811224f52d8c93e915d182a927f 100644 (file)
@@ -3,6 +3,9 @@ module anydata {
     prefix ad;
     yang-version 1.1;
 
+    feature my-feature-1;
+    feature my-feature-2;
+
     anydata my-anydata {
         when "1!=0";
         must "1=1";
index 8b98c19f641f29551f8239c36eb46ae7f4098b22..ca6ad400b1532a44d5f9459077f70c08b2e2031e 100644 (file)
@@ -3,6 +3,8 @@
     <namespace uri="action" />
     <prefix value="ac" />
     <yang-version value="1.1" />
+    <feature name="my-feature-1"/>
+    <feature name="my-feature-2"/>
     <container name="root">
         <action name="a">
             <if-feature name="my-feature-1 or my-feature-2" />
index 98c2c4cb9e39ddbd35d270b509fb963503d26e6f..5bd1497b0999c6f8037e9d41ff26dad43f956298 100644 (file)
@@ -3,6 +3,8 @@
     <namespace uri="anydata" />
     <prefix value="ad" />
     <yang-version value="1.1" />
+    <feature name="my-feature-1"/>
+    <feature name="my-feature-2"/>
     <anydata name="my-anydata">
         <when condition="1!=0" />
         <must condition="1=1" />
index d839150ab4269ca3bcf88e13a72f77231b27ff26..ddc7854d84665a19a9d9e6a988e6e1b9f28cfbcf 100644 (file)
@@ -2,6 +2,9 @@ module foobar {
     namespace "foobar-namespace";
     prefix "foobar-prefix";
 
+    feature test-feature-1;
+    feature foobar-feature;
+
     container test-container-a {
         if-feature test-feature-1;
 
@@ -23,4 +26,4 @@ module foobar {
             type string;
         }
     }
-}
\ No newline at end of file
+}
index 4ca9a88b750c7e117e86e4ffc1009008ba5bdcb6..0f75b720aa02e7c213b070278950d59a8fdb755e 100644 (file)
@@ -7,6 +7,13 @@
  */
 package org.opendaylight.yangtools.yang.parser.rfc7950.stmt.if_feature;
 
+import static com.google.common.base.Verify.verify;
+import static com.google.common.base.Verify.verifyNotNull;
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
 import java.util.Set;
 import java.util.function.Predicate;
 import org.opendaylight.yangtools.yang.common.QName;
@@ -15,13 +22,24 @@ 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.IfFeatureExpr;
 import org.opendaylight.yangtools.yang.model.api.stmt.IfFeatureStatement;
+import org.opendaylight.yangtools.yang.parser.spi.FeatureNamespace;
 import org.opendaylight.yangtools.yang.parser.spi.meta.AbstractStatementSupport;
+import org.opendaylight.yangtools.yang.parser.spi.meta.InferenceException;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ModelActionBuilder;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ModelActionBuilder.InferenceAction;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ModelActionBuilder.InferenceContext;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ModelActionBuilder.Prerequisite;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ModelProcessingPhase;
 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.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 public final class IfFeatureStatementSupport extends AbstractStatementSupport<Predicate<Set<QName>>, IfFeatureStatement,
         EffectiveStatement<Predicate<Set<QName>>, IfFeatureStatement>> {
+    private static final Logger LOG = LoggerFactory.getLogger(IfFeatureStatementSupport.class);
     private static final SubstatementValidator SUBSTATEMENT_VALIDATOR = SubstatementValidator.builder(
         YangStmtMapping.IF_FEATURE)
         .build();
@@ -36,13 +54,47 @@ public final class IfFeatureStatementSupport extends AbstractStatementSupport<Pr
     }
 
     @Override
-    public Predicate<Set<QName>> parseArgumentValue(final StmtContext<?, ?, ?> ctx, final String value) {
+    public IfFeatureExpr parseArgumentValue(final StmtContext<?, ?, ?> ctx, final String value) {
         if (YangVersion.VERSION_1_1.equals(ctx.getRootVersion())) {
             return IfFeaturePredicateVisitor.parseIfFeatureExpression(ctx, value);
         }
         return IfFeatureExpr.isPresent(StmtContextUtils.parseNodeIdentifier(ctx, value));
     }
 
+    @Override
+    public void onFullDefinitionDeclared(final Mutable<Predicate<Set<QName>>, IfFeatureStatement,
+            EffectiveStatement<Predicate<Set<QName>>, IfFeatureStatement>> stmt) {
+        super.onFullDefinitionDeclared(stmt);
+
+        final ModelActionBuilder verifyFeatures = stmt.newInferenceAction(ModelProcessingPhase.EFFECTIVE_MODEL);
+        final Map<Prerequisite<?>, QName> backRef = new HashMap<>();
+        final Predicate<Set<QName>> argument = stmt.getStatementArgument();
+        verify(argument instanceof IfFeatureExpr, "Unexpected argument %s", argument);
+        for (QName feature : ((IfFeatureExpr) argument).getReferencedFeatures()) {
+            backRef.put(verifyFeatures.requiresCtx(stmt, FeatureNamespace.class, feature,
+                ModelProcessingPhase.EFFECTIVE_MODEL), feature);
+        }
+
+        verifyFeatures.apply(new InferenceAction() {
+            @Override
+            public void apply(final InferenceContext ctx) {
+                LOG.debug("Resolved all feature references in {}", backRef.values());
+            }
+
+            @Override
+            public void prerequisiteFailed(final Collection<? extends Prerequisite<?>> failed) {
+                final Set<QName> unresolvedFeatures = new HashSet<>();
+                for (Prerequisite<?> prereq : failed) {
+                    unresolvedFeatures.add(verifyNotNull(backRef.get(prereq)));
+                }
+
+                throw new InferenceException(stmt.getStatementSourceReference(),
+                    "Failed to resolve feature references %s in \"%s\"", unresolvedFeatures,
+                    stmt.rawStatementArgument());
+            }
+        });
+    }
+
     @Override
     public IfFeatureStatement createDeclared(final StmtContext<Predicate<Set<QName>>, IfFeatureStatement, ?> ctx) {
         return new IfFeatureStatementImpl(ctx);
index e86b7f9e6c7055269ff219e5651ca36f60bdb0bc..9f5377c5db8f38766edb51b48562f57e2a56198f 100644 (file)
@@ -336,7 +336,7 @@ public class DeclaredStatementsTest {
         assertEquals("ext-argument", argumentStatement.getName().getLocalName());
         assertTrue(argumentStatement.getYinElement().getValue());
 
-        assertEquals(1, moduleStatement.getFeatures().size());
+        assertEquals(2, moduleStatement.getFeatures().size());
         final FeatureStatement featureStatement = moduleStatement.getFeatures().iterator().next();
         assertEquals(Status.CURRENT, featureStatement.getStatus().getValue());
         assertEquals("test description", featureStatement.getDescription().getText());
index 6c16627b1ef8b369af45c5dac10285a1ad7d4cf6..44e219c129bad600ab6bc48588e7818513d5f0e0 100644 (file)
@@ -570,7 +570,7 @@ public class YangParserTest {
     @Test
     public void testFeature() {
         final Set<FeatureDefinition> features = baz.getFeatures();
-        assertEquals(1, features.size());
+        assertEquals(3, features.size());
     }
 
     @Test
index d70ece5a1350a5e8f14ebfcfb57aa9c1ae3d86ee..6111b93fe91c531b3bba3a93e49fee123acb51b7 100644 (file)
@@ -2,6 +2,8 @@ module anyxml-declared-test {
     namespace "anyxml-declared-test";
     prefix "axdt";
 
+    feature foobar-feature;
+
     anyxml foobar {
         when "foo = 'bar'" {
             description "when description";
@@ -20,4 +22,4 @@ module anyxml-declared-test {
         reference "anyxml reference";
         mandatory "false";
     }
-}
\ No newline at end of file
+}
index 20f172ee2cb5b9d25dcdfb5058a4bdd7900fe528..01aec1eab852d818cf7dd34c550c9a3fd02df98f 100644 (file)
@@ -2,6 +2,9 @@ module choice-declared-test {
     namespace "choice-declared-test";
     prefix "chdt";
 
+    feature foo-feature;
+    feature foobar-feature;
+
     choice test-choice {
         default case-two;
         config false;
@@ -36,4 +39,4 @@ module choice-declared-test {
             }
         }
     }
-}
\ No newline at end of file
+}
index 1064190cfeb876ca388288e31e226b73d31ee0bd..01842ed579667ae570385dd92ffc8993ec043671 100644 (file)
@@ -2,6 +2,8 @@ module container-declared-test {
     namespace "container-declared-test";
     prefix "cdt";
 
+    feature foo-feature;
+
     container test-container {
         when "foo = 'bar'";
         if-feature foo-feature;
@@ -28,4 +30,4 @@ module container-declared-test {
             type string;
         }
     }
-}
\ No newline at end of file
+}
index 5f0d708bf7a5d96a6e6c6b5bd69e1349fd534804..555f1b9b8c2fc41186eb8da3232697521d6652ba 100644 (file)
@@ -33,6 +33,8 @@ module root-module-declared-test {
         reference "test reference";
     }
 
+    feature required-feature;
+
     identity test-base-id;
 
     identity test-id {
@@ -52,4 +54,4 @@ module root-module-declared-test {
         reference "test reference";
         units "meter";
     }
-}
\ No newline at end of file
+}
index cbe1ab9e712fdbb3c75c9b1dfd8ed3134a72d3a8..8f93e0b944e0d27eb0b90e9608967c12c957dc53 100644 (file)
@@ -2,9 +2,11 @@ module bar {
     namespace "bar-namespace";
     prefix "bar-prefix";
 
+    feature imp-feature;
+
     container bar-cont {
         leaf bar-cont-leaf {
             type string;
         }
     }
-}
\ No newline at end of file
+}
index 1688a31d8332bd2ad9117d83bf485300e5913bcf..a147247955161bed4fd8e5b7e12a21e0e8ae77a7 100644 (file)
@@ -6,6 +6,11 @@ module foo {
         prefix br;
     }
 
+    feature foo-feature;
+    feature test-feature-1;
+    feature test-feature-2;
+    feature test-feature-3;
+
     container test-container-a {
         if-feature foo-feature;
 
@@ -122,4 +127,4 @@ module foo {
             }
         }
     }
-}
\ No newline at end of file
+}
index fdff04564ccbc940c56735b385a0ee673ed11582..b54db27fb07c59d47c1f2aca893f1c01c7730100 100644 (file)
@@ -54,6 +54,9 @@ module baz {
                 flash or disk) that can be used to store syslog messages.";
     }
 
+    feature candidate;
+    feature startup;
+
     extension c-define {
         description "Takes as argument a name string. Makes the code generator use
                 the given name in the #define.";
index 8b9ded02cb1d9d7ae9a0b3ffdf96bb3bb7183604..c50ce618d3697a9db8e5f265024abb1b3278d639 100644 (file)
@@ -58,6 +58,9 @@ module baz {
              store syslog messages.";
     }
 
+    feature candidate;
+    feature startup;
+
     extension c-define {
         description "Takes as argument a name string. Makes the code generator use the given name in the #define.";
         argument "name" {
index b1b4b5b4a91362b30b872f0e33caac9f44d3af47..d4f0f6352beab4950c73fe32dea139f376b106b4 100644 (file)
@@ -7,6 +7,13 @@ module foo {
         if-feature identity-feature;
     }
 
+    feature identity-feature;
+    feature mandatory-leaf;
+    feature ssh;
+    feature tls;
+    feature two;
+    feature three;
+
     container root {
         uses grp {
             refine grp-leaf {
index d5b894eaf530e538c7aa749e98c2929309e2e09f..1e93349b60b158f08bfa94d3dedbacc645d15660 100644 (file)
@@ -3,6 +3,9 @@ module correct-list-keys-test {
     namespace "correct-list-keys-test";
     prefix "test";
 
+    feature feature-b;
+    feature feature-d;
+
     list valid-list-a {
         leaf a1 {
             type string;
index 6cad0a5319da826c60ad905e185ad9bc1287a9e2..de092fad0b1946b39d6e9c78e89a58040963fd05 100644 (file)
@@ -58,6 +58,9 @@ module baz {
              store syslog messages.";
     }
 
+    feature candidate;
+    feature startup;
+
     extension c-define {
         description "Takes as argument a name string. Makes the code generator use the given name in the #define.";
         argument "name" {
index 8b9ded02cb1d9d7ae9a0b3ffdf96bb3bb7183604..c50ce618d3697a9db8e5f265024abb1b3278d639 100644 (file)
@@ -58,6 +58,9 @@ module baz {
              store syslog messages.";
     }
 
+    feature candidate;
+    feature startup;
+
     extension c-define {
         description "Takes as argument a name string. Makes the code generator use the given name in the #define.";
         argument "name" {
index 247aceb56512987dff3cf111b2d31e4a6a1510c0..1e7e991b4ca78248acc34f8f452d0025c6da413d 100644 (file)
@@ -26,7 +26,7 @@ module simple-nodes {
 
     // NOTE: simple comment
 
-
+    feature has-name;
 
     anyxml data {
          config false;
index 9ccf0c73e7e83a47076ea711186853f926b733e8..da60a83bba161d2572ae18244e116a642c4e7311 100644 (file)
@@ -21,6 +21,8 @@ module simple-nodes {
 
     // NOTE: simple comment
 
+    feature has-name;
+
     anyxml data {
         config false;
         description "anyxml desc";
@@ -127,4 +129,4 @@ module simple-nodes {
         when "class != 'wheel'";
     }
 
-}
\ No newline at end of file
+}