about summary refs log tree commit diff
path: root/nixpkgs/lib
diff options
context:
space:
mode:
authorAlyssa Ross <hi@alyssa.is>2023-08-23 10:09:14 +0000
committerAlyssa Ross <hi@alyssa.is>2023-08-26 09:07:03 +0000
commit63dabcc77ef9a56655e1ca2ab2e25e6163a72c1f (patch)
treed58934cb48f9c953b19a0d0d5cffc0d0c5561471 /nixpkgs/lib
parentc4eef3dacb2a3d359561f30917d9e3cc4e041be9 (diff)
parent91a22f76cd1716f9d0149e8a5c68424bb691de15 (diff)
downloadnixlib-63dabcc77ef9a56655e1ca2ab2e25e6163a72c1f.tar
nixlib-63dabcc77ef9a56655e1ca2ab2e25e6163a72c1f.tar.gz
nixlib-63dabcc77ef9a56655e1ca2ab2e25e6163a72c1f.tar.bz2
nixlib-63dabcc77ef9a56655e1ca2ab2e25e6163a72c1f.tar.lz
nixlib-63dabcc77ef9a56655e1ca2ab2e25e6163a72c1f.tar.xz
nixlib-63dabcc77ef9a56655e1ca2ab2e25e6163a72c1f.tar.zst
nixlib-63dabcc77ef9a56655e1ca2ab2e25e6163a72c1f.zip
Merge branch 'nixos-unstable' of https://github.com/NixOS/nixpkgs
Conflicts:
	nixpkgs/pkgs/build-support/go/module.nix
	nixpkgs/pkgs/development/python-modules/django-mailman3/default.nix
Diffstat (limited to 'nixpkgs/lib')
-rw-r--r--nixpkgs/lib/customisation.nix38
-rw-r--r--nixpkgs/lib/default.nix4
-rw-r--r--nixpkgs/lib/lists.nix34
-rw-r--r--nixpkgs/lib/meta.nix25
-rw-r--r--nixpkgs/lib/modules.nix34
-rw-r--r--nixpkgs/lib/path/default.nix320
-rw-r--r--nixpkgs/lib/path/tests/default.nix9
-rw-r--r--nixpkgs/lib/strings.nix4
-rw-r--r--nixpkgs/lib/systems/parse.nix2
-rw-r--r--nixpkgs/lib/tests/misc.nix59
-rwxr-xr-xnixpkgs/lib/tests/modules.sh5
-rw-r--r--nixpkgs/lib/tests/modules/options-type-error-configuration.nix6
-rw-r--r--nixpkgs/lib/tests/modules/options-type-error-typical-nested.nix5
-rw-r--r--nixpkgs/lib/tests/modules/options-type-error-typical.nix5
-rw-r--r--nixpkgs/lib/trivial.nix4
-rw-r--r--nixpkgs/lib/types.nix10
16 files changed, 378 insertions, 186 deletions
diff --git a/nixpkgs/lib/customisation.nix b/nixpkgs/lib/customisation.nix
index a9281b1ab698..c74708fbe4e2 100644
--- a/nixpkgs/lib/customisation.nix
+++ b/nixpkgs/lib/customisation.nix
@@ -269,17 +269,33 @@ rec {
     let self = f self // {
           newScope = scope: newScope (self // scope);
           callPackage = self.newScope {};
-          overrideScope = g: lib.warn
-            "`overrideScope` (from `lib.makeScope`) is deprecated. Do `overrideScope' (self: super: { … })` instead of `overrideScope (super: self: { … })`. All other overrides have the parameters in that order, including other definitions of `overrideScope`. This was the only definition violating the pattern."
-            (makeScope newScope (lib.fixedPoints.extends (lib.flip g) f));
-          overrideScope' = g: makeScope newScope (lib.fixedPoints.extends g f);
+          overrideScope = g: makeScope newScope (lib.fixedPoints.extends g f);
+          # Remove after 24.11 is released.
+          overrideScope' = g: lib.warnIf (lib.isInOldestRelease 2311)
+            "`overrideScope'` (from `lib.makeScope`) has been renamed to `overrideScope`."
+            (makeScope newScope (lib.fixedPoints.extends g f));
           packages = f;
         };
     in self;
 
+  /* backward compatibility with old uncurried form; deprecated */
+  makeScopeWithSplicing =
+    splicePackages: newScope: otherSplices: keep: extra: f:
+    makeScopeWithSplicing' {
+      inherit splicePackages newScope otherSplices keep extra f;
+    };
+
   /* Like the above, but aims to support cross compilation. It's still ugly, but
      hopefully it helps a little bit. */
