diff --git a/lib/tests/modules/types-attrTag.nix b/lib/tests/modules/types-attrTag.nix index d48badae391f..167f839c027c 100644 --- a/lib/tests/modules/types-attrTag.nix +++ b/lib/tests/modules/types-attrTag.nix @@ -120,7 +120,17 @@ in assert config.merged.positive == { yay = 100; }; assert config.merged.extensi-foo == { extensible = "foo"; }; assert config.merged.extensi-bar == { extensible = "bar"; }; + assert config.docs."submodules..foo.bar".declarations == [ __curPos.file ]; assert config.docs."submodules..foo.bar".type == "signed integer"; + assert + lib.length + (options.submodules.type.nestedTypes.elemType.nestedTypes.foo.type.getSubOptions [ ]) + .bar.declarationPositions == 1; + assert + (lib.head + (options.submodules.type.nestedTypes.elemType.nestedTypes.foo.type.getSubOptions [ ]) + .bar.declarationPositions + ).file == __curPos.file; assert config.docs."submodules..qux".type == "string"; assert config.docs."submodules..qux".declarations == [ __curPos.file ]; assert @@ -133,8 +143,11 @@ in assert config.docs."submodules..qux".description == "A qux for when you don't want a foo"; assert config.docs."submodules..qux".readOnly == false; assert config.docs."submodules..qux".visible == true; - # Not available (yet?) - # assert config.docs."submodules..qux".declarationsWithPositions == [ ... ]; + assert + lib.length options.submodules.type.nestedTypes.elemType.nestedTypes.qux.declarationPositions == 1; + assert + (lib.head options.submodules.type.nestedTypes.elemType.nestedTypes.qux.declarationPositions).file + == __curPos.file; assert options.submodules.declarations == [ __curPos.file ]; assert lib.length options.submodules.declarationPositions == 1; assert (lib.head options.submodules.declarationPositions).file == __curPos.file; diff --git a/lib/types.nix b/lib/types.nix index e7a8763983a1..8ed7bec21713 100644 --- a/lib/types.nix +++ b/lib/types.nix @@ -1140,6 +1140,27 @@ rec { else throw "The option `${showOption loc}` is defined as ${lib.strings.escapeNixIdentifier choice}, but ${lib.strings.escapeNixIdentifier choice} is not among the valid choices (${choicesStr}). Value ${choice} was defined in ${showFiles (getFiles defs)}."; nestedTypes = tags; + getSubModules = + let + tagsWithSubModules = filterAttrs (_: mods: mods != null) ( + mapAttrs (_: opt: opt.type.getSubModules) tags + ); + in + if tagsWithSubModules == { } then null else [ tagsWithSubModules ]; + substSubModules = + allWrappedModules: + let + tagsWithNewTypes = zipAttrsWith (tag: tags.${tag}.type.substSubModules) ( + concatMap ( + { _file, imports }: map (mapAttrs (_: imports: { inherit _file imports; })) imports + ) allWrappedModules + ); + in + attrTag ( + mapAttrs ( + tag: opt: opt // (optionalAttrs (tagsWithNewTypes ? ${tag}) { type = tagsWithNewTypes.${tag}; }) + ) tags + ); functor = defaultFunctor "attrTag" // { type = { tags, ... }: lib.types.attrTag tags; payload = { inherit tags; };