summary refs log tree commit diff
diff options
context:
space:
mode:
authorLinus Heckemann <git@sphalerite.org>2021-09-29 11:03:31 +0200
committerGitHub <noreply@github.com>2021-09-29 11:03:31 +0200
commita3df3d05e5db17b26ff886f53a58761a547561cc (patch)
tree3048af8fedf275f86fe60185936381823faffdd8
parent40f34902e623ad3c130bbdb2a65c53610e933307 (diff)
parent681758d41588c27aaa6f3c3a72184dcc4d0aeba8 (diff)
downloadnixpkgs-a3df3d05e5db17b26ff886f53a58761a547561cc.tar
nixpkgs-a3df3d05e5db17b26ff886f53a58761a547561cc.tar.gz
nixpkgs-a3df3d05e5db17b26ff886f53a58761a547561cc.tar.bz2
nixpkgs-a3df3d05e5db17b26ff886f53a58761a547561cc.tar.lz
nixpkgs-a3df3d05e5db17b26ff886f53a58761a547561cc.tar.xz
nixpkgs-a3df3d05e5db17b26ff886f53a58761a547561cc.tar.zst
nixpkgs-a3df3d05e5db17b26ff886f53a58761a547561cc.zip
Merge pull request #131205 from Ma27/showdefs-overflow
lib/modules: improve errors for `options`/`config`-mixups
-rw-r--r--lib/generators.nix27
-rw-r--r--lib/modules.nix25
-rw-r--r--lib/options.nix4
-rw-r--r--lib/tests/misc.nix19
4 files changed, 66 insertions, 9 deletions
diff --git a/lib/generators.nix b/lib/generators.nix
index 0cec4d2dd62..79ae9055ce3 100644
--- a/lib/generators.nix
+++ b/lib/generators.nix
@@ -197,6 +197,30 @@ rec {
     */
   toYAML = {}@args: toJSON args;
 
+  withRecursion =
+    args@{
+      /* If this option is not null, the given value will stop evaluating at a certain depth */
+      depthLimit
+      /* If this option is true, an error will be thrown, if a certain given depth is exceeded */
+    , throwOnDepthLimit ? true
+    }:
+      assert builtins.isInt depthLimit;
+      let
+        transform = depth:
+          if depthLimit != null && depth > depthLimit then
+            if throwOnDepthLimit
+              then throw "Exceeded maximum eval-depth limit of ${toString depthLimit} while trying to evaluate with `generators.withRecursion'!"
+              else const "<unevaluated>"
+          else id;
+        mapAny = with builtins; depth: v:
+          let
+            evalNext = x: mapAny (depth + 1) (transform (depth + 1) x);
+          in
+            if isAttrs v then mapAttrs (const evalNext) v
+            else if isList v then map evalNext v
+            else transform (depth + 1) v;
+      in
+        mapAny 0;
 
   /* Pretty print a value, akin to `builtins.trace`.
     * Should probably be a builtin as well.
@@ -208,7 +232,8 @@ rec {
     allowPrettyValues ? false,
     /* If this option is true, the output is indented with newlines for attribute sets and lists */
     multiline ? true
-  }@args: let
+  }@args:
+    let
     go = indent: v: with builtins;
     let     isPath   = v: typeOf v == "path";
             introSpace = if multiline then "\n${indent}  " else " ";
diff --git a/lib/modules.nix b/lib/modules.nix
index b124ea000a2..46ae3f13631 100644
--- a/lib/modules.nix
+++ b/lib/modules.nix
@@ -162,13 +162,24 @@ rec {
             baseMsg = "The option `${showOption (prefix ++ firstDef.prefix)}' does not exist. Definition values:${showDefs [ firstDef ]}";
           in
             if attrNames options == [ "_module" ]
-              then throw ''
-                ${baseMsg}
-
-                However there are no options defined in `${showOption prefix}'. Are you sure you've
-                declared your options properly? This can happen if you e.g. declared your options in `types.submodule'
-                under `config' rather than `options'.
-              ''
+              then
+                let
+                  optionName = showOption prefix;
+                in
+                  if optionName == ""
+                    then throw ''
+                      ${baseMsg}
+
+                      It seems as if you're trying to declare an option by placing it into `config' rather than `options'!
+                    ''
+                  else
+                    throw ''
+                      ${baseMsg}
+
+                      However there are no options defined in `${showOption prefix}'. Are you sure you've
+                      declared your options properly? This can happen if you e.g. declared your options in `types.submodule'
+                      under `config' rather than `options'.
+                    ''
             else throw baseMsg
         else null;
 
diff --git a/lib/options.nix b/lib/options.nix
index 204c86df9f5..119a67fb7d8 100644
--- a/lib/options.nix
+++ b/lib/options.nix
@@ -247,7 +247,9 @@ rec {
   showDefs = defs: concatMapStrings (def:
     let
       # Pretty print the value for display, if successful
-      prettyEval = builtins.tryEval (lib.generators.toPretty {} def.value);
+      prettyEval = builtins.tryEval
+        (lib.generators.toPretty { }
+          (lib.generators.withRecursion { depthLimit = 10; throwOnDepthLimit = false; } def.value));
       # Split it into its lines
       lines = filter (v: ! isList v) (builtins.split "\n" prettyEval.value);
       # Only display the first 5 lines, and indent them for better visibility
diff --git a/lib/tests/misc.nix b/lib/tests/misc.nix
index 4b2e5afc1d6..00eeaa2a77d 100644
--- a/lib/tests/misc.nix
+++ b/lib/tests/misc.nix
@@ -529,6 +529,25 @@ runTests {
     };
   };
 
+  testToPrettyLimit =
+    let
+      a.b = 1;
+      a.c = a;
+    in {
+      expr = generators.toPretty { } (generators.withRecursion { throwOnDepthLimit = false; depthLimit = 2; } a);
+      expected = "{\n  b = 1;\n  c = {\n    b = \"<unevaluated>\";\n    c = {\n      b = \"<unevaluated>\";\n      c = \"<unevaluated>\";\n    };\n  };\n}";
+    };
+
+  testToPrettyLimitThrow =
+    let
+      a.b = 1;
+      a.c = a;
+    in {
+      expr = (builtins.tryEval
+        (generators.toPretty { } (generators.withRecursion { depthLimit = 2; } a))).success;
+      expected = false;
+    };
+
   testToPrettyMultiline = {
     expr = mapAttrs (const (generators.toPretty { })) rec {
       list = [ 3 4 [ false ] ];