-  makeScopeWithSplicing = splicePackages: newScope: otherSplices: keep: extra: f:
+  makeScopeWithSplicing' =
+    { splicePackages
+    , newScope
+    }:
+    { otherSplices
+    , keep ? (_self: {})
+    , extra ? (_spliced0: {})
+    , f
+    }:
     let
       spliced0 = splicePackages {
         pkgsBuildBuild = otherSplices.selfBuildBuild;
@@ -295,13 +311,11 @@ rec {
         callPackage = newScope spliced; # == self.newScope {};
         # N.B. the other stages of the package set spliced in are *not*
         # overridden.
-        overrideScope = g: makeScopeWithSplicing
-          splicePackages
-          newScope
-          otherSplices
-          keep
-          extra
-          (lib.fixedPoints.extends g f);
+        overrideScope = g: (makeScopeWithSplicing'
+          { inherit splicePackages newScope; }
+          { inherit otherSplices keep extra;
+            f = lib.fixedPoints.extends g f;
+          });
         packages = f;
       };
     in self;
diff --git a/nixpkgs/lib/default.nix b/nixpkgs/lib/default.nix
index 73b8ad871544..136f4a4a4637 100644
--- a/nixpkgs/lib/default.nix
+++ b/nixpkgs/lib/default.nix
@@ -112,11 +112,11 @@ let
       noDepEntry fullDepEntry packEntry stringAfter;
     inherit (self.customisation) overrideDerivation makeOverridable
       callPackageWith callPackagesWith extendDerivation hydraJob
-      makeScope makeScopeWithSplicing;
+      makeScope makeScopeWithSplicing makeScopeWithSplicing';
     inherit (self.derivations) lazyDerivation;
     inherit (self.meta) addMetaAttrs dontDistribute setName updateName
       appendToName mapDerivationAttrset setPrio lowPrio lowPrioSet hiPrio
-      hiPrioSet getLicenseFromSpdxId getExe;
+      hiPrioSet getLicenseFromSpdxId getExe getExe';
     inherit (self.filesystem) pathType pathIsDirectory pathIsRegularFile;
     inherit (self.sources) cleanSourceFilter
       cleanSource sourceByRegex sourceFilesBySuffices
diff --git a/nixpkgs/lib/lists.nix b/nixpkgs/lib/lists.nix
index 3e058b3f1aef..0800aeb65451 100644
--- a/nixpkgs/lib/lists.nix
+++ b/nixpkgs/lib/lists.nix
@@ -638,6 +638,40 @@ rec {
     # Input list
     list: sublist count (length list) list;
 
+  /* Whether the first list is a prefix of the second list.
+
+  Type: hasPrefix :: [a] -> [a] -> bool
+
+  Example:
+    hasPrefix [ 1 2 ] [ 1 2 3 4 ]
+    => true
+    hasPrefix [ 0 1 ] [ 1 2 3 4 ]
+    => false
+  */
+  hasPrefix =
+    list1:
+    list2:
+    take (length list1) list2 == list1;
+
+  /* Remove the first list as a prefix from the second list.
+  Error if the first list isn't a prefix of the second list.
+
+  Type: removePrefix :: [a] -> [a] -> [a]
+
+  Example:
+    removePrefix [ 1 2 ] [ 1 2 3 4 ]
+    => [ 3 4 ]
+    removePrefix [ 0 1 ] [ 1 2 3 4 ]
+    => <error>
+  */
+  removePrefix =
+    list1:
+    list2:
+    if hasPrefix list1 list2 then
+      drop (length list1) list2
+    else
+      throw "lib.lists.removePrefix: First argument is not a list prefix of the second argument";
+
   /* Return a list consisting of at most `count` elements of `list`,
      starting at index `start`.
 
diff --git a/nixpkgs/lib/meta.nix b/nixpkgs/lib/meta.nix
index 21404b3a2bfa..44730a71551e 100644
--- a/nixpkgs/lib/meta.nix
+++ b/nixpkgs/lib/meta.nix
@@ -143,9 +143,24 @@ rec {
        => "/nix/store/am9ml4f4ywvivxnkiaqwr0hyxka1xjsf-mustache-go-1.3.0/bin/mustache"
   */
   getExe = x:
-    "${lib.getBin x}/bin/${x.meta.mainProgram or (
-      # This could be turned into an error when 23.05 is at end of life
-      lib.warn "getExe: Package ${lib.strings.escapeNixIdentifier x.meta.name or x.pname or x.name} does not have the meta.mainProgram attribute. We'll assume that the main program has the same name for now, but this behavior is deprecated, because it leads to surprising errors when the assumption does not hold. If the package has a main program, please set `meta.mainProgram` in its definition to make this warning go away. Otherwise, if the package does not have a main program, or if you don't control its definition, specify the full path to the program, such as \"\${lib.getBin foo}/bin/bar\"."
-      lib.getName x
-    )}";
+    let
+      y = x.meta.mainProgram or (
+        # This could be turned into an error when 23.05 is at end of life
+        lib.warn "getExe: Package ${lib.strings.escapeNixIdentifier x.meta.name or x.pname or x.name} does not have the meta.mainProgram attribute. We'll assume that the main program has the same name for now, but this behavior is deprecated, because it leads to surprising errors when the assumption does not hold. If the package has a main program, please set `meta.mainProgram` in its definition to make this warning go away. Otherwise, if the package does not have a main program, or if you don't control its definition, use getExe' to specify the name to the program, such as lib.getExe' foo \"bar\"."
+          lib.getName
+          x
+      );
+    in
+    getExe' x y;
+
+  /* Get the path of a program of a derivation.
+
+     Type: getExe' :: derivation -> string -> string
+     Example:
+       getExe' pkgs.hello "hello"
+       => "/nix/store/g124820p9hlv4lj8qplzxw1c44dxaw1k-hello-2.12/bin/hello"
+       getExe' pkgs.imagemagick "convert"
+       => "/nix/store/5rs48jamq7k6sal98ymj9l4k2bnwq515-imagemagick-7.1.1-15/bin/convert"
+  */
+  getExe' = x: y: "${lib.getBin x}/bin/${y}";
 }
