Merge pull request #99132 from Infinisil/recursive-type-deprecation

Recursive type deprecation
This commit is contained in:
Robert Hensing 2021-05-05 11:13:37 +02:00 committed by GitHub
commit ce93c98ce2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 83 additions and 11 deletions

View File

@ -515,11 +515,20 @@ rec {
# yield a value computed from the definitions # yield a value computed from the definitions
value = if opt ? apply then opt.apply res.mergedValue else res.mergedValue; value = if opt ? apply then opt.apply res.mergedValue else res.mergedValue;
warnDeprecation = # Issue deprecation warnings recursively over all nested types of the
warnIf (opt.type.deprecationMessage != null) # given type. But don't recurse if a type with the same name was already
"The type `types.${opt.type.name}' of option `${showOption loc}' defined in ${showFiles opt.declarations} is deprecated. ${opt.type.deprecationMessage}"; # 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; { value = builtins.addErrorContext "while evaluating the option `${showOption loc}':" value;
inherit (res.defsFinal') highestPrio; inherit (res.defsFinal') highestPrio;
definitions = map (def: def.value) res.defsFinal; definitions = map (def: def.value) res.defsFinal;

View File

@ -175,6 +175,9 @@ checkConfigOutput "true" config.submodule.config ./declare-submoduleWith-noshort
## submoduleWith should merge all modules in one swoop ## submoduleWith should merge all modules in one swoop
checkConfigOutput "true" config.submodule.inner ./declare-submoduleWith-modules.nix checkConfigOutput "true" config.submodule.inner ./declare-submoduleWith-modules.nix
checkConfigOutput "true" config.submodule.outer ./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 ## Paths should be allowed as values and work as expected
checkConfigOutput "true" config.submodule.enable ./declare-submoduleWith-path.nix 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 "b a" config.result ./functionTo/list-order.nix
checkConfigOutput "a c" config.result ./functionTo/merging-attrs.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 <<EOF cat <<EOF
====== module tests ====== ====== module tests ======
$pass Pass $pass Pass

View File

@ -8,9 +8,6 @@
default = false; default = false;
}; };
} }
{
outer = true;
}
]; ];
}; };
default = {}; default = {};
@ -25,6 +22,7 @@
}) })
{ {
inner = true; inner = true;
outer = true;
} }
]; ];
} }

View File

@ -0,0 +1,39 @@
{ lib, ... }: {
options.simple = lib.mkOption {
type = lib.mkOptionType {
name = "simple";
deprecationMessage = "simple shall not be used";
};
default = throw "";
};
options.infinite = lib.mkOption {
type =
let
t = lib.mkOptionType {
name = "infinite";
deprecationMessage = "infinite shall not be used";
};
r = lib.types.either t (lib.types.attrsOf r);
in r;
default = throw "";
};
options.nested = lib.mkOption {
type =
let
left = lib.mkOptionType {
name = "left";
deprecationMessage = "left shall not be used";
};
right = lib.mkOptionType {
name = "right";
deprecationMessage = "right shall not be used";
};
in lib.types.either left right;
default = throw "";
};
}

View File

