diff --git a/CHANGES.txt b/CHANGES.txt
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -1,5 +1,19 @@
 Changelog for HLint (* = breaking change)
 
+3.3.2, released 2021-08-28
+    #1244, add `only` restriction to modules
+    #1278, make --ignore-glob patterns also ignore directories
+    #1268, move nub/sort/reverse over catMaybes/lefts/rights
+    #1276, fix some incorrect unused LANGUAGE warnings
+    #1271, suggest foldr (<>) mempty ==> fold (not mconcat)
+    #1274, make the (& f) ==> f hint apply more
+    #1264, suggest eta reduction under a where
+    #1266, suggest () <$ x ==> void x
+    #1223, add some traverse laws
+    #1254, suggest null [x] ==> False
+    #1253, suggest reverse . init ==> tail . reverse
+    #1253, suggest null . concat ==> all null
+    #1255, suggest filter instead of list comprehension in teaching
 3.3.1, released 2021-04-26
     #1221, allow restrictions to use wildcards
     #1225, treat A{} as not-atomic for bracket hints
diff --git a/README.md b/README.md
--- a/README.md
+++ b/README.md
@@ -1,4 +1,4 @@
-# HLint [![Hackage version](https://img.shields.io/hackage/v/hlint.svg?label=Hackage)](https://hackage.haskell.org/package/hlint) [![Stackage version](https://www.stackage.org/package/hlint/badge/nightly?label=Stackage)](https://www.stackage.org/package/hlint) [![Build status](https://img.shields.io/github/workflow/status/ndmitchell/hlint/ci.svg)](https://github.com/ndmitchell/hlint/actions)
+# HLint [![Hackage version](https://img.shields.io/hackage/v/hlint.svg?label=Hackage)](https://hackage.haskell.org/package/hlint) [![Stackage version](https://www.stackage.org/package/hlint/badge/nightly?label=Stackage)](https://www.stackage.org/package/hlint) [![Build status](https://img.shields.io/github/workflow/status/ndmitchell/hlint/ci/master.svg)](https://github.com/ndmitchell/hlint/actions)
 
 HLint is a tool for suggesting possible improvements to Haskell code. These suggestions include ideas such as using alternative functions, simplifying code and spotting redundancies. This document is structured as follows:
 
@@ -281,7 +281,7 @@
 
 * `{-# ANN module "HLint: ignore" #-}` or `{-# HLINT ignore #-}` or `{- HLINT ignore -}` - ignore all hints in this module (use `module` literally, not the name of the module).
 * `{-# ANN module "HLint: ignore Eta reduce" #-}` or `{-# HLINT ignore "Eta reduce" #-}` or `{- HLINT ignore "Eta reduce" -}` - ignore all eta reduction suggestions in this module.
-* `{-# ANN myFunction "HLint: ignore" #-}` or `{-# HLINT ignore myFunction #-}` or `{- HLINT ignore myFunction -}` - don't give any hints in the function `myFunction`.
+* `{-# ANN myFunction "HLint: ignore" #-}` or `{-# HLINT ignore myFunction #-}` or `{- HLINT ignore myFunction -}` - don't give any hints in the function `myFunction`. This may be combined with hint names, `{- HLINT ignore myFunction "Eta reduce" -}`, to only ignore that hint in that function.
 * `{-# ANN myFunction "HLint: error" #-}` or `{-# HLINT error myFunction #-}` or `{- HLINT error myFunction -}` - any hint in the function `myFunction` is an error.
 * `{-# ANN module "HLint: error Use concatMap" #-}` or `{-# HLINT error "Use concatMap" #-}` or `{- HLINT error "Use concatMap" -}` - the hint to use `concatMap` is an error (you may also use `warn` or `suggest` in place of `error` for other severity levels).
 
@@ -361,9 +361,14 @@
   - {name: [Data.Set, Data.HashSet], as: Set}
   - {name: Control.Arrow, within: []}
   - {name: Control.Monad.State, badidents: [modify, get, put], message: "Use Control.Monad.State.Class instead"}
