schema compile CHANGE support for deviation of mandatory property
diff --git a/src/tree_schema.h b/src/tree_schema.h
index 1c23753..376bab4 100644
--- a/src/tree_schema.h
+++ b/src/tree_schema.h
@@ -567,6 +567,7 @@
  *       6 LYS_MAND_TRUE    |x|x|x|x|x|x| | | | | | | | |
  *                          +-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  *       7 LYS_ORDBY_USER   | | | |x|x| | | | | | | | | |
+ *         LYS_MAND_FALSE   | |x|x| | |x| | | | | | | | |
  *                          +-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  *       8 LYS_ORDBY_SYSTEM | | | |x|x| | | | | | | | | |
  *         LYS_PRESENCE     |x| | | | | | | | | | | | | |
@@ -601,7 +602,9 @@
                                           The ::lysc_node_leaflist and ::lysc_node_leaflist have this flag in case that min-elements > 0.
                                           The ::lysc_node_container has this flag if it is not a presence container and it has at least one
                                           child with LYS_MAND_TRUE. */
-#define LYS_MAND_FALSE   0x40        /**< mandatory false; applicable only to ::lysp_node_choice, ::lysp_node_leaf and ::lysp_node_anydata */
+#define LYS_MAND_FALSE   0x40        /**< mandatory false; applicable only to ::lysp_node_choice/::lysc_node_choice,
+                                          ::lysp_node_leaf/::lysc_node_leaf and ::lysp_node_anydata/::lysc_node_anydata.
+                                          This flag is present only in case the mandatory false statement was explicitly specified. */
 #define LYS_MAND_MASK    0x60        /**< mask for mandatory values */
 #define LYS_PRESENCE     0x80        /**< flag for presence property of a container, applicable only to ::lysc_node_container */
 #define LYS_UNIQUE       0x80        /**< flag for leafs being part of a unique set, applicable only to ::lysc_node_leaf */
@@ -635,7 +638,7 @@
                                           away when it is refined to be mandatory node. Similarly it is used for deviations to distinguish
                                           between own default or the default values taken from the type. */
 #define LYS_SET_UNITS    0x0400      /**< flag to know if the leaf's/leaflist's units are their own (flag set) or it is taken from the type. */
-#define LYS_SET_CONFIG   0x0800      /**< flag to know if the config property set explicitely (flag set) or it is inherited. */
+#define LYS_SET_CONFIG   0x0800      /**< flag to know if the config property was set explicitly (flag set) or it is inherited. */
 
 #define LYS_FLAGS_COMPILED_MASK 0xff /**< mask for flags that maps to the compiled structures */
 /** @} */
