Merge pull request #242339 from hercules-ci/modules-catch-bare-type

lib/modules: Report a good error when option tree has bare type

authored by

Silvan Mosberger and committed by
GitHub
f10a24ed 737212ec

+54 -1
+33 -1
lib/modules.nix
··· 630 630 loc = prefix ++ [name]; 631 631 defns = pushedDownDefinitionsByName.${name} or []; 632 632 defns' = rawDefinitionsByName.${name} or []; 633 - optionDecls = filter (m: isOption m.options) decls; 633 + optionDecls = filter 634 + (m: m.options?_type 635 + && (m.options._type == "option" 636 + || throwDeclarationTypeError loc m.options._type 637 + ) 638 + ) 639 + decls; 634 640 in 635 641 if length optionDecls == length decls then 636 642 let opt = fixupOptionType loc (mergeOptionDecls loc decls); ··· 691 697 }) defs 692 698 ) unmatchedDefnsByName); 693 699 }; 700 + 701 + throwDeclarationTypeError = loc: actualTag: 702 + let 703 + name = lib.strings.escapeNixIdentifier (lib.lists.last loc); 704 + path = showOption loc; 705 + depth = length loc; 706 + 707 + paragraphs = [ 708 + "Expected an option declaration at option path `${path}` but got an attribute set with type ${actualTag}" 709 + ] ++ optional (actualTag == "option-type") '' 710 + When declaring an option, you must wrap the type in a `mkOption` call. It should look somewhat like: 711 + ${comment} 712 + ${name} = lib.mkOption { 713 + description = ...; 714 + type = <the type you wrote for ${name}>; 715 + ... 716 + }; 717 + ''; 718 + 719 + # Ideally we'd know the exact syntax they used, but short of that, 720 + # we can only reliably repeat the last. However, we repeat the 721 + # full path in a non-misleading way here, in case they overlook 722 + # the start of the message. Examples attract attention. 723 + comment = optionalString (depth > 1) "\n # ${showOption loc}"; 724 + in 725 + throw (concatStringsSep "\n\n" paragraphs); 694 726 695 727 /* Merge multiple option declarations into a single declaration. In 696 728 general, there should be only one declaration of each option.
+5
lib/tests/modules.sh
··· 393 393 config.set \ 394 394 ./declare-set.nix ./declare-enable-nested.nix 395 395 396 + # Options: accidental use of an option-type instead of option (or other tagged type; unlikely) 397 + checkConfigError 'Expected an option declaration at option path .result. but got an attribute set with type option-type' config.result ./options-type-error-typical.nix 398 + checkConfigError 'Expected an option declaration at option path .result.here. but got an attribute set with type option-type' config.result.here ./options-type-error-typical-nested.nix 399 + checkConfigError 'Expected an option declaration at option path .result. but got an attribute set with type configuration' config.result ./options-type-error-configuration.nix 400 + 396 401 # Check that that merging of option collisions doesn't depend on type being set 397 402 checkConfigError 'The option .group..*would be a parent of the following options, but its type .<no description>. does not support nested options.\n\s*- option.s. with prefix .group.enable..*' config.group.enable ./merge-typeless-option.nix 398 403
+6
lib/tests/modules/options-type-error-configuration.nix
··· 1 + { lib, ... }: { 2 + options = { 3 + # unlikely mistake, but we can catch any attrset with _type 4 + result = lib.evalModules { modules = []; }; 5 + }; 6 + }
+5
lib/tests/modules/options-type-error-typical-nested.nix
··· 1 + { lib, ... }: { 2 + options = { 3 + result.here = lib.types.str; 4 + }; 5 + }
+5
lib/tests/modules/options-type-error-typical.nix
··· 1 + { lib, ... }: { 2 + options = { 3 + result = lib.types.str; 4 + }; 5 + }