diff options
author | Silvan Mosberger <contact@infinisil.com> | 2020-07-25 19:54:37 +0200 |
---|---|---|
committer | Profpatsch <mail@profpatsch.de> | 2022-04-01 22:03:05 +0200 |
commit | 1c00bf394867b07ed7a908408d8bc1d0afd9fa49 (patch) | |
tree | d7917805b5fdc34b7db02095d30792f26e9f122d /lib/customisation.nix | |
parent | f8c1aee5dac656dac5bab6de54b06ea2ccf3242a (diff) | |
download | nixlib-1c00bf394867b07ed7a908408d8bc1d0afd9fa49.tar nixlib-1c00bf394867b07ed7a908408d8bc1d0afd9fa49.tar.gz nixlib-1c00bf394867b07ed7a908408d8bc1d0afd9fa49.tar.bz2 nixlib-1c00bf394867b07ed7a908408d8bc1d0afd9fa49.tar.lz nixlib-1c00bf394867b07ed7a908408d8bc1d0afd9fa49.tar.xz nixlib-1c00bf394867b07ed7a908408d8bc1d0afd9fa49.tar.zst nixlib-1c00bf394867b07ed7a908408d8bc1d0afd9fa49.zip |
lib/customization: Improve callPackage error message for missing args
This uses the levenshtein distance to look through all possible arguments to find ones that are close to what was requested: error: Function in /home/infinisil/src/nixpkgs/pkgs/tools/text/ripgrep/default.nix called without required argument "fetchFromGithub", did you mean "fetchFromGitHub" or "fetchFromGitLab"? With https://github.com/NixOS/nix/pull/3468 (in current nixUnstable) the error message becomes even better, adding line location info
Diffstat (limited to 'lib/customisation.nix')
-rw-r--r-- | lib/customisation.nix | 51 |
1 files changed, 49 insertions, 2 deletions
diff --git a/lib/customisation.nix b/lib/customisation.nix index 234a528527d3..cc9a9b1c55d0 100644 --- a/lib/customisation.nix +++ b/lib/customisation.nix @@ -117,8 +117,55 @@ rec { callPackageWith = autoArgs: fn: args: let f = if lib.isFunction fn then fn else import fn; - auto = builtins.intersectAttrs (lib.functionArgs f) autoArgs; - in makeOverridable f (auto // args); + fargs = lib.functionArgs f; + + # All arguments that will be passed to the function + # This includes automatic ones and ones passed explicitly + allArgs = builtins.intersectAttrs fargs autoArgs // args; + + # A list of argument names that the function requires, but + # wouldn't be passed to it + missingArgs = lib.attrNames + # Filter out arguments that have a default value + (lib.filterAttrs (name: value: ! value) + # Filter out arguments that would be passed + (removeAttrs fargs (lib.attrNames allArgs))); + + # Get a list of suggested argument names for a given missing one + getSuggestions = arg: lib.pipe (autoArgs // args) [ + lib.attrNames + # Only use ones that are at most 2 edits away. While mork would work, + # levenshteinAtMost is only fast for 2 or less. + (lib.filter (lib.strings.levenshteinAtMost 2 arg)) + # Put strings with shorter distance first + (lib.sort (x: y: lib.strings.levenshtein x arg < lib.strings.levenshtein y arg)) + # Only take the first couple results + (lib.take 3) + # Quote all entries + (map (x: "\"" + x + "\"")) + ]; + + prettySuggestions = suggestions: + if suggestions == [] then "" + else if lib.length suggestions == 1 then ", did you mean ${lib.elemAt suggestions 0}?" + else ", did you mean ${lib.concatStringsSep ", " (lib.init suggestions)} or ${lib.last suggestions}?"; + + errorForArg = arg: + let + loc = builtins.unsafeGetAttrPos arg fargs; + # loc' can be removed once lib/minver.nix is >2.3.4, since that includes + # https://github.com/NixOS/nix/pull/3468 which makes loc be non-null + loc' = if loc != null then loc.file + ":" + toString loc.line + else if ! lib.isFunction fn then + toString fn + lib.optionalString (lib.sources.pathIsDirectory fn) "/default.nix" + else "<unknown location>"; + in "Function called without required argument \"${arg}\" at " + + "${loc'}${prettySuggestions (getSuggestions arg)}"; + + # Only show the error for the first missing argument + error = errorForArg (lib.head missingArgs); + + in if missingArgs == [] then makeOverridable f allArgs else throw error; /* Like callPackage, but for a function that returns an attribute |