diff --git a/src/tree_schema_compile.c b/src/tree_schema_compile.c
index e04e409..06054a6 100644
--- a/src/tree_schema_compile.c
+++ b/src/tree_schema_compile.c
@@ -3864,7 +3864,7 @@
     } else { /* unset flag */
         for (; parent && parent->nodetype == LYS_CONTAINER && (parent->flags & LYS_MAND_TRUE); parent = parent->parent) {
             for (iter = (struct lysc_node*)lysc_node_children(parent); iter; iter = iter->next) {
-                if (iter->flags && LYS_MAND_TRUE) {
+                if (iter->flags & LYS_MAND_TRUE) {
                     /* there is another mandatory node */
                     return;
                 }
@@ -4065,6 +4065,68 @@
 }
 
 /**
+ * @brief Apply refined or deviated mandatory flag to the target node.
+ *
+ * @param[in] ctx Compile context.
+ * @param[in] node Target node where the mandatory property is supposed to be changed.
+ * @param[in] mandatory_flag Node's mandatory flag to be applied to the @p node.
+ * @param[in] nodeid Schema nodeid used to identify target of refine/deviation (for logging).
+ * @param[in] refine_flag Flag to distinguish if the change is caused by refine (flag set) or deviation (for logging).
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lys_compile_change_mandatory(struct lysc_ctx *ctx, struct lysc_node *node, uint16_t mandatory_flag, const char *nodeid, int refine_flag)
+{
+    if (!(node->nodetype & (LYS_LEAF | LYS_ANYDATA | LYS_ANYXML | LYS_CHOICE))) {
+        LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SEMANTICS,
+               "Invalid %s of mandatory in \"%s\" - %s cannot hold mandatory statement.",
+               refine_flag ? "refine" : "deviation", nodeid, lys_nodetype2str(node->nodetype));
+        return LY_EVALID;
+    }
+
+    if (mandatory_flag & LYS_MAND_TRUE) {
+        /* check if node has default value */
+        if (node->nodetype & LYS_LEAF) {
+            if (node->flags & LYS_SET_DFLT) {
+                LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SEMANTICS,
+                       "Invalid %s of mandatory in \"%s\" - leaf already has \"default\" statement.",
+                       refine_flag ? "refine" : "deviation", nodeid);
+                return LY_EVALID;
+            } else {
+                /* remove the default value taken from the leaf's type */
+                FREE_STRING(ctx->ctx, ((struct lysc_node_leaf*)node)->dflt);
+                ((struct lysc_node_leaf*)node)->dflt = NULL;
+            }
+        } else if ((node->nodetype & LYS_CHOICE) && ((struct lysc_node_choice*)node)->dflt) {
+            LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SEMANTICS,
+                   "Invalid %s of mandatory in \"%s\" - choice already has \"default\" statement.",
+                   refine_flag ? "refine" : "deviation", nodeid);
+            return LY_EVALID;
+        }
+        if (node->parent && (node->parent->flags & LYS_SET_DFLT)) {
+            LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SEMANTICS,
+                   "Invalid %s of mandatory in \"%s\" under the default case.",
+                   refine_flag ? "refine" : "deviation", nodeid);
+            return LY_EVALID;
+        }
+
+        node->flags &= ~LYS_MAND_FALSE;
+        node->flags |= LYS_MAND_TRUE;
+        lys_compile_mandatory_parents(node->parent, 1);
+    } else {
+        /* make mandatory false */
+        node->flags &= ~LYS_MAND_TRUE;
+        node->flags |= LYS_MAND_FALSE;
+        lys_compile_mandatory_parents(node->parent, 0);
+        if ((node->nodetype & LYS_LEAF) && !((struct lysc_node_leaf*)node)->dflt) {
+            /* get the type's default value if any */
+            DUP_STRING(ctx->ctx, ((struct lysc_node_leaf*)node)->type->dflt, ((struct lysc_node_leaf*)node)->dflt);
+        }
+    }
+    return LY_SUCCESS;
+}
+
+/**
  * @brief Compile parsed uses statement - resolve target grouping and connect its content into parent.
  * If present, also apply uses's modificators.
  *
@@ -4289,48 +4351,7 @@
 
         /* mandatory */
         if (rfn->flags & LYS_MAND_MASK) {
-            if (!(node->nodetype & (LYS_LEAF | LYS_ANYDATA | LYS_ANYXML | LYS_CHOICE))) {
-                LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SEMANTICS,
-                       "Invalid refine of mandatory in \"%s\" - %s cannot hold mandatory statement.",
-                       rfn->nodeid, lys_nodetype2str(node->nodetype));
-                goto error;
-            }
-            /* in compiled flags, only the LYS_MAND_TRUE is present */
-            if (rfn->flags & LYS_MAND_TRUE) {
-                /* check if node has default value */
-                if (node->nodetype & LYS_LEAF) {
-                    if (node->flags & LYS_SET_DFLT) {
-                        LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SEMANTICS,
-                               "Invalid refine of mandatory in \"%s\" - leaf already has \"default\" statement.", rfn->nodeid);
-                        goto error;
-                    } else {
-                        /* remove the default value taken from the leaf's type */
-                        FREE_STRING(ctx->ctx, ((struct lysc_node_leaf*)node)->dflt);
-                        ((struct lysc_node_leaf*)node)->dflt = NULL;
-                    }
-                } else if ((node->nodetype & LYS_CHOICE) && ((struct lysc_node_choice*)node)->dflt) {
-                    LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SEMANTICS,
-                           "Invalid refine of mandatory in \"%s\" - choice already has \"default\" statement.", rfn->nodeid);
-                    goto error;
-                }
-                if (node->parent && (node->parent->flags & LYS_SET_DFLT)) {
-                    LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SEMANTICS,
-                           "Invalid refine of mandatory in \"%s\" - %s under the default case.",
-                           rfn->nodeid, lys_nodetype2str(node->nodetype));
-                    goto error;
-                }
-
-                node->flags |= LYS_MAND_TRUE;
-                lys_compile_mandatory_parents(node->parent, 1);
-            } else {
-                /* make mandatory false */
-                node->flags &= ~LYS_MAND_TRUE;
-                lys_compile_mandatory_parents(node->parent, 0);
-                if ((node->nodetype & LYS_LEAF) && !((struct lysc_node_leaf*)node)->dflt) {
-                    /* get the type's default value if any */
-                    DUP_STRING(ctx->ctx, ((struct lysc_node_leaf*)node)->type->dflt, ((struct lysc_node_leaf*)node)->dflt);
-                }
-            }
+            LY_CHECK_GOTO(lys_compile_change_mandatory(ctx, node, rfn->flags, rfn->nodeid, 1), error);
         }
 
         /* presence */
