diff --git a/lib/modules.nix b/lib/modules.nix index 42e88dbbf625..09f0908a69da 100644 --- a/lib/modules.nix +++ b/lib/modules.nix @@ -1126,6 +1126,29 @@ let __toString = _: showOption loc; }; + # Check that a type with v2 merge has a coherent check attribute. + # Throws an error if the type uses an ad-hoc `type // { check }` override. + # Returns the last argument like `seq`, allowing usage: checkV2MergeCoherence loc type expr + checkV2MergeCoherence = + loc: type: result: + if type.check.isV2MergeCoherent or false then + result + else + throw '' + The option `${showOption loc}' has a type `${type.description}' that uses + an ad-hoc `type // { check = ...; }' override, which is incompatible with + the v2 merge mechanism. + + Please use `lib.types.addCheck` instead of `type // { check }' to add + custom validation. For example: + + lib.types.addCheck baseType (value: /* your check */) + + instead of: + + baseType // { check = value: /* your check */; } + ''; + # Merge definitions of a value of a given type. mergeDefinitions = loc: type: defs: rec { defsFinal' = @@ -1201,10 +1224,13 @@ let ( if type.merge ? v2 then let - r = type.merge.v2 { - inherit loc; - defs = defsFinal; - }; + # Check for v2 merge coherence + r = checkV2MergeCoherence loc type ( + type.merge.v2 { + inherit loc; + defs = defsFinal; + } + ); in r // { diff --git a/lib/tests/modules.sh b/lib/tests/modules.sh index 5cd781752317..02f73310dd9b 100755 --- a/lib/tests/modules.sh +++ b/lib/tests/modules.sh @@ -806,6 +806,25 @@ checkConfigError 'A definition for option .* is not of type .signed integer.*' c checkConfigOutput '^true$' config.v2checkedPass ./add-check.nix checkConfigError 'A definition for option .* is not of type .attribute set of signed integer.*' config.v2checkedFail ./add-check.nix +# v2 merge check coherence +# Tests checkV2MergeCoherence call in modules.nix (mergeDefinitions for lazyAttrsOf) +checkConfigError 'ad-hoc.*override.*incompatible' config.adhocFail.foo ./v2-check-coherence.nix +# Tests checkV2MergeCoherence call in modules.nix (mergeDefinitions for lazyAttrsOf) +checkConfigError 'ad-hoc.*override.*incompatible' config.adhocOuterFail.bar ./v2-check-coherence.nix +# Tests checkV2MergeCoherence call in types.nix (either t1) +checkConfigError 'ad-hoc.*override.*incompatible' config.eitherLeftFail ./v2-check-coherence.nix +# Tests checkV2MergeCoherence call in types.nix (either t2) +checkConfigError 'ad-hoc.*override.*incompatible' config.eitherRightFail ./v2-check-coherence.nix +# Tests checkV2MergeCoherence call in types.nix (coercedTo coercedType) +checkConfigError 'ad-hoc.*override.*incompatible' config.coercedFromFail.bar ./v2-check-coherence.nix +# Tests checkV2MergeCoherence call in types.nix (coercedTo finalType) +checkConfigError 'ad-hoc.*override.*incompatible' config.coercedToFail.foo ./v2-check-coherence.nix +# Tests checkV2MergeCoherence call in types.nix (addCheck elemType) +checkConfigError 'ad-hoc.*override.*incompatible' config.addCheckNested.foo ./v2-check-coherence.nix +checkConfigError 'Please use.*lib.types.addCheck.*instead' config.adhocFail.foo ./v2-check-coherence.nix +checkConfigError 'A definition for option .* is not of type .*' config.addCheckFail.bar.baz ./v2-check-coherence.nix +checkConfigOutput '^true$' config.result ./v2-check-coherence.nix + cat <