@ -147,9 +147,13 @@ rec {
, # The deprecation message to display when this type is used by an option , # The deprecation message to display when this type is used by an option
# If null, the type isn't deprecated # If null, the type isn't deprecated
deprecationMessage ? null deprecationMessage ? null
, # The types that occur in the definition of this type. This is used to
# issue deprecation warnings recursively. Can also be used to reuse
# nested types
nestedTypes ? {}
}: }:
{ _type = "option-type"; { _type = "option-type";
inherit name check merge emptyValue getSubOptions getSubModules substSubModules typeMerge functor deprecationMessage; inherit name check merge emptyValue getSubOptions getSubModules substSubModules typeMerge functor deprecationMessage nestedTypes;
description = if description == null then name else description; description = if description == null then name else description;
}; };
@ -365,6 +369,7 @@ rec {
getSubModules = elemType.getSubModules; getSubModules = elemType.getSubModules;
substSubModules = m: listOf (elemType.substSubModules m); substSubModules = m: listOf (elemType.substSubModules m);
functor = (defaultFunctor name) // { wrapped = elemType; }; functor = (defaultFunctor name) // { wrapped = elemType; };
nestedTypes.elemType = elemType;
}; };
nonEmptyListOf = elemType: nonEmptyListOf = elemType:
@ -389,6 +394,7 @@ rec {
getSubModules = elemType.getSubModules; getSubModules = elemType.getSubModules;
substSubModules = m: attrsOf (elemType.substSubModules m); substSubModules = m: attrsOf (elemType.substSubModules m);
functor = (defaultFunctor name) // { wrapped = elemType; }; functor = (defaultFunctor name) // { wrapped = elemType; };
nestedTypes.elemType = elemType;
}; };
# A version of attrsOf that's lazy in its values at the expense of # A version of attrsOf that's lazy in its values at the expense of
@ -413,6 +419,7 @@ rec {
getSubModules = elemType.getSubModules; getSubModules = elemType.getSubModules;
substSubModules = m: lazyAttrsOf (elemType.substSubModules m); substSubModules = m: lazyAttrsOf (elemType.substSubModules m);
functor = (defaultFunctor name) // { wrapped = elemType; }; functor = (defaultFunctor name) // { wrapped = elemType; };
nestedTypes.elemType = elemType;
}; };
# TODO: drop this in the future: # TODO: drop this in the future:
@ -421,6 +428,7 @@ rec {
deprecationMessage = "Mixing lists with attribute values is no longer" deprecationMessage = "Mixing lists with attribute values is no longer"
+ " possible; please use `types.attrsOf` instead. See" + " possible; please use `types.attrsOf` instead. See"
+ " https://github.com/NixOS/nixpkgs/issues/1800 for the motivation."; + " https://github.com/NixOS/nixpkgs/issues/1800 for the motivation.";
nestedTypes.elemType = elemType;
}; };
# Value of given type but with no merging (i.e. `uniq list`s are not concatenated). # Value of given type but with no merging (i.e. `uniq list`s are not concatenated).
@ -433,6 +441,7 @@ rec {
getSubModules = elemType.getSubModules; getSubModules = elemType.getSubModules;
substSubModules = m: uniq (elemType.substSubModules m); substSubModules = m: uniq (elemType.substSubModules m);
functor = (defaultFunctor name) // { wrapped = elemType; }; functor = (defaultFunctor name) // { wrapped = elemType; };
nestedTypes.elemType = elemType;
}; };
# Null or value of ... # Null or value of ...
@ -451,6 +460,7 @@ rec {
getSubModules = elemType.getSubModules; getSubModules = elemType.getSubModules;
substSubModules = m: nullOr (elemType.substSubModules m); substSubModules = m: nullOr (elemType.substSubModules m);
functor = (defaultFunctor name) // { wrapped = elemType; }; functor = (defaultFunctor name) // { wrapped = elemType; };
nestedTypes.elemType = elemType;
}; };
functionTo = elemType: mkOptionType { functionTo = elemType: mkOptionType {
@ -535,6 +545,9 @@ rec {
substSubModules = m: submoduleWith (attrs // { substSubModules = m: submoduleWith (attrs // {
modules = m; modules = m;
}); });
nestedTypes = lib.optionalAttrs (freeformType != null) {
freeformType = freeformType;
};
functor = defaultFunctor name // { functor = defaultFunctor name // {
type = types.submoduleWith; type = types.submoduleWith;
payload = { payload = {
@ -596,6 +609,8 @@ rec {
then functor.type mt1 mt2 then functor.type mt1 mt2
else null; else null;
functor = (defaultFunctor name) // { wrapped = [ t1 t2 ]; }; functor = (defaultFunctor name) // { wrapped = [ t1 t2 ]; };
nestedTypes.left = t1;
nestedTypes.right = t2;
}; };
# Any of the types in the given list # Any of the types in the given list
@ -627,6 +642,8 @@ rec {
substSubModules = m: coercedTo coercedType coerceFunc (finalType.substSubModules m); substSubModules = m: coercedTo coercedType coerceFunc (finalType.substSubModules m);
typeMerge = t1: t2: null; typeMerge = t1: t2: null;
functor = (defaultFunctor name) // { wrapped = finalType; }; functor = (defaultFunctor name) // { wrapped = finalType; };
nestedTypes.coercedType = coercedType;
nestedTypes.finalType = finalType;
}; };
# Obsolete alternative to configOf. It takes its option # Obsolete alternative to configOf. It takes its option

View File

@ -37,7 +37,7 @@ in
enableProfilePath = mkEnableOption "exposing the Disnix profiles in the system's PATH"; enableProfilePath = mkEnableOption "exposing the Disnix profiles in the system's PATH";
profiles = mkOption { profiles = mkOption {
type = types.listOf types.string; type = types.listOf types.str;
default = [ "default" ]; default = [ "default" ];
example = [ "default" ]; example = [ "default" ];
description = "Names of the Disnix profiles to expose in the system's PATH"; description = "Names of the Disnix profiles to expose in the system's PATH";

View File

@ -112,7 +112,7 @@ let
http://tools.ietf.org/html/rfc4366#section-3.1 http://tools.ietf.org/html/rfc4366#section-3.1
''; '';
}; };
name = mkOpt types.string '' name = mkOpt types.str ''
Name of the remote read config, which if specified must be unique among remote read configs. Name of the remote read config, which if specified must be unique among remote read configs.
The name will be used in metrics and logging in place of a generated value to help users distinguish between The name will be used in metrics and logging in place of a generated value to help users distinguish between
remote read configs. remote read configs.
@ -174,7 +174,7 @@ let
write_relabel_configs = mkOpt (types.listOf promTypes.relabel_config) '' write_relabel_configs = mkOpt (types.listOf promTypes.relabel_config) ''
List of remote write relabel configurations. List of remote write relabel configurations.
''; '';
name = mkOpt types.string '' name = mkOpt types.str ''
Name of the remote write config, which if specified must be unique among remote write configs. Name of the remote write config, which if specified must be unique among remote write configs.
The name will be used in metrics and logging in place of a generated value to help users distinguish between The name will be used in metrics and logging in place of a generated value to help users distinguish between
remote write configs. remote write configs.