aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJohn Ericson <John.Ericson@Obsidian.Systems>2023-07-23 11:03:38 -0400
committerGitHub <noreply@github.com>2023-07-23 11:03:38 -0400
commit4685c9b55f51f0bd0a55883753d19ec2c213ca7f (patch)
treedb6046ea0d8398f4bf2f9dee9be07f9953f905bb
parentfe1fbdb5a14a059763fcc7434f2cd1e483dc00e6 (diff)
parent570a1a3ad773d93aa2128a8fba49f98e1e115d5d (diff)
Merge pull request #8664 from ncfavier/merge-dynamic-attrs
parser: merge nested dynamic attributes
-rw-r--r--doc/manual/src/release-notes/rl-next.md19
-rw-r--r--src/libexpr/parser.y1
-rw-r--r--tests/lang/eval-fail-dup-dynamic-attrs.err.exp8
-rw-r--r--tests/lang/eval-fail-dup-dynamic-attrs.nix4
-rw-r--r--tests/lang/eval-okay-merge-dynamic-attrs.exp1
-rw-r--r--tests/lang/eval-okay-merge-dynamic-attrs.nix13
6 files changed, 46 insertions, 0 deletions
diff --git a/doc/manual/src/release-notes/rl-next.md b/doc/manual/src/release-notes/rl-next.md
index 139d07188..160245a31 100644
--- a/doc/manual/src/release-notes/rl-next.md
+++ b/doc/manual/src/release-notes/rl-next.md
@@ -6,3 +6,22 @@
- Nix now allows unprivileged/[`allowed-users`](../command-ref/conf-file.md#conf-allowed-users) to sign paths.
Previously, only [`trusted-users`](../command-ref/conf-file.md#conf-trusted-users) users could sign paths.
+
+- Nested dynamic attributes are now merged correctly by the parser. For example:
+
+ ```nix
+ {
+ nested = { foo = 1; };
+ nested = { ${"ba" + "r"} = 2; };
+ }
+ ```
+
+ This used to silently discard `nested.bar`, but now behaves as one would expect and evaluates to:
+
+ ```nix
+ { nested = { bar = 2; foo = 1; }; }
+ ```
+
+ Note that the feature of merging multiple attribute set declarations is of questionable value.
+ It allows writing expressions that are very hard to read, for instance when there are many lines of code between two declarations of the same attribute.
+ This has been around for a long time and is therefore supported for backwards compatibility, but should not be relied upon.
diff --git a/src/libexpr/parser.y b/src/libexpr/parser.y
index 0a1ad9967..217c17382 100644
--- a/src/libexpr/parser.y
+++ b/src/libexpr/parser.y
@@ -137,6 +137,7 @@ static void addAttr(ExprAttrs * attrs, AttrPath && attrPath,
dupAttr(state, ad.first, j2->second.pos, ad.second.pos);
jAttrs->attrs.emplace(ad.first, ad.second);
}
+ jAttrs->dynamicAttrs.insert(jAttrs->dynamicAttrs.end(), ae->dynamicAttrs.begin(), ae->dynamicAttrs.end());
} else {
dupAttr(state, attrPath, pos, j->second.pos);
}
diff --git a/tests/lang/eval-fail-dup-dynamic-attrs.err.exp b/tests/lang/eval-fail-dup-dynamic-attrs.err.exp
new file mode 100644
index 000000000..e01f8e6d0
--- /dev/null
+++ b/tests/lang/eval-fail-dup-dynamic-attrs.err.exp
@@ -0,0 +1,8 @@
+error: dynamic attribute 'b' already defined at /pwd/lang/eval-fail-dup-dynamic-attrs.nix:2:11
+
+ at /pwd/lang/eval-fail-dup-dynamic-attrs.nix:3:11:
+
+ 2| set = { "${"" + "b"}" = 1; };
+ 3| set = { "${"b" + ""}" = 2; };
+ | ^
+ 4| }
diff --git a/tests/lang/eval-fail-dup-dynamic-attrs.nix b/tests/lang/eval-fail-dup-dynamic-attrs.nix
new file mode 100644
index 000000000..7ea17f6c8
--- /dev/null
+++ b/tests/lang/eval-fail-dup-dynamic-attrs.nix
@@ -0,0 +1,4 @@
+{
+ set = { "${"" + "b"}" = 1; };
+ set = { "${"b" + ""}" = 2; };
+}
diff --git a/tests/lang/eval-okay-merge-dynamic-attrs.exp b/tests/lang/eval-okay-merge-dynamic-attrs.exp
new file mode 100644
index 000000000..157d677ce
--- /dev/null
+++ b/tests/lang/eval-okay-merge-dynamic-attrs.exp
@@ -0,0 +1 @@
+{ set1 = { a = 1; b = 2; }; set2 = { a = 1; b = 2; }; set3 = { a = 1; b = 2; }; set4 = { a = 1; b = 2; }; }
diff --git a/tests/lang/eval-okay-merge-dynamic-attrs.nix b/tests/lang/eval-okay-merge-dynamic-attrs.nix
new file mode 100644
index 000000000..f459a554f
--- /dev/null
+++ b/tests/lang/eval-okay-merge-dynamic-attrs.nix
@@ -0,0 +1,13 @@
+{
+ set1 = { a = 1; };
+ set1 = { "${"b" + ""}" = 2; };
+
+ set2 = { "${"b" + ""}" = 2; };
+ set2 = { a = 1; };
+
+ set3.a = 1;
+ set3."${"b" + ""}" = 2;
+
+ set4."${"b" + ""}" = 2;
+ set4.a = 1;
+}