@@ -4990,6 +5011,15 @@
                 }
 
                 /* [mandatory-stmt] */
+                if (d_add->flags & LYS_MAND_MASK) {
+                    if (devs[u]->target->flags & LYS_MAND_MASK) {
+                        LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_REFERENCE,
+                               "Invalid deviation (%s) adding \"mandatory\" property which already exists (with value \"mandatory %s\").",
+                               devs[u]->nodeid, devs[u]->target->flags & LYS_MAND_TRUE ? "true" : "false");
+                        goto cleanup;
+                    }
+                    LY_CHECK_GOTO(lys_compile_change_mandatory(ctx, devs[u]->target, d_add->flags, devs[u]->nodeid, 0), cleanup);
+                }
 
                 /* [min-elements-stmt] */
 
@@ -5182,6 +5212,14 @@
                 }
 
                 /* [mandatory-stmt] */
+                if (d_rpl->flags & LYS_MAND_MASK) {
+                    if (!(devs[u]->target->flags & LYS_MAND_MASK)) {
+                        LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LY_VCODE_DEV_NOT_PRESENT, devs[u]->nodeid,
+                               "replacing", "mandatory", d_rpl->flags & LYS_MAND_TRUE ? "mandatory true" : "mandatory false");
+                        goto cleanup;
+                    }
+                    LY_CHECK_GOTO(lys_compile_change_mandatory(ctx, devs[u]->target, d_rpl->flags, devs[u]->nodeid, 0), cleanup);
+                }
 
                 /* [min-elements-stmt] */
 
diff --git a/tests/src/test_tree_schema_compile.c b/tests/src/test_tree_schema_compile.c
index 1edd22f..4355725 100644
--- a/tests/src/test_tree_schema_compile.c
+++ b/tests/src/test_tree_schema_compile.c
@@ -2334,7 +2334,7 @@
 
     assert_null(lys_parse_mem(ctx, "module ff {namespace urn:ff;prefix ff;import grp {prefix g;}"
                                         "uses g:grp {refine c/ch/a/a {mandatory true;}}}", LYS_IN_YANG));
-    logbuf_assert("Invalid refine of mandatory in \"c/ch/a/a\" - leaf under the default case.");
+    logbuf_assert("Invalid refine of mandatory in \"c/ch/a/a\" under the default case.");
 
     assert_null(lys_parse_mem(ctx, "module gg {namespace urn:gg;prefix gg;import grp {prefix g;}"
                                         "uses g:grp {refine c/x {default hello;}}}", LYS_IN_YANG));
