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
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;

View file

@ -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 <<EOF
====== module tests ======
$pass Pass

View file

@ -8,9 +8,6 @@
default = false;
};
}
{
outer = true;
}
];
};
default = {};
@ -25,6 +22,7 @@
})
{
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
# If null, the type isn't deprecated
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";
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;
};
@ -365,6 +369,7 @@ rec {
getSubModules = elemType.getSubModules;
substSubModules = m: listOf (elemType.substSubModules m);
functor = (defaultFunctor name) // { wrapped = elemType; };
nestedTypes.elemType = elemType;
};
nonEmptyListOf = elemType:
@ -389,6 +394,7 @@ rec {
getSubModules = elemType.getSubModules;
substSubModules = m: attrsOf (elemType.substSubModules m);
functor = (defaultFunctor name) // { wrapped = elemType; };
nestedTypes.elemType = elemType;
};
# A version of attrsOf that's lazy in its values at the expense of
@ -413,6 +419,7 @@ rec {
getSubModules = elemType.getSubModules;
substSubModules = m: lazyAttrsOf (elemType.substSubModules m);
functor = (defaultFunctor name) // { wrapped = elemType; };
nestedTypes.elemType = elemType;
};
# TODO: drop this in the future:
@ -421,6 +428,7 @@ rec {
deprecationMessage = "Mixing lists with attribute values is no longer"
+ " possible; please use `types.attrsOf` instead. See"
+ " 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).
@ -433,6 +441,7 @@ rec {
getSubModules = elemType.getSubModules;
substSubModules = m: uniq (elemType.substSubModules m);
functor = (defaultFunctor name) // { wrapped = elemType; };
nestedTypes.elemType = elemType;
};
# Null or value of ...
@ -451,6 +460,7 @@ rec {
getSubModules = elemType.getSubModules;
substSubModules = m: nullOr (elemType.substSubModules m);
functor = (defaultFunctor name) // { wrapped = elemType; };
nestedTypes.elemType = elemType;
};
functionTo = elemType: mkOptionType {
@ -535,6 +545,9 @@ rec {
substSubModules = m: submoduleWith (attrs // {
modules = m;
});
nestedTypes = lib.optionalAttrs (freeformType != null) {
freeformType = freeformType;
};
functor = defaultFunctor name // {
type = types.submoduleWith;
payload = {
@ -596,6 +609,8 @@ rec {
then functor.type mt1 mt2
else null;
functor = (defaultFunctor name) // { wrapped = [ t1 t2 ]; };
nestedTypes.left = t1;
nestedTypes.right = t2;
};
# Any of the types in the given list
@ -627,6 +642,8 @@ rec {
substSubModules = m: coercedTo coercedType coerceFunc (finalType.substSubModules m);
typeMerge = t1: t2: null;
functor = (defaultFunctor name) // { wrapped = finalType; };
nestedTypes.coercedType = coercedType;
nestedTypes.finalType = finalType;
};
# 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";
profiles = mkOption {
type = types.listOf types.string;
type = types.listOf types.str;
default = [ "default" ];
example = [ "default" ];
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
'';
};
name = mkOpt types.string ''
name = mkOpt types.str ''
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
remote read configs.
@ -174,7 +174,7 @@ let
write_relabel_configs = mkOpt (types.listOf promTypes.relabel_config) ''
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.
The name will be used in metrics and logging in place of a generated value to help users distinguish between
remote write configs.