lib.modules: Let module declare options directly in bare submodule

... where a bare submodule is an option that has a type like
`submoduleWith x`, as opposed to `attrsOf (submoduleWith x)`.

This makes migration unnecessary when introducing a freeform type
in an existing option tree.

Closes #146882
main
Robert Hensing 2 years ago
parent 33e8df0921
commit d030e2109f
  1. 22
      lib/modules.nix
  2. 5
      lib/tests/modules.sh
  3. 10
      lib/tests/modules/declare-bare-submodule-deep-option.nix
  4. 18
      lib/tests/modules/declare-bare-submodule-nested-option.nix
  5. 12
      lib/tests/modules/declare-bare-submodule.nix
  6. 4
      lib/tests/modules/define-bare-submodule-values.nix
  7. 5
      lib/types.nix

@ -474,6 +474,18 @@ rec {
[{ inherit (module) file; inherit value; }]
) configs;
# Convert an option tree decl to a submodule option decl
optionTreeToOption = decl:
if isOption decl.options
then decl
else decl // {
options = mkOption {
type = types.submoduleWith {
modules = [ { options = decl.options; } ];
};
};
};
resultsByName = mapAttrs (name: decls:
# We're descending into attribute ‘name’.
let
@ -493,7 +505,15 @@ rec {
firstOption = findFirst (m: isOption m.options) "" decls;
firstNonOption = findFirst (m: !isOption m.options) "" decls;
in
throw "The option `${showOption loc}' in `${firstOption._file}' is a prefix of options in `${firstNonOption._file}'."
if firstOption.options.type?isSubmodule && firstOption.options.type.isSubmodule
then
let opt = fixupOptionType loc (mergeOptionDecls loc (map optionTreeToOption decls));
in {
matchedOptions = evalOptionValue loc opt defns';
unmatchedDefns = [];
}
else
throw "The option `${showOption loc}' in `${firstOption._file}' is a prefix of options in `${firstNonOption._file}'."
else
mergeModules' loc decls defns) declsByName;

@ -62,6 +62,11 @@ checkConfigError() {
checkConfigOutput '^false$' config.enable ./declare-enable.nix
checkConfigError 'The option .* does not exist. Definition values:\n\s*- In .*: true' config.enable ./define-enable.nix
checkConfigOutput '^1$' config.bare-submodule.nested ./declare-bare-submodule.nix ./declare-bare-submodule-nested-option.nix
checkConfigOutput '^2$' config.bare-submodule.deep ./declare-bare-submodule.nix ./declare-bare-submodule-deep-option.nix
checkConfigOutput '^42$' config.bare-submodule.nested ./declare-bare-submodule.nix ./declare-bare-submodule-nested-option.nix ./declare-bare-submodule-deep-option.nix ./define-bare-submodule-values.nix
checkConfigOutput '^420$' config.bare-submodule.deep ./declare-bare-submodule.nix ./declare-bare-submodule-nested-option.nix ./declare-bare-submodule-deep-option.nix ./define-bare-submodule-values.nix
# Check integer types.
# unsigned
checkConfigOutput '^42$' config.value ./declare-int-unsigned-value.nix ./define-value-int-positive.nix

@ -0,0 +1,10 @@
{ lib, ... }:
let
inherit (lib) mkOption types;
in
{
options.bare-submodule.deep = mkOption {
type = types.int;
default = 2;
};
}

@ -0,0 +1,18 @@
{ lib, ... }:
let
inherit (lib) mkOption types;
in
{
options.bare-submodule = mkOption {
type = types.submoduleWith {
modules = [
{
options.nested = mkOption {
type = types.int;
default = 1;
};
}
];
};
};
}

@ -0,0 +1,12 @@
{ lib, ... }:
let
inherit (lib) mkOption types;
in
{
options.bare-submodule = mkOption {
type = types.submoduleWith {
modules = [ ];
};
default = {};
};
}

@ -0,0 +1,4 @@
{
bare-submodule.nested = 42;
bare-submodule.deep = 420;
}

@ -642,6 +642,11 @@ rec {
else throw "A submoduleWith option is declared multiple times with conflicting shorthandOnlyDefinesConfig values";
};
};
} // {
# Submodule-typed options get special treatment in order to facilitate
# certain migrations, such as the addition of freeformTypes onto
# existing option trees.
isSubmodule = true;
};
# A value from a set of allowed ones.

Loading…
Cancel
Save