@@ -2684,6 +2684,20 @@
     assert_string_equal("x", node->name);
     assert_true(node->flags & LYS_CONFIG_W);
 
+    assert_non_null(mod = lys_parse_mem(ctx, "module m {namespace urn:m;prefix m;"
+                                        "container a {leaf a {type string;}}"
+                                        "container b {leaf b {mandatory true; type string;}}"
+                                        "deviation /a/a {deviate add {mandatory true;}}"
+                                        "deviation /b/b {deviate replace {mandatory false;}}}", LYS_IN_YANG));
+    assert_non_null(node = mod->compiled->data);
+    assert_string_equal("a", node->name);
+    assert_true((node->flags & LYS_MAND_MASK) == LYS_MAND_TRUE);
+    assert_true((lysc_node_children(node)->flags & LYS_MAND_MASK) == LYS_MAND_TRUE);
+    assert_non_null(node = node->next);
+    assert_string_equal("b", node->name);
+    assert_false(node->flags & LYS_MAND_MASK); /* just unset on container */
+    assert_true((lysc_node_children(node)->flags & LYS_MAND_MASK) == LYS_MAND_FALSE);
+
     assert_null(lys_parse_mem(ctx, "module aa1 {namespace urn:aa1;prefix aa1;import a {prefix a;}"
                               "deviation /a:top/a:z {deviate not-supported;}}", LYS_IN_YANG));
     logbuf_assert("Invalid absolute-schema-nodeid value \"/a:top/a:z\" - target node not found.");
@@ -2791,6 +2805,22 @@
     assert_null(lys_parse_mem(ctx, "module jj5 {namespace urn:jj5;prefix jj5; container top {leaf x {type string; config true;}}"
                               "deviation /top {deviate add {config false;}}}", LYS_IN_YANG));
     logbuf_assert("Invalid deviation of config in \"/top\" - configuration node cannot be child of any state data node.");
+    assert_null(lys_parse_mem(ctx, "module jj6 {namespace urn:jj6;prefix jj6; leaf x {config false; type string;}"
+                              "deviation /x {deviate add {config true;}}}", LYS_IN_YANG));
+    logbuf_assert("Invalid deviation (/x) adding \"config\" property which already exists (with value \"config false\").");
+
+    assert_null(lys_parse_mem(ctx, "module kk1 {namespace urn:kk1;prefix kk1; container top {leaf a{type string;}}"
+                              "deviation /top {deviate add {mandatory true;}}}", LYS_IN_YANG));
+    logbuf_assert("Invalid deviation of mandatory in \"/top\" - container cannot hold mandatory statement.");
+    assert_null(lys_parse_mem(ctx, "module kk2 {namespace urn:kk2;prefix kk2; container top {leaf a{type string;}}"
+                              "deviation /top {deviate replace {mandatory true;}}}", LYS_IN_YANG));
+    logbuf_assert("Invalid deviation (/top) replacing \"mandatory\" property \"mandatory true\" which is not present.");
+    assert_null(lys_parse_mem(ctx, "module kk3 {namespace urn:kk3;prefix kk3; container top {leaf x {type string;}}"
+                              "deviation /top/x {deviate replace {mandatory true;}}}", LYS_IN_YANG));
+    logbuf_assert("Invalid deviation (/top/x) replacing \"mandatory\" property \"mandatory true\" which is not present.");
+    assert_null(lys_parse_mem(ctx, "module kk4 {namespace urn:kk4;prefix kk4; leaf x {mandatory true; type string;}"
+                              "deviation /x {deviate add {mandatory false;}}}", LYS_IN_YANG));
+    logbuf_assert("Invalid deviation (/x) adding \"mandatory\" property which already exists (with value \"mandatory true\").");
 
     *state = NULL;
     ly_ctx_destroy(ctx, NULL);