+  - {name: Control.Exception, only: [Exception], message: "Use UnliftIO.Exception instead"}
 ```
 
-This fragment requires that all imports of `Set` must be `qualified Data.Set as Set`, enforcing consistency. It also ensures the module `Control.Arrow` can't be used anywhere. It also prevents explicit imports of the `modify` identifier from `Control.Monad.State` (this is meant to allow you to prevent people from importing reexported identifiers).
+This fragment adds the following hints:
+* Requires that all imports of `Set` must be `qualified Data.Set as Set`, enforcing consistency
+* Ensures the module `Control.Arrow` can't be used anywhere
+* Prevents explicit imports of the given identifiers from `Control.Monad.State` (e.g. to prevent people from importing reexported identifiers).
+* Prevents all imports from `Control.Exception`, except `Exception`
 
 You can customize the `Note:` for restricted modules, functions and extensions, by providing a `message` field (default: `may break the code`).
 
diff --git a/data/hlint.yaml b/data/hlint.yaml
--- a/data/hlint.yaml
+++ b/data/hlint.yaml
@@ -66,6 +66,8 @@
     # I/O
 
     - warn: {lhs: putStrLn (show x), rhs: print x}
+    - warn: {lhs: putStr (x ++ "\n"), rhs: putStrLn x}
+    - warn: {lhs: putStr (x ++ y ++ "\n"), rhs: putStrLn (x ++ y)}
     - warn: {lhs: mapM_ putChar, rhs: putStr}
     - warn: {lhs: hGetChar stdin, rhs: getChar}
     - warn: {lhs: hGetLine stdin, rhs: getLine}
@@ -137,6 +139,7 @@
     - warn: {lhs: head (reverse x), rhs: last x}
     - warn: {lhs: head (drop n x), rhs: x !! n, side: isNat n}
     - warn: {lhs: head (drop n x), rhs: x !! max 0 n, side: not (isNat n) && not (isNeg n)}
+    - warn: {lhs: reverse (init x), rhs: tail (reverse x)}
     - warn: {lhs: reverse (tail (reverse x)), rhs: init x, note: IncreasesLaziness}
     - warn: {lhs: reverse (reverse x), rhs: x, note: IncreasesLaziness, name: Avoid reverse}
     - warn: {lhs: isPrefixOf (reverse x) (reverse y), rhs: isSuffixOf x y}
@@ -191,6 +194,7 @@
     - warn: {lhs: intercalate " ", rhs: unwords}
     - hint: {lhs: concat (intersperse x y), rhs: intercalate x y, side: notEq x " "}
     - hint: {lhs: concat (intersperse " " x), rhs: unwords x}
+    - warn: {lhs: null (concat x), rhs: all null x}
     - warn: {lhs: null (filter f x), rhs: not (any f x), name: Use any}
     - warn: {lhs: "filter f x == []", rhs: not (any f x), name: Use any}
     - warn: {lhs: "filter f x /= []", rhs: any f x}
@@ -240,13 +244,15 @@
     - warn: {lhs: mempty `mappend` x, rhs: x, name: "Monoid law, left identity"}
     - warn: {lhs: x <> mempty, rhs: x, name: "Monoid law, right identity"}
     - warn: {lhs: x `mappend` mempty, rhs: x, name: "Monoid law, right identity"}
-    - warn: {lhs: foldr (<>) mempty, rhs: mconcat}
-    - warn: {lhs: foldr mappend mempty, rhs: mconcat}
+    - warn: {lhs: foldr (<>) mempty, rhs: Data.Foldable.fold}
+    - warn: {lhs: foldr mappend mempty, rhs: Data.Foldable.fold}
     - warn: {lhs: mempty x, rhs: mempty, name: Evaluate}
     - warn: {lhs: x `mempty` y, rhs: mempty, name: Evaluate, note: "Make sure you didn't mean to use mappend instead of mempty"}
 
     # TRAVERSABLES
 
+    - warn: {lhs: traverse pure, rhs: pure, name: "Traversable law"}
+    - warn: {lhs: traverse (pure . f) x, rhs: pure (fmap f x), name: "Traversable law"}
     - warn: {lhs: sequenceA (map f x), rhs: traverse f x}
     - warn: {lhs: sequenceA (fmap f x), rhs: traverse f x}
     - warn: {lhs: sequenceA_ (map f x), rhs: traverse_ f x}
@@ -313,7 +319,7 @@
     - warn: {lhs: "uncurry f (a, b)", rhs: f a b}
     - warn: {lhs: ($) (f x), rhs: f x, name: Redundant $}
     - warn: {lhs: (f $), rhs: f, name: Redundant $}
-    - warn: {lhs: (Data.Function.& f), rhs: f, name: Redundant Data.Function.&}
+    - warn: {lhs: (& f), rhs: f, name: Redundant &}
     - hint: {lhs: \x -> y, rhs: const y, side: isAtom y && not (isWildcard y)}
         # If any isWildcard recursively then x may be used but not mentioned explicitly
     - warn: {lhs: flip f x y, rhs: f y x, side: isApp original && isAtom y}
@@ -465,6 +471,7 @@
     - hint: {lhs: a >> return (), rhs: Control.Monad.void a, side: isAtom a || isApp a}
     - warn: {lhs: fmap (const ()), rhs: Control.Monad.void}
     - warn: {lhs: const () <$> x, rhs: Control.Monad.void x}
+    - warn: {lhs: () <$ x, rhs: Control.Monad.void x}
     - warn: {lhs: flip (>=>), rhs: (<=<)}
     - warn: {lhs: flip (<=<), rhs: (>=>)}
     - warn: {lhs: flip (>>=), rhs: (=<<)}
@@ -608,6 +615,18 @@
     - warn: {lhs: isNothing (fmap f x), rhs: isNothing x}
     - warn: {lhs: fromJust (fmap f x), rhs: f (fromJust x), note: IncreasesLaziness}
     - warn: {lhs: mapMaybe f (fmap g x), rhs: mapMaybe (f . g) x, name: Redundant fmap}
+    - warn: {lhs: catMaybes (nub x), rhs: nub (catMaybes x), name: Move nub out}
+    - warn: {lhs: lefts (nub x), rhs: nub (lefts x), name: Move nub out}
+    - warn: {lhs: rights (nub x), rhs: nub (rights x), name: Move nub out}
+    - warn: {lhs: catMaybes (reverse x), rhs: reverse (catMaybes x), name: Move reverse out}
+    - warn: {lhs: lefts (reverse x), rhs: reverse (lefts x), name: Move reverse out}
+    - warn: {lhs: rights (reverse x), rhs: reverse (rights x), name: Move reverse out}
+    - warn: {lhs: catMaybes (sort x), rhs: sort (catMaybes x), name: Move sort out}
+    - warn: {lhs: lefts (sort x), rhs: sort (lefts x), name: Move sort out}
+    - warn: {lhs: rights (sort x), rhs: sort (rights x), name: Move sort out}
+    - warn: {lhs: catMaybes (nubOrd x), rhs: nubOrd (catMaybes x), name: Move nubOrd out}
+    - warn: {lhs: lefts (nubOrd x), rhs: nubOrd (lefts x), name: Move nubOrd out}
+    - warn: {lhs: rights (nubOrd x), rhs: nubOrd (rights x), name: Move nubOrd out}
 
     # EITHER
 
@@ -735,6 +754,7 @@
     - warn: {lhs: "fst (x,y)", rhs: x, name: Evaluate}
     - warn: {lhs: "snd (x,y)", rhs: "y", name: Evaluate}
     - warn: {lhs: "init [x]", rhs: "[]", name: Evaluate}
+    - warn: {lhs: "null [x]", rhs: "False", name: Evaluate}
     - warn: {lhs: "null []", rhs: "True", name: Evaluate}
     - warn: {lhs: "length []", rhs: "0", name: Evaluate}
     - warn: {lhs: "foldl f z []", rhs: z, name: Evaluate}
@@ -1005,6 +1025,7 @@
     - package base
     - package codeworld-api
     rules:
+    - warn: {lhs: "pictures []", rhs: blank, name: Evaluate}
     - warn: {lhs: "pictures [ p ]", rhs: p, name: Evaluate}
     - warn: {lhs: "pictures [ p, q ]", rhs: p & q, name: Evaluate}
     - hint: {lhs: foldl1 (&), rhs: pictures}
@@ -1019,6 +1040,7 @@
     - warn: {lhs: "lighter (- a)", rhs: "darker a"}
     - warn: {lhs: "duller (- a)", rhs: "brighter a"}
     - warn: {lhs: "darker (- a)", rhs: "lighter a"}
+    - warn: {lhs: translated x y (translated u v p), rhs: translated (x + u) (y + v) p, name: Use translated once}
 
 - group:
     name: teaching
@@ -1031,6 +1053,7 @@
     - hint: {lhs: "not (x || y)", rhs: "not x && not y", name: Apply De Morgan law}
     - hint: {lhs: "not (x && y)", rhs: "not x || not y", name: Apply De Morgan law}
     - hint: {lhs: "[ f x | x <- l ]", rhs: map f l}
+    - hint: {lhs: "[ x | x <- l, p x ]", rhs: filter p l}
     - warn: {lhs: foldr f c (reverse x), rhs: foldl (flip f) c x, name: Use left fold instead of right fold}
     - warn: {lhs: foldr1 f (reverse x), rhs: foldl1 (flip f) x, name: Use left fold instead of right fold}
     - warn: {lhs: foldl f c (reverse x), rhs: foldr (flip f) c x, note: IncreasesLaziness, name: Use right fold instead of left fold}
diff --git a/data/test-restrict.yaml b/data/test-restrict.yaml
--- a/data/test-restrict.yaml
+++ b/data/test-restrict.yaml
@@ -1,6 +1,8 @@
 - modules:
   - {name: Restricted.Module, within: []}
   - {name: Restricted.Module.Message, within: [], message: "Custom message"}
+  - {name: Restricted.Module.BadIdents, badidents: ['bad']}
+  - {name: Restricted.Module.OnlyIdents, only: ['good']}
 
 - functions:
   - {name: restricted, within: []}
diff --git a/hlint.cabal b/hlint.cabal
--- a/hlint.cabal
+++ b/hlint.cabal
@@ -1,7 +1,7 @@
-cabal-version:      >= 1.18
+cabal-version:      1.18
 build-type:         Simple
 name:               hlint
-version:            3.3.1
+version:            3.3.2
 license:            BSD3
 license-file:       LICENSE
 category:           Development
diff --git a/src/CmdLine.hs b/src/CmdLine.hs
--- a/src/CmdLine.hs
+++ b/src/CmdLine.hs
@@ -163,7 +163,7 @@
         ,cmdRefactor = nam_ "refactor" &= help "Automatically invoke `refactor` to apply hints"
         ,cmdRefactorOptions = nam_ "refactor-options" &= typ "OPTIONS" &= help "Options to pass to the `refactor` executable"
         ,cmdWithRefactor = nam_ "with-refactor" &= help "Give the path to refactor"
-        ,cmdIgnoreGlob = nam_ "ignore-glob" &= help "Ignore paths matching glob pattern"
+        ,cmdIgnoreGlob = nam_ "ignore-glob" &= help "Ignore paths matching glob pattern (e.g. foo/bar/*.hs)"
         ,cmdGenerateSummary = nam_ "generate-summary" &= opt "hints.md" &= help "Generate a summary of built-in hints"
         ,cmdTest = nam_ "test" &= help "Run the test suite"
         } &= auto &= explicit &= name "lint"
@@ -257,7 +257,7 @@
     isDir <- doesDirectoryExist $ p <\> file
     if isDir then do
         let ignoredDirectories = ["dist", "dist-newstyle"]
-            avoidDir x = let y = takeFileName x in "_" `isPrefixOf` y || ("." `isPrefixOf` y && not (all (== '.') y)) || y `elem` ignoredDirectories
+            avoidDir x = let y = takeFileName x in "_" `isPrefixOf` y || ("." `isPrefixOf` y && not (all (== '.') y)) || y `elem` ignoredDirectories || ignore x
             avoidFile x = let y = takeFileName x in "." `isPrefixOf` y || ignore x
         xs <- listFilesInside (pure . not . avoidDir) $ p <\> file
         pure [x | x <- xs, drop1 (takeExtension x) `elem` exts, not $ avoidFile x]
diff --git a/src/Config/Type.hs b/src/Config/Type.hs
--- a/src/Config/Type.hs
+++ b/src/Config/Type.hs
@@ -1,7 +1,7 @@
 
 module Config.Type(
     Severity(..), Classify(..), HintRule(..), Note(..), Setting(..),
-    Restrict(..), RestrictType(..), SmellType(..),
+    Restrict(..), RestrictType(..), RestrictIdents(..), SmellType(..),
     defaultHintName, isUnifyVar, showNotes, getSeverity, getRestrictType, getSmellType
     ) where
 
@@ -106,13 +106,26 @@
 
 data RestrictType = RestrictModule | RestrictExtension | RestrictFlag | RestrictFunction deriving (Show,Eq,Ord)
 
+data RestrictIdents
+    = NoRestrictIdents -- No restrictions on module imports
+    | ForbidIdents [String] -- Forbid importing the given identifiers from this module
+    | OnlyIdents [String] -- Forbid importing all identifiers from this module, except the given identifiers
+    deriving Show
+
+instance Semigroup RestrictIdents where
+    NoRestrictIdents <> ri = ri
+    ri <> NoRestrictIdents = ri
+    ForbidIdents x1 <> ForbidIdents y1 = ForbidIdents $ x1 <> y1
+    OnlyIdents x1 <> OnlyIdents x2 = OnlyIdents $ x1 <> x2
+    ri1 <> ri2 = error $ "Incompatible restrictions: " ++ show (ri1, ri2)
+
 data Restrict = Restrict
     {restrictType :: RestrictType
     ,restrictDefault :: Bool
     ,restrictName :: [String]
     ,restrictAs :: [String] -- for RestrictModule only, what module names you can import it as
     ,restrictWithin :: [(String, String)]
-    ,restrictBadIdents :: [String]
+    ,restrictIdents :: RestrictIdents -- for RestrictModule only, what identifiers can be imported from it
     ,restrictMessage :: Maybe String
     } deriving Show
 
diff --git a/src/Config/Yaml.hs b/src/Config/Yaml.hs
--- a/src/Config/Yaml.hs
+++ b/src/Config/Yaml.hs
@@ -292,14 +292,27 @@
         Just def -> do
             b <- parseBool def
             allowFields v ["default"]
-            pure $ Restrict restrictType b [] [] [] [] Nothing
+            pure $ Restrict restrictType b [] [] [] NoRestrictIdents Nothing
         Nothing -> do
             restrictName <- parseFieldOpt "name" v >>= maybe (pure []) parseArrayString
             restrictWithin <- parseFieldOpt "within" v >>= maybe (pure [("","")]) (parseArray >=> concatMapM parseWithin)
             restrictAs <- parseFieldOpt "as" v >>= maybe (pure []) parseArrayString
-            restrictBadIdents <- parseFieldOpt "badidents" v >>= maybe (pure []) parseArrayString
+
+            restrictBadIdents <- parseFieldOpt "badidents" v
+            restrictOnlyAllowedIdents <- parseFieldOpt "only" v
+            restrictIdents <-
+                case (restrictBadIdents, restrictOnlyAllowedIdents) of
+                    (Just badIdents, Nothing) -> ForbidIdents <$> parseArrayString badIdents
+                    (Nothing, Just onlyIdents) -> OnlyIdents <$> parseArrayString onlyIdents
+                    (Nothing, Nothing) -> pure NoRestrictIdents
+                    _ -> parseFail v "The following options are mutually exclusive: badidents, only"
+
             restrictMessage <- parseFieldOpt "message" v >>= maybeParse parseString
-            allowFields v $ ["as" | restrictType == RestrictModule] ++ ["badidents", "name", "within", "message"]
+            allowFields v $
+                ["name", "within", "message"] ++
+                if restrictType == RestrictModule
+                    then ["as", "badidents", "only"]
+                    else []
             pure Restrict{restrictDefault=True,..}
 
 parseWithin :: Val -> Parser [(String, String)] -- (module, decl)
diff --git a/src/Extension.hs b/src/Extension.hs
--- a/src/Extension.hs
+++ b/src/Extension.hs
@@ -27,6 +27,7 @@
   , NegativeLiterals -- Was not enabled by HSE and enabling breaks tests.
   , StarIsType -- conflicts with TypeOperators. StarIsType is currently enabled by default,
                -- so adding it here has no effect, but it may not be the case in future GHC releases.
+  , MonadComprehensions -- Discussed in https://github.com/ndmitchell/hlint/issues/1261
   ]
 
 -- | Extensions we turn on by default when parsing. Aim to parse as
diff --git a/src/Hint/Extensions.hs b/src/Hint/Extensions.hs
--- a/src/Hint/Extensions.hs
+++ b/src/Hint/Extensions.hs
@@ -95,6 +95,8 @@
 deriving instance Show Bar -- {-# LANGUAGE StandaloneDeriving #-}
 {-# LANGUAGE DeriveGeneric, GeneralizedNewtypeDeriving #-} \
 newtype Micro = Micro Int deriving Generic -- {-# LANGUAGE DeriveGeneric #-}
+{-# LANGUAGE DeriveGeneric, TypeFamilies #-} \
+data family Bar a; data instance Bar Foo = Foo deriving (Generic)
 {-# LANGUAGE GeneralizedNewtypeDeriving #-} \
 instance Class Int where {newtype MyIO a = MyIO a deriving NewClass}
 {-# LANGUAGE UnboxedTuples #-} \
@@ -510,8 +512,8 @@
 derives :: Located HsModule -> Derives
 derives (L _ m) =  mconcat $ map decl (childrenBi m) ++ map idecl (childrenBi m)
   where
-    idecl :: Located (DataFamInstDecl GhcPs) -> Derives
-    idecl (L _ (DataFamInstDecl (HsIB _ FamEqn {feqn_rhs=HsDataDefn {dd_ND=dn, dd_derivs=(L _ ds)}}))) = g dn ds
+    idecl :: DataFamInstDecl GhcPs -> Derives
+    idecl (DataFamInstDecl (HsIB _ FamEqn {feqn_rhs=HsDataDefn {dd_ND=dn, dd_derivs=(L _ ds)}})) = g dn ds
 
     decl :: LHsDecl GhcPs -> Derives
     decl (L _ (TyClD _ (DataDecl _ _ _ _ HsDataDefn {dd_ND=dn, dd_derivs=(L _ ds)}))) = g dn ds -- Data declaration.
diff --git a/src/Hint/Lambda.hs b/src/Hint/Lambda.hs
--- a/src/Hint/Lambda.hs
+++ b/src/Hint/Lambda.hs
@@ -1,4 +1,4 @@
-{-# LANGUAGE LambdaCase, PatternGuards, ViewPatterns #-}
+{-# LANGUAGE LambdaCase, PatternGuards, TupleSections, ViewPatterns #-}
 
 {-
     Concept:
@@ -32,6 +32,8 @@
 fun x y z = f x x y z -- fun x = f x x
 fun x y z = f g z -- fun x y = f g
 fun x = f . g $ x -- fun = f . g
+fun a b = f a b c where g x y = h x y -- g = h
+fun a b = let g x y = h x y in f a b c -- g = h
 f = foo (\y -> g x . h $ y) -- g x . h
 f = foo (\y -> g x . h $ y) -- @Message Avoid lambda
 f = foo ((*) x) -- (x *)
@@ -110,7 +112,7 @@
 import Data.List.Extra
 import Data.Set (Set)
 import qualified Data.Set as Set
-import Refact.Types hiding (RType(Match))
+import Refact.Types hiding (Match)
 import Data.Generics.Uniplate.DataOnly (universe, universeBi, transformBi)
 
 import GHC.Types.Basic
@@ -129,14 +131,22 @@
 lambdaHint :: DeclHint
 lambdaHint _ _ x
     =  concatMap (uncurry lambdaExp) (universeParentBi x)
-    ++ concatMap lambdaDecl (universe x)
+    ++ concatMap (uncurry lambdaBind) binds
+  where
+    binds =
+        ( case x of
+            -- Turn a top-level HsBind under a ValD into an LHsBind.
+            -- Also, its refact type needs to be Decl.
+            L loc (ValD _ bind) -> ((L loc bind, Decl) :)
+            _ -> id
+        )
+            ((,Bind) <$> universeBi x)
 
-lambdaDecl :: LHsDecl GhcPs -> [Idea]
-lambdaDecl
-    o@(L _ (ValD _
-        origBind@FunBind {fun_id = funName@(L loc1 _), fun_matches =
-            MG {mg_alts =
-                L _ [L _ (Match _ ctxt@(FunRhs _ Prefix _) pats (GRHSs _ [L _ (GRHS _ [] origBody@(L loc2 _))] bind))]}}))
+lambdaBind :: LHsBind GhcPs -> RType -> [Idea]
+lambdaBind
+    o@(L _ origBind@FunBind {fun_id = funName@(L loc1 _), fun_matches =
+        MG {mg_alts =
+            L _ [L _ (Match _ ctxt@(FunRhs _ Prefix _) pats (GRHSs _ [L _ (GRHS _ [] origBody@(L loc2 _))] bind))]}}) rtype
     | L _ (EmptyLocalBinds _) <- bind
     , isLambda $ fromParen origBody
     , null (universeBi pats :: [HsExpr GhcPs])
@@ -147,14 +157,14 @@
           refacts = case newBody of
               -- https://github.com/alanz/ghc-exactprint/issues/97
               L _ HsCase{} -> []
-              _ -> [Replace Decl (toSS o) sub tpl]
+              _ -> [Replace rtype (toSS o) sub tpl]
        in [warn "Redundant lambda" o (gen pats origBody) refacts]
 
     | let (newPats, newBody) = etaReduce pats origBody
     , length newPats < length pats, pvars (drop (length newPats) pats) `disjoint` varss bind
     = let (sub, tpl) = mkSubtsAndTpl newPats newBody
        in [warn "Eta reduce" (reform pats origBody) (reform newPats newBody)
-            [Replace Decl (toSS $ reform pats origBody) sub tpl]
+            [Replace rtype (toSS $ reform pats origBody) sub tpl]
           ]
     where reform :: [LPat GhcPs] -> LHsExpr GhcPs -> LHsDecl GhcPs
           reform ps b = L (combineSrcSpans loc1 loc2) $ ValD noExtField $
@@ -167,8 +177,7 @@
               sub = ("body", toSS newBody) : zip vars (map toSS newPats)
               tpl = unsafePrettyPrint (reform origPats varBody)
 
-lambdaDecl _ = []
-
+lambdaBind _ _ = []
 
 etaReduce :: [LPat GhcPs] -> LHsExpr GhcPs -> ([LPat GhcPs], LHsExpr GhcPs)
 etaReduce (unsnoc -> Just (ps, view -> PVar_ p)) (L _ (HsApp _ x (view -> Var_ y)))
diff --git a/src/Hint/Restrict.hs b/src/Hint/Restrict.hs
--- a/src/Hint/Restrict.hs
+++ b/src/Hint/Restrict.hs
@@ -1,5 +1,4 @@
 {-# LANGUAGE LambdaCase #-}
-{-# LANGUAGE MultiWayIf #-}
 {-# LANGUAGE RecordWildCards #-}
 {-# LANGUAGE TypeFamilies #-}
 {-# LANGUAGE ViewPatterns #-}
@@ -67,7 +66,7 @@
 data RestrictItem = RestrictItem
     {riAs :: [String]
     ,riWithin :: [(String, String)]
-    ,riBadIdents :: [String]
+    ,riRestrictIdents :: RestrictIdents
     ,riMessage :: Maybe String
     }
 
@@ -98,7 +97,7 @@
 
         rOthers = Map.map f $ Map.fromListWith (++) (map (second pure) ros)
         f rs = (all restrictDefault rs
-               ,Map.fromListWith (<>) [(s, RestrictItem restrictAs restrictWithin restrictBadIdents restrictMessage) | Restrict{..} <- rs, s <- restrictName])
+               ,Map.fromListWith (<>) [(s, RestrictItem restrictAs restrictWithin restrictIdents restrictMessage) | Restrict{..} <- rs, s <- restrictName])
 
 ideaMessage :: Maybe String -> Idea -> Idea
 ideaMessage (Just message) w = w{ideaNote=[Note message]}
@@ -135,24 +134,36 @@
    isGood def mp x = maybe def (within modu "" . riWithin) $ Map.lookup x mp
 
 checkImports :: String -> [LImportDecl GhcPs] -> (Bool, Map.Map String RestrictItem) -> [Idea]
-checkImports modu imp (def, mp) =
-    [ ideaMessage riMessage
-      $ if | not allowImport -> ideaNoTo $ warn "Avoid restricted module" i i []
-           | not allowIdent  -> ideaNoTo $ warn "Avoid restricted identifiers" i i []
-           | not allowQual   -> warn "Avoid restricted qualification" i (noLoc $ (unLoc i){ ideclAs=noLoc . mkModuleName <$> listToMaybe riAs} :: Located (ImportDecl GhcPs)) []
-           | otherwise       -> error "checkImports: unexpected case"
-    | i@(L _ ImportDecl {..}) <- imp
-    , let RestrictItem{..} = getRestrictItem def ideclName mp
-    , let allowImport = within modu "" riWithin
-    , let allowIdent = Set.disjoint
-                       (Set.fromList riBadIdents)
-                       (Set.fromList (maybe [] (\(b, lxs) -> if b then [] else concatMap (importListToIdents . unLoc) (unLoc lxs)) ideclHiding))
-    , let allowQual = maybe True (\x -> null riAs || moduleNameString (unLoc x) `elem` riAs) ideclAs
-    , not allowImport || not allowQual || not allowIdent
-    ]
+checkImports modu lImportDecls (def, mp) = mapMaybe getImportHint lImportDecls
+  where
+    getImportHint :: LImportDecl GhcPs -> Maybe Idea
+    getImportHint i@(L _ ImportDecl{..}) = do
+      let RestrictItem{..} = getRestrictItem def ideclName mp
+      either (Just . ideaMessage riMessage) (const Nothing) $ do
+        unless (within modu "" riWithin) $
+          Left $ ideaNoTo $ warn "Avoid restricted module" i i []
 
+        let importedIdents = Set.fromList $
+              case ideclHiding of
+                Just (False, lxs) -> concatMap (importListToIdents . unLoc) (unLoc lxs)
+                _ -> []
+            invalidIdents = case riRestrictIdents of
+              NoRestrictIdents -> Set.empty
+              ForbidIdents badIdents -> importedIdents `Set.intersection` Set.fromList badIdents
+              OnlyIdents onlyIdents -> importedIdents `Set.difference` Set.fromList onlyIdents
+        unless (Set.null invalidIdents) $
+          Left $ ideaNoTo $ warn "Avoid restricted identifiers" i i []
+
+        let qualAllowed = case (riAs, ideclAs) of
+              ([], _) -> True
+              (_, Nothing) -> True
+              (_, Just (L _ modName)) -> moduleNameString modName `elem` riAs
+        unless qualAllowed $ do
+          let i' = noLoc $ (unLoc i){ ideclAs = noLoc . mkModuleName <$> listToMaybe riAs }
+          Left $ warn "Avoid restricted qualification" i i' []
+
 getRestrictItem :: Bool -> Located ModuleName -> Map.Map String RestrictItem -> RestrictItem
-getRestrictItem def ideclName = fromMaybe (RestrictItem [] [("","") | def] [] Nothing) . lookupRestrictItem ideclName
+getRestrictItem def ideclName = fromMaybe (RestrictItem [] [("","") | def] NoRestrictIdents Nothing) . lookupRestrictItem ideclName
 
 lookupRestrictItem :: Located ModuleName -> Map.Map String RestrictItem -> Maybe RestrictItem
 lookupRestrictItem ideclName mp =
diff --git a/tests/hint.test b/tests/hint.test
--- a/tests/hint.test
+++ b/tests/hint.test
@@ -100,6 +100,82 @@
 1 hint
 
 ---------------------------------------------------------------------
+RUN tests/restricted-badidents-bad.lhs --hint=data/test-restrict.yaml
+FILE tests/restricted-badidents-bad.lhs
+> import Restricted.Module.BadIdents (bad)
+OUTPUT
+tests/restricted-badidents-bad.lhs:1:3-42: Warning: Avoid restricted identifiers
+Found:
+  import Restricted.Module.BadIdents ( bad )
+Note: may break the code
+
+1 hint
+
+---------------------------------------------------------------------
+RUN tests/restricted-badidents-multibad.lhs --hint=data/test-restrict.yaml
+FILE tests/restricted-badidents-multibad.lhs
+> import Restricted.Module.BadIdents (bad, good)
+OUTPUT
+tests/restricted-badidents-multibad.lhs:1:3-48: Warning: Avoid restricted identifiers
+Found:
+  import Restricted.Module.BadIdents ( bad, good )
+Note: may break the code
+
+1 hint
+
+---------------------------------------------------------------------
+RUN tests/restricted-badidents-valid.lhs --hint=data/test-restrict.yaml
+FILE tests/restricted-badidents-valid.lhs
+> import Restricted.Module.BadIdents (good)
+OUTPUT
+No hints
+
+---------------------------------------------------------------------
+RUN tests/restricted-badidents-universal.lhs --hint=data/test-restrict.yaml
+FILE tests/restricted-badidents-universal.lhs
+> import Restricted.Module.BadIdents
+OUTPUT
+No hints
+
+---------------------------------------------------------------------
+RUN tests/restricted-onlyidents-bad.lhs --hint=data/test-restrict.yaml
+FILE tests/restricted-onlyidents-bad.lhs
+> import Restricted.Module.OnlyIdents (bad)
+OUTPUT
+tests/restricted-onlyidents-bad.lhs:1:3-43: Warning: Avoid restricted identifiers
+Found:
+  import Restricted.Module.OnlyIdents ( bad )
+Note: may break the code
+
+1 hint
+
+---------------------------------------------------------------------
+RUN tests/restricted-onlyidents-multibad.lhs --hint=data/test-restrict.yaml
+FILE tests/restricted-onlyidents-multibad.lhs
+> import Restricted.Module.OnlyIdents (bad, good)
+OUTPUT
+tests/restricted-onlyidents-multibad.lhs:1:3-49: Warning: Avoid restricted identifiers
+Found:
+  import Restricted.Module.OnlyIdents ( bad, good )
+Note: may break the code
+
+1 hint
+
+---------------------------------------------------------------------
+RUN tests/restricted-onlyidents-valid.lhs --hint=data/test-restrict.yaml
+FILE tests/restricted-onlyidents-valid.lhs
+> import Restricted.Module.OnlyIdents (good)
+OUTPUT
+No hints
+
+---------------------------------------------------------------------
+RUN tests/restricted-onlyidents-universal.lhs --hint=data/test-restrict.yaml
+FILE tests/restricted-onlyidents-universal.lhs
+> import Restricted.Module.OnlyIdents
+OUTPUT
+No hints
+
+---------------------------------------------------------------------
 RUN tests/restricted-function.lhs --hint=data/test-restrict.yaml
 FILE tests/restricted-function.lhs
 > main = restricted ()
