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 <