diff --git a/nixpkgs/lib/modules.nix b/nixpkgs/lib/modules.nix
index 4966619f6630..5c2fb48868c1 100644
--- a/nixpkgs/lib/modules.nix
+++ b/nixpkgs/lib/modules.nix
@@ -630,7 +630,13 @@ let
           loc = prefix ++ [name];
           defns = pushedDownDefinitionsByName.${name} or [];
           defns' = rawDefinitionsByName.${name} or [];
-          optionDecls = filter (m: isOption m.options) decls;
+          optionDecls = filter
+            (m: m.options?_type
+                && (m.options._type == "option"
+                    || throwDeclarationTypeError loc m.options._type m._file
+                )
+            )
+            decls;
         in
           if length optionDecls == length decls then
             let opt = fixupOptionType loc (mergeOptionDecls loc decls);
@@ -692,6 +698,32 @@ let
           ) unmatchedDefnsByName);
     };
 
+  throwDeclarationTypeError = loc: actualTag: file:
+    let
+      name = lib.strings.escapeNixIdentifier (lib.lists.last loc);
+      path = showOption loc;
+      depth = length loc;
+
+      paragraphs = [
+        "In module ${file}: expected an option declaration at option path `${path}` but got an attribute set with type ${actualTag}"
+      ] ++ optional (actualTag == "option-type") ''
+          When declaring an option, you must wrap the type in a `mkOption` call. It should look somewhat like:
+              ${comment}
+              ${name} = lib.mkOption {
+                description = ...;
+                type = <the type you wrote for ${name}>;
+                ...
+              };
+        '';
+
+      # Ideally we'd know the exact syntax they used, but short of that,
+      # we can only reliably repeat the last. However, we repeat the
+      # full path in a non-misleading way here, in case they overlook
+      # the start of the message. Examples attract attention.
+      comment = optionalString (depth > 1) "\n    # ${showOption loc}";
+    in
+    throw (concatStringsSep "\n\n" paragraphs);
+
   /* Merge multiple option declarations into a single declaration.  In
      general, there should be only one declaration of each option.
      The exception is the ‘options’ attribute, which specifies
diff --git a/nixpkgs/lib/path/default.nix b/nixpkgs/lib/path/default.nix
index 5c6c5f608954..1a55a2a7be8d 100644
--- a/nixpkgs/lib/path/default.nix
+++ b/nixpkgs/lib/path/default.nix
@@ -121,17 +121,18 @@ let
 
 in /* No rec! Add dependencies on this file at the top. */ {
 
-  /* Append a subpath string to a path.
+  /*
+    Append a subpath string to a path.
 
     Like `path + ("/" + string)` but safer, because it errors instead of returning potentially surprising results.
     More specifically, it checks that the first argument is a [path value type](https://nixos.org/manual/nix/stable/language/values.html#type-path"),
-    and that the second argument is a valid subpath string (see `lib.path.subpath.isValid`).
+    and that the second argument is a [valid subpath string](#function-library-lib.path.subpath.isValid).
 
     Laws:
 
-    - Not influenced by subpath normalisation
+    - Not influenced by subpath [normalisation](#function-library-lib.path.subpath.normalise):
 
-        append p s == append p (subpath.normalise s)
+          append p s == append p (subpath.normalise s)
 
     Type:
       append :: Path -> String -> Path
@@ -175,26 +176,26 @@ in /* No rec! Add dependencies on this file at the top. */ {
     path + ("/" + subpath);
 
   /*
-  Whether the first path is a component-wise prefix of the second path.
+    Whether the first path is a component-wise prefix of the second path.
 
-  Laws:
+    Laws:
 
-  - `hasPrefix p q` is only true if `q == append p s` for some subpath `s`.
+    - `hasPrefix p q` is only true if [`q == append p s`](#function-library-lib.path.append) for some [subpath](#function-library-lib.path.subpath.isValid) `s`.
 
-  - `hasPrefix` is a [non-strict partial order](https://en.wikipedia.org/wiki/Partially_ordered_set#Non-strict_partial_order) over the set of all path values
+    - `hasPrefix` is a [non-strict partial order](https://en.wikipedia.org/wiki/Partially_ordered_set#Non-strict_partial_order) over the set of all path values.
 
-  Type:
-    hasPrefix :: Path -> Path -> Bool
+    Type:
+      hasPrefix :: Path -> Path -> Bool
 
-  Example:
-    hasPrefix /foo /foo/bar
-    => true
-    hasPrefix /foo /foo
-    => true
-    hasPrefix /foo/bar /foo
-    => false
-    hasPrefix /. /foo
-    => true
+    Example:
+      hasPrefix /foo /foo/bar
+      => true
+      hasPrefix /foo /foo
+      => true
+      hasPrefix /foo/bar /foo
+      => false
+      hasPrefix /. /foo
+      => true
   */
   hasPrefix =
     path1:
@@ -219,27 +220,27 @@ in /* No rec! Add dependencies on this file at the top. */ {
         take (length path1Deconstructed.components) path2Deconstructed.components == path1Deconstructed.components;
 
   /*
-  Remove the first path as a component-wise prefix from the second path.
-  The result is a normalised subpath string, see `lib.path.subpath.normalise`.
+    Remove the first path as a component-wise prefix from the second path.
+    The result is a [normalised subpath string](#function-library-lib.path.subpath.normalise).
 
-  Laws:
+    Laws:
 
-  - Inverts `append` for normalised subpaths:
+    - Inverts [`append`](#function-library-lib.path.append) for [normalised subpath string](#function-library-lib.path.subpath.normalise):
 
-        removePrefix p (append p s) == subpath.normalise s
+          removePrefix p (append p s) == subpath.normalise s
 
-  Type:
-    removePrefix :: Path -> Path -> String
+    Type:
+      removePrefix :: Path -> Path -> String
 
-  Example:
-    removePrefix /foo /foo/bar/baz
-    => "./bar/baz"
-    removePrefix /foo /foo
-    => "./."
-    removePrefix /foo/bar /foo
-    => <error>
-    removePrefix /. /foo
-    => "./foo"
+    Example:
+      removePrefix /foo /foo/bar/baz
+      => "./bar/baz"
+      removePrefix /foo /foo
+      => "./."
+      removePrefix /foo/bar /foo
+      => <error>
+      removePrefix /. /foo
+      => "./foo"
   */
   removePrefix =
     path1:
@@ -272,41 +273,43 @@ in /* No rec! Add dependencies on this file at the top. */ {
         joinRelPath components;
 
   /*
-  Split the filesystem root from a [path](https://nixos.org/manual/nix/stable/language/values.html#type-path).
-  The result is an attribute set with these attributes:
-  - `root`: The filesystem root of the path, meaning that this directory has no parent directory.
-  - `subpath`: The [normalised subpath string](#function-library-lib.path.subpath.normalise) that when [appended](#function-library-lib.path.append) to `root` returns the original path.
+    Split the filesystem root from a [path](https://nixos.org/manual/nix/stable/language/values.html#type-path).
+    The result is an attribute set with these attributes:
+    - `root`: The filesystem root of the path, meaning that this directory has no parent directory.
+    - `subpath`: The [normalised subpath string](#function-library-lib.path.subpath.normalise) that when [appended](#function-library-lib.path.append) to `root` returns the original path.
 
-  Laws:
-  - [Appending](#function-library-lib.path.append) the `root` and `subpath` gives the original path:
+    Laws:
+    - [Appending](#function-library-lib.path.append) the `root` and `subpath` gives the original path:
 
-        p ==
-          append
-            (splitRoot p).root
-            (splitRoot p).subpath
+          p ==
+            append
+              (splitRoot p).root
+              (splitRoot p).subpath
 
-  - Trying to get the parent directory of `root` using [`readDir`](https://nixos.org/manual/nix/stable/language/builtins.html#builtins-readDir) returns `root` itself:
+    - Trying to get the parent directory of `root` using [`readDir`](https://nixos.org/manual/nix/stable/language/builtins.html#builtins-readDir) returns `root` itself:
 
-        dirOf (splitRoot p).root == (splitRoot p).root
+          dirOf (splitRoot p).root == (splitRoot p).root
 
-  Type:
-    splitRoot :: Path -> { root :: Path, subpath :: String }
+    Type:
+      splitRoot :: Path -> { root :: Path, subpath :: String }
 
-  Example:
-    splitRoot /foo/bar
-    => { root = /.; subpath = "./foo/bar"; }
+    Example:
+      splitRoot /foo/bar
+      => { root = /.; subpath = "./foo/bar"; }
 
-    splitRoot /.
-    => { root = /.; subpath = "./."; }
+      splitRoot /.
+      => { root = /.; subpath = "./."; }
 
-    # Nix neutralises `..` path components for all path values automatically
-    splitRoot /foo/../bar
-    => { root = /.; subpath = "./bar"; }
+      # Nix neutralises `..` path components for all path values automatically
+      splitRoot /foo/../bar
+      => { root = /.; subpath = "./bar"; }
 
-    splitRoot "/foo/bar"
-    => <error>
+      splitRoot "/foo/bar"
+      => <error>
   */
-  splitRoot = path:
+  splitRoot =
+    # The path to split the root off of
+    path:
     assert assertMsg
       (isPath path)
       "lib.path.splitRoot: Argument is of type ${typeOf path}, but a path was expected";
@@ -317,46 +320,47 @@ in /* No rec! Add dependencies on this file at the top. */ {
       subpath = joinRelPath deconstructed.components;
     };
 
-  /* Whether a value is a valid subpath string.
+  /*
+    Whether a value is a valid subpath string.
 
-  A subpath string points to a specific file or directory within an absolute base directory.
-  It is a stricter form of a relative path that excludes `..` components, since those could escape the base directory.
+    A subpath string points to a specific file or directory within an absolute base directory.
+    It is a stricter form of a relative path that excludes `..` components, since those could escape the base directory.
 
-  - The value is a string
+    - The value is a string.
 
-  - The string is not empty
+    - The string is not empty.
 
-  - The string doesn't start with a `/`
+    - The string doesn't start with a `/`.
 
-  - The string doesn't contain any `..` path components
+    - The string doesn't contain any `..` path components.
 
-  Type:
-    subpath.isValid :: String -> Bool
+    Type:
+      subpath.isValid :: String -> Bool
 
-  Example:
-    # Not a string
-    subpath.isValid null
-    => false
+    Example:
+      # Not a string
+      subpath.isValid null
+      => false
 
-    # Empty string
-    subpath.isValid ""
-    => false
+      # Empty string
+      subpath.isValid ""
+      => false
 
-    # Absolute path
-    subpath.isValid "/foo"
-    => false
+      # Absolute path
+      subpath.isValid "/foo"
+      => false
 
-    # Contains a `..` path component
-    subpath.isValid "../foo"
-    => false
+      # Contains a `..` path component
+      subpath.isValid "../foo"
+      => false
 
-    # Valid subpath
-    subpath.isValid "foo/bar"
-    => true
+      # Valid subpath
+      subpath.isValid "foo/bar"
+      => true
 
-    # Doesn't need to be normalised
-    subpath.isValid "./foo//bar/"
-    => true
+      # Doesn't need to be normalised
+      subpath.isValid "./foo//bar/"
+      => true
   */
   subpath.isValid =
     # The value to check
@@ -364,15 +368,16 @@ in /* No rec! Add dependencies on this file at the top. */ {
     subpathInvalidReason value == null;
 
 
-  /* Join subpath strings together using `/`, returning a normalised subpath string.
+  /*
+    Join subpath strings together using `/`, returning a normalised subpath string.
 
     Like `concatStringsSep "/"` but safer, specifically:
 
-    - All elements must be valid subpath strings, see `lib.path.subpath.isValid`
+    - All elements must be [valid subpath strings](#function-library-lib.path.subpath.isValid).
 
-    - The result gets normalised, see `lib.path.subpath.normalise`
+    - The result gets [normalised](#function-library-lib.path.subpath.normalise).
 
-    - The edge case of an empty list gets properly handled by returning the neutral subpath `"./."`
+    - The edge case of an empty list gets properly handled by returning the neutral subpath `"./."`.
 
     Laws:
 
@@ -386,12 +391,12 @@ in /* No rec! Add dependencies on this file at the top. */ {
           subpath.join [ (subpath.normalise p) "./." ] == subpath.normalise p
           subpath.join [ "./." (subpath.normalise p) ] == subpath.normalise p
 
-    - Normalisation - the result is normalised according to `lib.path.subpath.normalise`:
+    - Normalisation - the result is [normalised](#function-library-lib.path.subpath.normalise):
 
           subpath.join ps == subpath.normalise (subpath.join ps)
 
-    - For non-empty lists, the implementation is equivalent to normalising the result of `concatStringsSep "/"`.
-      Note that the above laws can be derived from this one.
+    - For non-empty lists, the implementation is equivalent to [normalising](#function-library-lib.path.subpath.normalise) the result of `concatStringsSep "/"`.
+      Note that the above laws can be derived from this one:
 
           ps != [] -> subpath.join ps == subpath.normalise (concatStringsSep "/" ps)
 
@@ -439,108 +444,109 @@ in /* No rec! Add dependencies on this file at the top. */ {
       ) 0 subpaths;
 
   /*
-  Split [a subpath](#function-library-lib.path.subpath.isValid) into its path component strings.
-  Throw an error if the subpath isn't valid.
-  Note that the returned path components are also valid subpath strings, though they are intentionally not [normalised](#function-library-lib.path.subpath.normalise).
+    Split [a subpath](#function-library-lib.path.subpath.isValid) into its path component strings.
+    Throw an error if the subpath isn't valid.
+    Note that the returned path components are also [valid subpath strings](#function-library-lib.path.subpath.isValid), though they are intentionally not [normalised](#function-library-lib.path.subpath.normalise).
 
-  Laws:
+    Laws:
 
-  - Splitting a subpath into components and [joining](#function-library-lib.path.subpath.join) the components gives the same subpath but [normalised](#function-library-lib.path.subpath.normalise):
+    - Splitting a subpath into components and [joining](#function-library-lib.path.subpath.join) the components gives the same subpath but [normalised](#function-library-lib.path.subpath.normalise):
 
-        subpath.join (subpath.components s) == subpath.normalise s
+          subpath.join (subpath.components s) == subpath.normalise s
 
-  Type:
-    subpath.components :: String -> [ String ]
+    Type:
+      subpath.components :: String -> [ String ]
 
-  Example:
-    subpath.components "."
-    => [ ]
+    Example:
+      subpath.components "."
+      => [ ]
 
-    subpath.components "./foo//bar/./baz/"
-    => [ "foo" "bar" "baz" ]
+      subpath.components "./foo//bar/./baz/"
+      => [ "foo" "bar" "baz" ]
 
-    subpath.components "/foo"
-    => <error>
+      subpath.components "/foo"
+      => <error>
   */
   subpath.components =
+    # The subpath string to split into components
     subpath:
     assert assertMsg (isValid subpath) ''
       lib.path.subpath.components: Argument is not a valid subpath string:
           ${subpathInvalidReason subpath}'';
     splitRelPath subpath;
 
-  /* Normalise a subpath. Throw an error if the subpath isn't valid, see
-  `lib.path.subpath.isValid`
+  /*
+    Normalise a subpath. Throw an error if the subpath isn't [valid](#function-library-lib.path.subpath.isValid).
 
-  - Limit repeating `/` to a single one
+    - Limit repeating `/` to a single one.
 
-  - Remove redundant `.` components
+    - Remove redundant `.` components.
 
-  - Remove trailing `/` and `/.`
+    - Remove trailing `/` and `/.`.
 
-  - Add leading `./`
+    - Add leading `./`.
 
-  Laws:
+    Laws:
 
-  - Idempotency - normalising multiple times gives the same result:
+    - Idempotency - normalising multiple times gives the same result:
 
-        subpath.normalise (subpath.normalise p) == subpath.normalise p
+          subpath.normalise (subpath.normalise p) == subpath.normalise p
 
-  - Uniqueness - there's only a single normalisation for the paths that lead to the same file system node:
+    - Uniqueness - there's only a single normalisation for the paths that lead to the same file system node:
 
-        subpath.normalise p != subpath.normalise q -> $(realpath ${p}) != $(realpath ${q})
+          subpath.normalise p != subpath.normalise q -> $(realpath ${p}) != $(realpath ${q})
 
-  - Don't change the result when appended to a Nix path value:
+    - Don't change the result when [appended](#function-library-lib.path.append) to a Nix path value:
 
-        base + ("/" + p) == base + ("/" + subpath.normalise p)
+          append base p == append base (subpath.normalise p)
 
-  - Don't change the path according to `realpath`:
+    - Don't change the path according to `realpath`:
 
-        $(realpath ${p}) == $(realpath ${subpath.normalise p})
+          $(realpath ${p}) == $(realpath ${subpath.normalise p})
 
-  - Only error on invalid subpaths:
+    - Only error on [invalid subpaths](#function-library-lib.path.subpath.isValid):
 
-        builtins.tryEval (subpath.normalise p)).success == subpath.isValid p
+          builtins.tryEval (subpath.normalise p)).success == subpath.isValid p
 
-  Type:
-    subpath.normalise :: String -> String
+    Type:
+      subpath.normalise :: String -> String
 
-  Example:
-    # limit repeating `/` to a single one
-    subpath.normalise "foo//bar"
-    => "./foo/bar"
+    Example:
+      # limit repeating `/` to a single one
+      subpath.normalise "foo//bar"
+      => "./foo/bar"
 
-    # remove redundant `.` components
-    subpath.normalise "foo/./bar"
-    => "./foo/bar"
+      # remove redundant `.` components
+      subpath.normalise "foo/./bar"
+      => "./foo/bar"
 
-    # add leading `./`
-    subpath.normalise "foo/bar"
-    => "./foo/bar"
+      # add leading `./`
+      subpath.normalise "foo/bar"
+      => "./foo/bar"
 
-    # remove trailing `/`
-    subpath.normalise "foo/bar/"
-    => "./foo/bar"
+      # remove trailing `/`
+      subpath.normalise "foo/bar/"
+      => "./foo/bar"
 
-    # remove trailing `/.`
-    subpath.normalise "foo/bar/."
-    => "./foo/bar"
+      # remove trailing `/.`
+      subpath.normalise "foo/bar/."
+      => "./foo/bar"
 
-    # Return the current directory as `./.`
-    subpath.normalise "."
-    => "./."
+      # Return the current directory as `./.`
+      subpath.normalise "."
+      => "./."
 
-    # error on `..` path components
-    subpath.normalise "foo/../bar"
-    => <error>
+      # error on `..` path components
+      subpath.normalise "foo/../bar"
+      => <error>
 
-    # error on empty string
-    subpath.normalise ""
-    => <error>
+      # error on empty string
+      subpath.normalise ""
+      => <error>
 
-    # error on absolute path
-    subpath.normalise "/foo"
-    => <error>
+      # error on absolute path
+      subpath.normalise "/foo"
+      => <error>
   */
   subpath.normalise =
     # The subpath string to normalise
diff --git a/nixpkgs/lib/path/tests/default.nix b/nixpkgs/lib/path/tests/default.nix
index 6b8e515f4330..50d40cdfa476 100644
--- a/nixpkgs/lib/path/tests/default.nix
+++ b/nixpkgs/lib/path/tests/default.nix
@@ -18,7 +18,14 @@ pkgs.runCommand "lib-path-tests" {
   ];
 } ''
   # Needed to make Nix evaluation work
-  export NIX_STATE_DIR=$(mktemp -d)
+  export TEST_ROOT=$(pwd)/test-tmp
+  export NIX_BUILD_HOOK=
+  export NIX_CONF_DIR=$TEST_ROOT/etc
+  export NIX_LOCALSTATE_DIR=$TEST_ROOT/var
+  export NIX_LOG_DIR=$TEST_ROOT/var/log/nix
+  export NIX_STATE_DIR=$TEST_ROOT/var/nix
+  export NIX_STORE_DIR=$TEST_ROOT/store
+  export PAGER=cat
 
   cp -r ${libpath} lib
   export TEST_LIB=$PWD/lib
diff --git a/nixpkgs/lib/strings.nix b/nixpkgs/lib/strings.nix
index 1eb6cf9c1afb..df891c899887 100644
--- a/nixpkgs/lib/strings.nix
+++ b/nixpkgs/lib/strings.nix
@@ -629,10 +629,10 @@ rec {
             This behavior is deprecated and will throw an error in the future.''
     (let
       preLen = stringLength prefix;
-      sLen = stringLength str;
     in
       if substring 0 preLen str == prefix then
-        substring preLen (sLen - preLen) str
+        # -1 will take the string until the end
+        substring preLen (-1) str
       else
         str);
 
diff --git a/nixpkgs/lib/systems/parse.nix b/nixpkgs/lib/systems/parse.nix
index 6eb4f27cc519..34bfd94b3ce5 100644
--- a/nixpkgs/lib/systems/parse.nix
+++ b/nixpkgs/lib/systems/parse.nix
@@ -221,6 +221,8 @@ rec {
   vendors = setTypes types.openVendor {
     apple = {};
     pc = {};
+    knuth = {};
+
     # Actually matters, unlocking some MinGW-w64-specific options in GCC. See
     # bottom of https://sourceforge.net/p/mingw-w64/wiki2/Unicode%20apps/
     w64 = {};
diff --git a/nixpkgs/lib/tests/misc.nix b/nixpkgs/lib/tests/misc.nix
index 887ea39a080d..6d55ae684771 100644
--- a/nixpkgs/lib/tests/misc.nix
+++ b/nixpkgs/lib/tests/misc.nix
@@ -349,6 +349,27 @@ runTests {
     expected = true;
   };
 
+  testRemovePrefixExample1 = {
+    expr = removePrefix "foo." "foo.bar.baz";
+    expected = "bar.baz";
+  };
+  testRemovePrefixExample2 = {
+    expr = removePrefix "xxx" "foo.bar.baz";
+    expected = "foo.bar.baz";
+  };
+  testRemovePrefixEmptyPrefix = {
+    expr = removePrefix "" "foo";
+    expected = "foo";
+  };
+  testRemovePrefixEmptyString = {
+    expr = removePrefix "foo" "";
+    expected = "";
+  };
+  testRemovePrefixEmptyBoth = {
+    expr = removePrefix "" "";
+    expected = "";
+  };
+
   testNormalizePath = {
     expr = strings.normalizePath "//a/b//c////d/";
     expected = "/a/b/c/d/";
@@ -492,6 +513,44 @@ runTests {
     ([ 1 2 3 ] == (take 4 [  1 2 3 ]))
   ];
 
+  testListHasPrefixExample1 = {
+    expr = lists.hasPrefix [ 1 2 ] [ 1 2 3 4 ];
+    expected = true;
+  };
+  testListHasPrefixExample2 = {
+    expr = lists.hasPrefix [ 0 1 ] [ 1 2 3 4 ];
+    expected = false;
+  };
+  testListHasPrefixLazy = {
+    expr = lists.hasPrefix [ 1 ] [ 1 (abort "lib.lists.hasPrefix is not lazy") ];
+    expected = true;
+  };
+  testListHasPrefixEmptyPrefix = {
+    expr = lists.hasPrefix [ ] [ 1 2 ];
+    expected = true;
+  };
+  testListHasPrefixEmptyList = {
+    expr = lists.hasPrefix [ 1 2 ] [ ];
+    expected = false;
+  };
+
+  testListRemovePrefixExample1 = {
+    expr = lists.removePrefix [ 1 2 ] [ 1 2 3 4 ];
+    expected = [ 3 4 ];
+  };
+  testListRemovePrefixExample2 = {
+    expr = (builtins.tryEval (lists.removePrefix [ 0 1 ] [ 1 2 3 4 ])).success;
+    expected = false;
+  };
+  testListRemovePrefixEmptyPrefix = {
+    expr = lists.removePrefix [ ] [ 1 2 ];
+    expected = [ 1 2 ];
+  };
+  testListRemovePrefixEmptyList = {
+    expr = (builtins.tryEval (lists.removePrefix [ 1 2 ] [ ])).success;
+    expected = false;
+  };
+
   testFoldAttrs = {
     expr = foldAttrs (n: a: [n] ++ a) [] [
     { a = 2; b = 7; }
diff --git a/nixpkgs/lib/tests/modules.sh b/nixpkgs/lib/tests/modules.sh
index b933a24a57a1..2c5e4cdbcec1 100755
--- a/nixpkgs/lib/tests/modules.sh
+++ b/nixpkgs/lib/tests/modules.sh
@@ -393,6 +393,11 @@ checkConfigError \
   config.set \
   ./declare-set.nix ./declare-enable-nested.nix
 
+# Options: accidental use of an option-type instead of option (or other tagged type; unlikely)
+checkConfigError 'In module .*/options-type-error-typical.nix: expected an option declaration at option path .result. but got an attribute set with type option-type' config.result ./options-type-error-typical.nix
+checkConfigError 'In module .*/options-type-error-typical-nested.nix: expected an option declaration at option path .result.here. but got an attribute set with type option-type' config.result.here ./options-type-error-typical-nested.nix
+checkConfigError 'In module .*/options-type-error-configuration.nix: expected an option declaration at option path .result. but got an attribute set with type configuration' config.result ./options-type-error-configuration.nix
+
 # Check that that merging of option collisions doesn't depend on type being set
 checkConfigError 'The option .group..*would be a parent of the following options, but its type .<no description>. does not support nested options.\n\s*- option.s. with prefix .group.enable..*' config.group.enable ./merge-typeless-option.nix
 
diff --git a/nixpkgs/lib/tests/modules/options-type-error-configuration.nix b/nixpkgs/lib/tests/modules/options-type-error-configuration.nix
new file mode 100644
index 000000000000..bcd6db89487a
--- /dev/null
+++ b/nixpkgs/lib/tests/modules/options-type-error-configuration.nix
@@ -0,0 +1,6 @@
+{ lib, ... }: {
+  options = {
+    # unlikely mistake, but we can catch any attrset with _type
+    result = lib.evalModules { modules = []; };
+  };
+}
diff --git a/nixpkgs/lib/tests/modules/options-type-error-typical-nested.nix b/nixpkgs/lib/tests/modules/options-type-error-typical-nested.nix
new file mode 100644
index 000000000000..2c07e19fb8ae
--- /dev/null
+++ b/nixpkgs/lib/tests/modules/options-type-error-typical-nested.nix
@@ -0,0 +1,5 @@
+{ lib, ... }: {
+  options = {
+    result.here = lib.types.str;
+  };
+}
diff --git a/nixpkgs/lib/tests/modules/options-type-error-typical.nix b/nixpkgs/lib/tests/modules/options-type-error-typical.nix
new file mode 100644
index 000000000000..416f436e0ad7
--- /dev/null
+++ b/nixpkgs/lib/tests/modules/options-type-error-typical.nix
@@ -0,0 +1,5 @@
+{ lib, ... }: {
+  options = {
+    result = lib.types.str;
+  };
+}
diff --git a/nixpkgs/lib/trivial.nix b/nixpkgs/lib/trivial.nix
index 34c100959e6f..c23fc6070be4 100644
--- a/nixpkgs/lib/trivial.nix
+++ b/nixpkgs/lib/trivial.nix
@@ -307,14 +307,14 @@ rec {
 
   /* Reads a JSON file.
 
-     Type :: path -> any
+     Type: importJSON :: path -> any
   */
   importJSON = path:
     builtins.fromJSON (builtins.readFile path);
 
   /* Reads a TOML file.
 
-     Type :: path -> any
+     Type: importTOML :: path -> any
   */
   importTOML = path:
     builtins.fromTOML (builtins.readFile path);
diff --git a/nixpkgs/lib/types.nix b/nixpkgs/lib/types.nix
index ddd37f260c9a..5ffbecda5db3 100644
--- a/nixpkgs/lib/types.nix
+++ b/nixpkgs/lib/types.nix
@@ -436,10 +436,12 @@ rec {
 
     # Deprecated; should not be used because it quietly concatenates
     # strings, which is usually not what you want.
-    string = separatedString "" // {
-      name = "string";
-      deprecationMessage = "See https://github.com/NixOS/nixpkgs/pull/66346 for better alternative types.";
-    };
+    # We use a lib.warn because `deprecationMessage` doesn't trigger in nested types such as `attrsOf string`
+    string = lib.warn
+      "The type `types.string` is deprecated. See https://github.com/NixOS/nixpkgs/pull/66346 for better alternative types."
+      (separatedString "" // {
+        name = "string";
+      });
 
     passwdEntry = entryType: addCheck entryType (str: !(hasInfix ":" str || hasInfix "\n" str)) // {
       name = "passwdEntry ${entryType.name}";