diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -3,6 +3,11 @@
 
 The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
 
+## 2.11.0 - 2026-09-15
+
+### Added
+* Weeder now reports explicitly configured `roots` patterns, `root-instances` entries and `root-modules` patterns that match no identifiers, so stale configuration entries are surfaced as weeds (self-weeding). Defaults are left alone. (#204)
+
 ## 2.10.0 - 2025-07-29
 
 ### Added
diff --git a/README.md b/README.md
--- a/README.md
+++ b/README.md
@@ -111,6 +111,35 @@
                  ]
 ```
 
+## Self-weeding
+
+Over the lifetime of a project, roots that were once needed can become stale:
+the declaration they pointed at gets renamed or removed, leaving behind a
+`roots` pattern, `root-instances` entry or `root-modules` pattern that no longer
+matches anything. Such an entry is a weed in the configuration itself.
+
+Weeder reports these automatically. Any `roots` pattern that matches no
+declaration, any `root-instances` entry that matches no instance, and any
+`root-modules` pattern that matches no module, is reported alongside the regular
+weeds (and likewise contributes to the weeds-found exit code):
+
+``` shell
+$ weeder
+no declaration matches roots entry "^Main.runServer$"
+no instance matches root-instances entry { class = "\\.ToJSON$" }
+no module matches root-modules entry "^Test\\."
+```
+
+A `roots` pattern counts as matching as long as it matches any identifier Weeder
+is aware of, even one that never appears in the output (such as a type or
+constructor when `unused-types` is disabled), so a root naming real code is not
+reported just because of the current analysis mode.
+
+Only entries you have explicitly configured are reported. Default `roots` and
+`root-instances` are left alone, since telling you that a default you never
+wrote is currently unused would not be actionable. When `type-class-roots` is
+set, `root-instances` is ignored entirely, so its entries are not reported.
+
 ## Exit codes
 
 Weeder emits the following exit codes:
diff --git a/src/Weeder.hs b/src/Weeder.hs
--- a/src/Weeder.hs
+++ b/src/Weeder.hs
@@ -18,6 +18,7 @@
   , analyseHieFile
   , emptyAnalysis
   , outputableDeclarations
+  , localDeclarations
 
     -- ** Reachability
   , Root(..)
@@ -29,7 +30,7 @@
    where
 
 -- algebraic-graphs
-import Algebra.Graph ( Graph, edge, empty, overlay, vertex, stars, star, overlays )
+import Algebra.Graph ( Graph, edge, empty, overlay, vertex, stars, star, overlays, vertexSet )
 import Algebra.Graph.ToGraph ( dfs )
 
 -- base
@@ -245,6 +246,18 @@
 outputableDeclarations :: Analysis -> Set Declaration
 outputableDeclarations Analysis{ declarationSites } =
   Map.keysSet declarationSites
+
+
+-- | Every declaration defined in one of the analysed modules, including ones
+-- that never appear in the output (such as types and constructors when
+-- @unused-types@ is disabled). Used to decide whether a configured root matches
+-- any real identifier in the project, as opposed to whether it keeps anything
+-- alive. Declarations from external packages — which appear in the graph only
+-- as dependency targets — are excluded, so a root that matches only an external
+-- symbol is still reported as unused.
+localDeclarations :: Analysis -> Set Declaration
+localDeclarations Analysis{ dependencyGraph, modulePaths } =
+  Set.filter (\d -> declModule d `Map.member` modulePaths) (vertexSet dependencyGraph)
 
 
 -- Generate an initial graph of the current HieFile.
diff --git a/src/Weeder/Config.hs b/src/Weeder/Config.hs
--- a/src/Weeder/Config.hs
+++ b/src/Weeder/Config.hs
@@ -17,11 +17,17 @@
   , configToToml
   , decodeNoDefaults
   , defaultConfig
+    -- * Compiled regular expressions
+  , CompiledRegex(..)
+    -- * Configuration provenance
+  , Configured(..)
+  , configuredValue
     -- * Marking instances as roots
   , InstancePattern
   , modulePattern
   , instancePattern
   , classPattern
+  , showInstancePattern
   , pattern InstanceOnly
   , pattern ClassOnly
   , pattern ModuleOnly
@@ -37,6 +43,9 @@
 -- containers
 import Data.Containers.ListUtils (nubOrd)
 
+-- text
+import Data.Text (Text)
+
 -- regex-tdfa
 import Text.Regex.TDFA ( Regex, RegexOptions ( defaultExecOpt, defaultCompOpt ) )
 import Text.Regex.TDFA.TDFA ( patternToRegex )
@@ -47,9 +56,35 @@
 
 
 -- | Configuration for Weeder analysis.
-type Config = ConfigType Regex
+type Config = ConfigType CompiledRegex
 
 
+-- | A compiled regular expression, paired with the source string it was
+-- compiled from. We keep the source around so that we can report which
+-- configured pattern is responsible when a pattern matches no identifiers.
+data CompiledRegex = CompiledRegex
+  { regexSource :: String
+  , compiledRegex :: Regex
+  }
+
+
+-- | A configured value, and whether it was set explicitly or left at its
+-- default. We track this for the root sections so that self-weeding only
+-- reports entries the user actually wrote: pointing out that a default they
+-- never configured is unused would not be actionable.
+data Configured a
+  = Configured a
+  | Default a
+  deriving (Eq, Show, Functor, Foldable, Traversable)
+
+
+-- | The configured value, regardless of where it came from.
+configuredValue :: Configured a -> a
+configuredValue = \case
+  Configured a -> a
+  Default a -> a
+
+
 -- | Configuration that has been parsed from TOML (and can still be
 -- converted back), but not yet compiled to a 'Config'.
 type ConfigParsed = ConfigType String
@@ -57,19 +92,19 @@
 
 -- | Underlying type for 'Config' and 'ConfigParsed'.
 data ConfigType a = Config
-  { rootPatterns :: [a]
+  { rootPatterns :: Configured [a]
     -- ^ Any declarations matching these regular expressions will be added to
     -- the root set.
   , typeClassRoots :: Bool
     -- ^ If True, consider all declarations in a type class as part of the root
     -- set. Overrides root-instances.
-  , rootInstances :: [InstancePattern a]
+  , rootInstances :: Configured [InstancePattern a]
     -- ^ All matching instances will be added to the root set. An absent field
     -- will always match.
   , unusedTypes :: Bool
     -- ^ Toggle to look for and output unused types. Type family instances will
     -- be marked as implicit roots.
-  , rootModules :: [a]
+  , rootModules :: Configured [a]
     -- ^ All matching modules will be added to the root set.
   } deriving (Eq, Show, Functor, Foldable, Traversable)
 
@@ -98,14 +133,26 @@
 
 defaultConfig :: ConfigParsed
 defaultConfig = Config
-  { rootPatterns = [ "Main.main", "^Paths_.*"]
+  { rootPatterns = Default defaultRootPatterns
   , typeClassRoots = False
-  , rootInstances = [ ClassOnly "\\.IsString$", ClassOnly "\\.IsList$" ]
+  , rootInstances = Default defaultRootInstances
   , unusedTypes = False
-  , rootModules = mempty
+  , rootModules = Default defaultRootModules
   }
 
 
+defaultRootPatterns :: [String]
+defaultRootPatterns = [ "Main.main", "^Paths_.*" ]
+
+
+defaultRootInstances :: [InstancePattern String]
+defaultRootInstances = [ ClassOnly "\\.IsString$", ClassOnly "\\.IsList$" ]
+
+
+defaultRootModules :: [String]
+defaultRootModules = mempty
+
+
 instance TOML.DecodeTOML Config where
   tomlDecoder = do
     conf <- TOML.tomlDecoder
@@ -114,22 +161,30 @@
 
 instance TOML.DecodeTOML ConfigParsed where
   tomlDecoder = do
-    rootPatterns <- TOML.getFieldOr (rootPatterns defaultConfig) "roots"
+    rootPatterns <- getConfigured defaultRootPatterns "roots"
     typeClassRoots <- TOML.getFieldOr (typeClassRoots defaultConfig) "type-class-roots"
-    rootInstances <- TOML.getFieldOr (rootInstances defaultConfig) "root-instances"
+    rootInstances <- getConfigured defaultRootInstances "root-instances"
     unusedTypes <- TOML.getFieldOr (unusedTypes defaultConfig) "unused-types"
-    rootModules <- TOML.getFieldOr (rootModules defaultConfig) "root-modules"
+    rootModules <- getConfigured defaultRootModules "root-modules"
 
     pure Config{..}
 
 
+-- | Decode an optional field, marking it 'Configured' when present and falling
+-- back to the given 'Default' otherwise.
+getConfigured :: TOML.DecodeTOML a => a -> Text -> TOML.Decoder (Configured a)
+getConfigured def key = maybe (Default def) Configured <$> TOML.getFieldOpt key
+
+
 decodeNoDefaults :: TOML.Decoder Config
 decodeNoDefaults = do
-  rootPatterns <- TOML.getField "roots"
+  -- In this mode every field must be specified, so every root section is
+  -- explicit by construction.
+  rootPatterns <- Configured <$> TOML.getField "roots"
   typeClassRoots <- TOML.getField "type-class-roots"
-  rootInstances <- TOML.getField "root-instances"
+  rootInstances <- Configured <$> TOML.getField "root-instances"
   unusedTypes <- TOML.getField "unused-types"
-  rootModules <- TOML.getField "root-modules"
+  rootModules <- Configured <$> TOML.getField "root-modules"
 
   either fail pure $ compileConfig Config{..}
 
@@ -181,28 +236,28 @@
       moduleField m = "module = " ++ show m
 
 
-compileRegex :: String -> Either String Regex
-compileRegex = bimap show (\p -> patternToRegex p defaultCompOpt defaultExecOpt) . parseRegex
+compileRegex :: String -> Either String CompiledRegex
+compileRegex src =
+  bimap show (\p -> CompiledRegex src (patternToRegex p defaultCompOpt defaultExecOpt)) (parseRegex src)
 
 
 compileConfig :: ConfigParsed -> Either String Config
 compileConfig conf@Config{ rootInstances, rootPatterns, rootModules } =
   traverse compileRegex conf'
   where
-    rootInstances' = nubOrd rootInstances
-    rootPatterns' = nubOrd rootPatterns
-    rootModules' = nubOrd rootModules
-    conf' = conf{ rootInstances = rootInstances', rootPatterns = rootPatterns', rootModules = rootModules' }
+    conf' = conf
+      { rootInstances = fmap nubOrd rootInstances
+      , rootPatterns = fmap nubOrd rootPatterns
+      , rootModules = fmap nubOrd rootModules
+      }
 
 
 configToToml :: ConfigParsed -> String
 configToToml Config{..}
   = unlines . intersperse mempty $
-      [ "roots = " ++ show rootPatterns
+      [ "roots = " ++ show (configuredValue rootPatterns)
       , "type-class-roots = " ++ map toLower (show typeClassRoots)
-      , "root-instances = " ++ "[" ++ intercalate "," (map showInstancePattern rootInstances') ++ "]"
+      , "root-instances = " ++ "[" ++ intercalate "," (map showInstancePattern (configuredValue rootInstances)) ++ "]"
       , "unused-types = " ++ map toLower (show unusedTypes)
-      , "root-modules = " ++ show rootModules
+      , "root-modules = " ++ show (configuredValue rootModules)
       ]
-  where
-    rootInstances' = rootInstances
diff --git a/src/Weeder/Run.hs b/src/Weeder/Run.hs
--- a/src/Weeder/Run.hs
+++ b/src/Weeder/Run.hs
@@ -4,7 +4,7 @@
 {-# language NamedFieldPuns #-}
 {-# LANGUAGE FlexibleContexts #-}
 
-module Weeder.Run ( runWeeder, Weed(..), formatWeed ) where
+module Weeder.Run ( runWeeder, Weed(..), DeclarationWeed(..), DeadRoot(..), formatWeed ) where
 
 -- base
 import Control.Applicative ( liftA2 )
@@ -44,7 +44,16 @@
 import Weeder.Config
 
 
-data Weed = Weed
+-- | Something Weeder found that is unused: either a dead declaration in the
+-- analysed code, or a configured root that matched no identifiers (a weed in
+-- the configuration itself).
+data Weed
+  = WeedDeclaration DeclarationWeed
+  | WeedRoot DeadRoot
+
+
+-- | A dead declaration: code that is written but never reachable from a root.
+data DeclarationWeed = DeclarationWeed
   { weedPackage :: String
   , weedPath :: FilePath
   , weedLine :: Int
@@ -54,12 +63,29 @@
   }
 
 
+-- | A configured root that no longer applies because it matches nothing.
+data DeadRoot
+  = -- | A @roots@ pattern (its source string) that matched no declaration.
+    DeadRootPattern String
+  | -- | A @root-instances@ entry (pretty-printed) that matched no instance.
+    DeadRootInstance String
+  | -- | A @root-modules@ pattern (its source string) that matched no module.
+    DeadRootModule String
+
+
 formatWeed :: Weed -> String
-formatWeed Weed{..} =
-  weedPackage <> ": " <> weedPath <> ":" <> show weedLine <> ":" <> show weedCol <> ": "
-    <> case weedPrettyPrintedType of
-      Nothing -> occNameString ( declOccName weedDeclaration )
-      Just t -> "(Instance) :: " <> t
+formatWeed = \case
+  WeedDeclaration DeclarationWeed{..} ->
+    weedPackage <> ": " <> weedPath <> ":" <> show weedLine <> ":" <> show weedCol <> ": "
+      <> case weedPrettyPrintedType of
+        Nothing -> occNameString ( declOccName weedDeclaration )
+        Just t -> "(Instance) :: " <> t
+  WeedRoot (DeadRootPattern src) ->
+    "no declaration matches roots entry " <> show src
+  WeedRoot (DeadRootInstance s) ->
+    "no instance matches root-instances entry " <> s
+  WeedRoot (DeadRootModule src) ->
+    "no module matches root-modules entry " <> show src
 
 -- | Run Weeder on the given .hie files with the given 'Config'.
 --
@@ -96,14 +122,14 @@
       Set.filter
         ( \d ->
             any
-              (`matchTest` displayDeclaration d)
-              rootPatterns
+              ( \p -> matchTest ( compiledRegex p ) ( displayDeclaration d ) )
+              ( configuredValue rootPatterns )
         )
         ( outputableDeclarations analysis )
 
     matchingModules =
       Set.filter
-        ((\s -> any (`matchTest` s) rootModules) . moduleNameString . moduleName)
+        ((\s -> any (\p -> matchTest ( compiledRegex p ) s) ( configuredValue rootModules )) . moduleNameString . moduleName)
       ( Map.keysSet $ exports analysis )
 
     reachableSet =
@@ -133,41 +159,113 @@
         )
         dead
 
-    weeds =
+    declarationWeeds =
       Map.toList warnings & concatMap \( weedPath, declarations ) ->
         sortOn fst declarations & map \( (weedPackage, (weedLine, weedCol)) , weedDeclaration ) ->
-          Weed { weedPrettyPrintedType = Map.lookup weedDeclaration (prettyPrintedType analysis)
-               , weedPackage
-               , weedPath
-               , weedLine
-               , weedCol
-               , weedDeclaration
-               }
+          WeedDeclaration DeclarationWeed
+            { weedPrettyPrintedType = Map.lookup weedDeclaration (prettyPrintedType analysis)
+            , weedPackage
+            , weedPath
+            , weedLine
+            , weedCol
+            , weedDeclaration
+            }
 
+    -- A @roots@ pattern that matches no identifier in the project is a weed in
+    -- the configuration itself: it no longer applies to any declaration. We
+    -- match against every local declaration rather than only the outputable
+    -- ones, so that a root naming a real type or constructor is not flagged just
+    -- because @unused-types@ happens to be disabled. Only patterns the user
+    -- explicitly configured are reported, since pointing out that an
+    -- unconfigured default is unused is not actionable.
+    deadRootPatterns =
+      case rootPatterns of
+        Default _ -> []
+        Configured patterns ->
+          [ regexSource p
+          | p <- patterns
+          , not $
+              any
+                ( \d -> matchTest ( compiledRegex p ) ( displayDeclaration d ) )
+                ( localDeclarations analysis )
+          ]
+
+    -- A @root-instances@ entry that matches no instance is likewise a weed.
+    -- When 'typeClassRoots' is set, @root-instances@ is ignored entirely, so we
+    -- don't report its entries.
+    instanceRoots =
+      [ ( d, c ) | InstanceRoot d c <- Set.toList ( implicitRoots analysis ) ]
+
+    deadRootInstances
+      | typeClassRoots = []
+      | otherwise =
+          case rootInstances of
+            Default _ -> []
+            Configured patterns ->
+              [ showInstancePattern ( regexSource <$> ip )
+              | ip <- patterns
+              , not $ any ( matchesInstancePattern analysis ip ) instanceRoots
+              ]
+
+    -- A @root-modules@ pattern that matches none of the modules Weeder analysed
+    -- is also a weed.
+    knownModuleNames =
+      map ( moduleNameString . moduleName ) ( Map.keys ( modulePaths analysis ) )
+
+    deadRootModules =
+      case rootModules of
+        Default _ -> []
+        Configured patterns ->
+          [ regexSource p
+          | p <- patterns
+          , not $ any ( \m -> matchTest ( compiledRegex p ) m ) knownModuleNames
+          ]
+
+    weeds =
+      declarationWeeds
+        <> map ( WeedRoot . DeadRootPattern ) deadRootPatterns
+        <> map ( WeedRoot . DeadRootInstance ) deadRootInstances
+        <> map ( WeedRoot . DeadRootModule ) deadRootModules
+
   in (weeds, analysis)
 
   where
 
     filterImplicitRoots :: Analysis -> Set Root -> Set Root
-    filterImplicitRoots Analysis{ prettyPrintedType, modulePaths } = Set.filter $ \case
+    filterImplicitRoots analysis = Set.filter $ \case
       DeclarationRoot _ -> True -- keep implicit roots for rewrite rules etc
 
       ModuleRoot _ -> True
 
-      InstanceRoot d c -> typeClassRoots || matchingType
-        where
-          matchingType =
-            let mt = Map.lookup d prettyPrintedType
-                matches = maybe (const False) (flip matchTest) mt
-            in any (maybe True matches) filteredInstances
+      -- [tag:RootInstanceMatching] The reachability check here and the
+      -- dead-root-instance check in 'deadRootInstances' must agree on what it
+      -- means for a 'root-instances' entry to match an instance; both go
+      -- through 'matchesInstancePattern'.
+      InstanceRoot d c ->
+        typeClassRoots
+          || any ( \ip -> matchesInstancePattern analysis ip ( d, c ) ) ( configuredValue rootInstances )
 
-          filteredInstances =
-            map instancePattern
-            . filter (maybe True (`matchTest` displayDeclaration c) . classPattern)
-            . filter (maybe True modulePathMatches . modulePattern)
-            $ rootInstances
 
-          modulePathMatches p = maybe False (p `matchTest`) (Map.lookup ( declModule d ) modulePaths)
+-- | Does a @root-instances@ pattern match a given instance root (the
+-- declaration of the instance and the declaration of its parent class)? An
+-- absent field always matches.
+--
+-- [ref:RootInstanceMatching]
+matchesInstancePattern
+  :: Analysis -> InstancePattern CompiledRegex -> ( Declaration, Declaration ) -> Bool
+matchesInstancePattern Analysis{ prettyPrintedType, modulePaths } ip ( d, c ) =
+       maybe True moduleMatches ( modulePattern ip )
+    && maybe True classMatches ( classPattern ip )
+    && maybe True typeMatches ( instancePattern ip )
+  where
+    moduleMatches p =
+      maybe False ( matchTest ( compiledRegex p ) ) ( Map.lookup ( declModule d ) modulePaths )
+
+    classMatches p =
+      matchTest ( compiledRegex p ) ( displayDeclaration c )
+
+    typeMatches p =
+      maybe False ( matchTest ( compiledRegex p ) ) ( Map.lookup d prettyPrintedType )
 
 
 displayDeclaration :: Declaration -> String
diff --git a/test/Main.hs b/test/Main.hs
--- a/test/Main.hs
+++ b/test/Main.hs
@@ -78,7 +78,10 @@
   handle (\e -> hPrint stderr (e :: IOException)) $
     writeFile (hieDirectory <.> ".dot") graph'
   -- Normalize weedPackage. We get different values here based on our version of cabal-install/ghc.
-  let weeds' = map (\weed -> weed {Weeder.Run.weedPackage = "main"}) weeds
+  let normalize w = case w of
+        Weeder.Run.WeedDeclaration d -> Weeder.Run.WeedDeclaration d {Weeder.Run.weedPackage = "main"}
+        _ -> w
+      weeds' = map normalize weeds
   pure (LBS.fromStrict $ encodeUtf8 $ pack $ unlines $ map Weeder.Run.formatWeed weeds')
   where
     configExpr = hieDirectory <.> ".toml"
diff --git a/test/Spec/DefaultsNotReported.stdout b/test/Spec/DefaultsNotReported.stdout
new file mode 100644
--- /dev/null
+++ b/test/Spec/DefaultsNotReported.stdout
diff --git a/test/Spec/DefaultsNotReported.toml b/test/Spec/DefaultsNotReported.toml
new file mode 100644
--- /dev/null
+++ b/test/Spec/DefaultsNotReported.toml
@@ -0,0 +1,6 @@
+# roots and root-instances are deliberately left unset, so they fall back to
+# their defaults (which match nothing here). root-modules is set explicitly to
+# keep the module alive. Self-weeding must stay silent: defaults are not weeds.
+root-modules = [ '^Spec\.DefaultsNotReported\.' ]
+
+type-class-roots = false
diff --git a/test/Spec/DefaultsNotReported/DefaultsNotReported.hs b/test/Spec/DefaultsNotReported/DefaultsNotReported.hs
new file mode 100644
--- /dev/null
+++ b/test/Spec/DefaultsNotReported/DefaultsNotReported.hs
@@ -0,0 +1,10 @@
+module Spec.DefaultsNotReported.DefaultsNotReported where
+
+-- This module is kept entirely alive by an explicit `root-modules` entry, so
+-- there are no dead declarations. The point of the test is that the *default*
+-- `roots` (`Main.main`, `^Paths_.*`) and the default `root-instances`
+-- (`IsString`, `IsList`) -- none of which match anything here -- are NOT
+-- reported, because they were never configured explicitly. The output is empty.
+
+value :: Int
+value = 5
diff --git a/test/Spec/SelfWeeding.stdout b/test/Spec/SelfWeeding.stdout
new file mode 100644
--- /dev/null
+++ b/test/Spec/SelfWeeding.stdout
@@ -0,0 +1,3 @@
+no declaration matches roots entry "Spec.SelfWeeding.SelfWeeding.doesNotExist"
+no instance matches root-instances entry { class = "\\.NoSuchClass$" }
+no module matches root-modules entry "^No.Such.Module$"
diff --git a/test/Spec/SelfWeeding.toml b/test/Spec/SelfWeeding.toml
new file mode 100644
--- /dev/null
+++ b/test/Spec/SelfWeeding.toml
@@ -0,0 +1,9 @@
+roots = [ "Spec.SelfWeeding.SelfWeeding.root"
+        , "Spec.SelfWeeding.SelfWeeding.doesNotExist"
+        ]
+
+type-class-roots = false
+
+root-instances = [ { class = '\.NoSuchClass$' } ]
+
+root-modules = [ "^No.Such.Module$" ]
diff --git a/test/Spec/SelfWeeding/SelfWeeding.hs b/test/Spec/SelfWeeding/SelfWeeding.hs
new file mode 100644
--- /dev/null
+++ b/test/Spec/SelfWeeding/SelfWeeding.hs
@@ -0,0 +1,12 @@
+module Spec.SelfWeeding.SelfWeeding where
+
+-- A single reachable root and a helper it uses, so there are no dead
+-- declarations: the only weeds in this test come from the configuration
+-- itself (a root pattern, a root-instance and a root-module that match
+-- nothing).
+
+root :: Int
+root = helper + 1
+
+helper :: Int
+helper = 41
diff --git a/test/UnitTests/Weeder/ConfigSpec.hs b/test/UnitTests/Weeder/ConfigSpec.hs
--- a/test/UnitTests/Weeder/ConfigSpec.hs
+++ b/test/UnitTests/Weeder/ConfigSpec.hs
@@ -15,11 +15,11 @@
 configToTomlTests :: Assertion
 configToTomlTests =
   let cf = Config
-        { rootPatterns = mempty
+        { rootPatterns = Configured mempty
         , typeClassRoots = True
-        , rootInstances = [InstanceOnly "Quux\\\\[\\]", ClassOnly "[\\[\\\\[baz" <> ModuleOnly "[Quuux]", InstanceOnly "[\\[\\\\[baz" <> ClassOnly "[Quuux]" <> ModuleOnly "[Quuuux]"]
+        , rootInstances = Configured [InstanceOnly "Quux\\\\[\\]", ClassOnly "[\\[\\\\[baz" <> ModuleOnly "[Quuux]", InstanceOnly "[\\[\\\\[baz" <> ClassOnly "[Quuux]" <> ModuleOnly "[Quuuux]"]
         , unusedTypes = True
-        , rootModules = ["Foo\\.Bar", "Baz"]
+        , rootModules = Configured ["Foo\\.Bar", "Baz"]
         }
       cf' = T.pack $ configToToml cf
    in TOML.decode cf' `shouldBe` Right cf
diff --git a/weeder.cabal b/weeder.cabal
--- a/weeder.cabal
+++ b/weeder.cabal
@@ -5,7 +5,7 @@
 author:        Ollie Charles <ollie@ocharles.org.uk>
 maintainer:    Ollie Charles <ollie@ocharles.org.uk>
 build-type:    Simple
-version:       2.10.0
+version:       2.11.0
 copyright:     Neil Mitchell 2017-2020, Oliver Charles 2020-2025
 synopsis:      Detect dead code
 description:   Find declarations.
@@ -26,20 +26,20 @@
 
 library
   build-depends:
-    , algebraic-graphs     ^>= 0.7
+    , algebraic-graphs     >= 0.7 && <0.9
     , async                ^>= 2.2.0
-    , base                 >= 4.17 && < 4.22
+    , base                 >= 4.17 && < 4.23
     , bytestring           >= 0.10.9 && < 0.13
     , containers           >= 0.6.2.1 && < 0.9
     , directory            ^>= 1.3.3.2
     , filepath             ^>= 1.4.2.1 || ^>= 1.5
-    , generic-lens         ^>= 2.2.0.0
-    , ghc                  >= 9.4 && < 9.13
+    , generic-lens         >= 2.2 && <2.4
+    , ghc                  >= 9.4 && < 9.16
     , Glob                 ^>= 0.9 || ^>= 0.10
     , lens                 >= 5.1 && < 5.4
     , mtl                  ^>= 2.2.2 || ^>= 2.3
     , optparse-applicative >= 0.14.3 && < 0.20
-    , parallel             ^>= 3.2.0.0
+    , parallel             >= 3.2 && <3.4
     , regex-tdfa           ^>= 1.2.0.0 || ^>= 1.3.1.0
     , text                 ^>= 2.0.1 || ^>= 2.1
     , toml-reader          >= 0.2.0.0 && < 0.3.1
@@ -100,6 +100,7 @@
     Spec.ConfigInstanceModules.Module1
     Spec.ConfigInstanceModules.Module2
     Spec.ConfigInstanceModules.Module3
+    Spec.DefaultsNotReported.DefaultsNotReported
     Spec.DeriveGeneric.DeriveGeneric
     Spec.InstanceRootConstraint.InstanceRootConstraint
     Spec.InstanceTypeclass.InstanceTypeclass
@@ -113,6 +114,7 @@
     Spec.OverloadedStrings.OverloadedStrings
     Spec.RangeEnum.RangeEnum
     Spec.RootClasses.RootClasses
+    Spec.SelfWeeding.SelfWeeding
     Spec.StandaloneDeriving.StandaloneDeriving
     Spec.TypeAliasGADT.TypeAliasGADT
     Spec.TypeDataDecl.TypeDataDecl
