diff --git a/lib/modules.nix b/lib/modules.nix index d515ee24d16e..04b65d791b58 100644 --- a/lib/modules.nix +++ b/lib/modules.nix @@ -515,11 +515,20 @@ rec { # yield a value computed from the definitions value = if opt ? apply then opt.apply res.mergedValue else res.mergedValue; - warnDeprecation = - warnIf (opt.type.deprecationMessage != null) - "The type `types.${opt.type.name}' of option `${showOption loc}' defined in ${showFiles opt.declarations} is deprecated. ${opt.type.deprecationMessage}"; + # Issue deprecation warnings recursively over all nested types of the + # given type. But don't recurse if a type with the same name was already + # visited before in order to prevent infinite recursion. So this only + # warns once per type name. + # Returns the new set of visited type names + recursiveWarn = visited: type: + let + maybeWarn = warnIf (type.deprecationMessage != null) + "The type `types.${type.name}' of option `${showOption loc}' defined in ${showFiles opt.declarations} is deprecated. ${type.deprecationMessage}"; + in + if visited ? ${type.name} then visited + else lib.foldl' recursiveWarn (maybeWarn visited // { ${type.name} = null; }) (lib.attrValues type.nestedTypes); - in warnDeprecation opt // + in builtins.seq (recursiveWarn {} opt.type) opt // { value = builtins.addErrorContext "while evaluating the option `${showOption loc}':" value; inherit (res.defsFinal') highestPrio; definitions = map (def: def.value) res.defsFinal; diff --git a/lib/tests/modules.sh b/lib/tests/modules.sh index 2eddeec07b1a..4259c538bb43 100755 --- a/lib/tests/modules.sh +++ b/lib/tests/modules.sh @@ -175,6 +175,9 @@ checkConfigOutput "true" config.submodule.config ./declare-submoduleWith-noshort ## submoduleWith should merge all modules in one swoop checkConfigOutput "true" config.submodule.inner ./declare-submoduleWith-modules.nix checkConfigOutput "true" config.submodule.outer ./declare-submoduleWith-modules.nix +# Should also be able to evaluate the type name (which evaluates freeformType, +# which evaluates all the modules defined by the type) +checkConfigOutput "submodule" options.submodule.type.description ./declare-submoduleWith-modules.nix ## Paths should be allowed as values and work as expected checkConfigOutput "true" config.submodule.enable ./declare-submoduleWith-path.nix @@ -269,6 +272,12 @@ checkConfigError 'A definition for option .fun.\[function body\]. is not of type checkConfigOutput "b a" config.result ./functionTo/list-order.nix checkConfigOutput "a c" config.result ./functionTo/merging-attrs.nix +## Type deprecation +checkConfigError 'The type `types.simple'\'' of option `simple'\'' defined in .* is deprecated. simple shall not be used' config.simple ./type-deprecation.nix +checkConfigError 'The type `types.infinite'\'' of option `infinite'\'' defined in .* is deprecated. infinite shall not be used' config.infinite ./type-deprecation.nix +checkConfigError 'The type `types.left'\'' of option `nested'\'' defined in .* is deprecated. left shall not be used' config.nested ./type-deprecation.nix +checkConfigError 'The type `types.right'\'' of option `nested'\'' defined in .* is deprecated. right shall not be used' config.nested ./type-deprecation.nix + cat <