From 0f54833b4a093521fe640a7bd8a67766d21e2db5 Mon Sep 17 00:00:00 2001 From: Winter Date: Thu, 11 Sep 2025 22:48:47 -0400 Subject: [PATCH] lib/modules: add suggestions to invalid option name errors This change introduces the suggesting of possible valid option names for a given invalid option, based on the keys of said invalid option's parent attrset. That is, `foo.bar.baz` will only be suggested keys from `foo.bar`, while `(config).baz` will only be suggested keys from the toplevel. --- lib/modules.nix | 15 ++++++++++++++- lib/tests/modules.sh | 4 ++++ lib/tests/modules/error-typo-nested.nix | 18 ++++++++++++++++++ .../modules/error-typo-outside-with-nested.nix | 18 ++++++++++++++++++ 4 files changed, 54 insertions(+), 1 deletion(-) create mode 100644 lib/tests/modules/error-typo-nested.nix create mode 100644 lib/tests/modules/error-typo-outside-with-nested.nix diff --git a/lib/modules.nix b/lib/modules.nix index c55d276d4970..fd5cf76eda03 100644 --- a/lib/modules.nix +++ b/lib/modules.nix @@ -20,12 +20,14 @@ let head id imap1 + init isAttrs isBool isFunction oldestSupportedReleaseIsAtLeast isList isString + last length mapAttrs mapAttrsToList @@ -35,6 +37,7 @@ let optionalAttrs optionalString recursiveUpdate + remove reverseList sort seq @@ -60,6 +63,7 @@ let ; inherit (lib.strings) isConvertibleWithToString + levenshtein ; showDeclPrefix = @@ -303,8 +307,17 @@ let addErrorContext "while evaluating the error message for definitions for `${optText}', which is an option that does not exist" (addErrorContext "while evaluating a definition from `${firstDef.file}'" (showDefs [ firstDef ])); + + prefix' = init (prefix ++ firstDef.prefix); + adj = attrNames (attrByPath prefix' { } options); + adj' = if prefix' == [ ] then remove "_module" adj else adj; + lev = levenshtein (last firstDef.prefix); + closest = if adj' == [ ] then null else head (sort (p: q: lev p < lev q) adj'); + suggestion = + optionalString (closest != null) + "\n\nDid you mean `${showOption (prefix' ++ [ closest ])}'?"; in - "The option `${optText}' does not exist. Definition values:${defText}"; + "The option `${optText}' does not exist. Definition values:${defText}${suggestion}"; in if attrNames options == [ "_module" ] diff --git a/lib/tests/modules.sh b/lib/tests/modules.sh index a4aa5201715c..69fb2aa9c7ae 100755 --- a/lib/tests/modules.sh +++ b/lib/tests/modules.sh @@ -741,6 +741,10 @@ checkConfigError 'attribute .*bar.* not found' config.sub.conditionalImportAsNix checkConfigError 'attribute .*foo.* not found' config.sub.conditionalImportAsDarwin.foo ./specialArgs-class.nix checkConfigOutput '"foo"' config.sub.conditionalImportAsDarwin.bar ./specialArgs-class.nix +# Option name suggestions +checkConfigError 'Did you mean .set\.enable.\?' config.set ./error-typo-nested.nix +checkConfigError 'Did you mean .set.\?' config ./error-typo-outside-with-nested.nix + cat <