diff --git a/CHANGES.txt b/CHANGES.txt
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -1,5 +1,10 @@
 Changelog for HLint (* = breaking change)
 
+2.2.1, released 2019-07-22
+    #713, make sure -XNoPatternSynonyms works (fix regression)
+    #700, add some Monoid and Alternative hints
+    Add createModuleEx to the API
+    #698, don't suggest a replacement for DerivingStrategies
 2.2, released 2019-06-26
 *   Remove functions and make some things abstract in HLint3 API
 2.1.26, released 2019-06-26
diff --git a/data/hlint.yaml b/data/hlint.yaml
--- a/data/hlint.yaml
+++ b/data/hlint.yaml
@@ -210,6 +210,15 @@
     - warn: {lhs: zipWith f (repeat x), rhs: map (f x)}
     - warn: {lhs: zipWith f y (repeat z), rhs: map (\x -> f x z) y}
 
+    # MONOIDS
+
+    - warn: {lhs: mempty <> x, rhs: x, name: "Monoid law, left identity"}
+    - 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}
+
     # TRAVERSABLES
 
     - warn: {lhs: sequenceA (map f x), rhs: traverse f x}
@@ -441,6 +450,8 @@
     - warn: {lhs: Just <$> a <|> pure Nothing, rhs: optional a}
     - hint: {lhs: m >>= pure . f, rhs: f <$> m}
     - hint: {lhs: pure . f =<< m, rhs: f <$> m}
+    - warn: {lhs: empty <|> x, rhs: x, name: "Alternative law, left identity"}
+    - warn: {lhs: x <|> empty, rhs: x, name: "Alternative law, right identity"}
 
 
     # LIST COMP
diff --git a/hlint.cabal b/hlint.cabal
--- a/hlint.cabal
+++ b/hlint.cabal
@@ -1,7 +1,7 @@
 cabal-version:      >= 1.18
 build-type:         Simple
 name:               hlint
-version:            2.2
+version:            2.2.1
 license:            BSD3
 license-file:       LICENSE
 category:           Development
diff --git a/src/Apply.hs b/src/Apply.hs
--- a/src/Apply.hs
+++ b/src/Apply.hs
@@ -1,3 +1,4 @@
+{-# LANGUAGE PackageImports #-}
 
 module Apply(applyHints, applyHintFile, applyHintFiles) where
 
@@ -5,6 +6,7 @@
 import Data.Monoid
 import HSE.All
 import Hint.All
+import GHC.Util
 import Idea
 import Data.Tuple.Extra
 import Data.Either
@@ -13,6 +15,8 @@
 import Data.Ord
 import Config.Type
 import Config.Haskell
+import "ghc-lib-parser" HsSyn
+import qualified "ghc-lib-parser" SrcLoc as GHC
 import qualified Data.HashSet as Set
 import Prelude
 
@@ -40,26 +44,27 @@
 --   When given multiple modules at once this function attempts to find hints between modules,
 --   which is slower and often pointless (by default HLint passes modules singularly, using
 --   @--cross@ to pass all modules together).
-applyHints {- PUBLIC -} :: [Classify] -> Hint -> [(Module SrcSpanInfo, [Comment])] -> [Idea]
+applyHints {- PUBLIC -} :: [Classify] -> Hint -> [ModuleEx] -> [Idea]
 applyHints cs = applyHintsReal $ map SettingClassify cs
 
-applyHintsReal :: [Setting] -> Hint -> [(Module_, [Comment])] -> [Idea]
+applyHintsReal :: [Setting] -> Hint -> [ModuleEx] -> [Idea]
 applyHintsReal settings hints_ ms = concat $
-    [ map (classify classifiers . removeRequiresExtensionNotes m) $
+    [ map (classify classifiers . removeRequiresExtensionNotes (hseModule m)) $
         order [] (hintModule hints settings nm m) `merge`
-        concat [order [fromNamed d] $ decHints d | d <- moduleDecls m] `merge`
-        concat [order [] $ hintComment hints settings c | c <- cs]
-    | (nm,(m,cs)) <- mns
-    , let classifiers = cls ++ mapMaybe readPragma (universeBi m) ++ concatMap readComment cs
+        concat [order [fromNamed d] $ decHints d | d <- moduleDecls (hseModule m)] `merge`
+        concat [order [declName $ GHC.unLoc d] $ decHints' d | d <- hsmodDecls $ GHC.unLoc $ ghcModule m]
+    | (nm, m) <- mns
+    , let classifiers = cls ++ mapMaybe readPragma (universeBi (hseModule m)) ++ concatMap readComment (ghcComments m)
     , seq (length classifiers) True -- to force any errors from readPragma or readComment
     , let decHints = hintDecl hints settings nm m -- partially apply
-    , let order n = map (\i -> i{ideaModule= f $ moduleName m : ideaModule i, ideaDecl= f $ n ++ ideaDecl i}) . sortOn ideaSpan
+    , let decHints' = hintDecl' hints settings nm m -- partially apply
+    , let order n = map (\i -> i{ideaModule= f $ moduleName (hseModule m) : ideaModule i, ideaDecl = f $ n ++ ideaDecl i}) . sortOn ideaSpan
     , let merge = mergeBy (comparing ideaSpan)] ++
-    [map (classify cls) (hintModules hints settings $ map (second fst) mns)]
+    [map (classify cls) (hintModules hints settings mns)]
     where
         f = nubOrd . filter (/= "")
         cls = [x | SettingClassify x <- settings]
-        mns = map (scopeCreate . fst &&& id) ms
+        mns = map (\x -> (scopeCreate (hseModule x), x)) ms
         hints = (if length ms <= 1 then noModules else id) hints_
         noModules h = h{hintModules = \_ _ -> []} `mappend` mempty{hintModule = \s a b -> hintModules h s [(a,b)]}
 
@@ -72,17 +77,17 @@
         keep _ = True
 
 -- | Given a list of settings (a way to classify) and a list of hints, run them over a list of modules.
-executeHints :: [Setting] -> [(Module_, [Comment])] -> [Idea]
+executeHints :: [Setting] -> [ModuleEx] -> [Idea]
 executeHints s = applyHintsReal s (allHints s)
 
 
 -- | Return either an idea (a parse error) or the module. In IO because might call the C pre processor.
-parseModuleApply :: ParseFlags -> [Setting] -> FilePath -> Maybe String -> IO (Either Idea (Module_, [Comment]))
+parseModuleApply :: ParseFlags -> [Setting] -> FilePath -> Maybe String -> IO (Either Idea ModuleEx)
 parseModuleApply flags s file src = do
     res <- parseModuleEx (parseFlagsAddFixities [x | Infix x <- s] flags) file src
     case res of
-        Right (ModuleEx (m, c) _)  -> return $ Right (m,c)
-        Left (ParseError sl msg ctxt) ->
+      Right r -> return $ Right r
+      Left (ParseError sl msg ctxt) ->
             return $ Left $ classify [x | SettingClassify x <- s] $ rawIdeaN Error "Parse error" (mkSrcSpan sl sl) ctxt Nothing []
 
 
diff --git a/src/Config/Compute.hs b/src/Config/Compute.hs
--- a/src/Config/Compute.hs
+++ b/src/Config/Compute.hs
@@ -18,7 +18,7 @@
     case x of
         Left (ParseError sl msg _) ->
             return ("# Parse error " ++ showSrcLoc sl ++ ": " ++ msg, [])
-        Right (ModuleEx (m, _) _) -> do
+        Right (ModuleEx m _ _ _) -> do
             let xs = concatMap (findSetting $ UnQual an) (moduleDecls m)
                 r = concatMap (readSetting mempty) xs
                 s = unlines $ ["# hints found in " ++ file] ++ concatMap renderSetting r ++ ["# no hints found" | null xs]
diff --git a/src/Config/Haskell.hs b/src/Config/Haskell.hs
--- a/src/Config/Haskell.hs
+++ b/src/Config/Haskell.hs
@@ -1,5 +1,5 @@
 {-# LANGUAGE PatternGuards, ViewPatterns, ScopedTypeVariables, TupleSections #-}
-
+{-# LANGUAGE PackageImports #-}
 module Config.Haskell(
     readPragma,
     readComment,
@@ -16,6 +16,9 @@
 import Config.Type
 import Util
 import Prelude
+import GHC.Util
+import "ghc-lib-parser" SrcLoc as GHC
+import "ghc-lib-parser" ApiAnnotation
 
 
 addInfix = parseFlagsAddFixities $ infix_ (-1) ["==>"]
@@ -31,7 +34,7 @@
     case res of
         Left (ParseError sl msg err) ->
             error $ "Config parse failure at " ++ showSrcLoc sl ++ ": " ++ msg ++ "\n" ++ err
-        Right (ModuleEx (m, cs) _) -> return $ readSettings m ++ map SettingClassify (concatMap readComment cs)
+        Right modEx@(ModuleEx m _ _ _) -> return $ readSettings m ++ map SettingClassify (concatMap readComment (ghcComments modEx))
 
 
 -- | Given a module containing HLint settings information return the 'Classify' rules and the 'HintRule' expressions.
@@ -78,22 +81,23 @@
         f _ _ = Nothing
 
 
-readComment :: Comment -> [Classify]
-readComment o@(Comment True _ x)
+readComment :: GHC.Located AnnotationComment -> [Classify]
+readComment c@(L pos AnnBlockComment{})
     | (hash, x) <- maybe (False, x) (True,) $ stripPrefix "#" x
     , x <- trim x
     , (hlint, x) <- word1 x
     , lower hlint == "hlint"
     = f hash x
     where
+        x = commentText c
         f hash x
             | Just x <- if hash then stripSuffix "#" x else Just x
             , (sev, x) <- word1 x
             , Just sev <- getSeverity sev
             , (things, x) <- g x
-            , Just (hint :: String) <- if x == "" then Just "" else readMaybe x
+            , Just hint <- if x == "" then Just "" else readMaybe x
             = map (Classify sev hint "") $ ["" | null things] ++ things
-        f hash _ = errorOnComment o $ "bad HLINT pragma, expected:\n    {-" ++ h ++ " HLINT <severity> <identifier> \"Hint name\" " ++ h ++ "-}"
+        f hash _ = errorOnComment c $ "bad HLINT pragma, expected:\n    {-" ++ h ++ " HLINT <severity> <identifier> \"Hint name\" " ++ h ++ "-}"
             where h = ['#' | hash]
 
         g x | (s, x) <- word1 x
@@ -155,8 +159,9 @@
     ": Error while reading hint file, " ++ msg ++ "\n" ++
     prettyPrint val
 
-errorOnComment :: Comment -> String -> b
-errorOnComment (Comment b ann x) msg = exitMessageImpure $
-    showSrcLoc (getPointLoc ann) ++
+errorOnComment :: GHC.Located AnnotationComment -> String -> b
+errorOnComment c@(L s _) msg = exitMessageImpure $
+    let isMultiline = isCommentMultiline c in
+    showSrcLoc (ghcSrcLocToHSE $ GHC.srcSpanStart s) ++
     ": Error while reading hint file, " ++ msg ++ "\n" ++
-    (if b then "{-" else "--") ++ x ++ (if b then "-}" else "")
+    (if isMultiline then "{-" else "--") ++ commentText c ++ (if isMultiline then "-}" else "")
diff --git a/src/Config/Yaml.hs b/src/Config/Yaml.hs
--- a/src/Config/Yaml.hs
+++ b/src/Config/Yaml.hs
@@ -159,8 +159,8 @@
 parseConfigYaml :: Val -> Parser ConfigYaml
 parseConfigYaml v = do
     vs <- parseArray v
-    fmap ConfigYaml $ forM vs $ \o@v -> do
-        (s, v) <- parseObject1 v
+    fmap ConfigYaml $ forM vs $ \o -> do
+        (s, v) <- parseObject1 o
         case s of
             "package" -> ConfigPackage <$> parsePackage v
             "group" -> ConfigGroup <$> parseGroup v
diff --git a/src/GHC/Util.hs b/src/GHC/Util.hs
--- a/src/GHC/Util.hs
+++ b/src/GHC/Util.hs
@@ -1,5 +1,5 @@
 {-# LANGUAGE PackageImports #-}
-{-# LANGUAGE TypeFamilies #-}
+{-# LANGUAGE TypeFamilies, NamedFieldPuns #-}
 {-# OPTIONS_GHC -fno-warn-missing-fields #-}
 
 module GHC.Util (
@@ -12,6 +12,12 @@
   , SDoc
   , Located
   , readExtension
+  , commentText, isCommentMultiline
+  , declName
+  , unsafePrettyPrint
+  , eqMaybe
+  , noloc, unloc, getloc, noext
+  , isForD
   -- Temporary : Export these so GHC doesn't consider them unused and
   -- tell weeder to ignore them.
   , isAtom, addParen, paren, isApp, isOpApp, isAnyApp, isDot, isSection, isDotApp
@@ -27,6 +33,7 @@
 import "ghc-lib-parser" Lexer
 import "ghc-lib-parser" Parser
 import "ghc-lib-parser" SrcLoc
+import "ghc-lib-parser" OccName
 import "ghc-lib-parser" FastString
 import "ghc-lib-parser" StringBuffer
 import "ghc-lib-parser" ErrUtils
@@ -35,8 +42,9 @@
 import "ghc-lib-parser" Panic
 import "ghc-lib-parser" HscTypes
 import "ghc-lib-parser" HeaderInfo
+import "ghc-lib-parser" ApiAnnotation
 
-import Data.List
+import Data.List.Extra
 import System.FilePath
 import Language.Preprocessor.Unlit
 import qualified Data.Map.Strict as Map
@@ -60,25 +68,18 @@
 fakeLlvmConfig :: (LlvmTargets, LlvmPasses)
 fakeLlvmConfig = ([], [])
 
-badExtensions :: [Extension]
-badExtensions =
-  [
-    AlternativeLayoutRule
-  , AlternativeLayoutRuleTransitional
-  , Arrows
-  , TransformListComp
-  , UnboxedTuples
-  , UnboxedSums
-  , QuasiQuotes
-  , RecursiveDo
- ]
-
-enabledExtensions :: [Extension]
-enabledExtensions = filter (`notElem` badExtensions) enumerateExtensions
-
 baseDynFlags :: DynFlags
-baseDynFlags = foldl' xopt_set
-             (defaultDynFlags fakeSettings fakeLlvmConfig) enabledExtensions
+baseDynFlags =
+  -- The list of default enabled extensions is empty except for
+  -- 'TemplateHaskellQuotes'. This is because:
+  --  * The extensions to enable/disable are set exclusively in
+  --    'parsePragmasIntoDynFlags' based solely on HSE parse flags
+  --    (and source level annotations);
+  --  * 'TemplateHaskellQuotes' is not a known HSE extension but IS
+  --    needed if the GHC parse is to succeed for the unit-test at
+  --    hlint.yaml:860
+  let enable = [TemplateHaskellQuotes]
+  in foldl' xopt_set (defaultDynFlags fakeSettings fakeLlvmConfig) enable
 
 -- | Adjust the input 'DynFlags' to take into account language
 -- extensions to explicitly enable/disable as well as language
@@ -94,7 +95,8 @@
     (flags, _, _) <- parseDynamicFilePragma flags opts
     let flags' =  foldl' xopt_set flags enable
     let flags'' = foldl' xopt_unset flags' disable
-    return $ Right flags''
+    let flags''' = flags'' `gopt_set` Opt_KeepRawTokenStream
+    return $ Right flags'''
   where
     catchErrors :: IO (Either String DynFlags) -> IO (Either String DynFlags)
     catchErrors act = handleGhcException reportErr
@@ -118,7 +120,7 @@
 -- (of which at least one of them came from this project originally).
 
 -- | 'isAtom e' if 'e' requires no bracketing ever.
-isAtom :: (p ~ GhcPass pass) => HsExpr p -> Bool
+isAtom :: HsExpr GhcPs -> Bool
 isAtom x = case x of
   HsVar {}          -> True
   HsUnboundVar {}   -> True
@@ -156,30 +158,30 @@
     isNegativeOverLit _ = False
 
 -- | 'addParen e' wraps 'e' in parens.
-addParen :: (p ~ GhcPass pass) => HsExpr p -> HsExpr p
+addParen :: HsExpr GhcPs -> HsExpr GhcPs
 addParen e = HsPar noExt (noLoc e)
 
 -- | 'paren e' wraps 'e' in parens if 'e' is non-atomic.
-paren :: (p ~ GhcPass pass) => HsExpr GhcPs -> HsExpr GhcPs
+paren :: HsExpr GhcPs -> HsExpr GhcPs
 paren x
   | isAtom x  = x
   | otherwise = addParen x
 
 -- | 'isApp e' if 'e' is a (regular) application.
-isApp :: (p ~ GhcPass pass) => HsExpr p -> Bool
+isApp :: HsExpr GhcPs -> Bool
 isApp x = case x of
   HsApp {}  -> True
   _         -> False
 
 -- | 'isOpApp e' if 'e' is an operator application.
-isOpApp :: (p ~ GhcPass pass) => HsExpr p -> Bool
+isOpApp :: HsExpr GhcPs -> Bool
 isOpApp x = case x of
   OpApp {}   -> True
   _          -> False
 
 -- | 'isAnyApp e' if 'e' is either an application or operator
 -- application.
-isAnyApp :: (p ~ GhcPass pass) => HsExpr p -> Bool
+isAnyApp :: HsExpr GhcPs -> Bool
 isAnyApp x = isApp x || isOpApp x
 
 -- | 'isDot e'  if 'e' is the unqualifed variable '.'.
@@ -190,27 +192,99 @@
   | otherwise                          = False
 
 -- | 'isSection e' if 'e' is a section.
-isSection :: (p ~ GhcPass pass) => HsExpr p -> Bool
+isSection :: HsExpr GhcPs -> Bool
 isSection x = case x of
   SectionL {} -> True
   SectionR {} -> True
   _           -> False
 
+-- | 'isForD d' if 'd' is a foreign declaration.
+isForD :: HsDecl GhcPs -> Bool
+isForD ForD{} = True
+isForD _ = False
+
 -- | 'isDotApp e' if 'e' is dot application.
 isDotApp :: HsExpr GhcPs -> Bool
 isDotApp (OpApp _ _ (L _ op) _) = isDot op
 isDotApp _ = False
 
--- | All available GHC extensions
-enumerateExtensions :: [Extension]
--- 'Cpp' are the first and last cases of type 'Extension' in
--- 'libraries/ghc-boot-th/GHC/LanguageExtensions/Type.hs'. When we are
--- on a version of GHC that has MR
--- https://gitlab.haskell.org/ghc/ghc/merge_requests/826, we can
--- replace them with 'minBound' and 'maxBound' respectively.
-enumerateExtensions = [Cpp .. StarIsType]
-
 -- | Parse a GHC extension
 readExtension :: String -> Maybe Extension
 readExtension x = Map.lookup x exts
   where exts = Map.fromList [(show x, x) | x <- [Cpp .. StarIsType]]
+
+trimCommentStart :: String -> String
+trimCommentStart s
+    | Just s <- stripPrefix "{-" s = s
+    | Just s <- stripPrefix "--" s = s
+    | otherwise = s
+
+trimCommentEnd :: String -> String
+trimCommentEnd s
+    | Just s <- stripSuffix "-}" s = s
+    | otherwise = s
+
+trimCommentDelims :: String -> String
+trimCommentDelims = trimCommentEnd . trimCommentStart
+
+-- | Access to a comment's text.
+commentText :: Located AnnotationComment -> String
+commentText (L _ (AnnDocCommentNext s)) = trimCommentDelims s
+commentText (L _ (AnnDocCommentPrev s)) = trimCommentDelims s
+commentText (L _ (AnnDocCommentNamed s)) = trimCommentDelims s
+commentText (L _ (AnnDocSection _ s)) = trimCommentDelims s
+commentText (L _ (AnnDocOptions s)) = trimCommentDelims s
+commentText (L _ (AnnLineComment s)) = trimCommentDelims s
+commentText (L _ (AnnBlockComment s)) = trimCommentDelims s
+
+isCommentMultiline :: Located AnnotationComment -> Bool
+isCommentMultiline (L _ (AnnBlockComment _)) = True
+isCommentMultiline _ = False
+
+declName :: HsDecl GhcPs -> String
+declName (TyClD _ FamDecl{tcdFam=FamilyDecl{fdLName}}) = occNameString $ occName $ unLoc fdLName
+declName (TyClD _ SynDecl{tcdLName}) = occNameString $ occName $ unLoc tcdLName
+declName (TyClD _ DataDecl{tcdLName}) = occNameString $ occName $ unLoc tcdLName
+declName (TyClD _ ClassDecl{tcdLName}) = occNameString $ occName $ unLoc tcdLName
+declName (ValD _ FunBind{fun_id})  = occNameString $ occName $ unLoc fun_id
+declName (ValD _ VarBind{var_id})  = occNameString $ occName var_id
+declName (ValD _ (PatSynBind _ PSB{psb_id})) = occNameString $ occName $ unLoc psb_id
+declName (SigD _ (TypeSig _ (x:_) _)) = occNameString $ occName $ unLoc x
+declName (SigD _ (PatSynSig _ (x:_) _)) = occNameString $ occName $ unLoc x
+declName (SigD _ (ClassOpSig _ _ (x:_) _)) = occNameString $ occName $ unLoc x
+declName (ForD _ ForeignImport{fd_name}) = occNameString $ occName $ unLoc fd_name
+declName (ForD _ ForeignExport{fd_name}) = occNameString $ occName $ unLoc fd_name
+declName _ = ""
+
+-- \"Unsafely\" in this case means that it uses the following
+-- 'DynFlags' for printing -
+-- <http://hackage.haskell.org/package/ghc-lib-parser-8.8.0.20190424/docs/src/DynFlags.html#v_unsafeGlobalDynFlags
+-- unsafeGlobalDynFlags> This could lead to the issues documented
+-- there, but it also might not be a problem for our use case.  TODO:
+-- Decide whether this really is unsafe, and if it is, what needs to
+-- be done to make it safe.
+unsafePrettyPrint :: (Outputable.Outputable a) => a -> String
+unsafePrettyPrint = Outputable.showSDocUnsafe . Outputable.ppr
+
+-- | Test if two AST elements are equal modulo annotations.
+(=~=) :: Eq a => Located a -> Located a -> Bool
+a =~= b = unLoc a == unLoc b
+
+-- | Compare two 'Maybe (Located a)' values for equality modulo
+-- locations.
+eqMaybe:: Eq a => Maybe (Located a) -> Maybe (Located a) -> Bool
+eqMaybe (Just x) (Just y) = x =~= y
+eqMaybe Nothing Nothing = True
+eqMaybe _ _ = False
+
+noloc :: e -> Located e
+noloc = noLoc
+
+unloc :: Located e -> e
+unloc = unLoc
+
+getloc :: Located e -> SrcSpan
+getloc = getLoc
+
+noext :: NoExt
+noext = noExt
diff --git a/src/Grep.hs b/src/Grep.hs
--- a/src/Grep.hs
+++ b/src/Grep.hs
@@ -26,6 +26,6 @@
         case res of
             Left (ParseError sl msg ctxt) ->
                 print $ rawIdeaN Error (if "Parse error" `isPrefixOf` msg then msg else "Parse error: " ++ msg) (mkSrcSpan sl sl) ctxt Nothing []
-            Right (ModuleEx (m, c) _) ->
-                forM_ (applyHints [] rule [(m, c)]) $ \i ->
+            Right m ->
+                forM_ (applyHints [] rule [m]) $ \i ->
                     print i{ideaHint="", ideaTo=Nothing}
diff --git a/src/HSE/All.hs b/src/HSE/All.hs
--- a/src/HSE/All.hs
+++ b/src/HSE/All.hs
@@ -7,8 +7,9 @@
     CppFlags(..), ParseFlags(..), defaultParseFlags,
     parseFlagsAddFixities, parseFlagsSetLanguage,
     ParseError(..), ModuleEx(..),
-    parseModuleEx,
-    freeVars, vars, varss, pvars
+    parseModuleEx, ghcComments,
+    freeVars, vars, varss, pvars,
+    ghcSpanToHSE, ghcSrcLocToHSE
     ) where
 
 import Language.Haskell.Exts.Util hiding (freeVars, Vars(..))
@@ -38,8 +39,32 @@
 import qualified "ghc-lib-parser" SrcLoc as GHC
 import qualified "ghc-lib-parser" ErrUtils
 import qualified "ghc-lib-parser" Outputable
+import qualified "ghc-lib-parser" Lexer as GHC
 import qualified "ghc-lib-parser" GHC.LanguageExtensions.Type as GHC
+import qualified "ghc-lib-parser" ApiAnnotation as GHC
 
+-- | Convert a GHC source loc into an HSE equivalent.
+ghcSrcLocToHSE :: GHC.SrcLoc -> SrcLoc
+ghcSrcLocToHSE (GHC.RealSrcLoc l) =
+  SrcLoc {
+      srcFilename = FastString.unpackFS (GHC.srcLocFile l)
+    , srcLine = GHC.srcLocLine l
+    , srcColumn = GHC.srcLocCol l
+    }
+ghcSrcLocToHSE (GHC.UnhelpfulLoc _) = noLoc
+
+-- | Convert a GHC source span into an HSE equivalent.
+ghcSpanToHSE :: GHC.SrcSpan -> SrcSpan
+ghcSpanToHSE (GHC.RealSrcSpan s) =
+  SrcSpan {
+      srcSpanFilename = FastString.unpackFS (GHC.srcSpanFile s)
+    , srcSpanStartLine = GHC.srcSpanStartLine s
+    , srcSpanStartColumn = GHC.srcSpanStartCol s
+    , srcSpanEndLine = GHC.srcSpanEndLine s
+    , srcSpanEndColumn = GHC.srcSpanEndCol s
+    }
+ghcSpanToHSE (GHC.UnhelpfulSpan _) = mkSrcSpan noLoc noLoc
+
 vars :: FreeVars a => a -> [String]
 freeVars :: FreeVars a => a -> Set String
 varss, pvars :: AllVars a => a -> [String]
@@ -162,14 +187,19 @@
 
 -- | Result of 'parseModuleEx', representing a parsed module.
 data ModuleEx = ModuleEx {
-    -- Combined 'hs-src-ext' and 'ghc-lib-parser' parse trees.
-    pm_hsext  :: (Module SrcSpanInfo, [Comment]) -- hs-src-ext result
-  , pm_ghclib :: Located (HsSyn.HsModule HsSyn.GhcPs) -- ghc-lib-parser result
+    hseModule :: Module SrcSpanInfo
+  , hseComments :: [Comment]
+  , ghcModule :: Located (HsSyn.HsModule HsSyn.GhcPs)
+  , ghcAnnotations :: GHC.ApiAnns
 }
 
+-- | Extract a list of all of a parsed module's comments.
+ghcComments :: ModuleEx -> [Located GHC.AnnotationComment]
+ghcComments m = concat (Map.elems $ snd (ghcAnnotations m))
+
 -- | Utility called from 'parseModuleEx' and 'hseFailOpParseModuleEx'.
 mkMode :: ParseFlags -> String -> ParseMode
-mkMode flags file = (hseFlags flags){parseFilename = file,fixities = Nothing }
+mkMode flags file = (hseFlags flags){ parseFilename = file,fixities = Nothing }
 
 -- | Error handler dispatcher. Invoked when HSE parsing has failed.
 failOpParseModuleEx :: String
@@ -262,8 +292,12 @@
         case dynFlags of
           Right ghcFlags ->
             case (parseFileContentsWithComments (mkMode flags file) ppstr, parseFileGhcLib file ppstr ghcFlags) of
-                (ParseOk (x, cs), POk _ a) ->
-                    return $ Right (ModuleEx (applyFixity fixity x, cs) a)
+                (ParseOk (x, cs), POk pst a) ->
+                    let anns =
+                          ( Map.fromListWith (++) $ GHC.annotations pst
+                          , Map.fromList ((GHC.noSrcSpan, GHC.comment_q pst) : GHC.annotations_comments pst)
+                          ) in
+                    return $ Right (ModuleEx (applyFixity fixity x) cs a anns)
                 -- Parse error if GHC parsing fails (see
                 -- https://github.com/ndmitchell/hlint/issues/645).
                 (ParseOk _, PFailed _ loc err) ->
diff --git a/src/HSE/Util.hs b/src/HSE/Util.hs
--- a/src/HSE/Util.hs
+++ b/src/HSE/Util.hs
@@ -1,4 +1,4 @@
-{-# LANGUAGE FlexibleContexts, ViewPatterns, TupleSections #-}
+{-# LANGUAGE FlexibleContexts, TupleSections #-}
 
 module HSE.Util(module HSE.Util, def) where
 
@@ -235,15 +235,6 @@
 
 ---------------------------------------------------------------------
 -- HSE FUNCTIONS
-
-isKindHash :: Type_ -> Bool
-isKindHash (TyParen _ x) = isKindHash x
-isKindHash (TyApp _ x _) = isKindHash x
-isKindHash (TyCon _ (fromQual -> Just (Ident _ s))) = "#" `isSuffixOf`  s
-isKindHash (TyTuple _ Unboxed _) = True
-isKindHash TyUnboxedSum{} = True
-isKindHash _ = False
-
 
 getEquations :: Decl s -> [Decl s]
 getEquations (FunBind s xs) = map (FunBind s . (:[])) xs
diff --git a/src/Hint/All.hs b/src/Hint/All.hs
--- a/src/Hint/All.hs
+++ b/src/Hint/All.hs
@@ -1,6 +1,6 @@
 
 module Hint.All(
-    Hint(..), HintBuiltin(..), DeclHint, ModuHint,
+    Hint(..), DeclHint, ModuHint,
     resolveHints, hintRules, builtinHints
     ) where
 
@@ -49,24 +49,24 @@
     HintMonad      -> decl monadHint
     HintLambda     -> decl lambdaHint
     HintBracket    -> decl bracketHint
-    HintNaming     -> decl namingHint
+    HintNaming     -> decl' namingHint
     HintPattern    -> decl patternHint
     HintImport     -> modu importHint
     HintExport     -> modu exportHint
+    HintComment    -> modu commentHint
     HintPragma     -> modu pragmaHint
     HintExtensions -> modu extensionsHint
     HintUnsafe     -> decl unsafeHint
     HintDuplicate  -> mods duplicateHint
-    HintComment    -> comm commentHint
-    HintNewType    -> decl newtypeHint
+    HintNewType    -> decl' newtypeHint
     HintRestrict   -> mempty{hintModule=restrictHint}
     HintSmell      -> mempty{hintDecl=smellHint,hintModule=smellModuleHint}
     where
         wrap = timed "Hint" (drop 4 $ show x) . forceList
         decl f = mempty{hintDecl=const $ \a b c -> wrap $ f a b c}
+        decl' f = mempty{hintDecl'=const $ \a b c -> wrap $ f a b c}
         modu f = mempty{hintModule=const $ \a b -> wrap $ f a b}
         mods f = mempty{hintModules=const $ \a -> wrap $ f a}
-        comm f = mempty{hintComment=const $ \a -> wrap $ f a}
 
 -- | A list of builtin hints, currently including entries such as @\"List\"@ and @\"Bracket\"@.
 builtinHints :: [(String, Hint)]
diff --git a/src/Hint/Comment.hs b/src/Hint/Comment.hs
--- a/src/Hint/Comment.hs
+++ b/src/Hint/Comment.hs
@@ -10,6 +10,7 @@
 <COMMENT> INLINE X
 </TEST>
 -}
+{-# LANGUAGE PackageImports #-}
 
 
 module Hint.Comment(commentHint) where
@@ -18,21 +19,31 @@
 import Data.Char
 import Data.List.Extra
 import Refact.Types(Refactoring(ModifyComment))
-
+import "ghc-lib-parser" SrcLoc
+import "ghc-lib-parser" ApiAnnotation
+import GHC.Util
 
 pragmas = words $
     "LANGUAGE OPTIONS_GHC INCLUDE WARNING DEPRECATED MINIMAL INLINE NOINLINE INLINABLE " ++
     "CONLIKE LINE SPECIALIZE SPECIALISE UNPACK NOUNPACK SOURCE"
 
 
-commentHint :: Comment -> [Idea]
-commentHint c@(Comment True span s)
-    | "#" `isSuffixOf` s && not ("#" `isPrefixOf` s) = [grab "Fix pragma markup" c $ '#':s]
-    | name `elem` pragmas = [grab "Use pragma syntax" c $ "# " ++ trim s ++ " #"]
-        where name = takeWhile (\x -> isAlphaNum x || x == '_') $ dropWhile isSpace s
-commentHint _ = []
+commentHint :: ModuHint
+commentHint _ m = concatMap chk (ghcComments m)
+    where
+        chk :: Located AnnotationComment -> [Idea]
+        chk comm
+          | isMultiline, "#" `isSuffixOf` s && not ("#" `isPrefixOf` s) = [grab "Fix pragma markup" comm $ '#':s]
+          | isMultiline, name `elem` pragmas = [grab "Use pragma syntax" comm $ "# " ++ trim s ++ " #"]
+               where
+                 isMultiline = isCommentMultiline comm
+                 s = commentText comm
+                 name = takeWhile (\x -> isAlphaNum x || x == '_') $ dropWhile isSpace s
+        chk _ = []
 
-grab :: String -> Comment -> String -> Idea
-grab msg (Comment typ pos s1) s2 = rawIdea Suggestion msg pos (f s1) (Just $ f s2) [] refact
-    where f s = if typ then "{-" ++ s ++ "-}" else "--" ++ s
-          refact = [ModifyComment (toRefactSrcSpan pos) (f s2)]
+        grab :: String -> Located AnnotationComment -> String -> Idea
+        grab msg o@(L pos _) s2 =
+          let s1 = commentText o in
+          rawIdea Suggestion msg (ghcSpanToHSE pos) (f s1) (Just $ f s2) [] refact
+            where f s = if isCommentMultiline o then "{-" ++ s ++ "-}" else "--" ++ s
+                  refact = [ModifyComment (toRefactSrcSpan (ghcSpanToHSE pos)) (f s2)]
diff --git a/src/Hint/Duplicate.hs b/src/Hint/Duplicate.hs
--- a/src/Hint/Duplicate.hs
+++ b/src/Hint/Duplicate.hs
@@ -32,7 +32,7 @@
 duplicateHint ms =
     dupes [(m,d,y) | (m,d,x) <- ds, Do _ y :: Exp S <- universeBi x] ++
     dupes [(m,d,y) | (m,d,x) <- ds, BDecls _ y :: Binds S <- universeBi x]
-    where ds = [(moduleName m, fromNamed d, d) | m <- map snd ms, d <- moduleDecls m]
+    where ds = [(moduleName (hseModule m), fromNamed d, d) | m <- map snd ms, d <- moduleDecls (hseModule m)]
 
 
 dupes :: (Pretty (f SrcSpan), Annotated f, Ord (f ())) => [(String, String, [f S])] -> [Idea]
diff --git a/src/Hint/Export.hs b/src/Hint/Export.hs
--- a/src/Hint/Export.hs
+++ b/src/Hint/Export.hs
@@ -9,18 +9,38 @@
 module Foo(module Foo, foo) where foo = 1 -- module Foo(..., foo) where
 </TEST>
 -}
+{-# LANGUAGE PackageImports #-}
 
 module Hint.Export(exportHint) where
 
 import Hint.Type
-
+import "ghc-lib-parser" HsSyn
+import qualified "ghc-lib-parser" Module as GHC
+import "ghc-lib-parser" SrcLoc as GHC
+import "ghc-lib-parser" OccName
+import "ghc-lib-parser" RdrName
 
 exportHint :: ModuHint
-exportHint _ (Module _ (Just o@(ModuleHead a name warning exports)) _ _ _)
-    | Nothing <- exports =
-        let o2 = ModuleHead a name warning $ Just $ ExportSpecList a [EModuleContents a name]
-        in [(ignore "Use module export list" o o2 []){ideaNote = [Note "an explicit list is usually better"]}]
-    | Just (ExportSpecList _ xs) <- exports, EModuleContents a name `elem_` xs =
-        let o2 = ModuleHead a name warning $ Just $ ExportSpecList a $ EVar a ellipses : delete_ (EModuleContents a name) xs
-        in [ignore "Use explicit module export list" o o2 []]
+exportHint _ (ModuleEx _ _ (L s m@HsModule {hsmodName = Just name, hsmodExports = exports}) _)
+  | Nothing <- exports =
+      let r = o{ hsmodExports = Just (GHC.noLoc [GHC.noLoc (IEModuleContents noExt name)] )} in
+      [(ignore' "Use module export list" (L s o) (GHC.noLoc r) []){ideaNote = [Note "an explicit list is usally better"]}]
+  | Just (L _ xs) <- exports
+  , mods <- [x | x <- xs, isMod x]
+  , modName <- GHC.moduleNameString (GHC.unLoc name)
+  , names <- [GHC.moduleNameString (GHC.unLoc n) | (L _ (IEModuleContents _ n)) <- mods]
+  , exports' <- [x | x <- xs, not (matchesModName modName x)]
+  , modName `elem` names =
+      let dots = mkRdrUnqual (mkVarOcc " ... ")
+          r = o{ hsmodExports = Just (GHC.noLoc (GHC.noLoc (IEVar noExt (GHC.noLoc (IEName (GHC.noLoc dots)))) : exports') )}
+      in
+        [ignore' "Use explicit module export list" (L s o) (GHC.noLoc r) []]
+      where
+          o = m{hsmodImports=[], hsmodDecls=[], hsmodDeprecMessage=Nothing, hsmodHaddockModHeader=Nothing }
+          isMod (L _ (IEModuleContents _ _)) = True
+          isMod _ = False
+
+          matchesModName m (L _ (IEModuleContents _ (L _ n))) = GHC.moduleNameString n == m
+          matchesModName _ _ = False
+
 exportHint _ _ = []
diff --git a/src/Hint/Extensions.hs b/src/Hint/Extensions.hs
--- a/src/Hint/Extensions.hs
+++ b/src/Hint/Extensions.hs
@@ -156,7 +156,7 @@
             [ Note $ "Extension " ++ prettyExtension x ++ " is " ++ reason x
             | x <- explainedRemovals])
         [ModifyComment (toSS o) newPragma]
-    | o@(LanguagePragma sl exts) <- modulePragmas x
+    | o@(LanguagePragma sl exts) <- modulePragmas (hseModule x)
     , let before = map (parseExtension . prettyPrint) exts
     , let after = filter (`Set.member` keep) before
     , before /= after
@@ -166,14 +166,14 @@
     , let newPragma = if null after then "" else prettyPrint $ LanguagePragma sl $ map (toNamed . prettyExtension) after
     ]
     where
-        usedTH = used TemplateHaskell x || used QuasiQuotes x
+        usedTH = used TemplateHaskell (hseModule x) || used QuasiQuotes (hseModule x)
             -- if TH or QuasiQuotes is on, can use all other extensions programmatically
 
         -- all the extensions defined to be used
-        extensions = Set.fromList [parseExtension $ fromNamed e | LanguagePragma _ exts <- modulePragmas x, e <- exts]
+        extensions = Set.fromList [parseExtension $ fromNamed e | LanguagePragma _ exts <- modulePragmas (hseModule x), e <- exts]
 
         -- those extensions we detect to be useful
-        useful = if usedTH then extensions  else Set.filter (`usedExt` x) extensions
+        useful = if usedTH then extensions  else Set.filter (`usedExt` hseModule x) extensions
 
         -- those extensions which are useful, but implied by other useful extensions
         implied = Map.fromList
@@ -192,7 +192,7 @@
             | e <- Set.toList $ extensions `Set.difference` keep
             , a <- extensionImplies e
             , a `Set.notMember` useful
-            , usedTH || usedExt a x
+            , usedTH || usedExt a (hseModule x)
             ]
 
         reason x =
diff --git a/src/Hint/Import.hs b/src/Hint/Import.hs
--- a/src/Hint/Import.hs
+++ b/src/Hint/Import.hs
@@ -1,3 +1,4 @@
+{-# LANGUAGE PackageImports #-}
 {-# LANGUAGE PatternGuards, ScopedTypeVariables, RecordWildCards #-}
 {-
     Reduce the number of import declarations.
@@ -48,61 +49,123 @@
 import Data.Maybe
 import Prelude
 
+import "ghc-lib-parser" FastString
+import "ghc-lib-parser" BasicTypes
+import "ghc-lib-parser" RdrName
+import "ghc-lib-parser" Module
+import "ghc-lib-parser" HsSyn as GHC
+import qualified "ghc-lib-parser" SrcLoc as GHC
+import GHC.Util
 
 importHint :: ModuHint
-importHint _ x = concatMap (wrap . snd) (groupSort
-                 [((fromNamed $ importModule i,importPkg i),i) | i <- universeBi x, not $ importSrc i]) ++
-                 concatMap (\x -> hierarchy x ++ combine1 x) (universeBi x)
-
-
-wrap :: [ImportDecl S] -> [Idea]
-wrap o = [ rawIdea Warning "Use fewer imports" (srcInfoSpan $ ann $ head o) (f o) (Just $ f x) [] rs
-         | Just (x, rs) <- [simplify o]]
-    where f = unlines . map prettyPrint
+importHint _ ModuleEx {ghcModule=GHC.L _ HsModule{hsmodImports=ms}} =
+  -- Ideas for combining multiple imports.
+  concatMap (reduceImports . snd) (
+    groupSort [((n, pkg), i) | i <- ms
+              , not $ ideclSource (unloc i)
+              , let i' = unloc i
+              , let n = unloc $ ideclName i'
+              , let pkg  = unpackFS . sl_fs <$> ideclPkgQual i']) ++
+  -- Ideas for removing redundant 'as' clauses.
+  concatMap stripRedundantAlias ms ++
+  -- Ideas for replacing deprecated imports by their preferred
+  -- equivalents.
+  concatMap preferHierarchicalImports ms
 
+reduceImports :: [LImportDecl GhcPs] -> [Idea]
+reduceImports ms =
+  [rawIdea Hint.Type.Warning "Use fewer imports"
+    (ghcSpanToHSE (getloc $ head ms)) (f ms) (Just $ f x) [] rs
+  | Just (x, rs) <- [simplify ms]]
+  where f = unlines . map unsafePrettyPrint
 
-simplify :: [ImportDecl S] -> Maybe ([ImportDecl S], [Refactoring R.SrcSpan])
+simplify :: [LImportDecl GhcPs]
+         -> Maybe ([LImportDecl GhcPs], [Refactoring R.SrcSpan])
 simplify [] = Nothing
-simplify (x:xs) = case simplifyHead x xs of
+simplify (x : xs) = case simplifyHead x xs of
     Nothing -> first (x:) <$> simplify xs
     Just (xs, rs) -> Just $ maybe (xs, rs) (second (++ rs)) $ simplify xs
 
-
-simplifyHead :: ImportDecl S -> [ImportDecl S] -> Maybe ([ImportDecl S], [Refactoring R.SrcSpan])
-simplifyHead x [] = Nothing
-simplifyHead x (y:ys) = case combine x y of
+simplifyHead :: LImportDecl GhcPs
+             -> [LImportDecl GhcPs]
+             -> Maybe ([LImportDecl GhcPs], [Refactoring R.SrcSpan])
+simplifyHead x (y : ys) = case combine x y of
     Nothing -> first (y:) <$> simplifyHead x ys
     Just (xy, rs) -> Just (xy : ys, rs)
-
-
-combine :: ImportDecl S -> ImportDecl S -> Maybe (ImportDecl S, [Refactoring R.SrcSpan])
-combine x y | qual, as, specs = Just (x, [Delete Import (toSS y)])
-           | qual, as, Just (ImportSpecList _ False xs) <- importSpecs x, Just (ImportSpecList _ False ys) <- importSpecs y = let newImp = x{importSpecs = Just $ ImportSpecList an False $ nub_ $ xs ++ ys}
-            in Just (newImp, [ Replace Import (toSS x)  [] (prettyPrint newImp)
-                             , Delete Import (toSS y) ] )
-
-           | qual, as, isNothing (importSpecs x) || isNothing (importSpecs y) =
-             let (newImp, toDelete) = if isNothing (importSpecs x) then (x, y) else (y, x)
-             in Just (newImp, [Delete Import (toSS toDelete)])
-           | not (importQualified x), qual, specs, length ass == 1 =
-             let (newImp, toDelete) = if isJust (importAs x) then (x, y) else (y, x)
-             in Just (newImp, [Delete Import (toSS toDelete)])
+simplifyHead x [] = Nothing
 
+combine :: LImportDecl GhcPs
+        -> LImportDecl GhcPs
+        -> Maybe (LImportDecl GhcPs, [Refactoring R.SrcSpan])
+combine x@(GHC.L _ x') y@(GHC.L _ y')
+  -- Both (un/)qualified, common 'as', same names : Delete the second.
+  | qual, as, specs = Just (x, [Delete Import (toSS' y)])
+    -- Both (un/)qualified, common 'as', different names : Merge the
+    -- second into the first and delete it.
+  | qual, as
+  , Just (False, xs) <- ideclHiding x'
+  , Just (False, ys) <- ideclHiding y' =
+      let newImp = noloc x'{ideclHiding = Just (False, noloc (unloc xs ++ unloc ys))}
+      in Just (newImp, [Replace Import (toSS' x) [] (unsafePrettyPrint (unloc newImp))
+                       , Delete Import (toSS' y)])
+  -- Both (un/qualified), common 'as', one has names the other doesn't
+  -- : Delete the one with names.
+  | qual, as, isNothing (ideclHiding x') || isNothing (ideclHiding y') =
+       let (newImp, toDelete) = if isNothing (ideclHiding x') then (x, y) else (y, x)
+       in Just (newImp, [Delete Import (toSS' toDelete)])
+  -- Both unqualified, same names, one (and only one) has an 'as'
+  -- clause : Delete the one without an 'as'.
+  | not (ideclQualified x'), qual, specs, length ass == 1 =
+       let (newImp, toDelete) = if isJust (ideclAs x') then (x, y) else (y, x)
+       in Just (newImp, [Delete Import (toSS' toDelete)])
+  -- No hints.
+  | otherwise = Nothing
     where
-        qual = importQualified x == importQualified y
-        as = importAs x `eqMaybe` importAs y
-        ass = mapMaybe importAs [x,y]
-        specs = importSpecs x `eqMaybe` importSpecs y
+        qual = ideclQualified x' == ideclQualified y'
+        as = ideclAs x' `GHC.Util.eqMaybe` ideclAs y'
+        ass = mapMaybe ideclAs [x', y']
+        specs = transformBi (const noSrcSpan) (ideclHiding x') ==
+                    transformBi (const noSrcSpan) (ideclHiding y')
 
-combine _ _ = Nothing
+stripRedundantAlias :: LImportDecl GhcPs -> [Idea]
+stripRedundantAlias x@(GHC.L loc i@GHC.ImportDecl {..})
+  -- Suggest 'import M as M' be just 'import M'.
+  | Just (unloc ideclName) == fmap unloc ideclAs =
+      [suggest' "Redundant as" x (GHC.L loc i{ideclAs=Nothing}) [RemoveAsKeyword (toSS' x)]]
+stripRedundantAlias _ = []
 
-combine1 :: ImportDecl S -> [Idea]
-combine1 i@ImportDecl{..}
-    | Just (dropAnn importModule) == fmap dropAnn importAs
-    = [suggest "Redundant as" i i{importAs=Nothing} [RemoveAsKeyword (toSS i)]]
-combine1 _ = []
+preferHierarchicalImports :: LImportDecl GhcPs -> [Idea]
+preferHierarchicalImports x@(GHC.L loc i@GHC.ImportDecl{ideclName=(GHC.L _ n),ideclPkgQual=Nothing})
+  -- Suggest 'import IO' be rewritten 'import System.IO, import
+  -- System.IO.Error, import Control.Exception(bracket, bracket_)'.
+  | n == mkModuleName "IO" && isNothing (ideclHiding i) =
+      [rawIdeaN Suggestion "Use hierarchical imports" (ghcSpanToHSE loc)
+      (trimStart $ unsafePrettyPrint i) (
+          Just $ unlines $ map (trimStart . unsafePrettyPrint)
+          [ f "System.IO" Nothing, f "System.IO.Error" Nothing
+          , f "Control.Exception" $ Just (False, noloc [mkLIE x | x <- ["bracket","bracket_"]])]) []]
+  -- Suggest that a module import like 'Monad' should be rewritten with
+  -- its hiearchical equivalent e.g. 'Control.Monad'.
+  | Just y <- lookup (moduleNameString n) newNames =
+    let newModuleName = y ++ "." ++ moduleNameString n
+        r = [Replace R.ModuleName (toSS' x) [] newModuleName] in
+    [suggest' "Use hierarchical imports"
+     x (noloc (desugarQual i){ideclName=noloc (mkModuleName newModuleName)}) r]
+  where
+    -- Substitute a new module name.
+    f a b = (desugarQual i){ideclName=noloc (mkModuleName a), ideclHiding=b}
+    -- Wrap a literal name into an 'IE' (import/export) value.
+    mkLIE :: String -> LIE GhcPs
+    mkLIE n = noloc $ IEVar noext (noloc (IEName (noloc (mkVarUnqual (fsLit n)))))
+    -- Rewrite 'import qualified X' as 'import qualified X as X'.
+    desugarQual :: GHC.ImportDecl GhcPs -> GHC.ImportDecl GhcPs
+    desugarQual i
+      | ideclQualified i && isNothing (ideclAs i) = i{ideclAs = Just (ideclName i)}
+      | otherwise = i
 
+preferHierarchicalImports _ = []
 
+newNames :: [(String, String)]
 newNames = let (*) = flip (,) in
     ["Control" * "Monad"
     ,"Data" * "Char"
@@ -118,28 +181,3 @@
     -- ,"System" * "Locale"
     -- ,"System" * "Time"
     ]
-
-
-hierarchy :: ImportDecl S -> [Idea]
-hierarchy i@ImportDecl{importModule=m@(ModuleName _ x),importPkg=Nothing} | Just y <- lookup x newNames
-    =
-    let newModuleName = y ++ "." ++ x
-        r = [Replace R.ModuleName (toSS m) [] newModuleName] in
-    [suggest "Use hierarchical imports" i (desugarQual i){importModule=ModuleName an newModuleName} r]
-
--- import IO is equivalent to
--- import System.IO, import System.IO.Error, import Control.Exception(bracket, bracket_)
-hierarchy i@ImportDecl{importModule=ModuleName _ "IO", importSpecs=Nothing,importPkg=Nothing}
-    = [rawIdeaN Suggestion "Use hierarchical imports" (srcInfoSpan $ ann i) (trimStart $ prettyPrint i) (
-          Just $ unlines $ map (trimStart . prettyPrint)
-          [f "System.IO" Nothing, f "System.IO.Error" Nothing
-          ,f "Control.Exception" $ Just $ ImportSpecList an False [IVar an $ toNamed x | x <- ["bracket","bracket_"]]]) []]
-    where f a b = (desugarQual i){importModule=ModuleName an a, importSpecs=b}
-
-hierarchy _ = []
-
-
--- import qualified X ==> import qualified X as X
-desugarQual :: ImportDecl S -> ImportDecl S
-desugarQual x | importQualified x && isNothing (importAs x) = x{importAs=Just (importModule x)}
-              | otherwise = x
diff --git a/src/Hint/Match.hs b/src/Hint/Match.hs
--- a/src/Hint/Match.hs
+++ b/src/Hint/Match.hs
@@ -91,7 +91,7 @@
 ---------------------------------------------------------------------
 -- PERFORM THE MATCHING
 
-findIdeas :: [HintRule] -> Scope -> Module S -> Decl_ -> [Idea]
+findIdeas :: [HintRule] -> Scope -> ModuleEx -> Decl_ -> [Idea]
 findIdeas matches s _ decl = timed "Hint" "Match apply" $ forceList
     [ (idea (hintRuleSeverity m) (hintRuleName m) x y [r]){ideaNote=notes}
     | decl <- findDecls decl
diff --git a/src/Hint/Naming.hs b/src/Hint/Naming.hs
--- a/src/Hint/Naming.hs
+++ b/src/Hint/Naming.hs
@@ -1,4 +1,6 @@
 {-# LANGUAGE FlexibleContexts #-}
+{-# LANGUAGE PackageImports #-}
+{-# LANGUAGE ViewPatterns #-}
 {-
     Suggest the use of camelCase
 
@@ -11,6 +13,7 @@
     Also allow test_ as it's a standard tasty-th idiom
     Also allow numbers separated by _
     Also don't suggest anything mentioned elsewhere in the module
+    Don't suggest for FFI, since they match their C names
 
 <TEST>
 data Yes = Foo | Bar'Test -- data Yes = Foo | BarTest
@@ -19,6 +22,7 @@
 data Yes = Foo {bar_cap :: Int}
 data No = FOO | BarBAR | BarBBar
 yes_foo = yes_foo + yes_foo -- yesFoo = ...
+yes_fooPattern Nothing = 0 -- yesFooPattern Nothing = ...
 no = 1 where yes_foo = 2
 a -== b = 1
 myTest = 1; my_test = 1
@@ -31,69 +35,86 @@
 _foo__ = 1
 section_1_1 = 1
 runMutator# = 1
+foreign import ccall hexml_node_child :: IO ()
 </TEST>
 -}
 
 
 module Hint.Naming(namingHint) where
 
-import Hint.Type
-import Data.List.Extra
+import Hint.Type (Idea, DeclHint', suggest', transformBi, isSym, toSrcSpan', ghcModule)
+import Data.List.Extra (nubOrd, isPrefixOf)
 import Data.Data
 import Data.Char
 import Data.Maybe
 import Refact.Types hiding (RType(Match))
 import qualified Data.Set as Set
 
-
-namingHint :: DeclHint
-namingHint _ modu = naming $ Set.fromList $ concatMap getNames $ moduleDecls modu
-
-naming :: Set.Set String -> Decl_ -> [Idea]
-naming seen x = [suggest "Use camelCase" x' x2' [Replace Bind (toSS x) [] (prettyPrint x2)] | not $ null res]
-    where res = [(n,y) | n <- nubOrd $ getNames x, Just y <- [suggestName n], not $ y `Set.member` seen]
-          x2 = replaceNames res x
-          x' = shorten x
-          x2' = shorten x2
+import GHC.Util
+import "ghc-lib-parser" BasicTypes
+import "ghc-lib-parser" FastString
+import "ghc-lib-parser" HsDecls
+import "ghc-lib-parser" HsExtension
+import "ghc-lib-parser" HsSyn
+import "ghc-lib-parser" OccName
+import "ghc-lib-parser" SrcLoc
 
+namingHint :: DeclHint'
+namingHint _ modu = naming $ Set.fromList $ concatMap getNames $ hsmodDecls $ unLoc (ghcModule modu)
 
-shorten :: Decl_ -> Decl_
-shorten x = case x of
-    FunBind sl (Match a b c d _:_) -> FunBind sl [f (Match a b c) d]
-    PatBind a b c _ -> f (PatBind a b) c
-    x -> x
+naming :: Set.Set String -> LHsDecl GhcPs -> [Idea]
+naming seen originalDecl =
+    [ suggest' "Use camelCase"
+               (shorten originalDecl)
+               (shorten replacedDecl)
+               [Replace Bind (toSrcSpan' originalDecl) [] (unsafePrettyPrint replacedDecl)]
+    | not $ null suggestedNames
+    ]
     where
-        dots = Var an ellipses
-        f cont (UnGuardedRhs _ _) = cont (UnGuardedRhs an dots) Nothing
-        f cont (GuardedRhss _ _) = cont (GuardedRhss an [GuardedRhs an [Qualifier an dots] dots]) Nothing
+        suggestedNames =
+            [ (originalName, suggestedName)
+            | not $ isForD $ unloc originalDecl
+            , originalName <- nubOrd $ getNames originalDecl
+            , Just suggestedName <- [suggestName originalName]
+            , not $ suggestedName `Set.member` seen
+            ]
+        replacedDecl = replaceNames suggestedNames originalDecl
 
+shorten :: LHsDecl GhcPs -> LHsDecl GhcPs
+shorten (L locDecl (ValD ttg0 bind@(FunBind _ _ matchGroup@(MG _ (L locMatches matches) _) _ _))) =
+    L locDecl (ValD ttg0 bind {fun_matches = matchGroup {mg_alts = L locMatches $ map shortenMatch matches}})
+shorten (L locDecl (ValD ttg0 bind@(PatBind _ _ grhss@(GRHSs _ rhss _) _))) =
+    L locDecl (ValD ttg0 bind {pat_rhs = grhss {grhssGRHSs = map shortenLGRHS rhss}})
+shorten x = x
 
-getNames :: Decl_ -> [String]
-getNames x = case x of
-    FunBind{} -> name
-    PatBind{} -> name
-    TypeDecl{} -> name
-    DataDecl _ _ _ _ cons _ -> name ++ [fromNamed x | QualConDecl _ _ _ x <- cons, x <- f x]
-    GDataDecl _ _ _ _ _ cons _ -> name ++ [fromNamed x | GadtDecl _ x _ _ _ _ <- cons]
-    TypeFamDecl{} -> name
-    DataFamDecl{} -> name
-    ClassDecl{} -> name
-    _ -> []
+shortenMatch :: LMatch GhcPs (LHsExpr GhcPs) -> LMatch GhcPs (LHsExpr GhcPs)
+shortenMatch (L locMatch match@(Match _ _ _ grhss@(GRHSs _ rhss _))) =
+    L locMatch match {m_grhss = grhss {grhssGRHSs = map shortenLGRHS rhss}}
+shortenMatch x = x
+
+shortenLGRHS :: LGRHS GhcPs (LHsExpr GhcPs) -> LGRHS GhcPs (LHsExpr GhcPs)
+shortenLGRHS (L locGRHS (GRHS ttg0 guards (L locExpr _))) =
+    L locGRHS (GRHS ttg0 guards (L locExpr dots))
     where
-        name = [fromNamed x]
+        dots :: HsExpr GhcPs
+        dots = HsLit NoExt (HsString (SourceText "...") (mkFastString "..."))
+shortenLGRHS x = x
 
-        f (ConDecl _ x _) = [x]
-        f (InfixConDecl _ _ x _) = [x]
-        f (RecDecl _ x _) = [x]
+getNames :: LHsDecl GhcPs -> [String]
+getNames (L _ decl) = declName decl : getConstructorNames decl
 
+getConstructorNames :: HsDecl GhcPs -> [String]
+getConstructorNames (TyClD _ (DataDecl _ _ _ _ (HsDataDefn _ _ _ _ _ cons _))) =
+    concatMap (map unsafePrettyPrint . getConNames . unLoc) cons
+getConstructorNames _ = []
 
 suggestName :: String -> Maybe String
-suggestName x
-    | isSym x || good || not (any isLower x) || any isDigit x ||
-        any (`isPrefixOf` x) ["prop_","case_","unit_","test_","spec_","scprop_","hprop_"] = Nothing
-    | otherwise = Just $ f x
+suggestName original
+    | isSym original || good || not (any isLower original) || any isDigit original ||
+        any (`isPrefixOf` original) ["prop_","case_","unit_","test_","spec_","scprop_","hprop_"] = Nothing
+    | otherwise = Just $ f original
     where
-        good = all isAlphaNum $ drp '_' $ drp '#' $ drp '\'' $ reverse $ drp '_' x
+        good = all isAlphaNum $ drp '_' $ drp '#' $ drp '\'' $ reverse $ drp '_' original
         drp x = dropWhile (== x)
 
         f xs = us ++ g ys
@@ -105,8 +126,8 @@
                  | otherwise = g xs
         g [] = []
 
-
-replaceNames :: Data a => [(String,String)] -> a -> a
-replaceNames rep = transformBi f
-    where f (Ident _ x) = Ident an $ fromMaybe x $ lookup x rep
-          f x = x
+replaceNames :: Data a => [(String, String)] -> a -> a
+replaceNames rep = transformBi replace
+    where
+        replace :: OccName -> OccName
+        replace (unsafePrettyPrint -> name) = mkOccName srcDataName $ fromMaybe name $ lookup name rep
diff --git a/src/Hint/NewType.hs b/src/Hint/NewType.hs
--- a/src/Hint/NewType.hs
+++ b/src/Hint/NewType.hs
@@ -1,3 +1,5 @@
+{-# LANGUAGE NamedFieldPuns #-}
+{-# LANGUAGE PackageImports #-}
 {-
     Suggest newtype instead of data for type declarations that have
     only one field. Don't suggest newtype for existentially
@@ -10,58 +12,129 @@
 data Foo a b = Foo a -- newtype Foo a b = Foo a
 data Foo = Foo { field1, field2 :: Int}
 data S a = forall b . Show b => S b
+{-# LANGUAGE RankNTypes #-}; data Foo = Foo (forall a. a) -- newtype Foo = Foo (forall a. a)
 data Color a = Red a | Green a | Blue a
 data Pair a b = Pair a b
 data Foo = Bar
 data Foo a = Eq a => MkFoo a
+data Foo a = () => Foo a -- newtype Foo a = Foo a
 data X = Y {-# UNPACK #-} !Int -- newtype X = Y Int
 data A = A {b :: !C} -- newtype A = A {b :: C}
 data A = A Int#
 {-# LANGUAGE UnboxedTuples #-}; data WithAnn x = WithAnn (# Ann, x #)
+{-# LANGUAGE UnboxedTuples #-}; data WithAnn x = WithAnn {getWithAnn :: (# Ann, x #)}
 data A = A () -- newtype A = A ()
-newtype Foo = Foo Int deriving (Show, Eq) -- newtype Foo = Foo Int deriving newtype (Show, Eq)
-newtype Foo = Foo { getFoo :: Int } deriving (Show, Eq) -- newtype Foo = Foo { getFoo :: Int } deriving newtype (Show, Eq)
+newtype Foo = Foo Int deriving (Show, Eq) --
+newtype Foo = Foo { getFoo :: Int } deriving (Show, Eq) --
 newtype Foo = Foo Int deriving stock Show
 </TEST>
 -}
 module Hint.NewType (newtypeHint) where
 
-import Hint.Type
+import Hint.Type (Idea, DeclHint', Note(DecreasesLaziness), ideaNote, ignoreNoSuggestion', suggestN')
 
-newtypeHint :: DeclHint
+import Data.List (isSuffixOf)
+import "ghc-lib-parser" HsDecls
+import "ghc-lib-parser" HsSyn
+import "ghc-lib-parser" Outputable
+import "ghc-lib-parser" SrcLoc
+
+newtypeHint :: DeclHint'
 newtypeHint _ _ x = newtypeHintDecl x ++ newTypeDerivingStrategiesHintDecl x
 
-newtypeHintDecl :: Decl_ -> [Idea]
-newtypeHintDecl x
-    | Just (DataType s, t, f) <- singleSimpleField x
-    = [(suggestN "Use newtype instead of data" x $ f (NewType s) $ fromTyBang t)
-            {ideaNote = [DecreasesLaziness | not $ isTyBang t]}]
+newtypeHintDecl :: LHsDecl GhcPs -> [Idea]
+newtypeHintDecl old
+    | Just WarnNewtype{newDecl, insideType} <- singleSimpleField old
+    = [(suggestN' "Use newtype instead of data" old newDecl)
+            {ideaNote = [DecreasesLaziness | warnBang insideType]}]
 newtypeHintDecl _ = []
 
+newTypeDerivingStrategiesHintDecl :: LHsDecl GhcPs -> [Idea]
+newTypeDerivingStrategiesHintDecl decl@(L _ (TyClD _ (DataDecl _ _ _ _ dataDef))) =
+    [ignoreNoSuggestion' "Use DerivingStrategies" decl | not $ isData dataDef, not $ hasAllStrategies dataDef]
+newTypeDerivingStrategiesHintDecl _ = []
 
-singleSimpleField :: Decl_ -> Maybe (DataOrNew S, Type_, DataOrNew S -> Type_ -> Decl_)
-singleSimpleField (DataDecl x1 dt x2 x3 [QualConDecl y1 Nothing Nothing ctor] x4)
-    | Just (t, ft) <- f ctor = Just (dt, t, \dt t -> DataDecl x1 dt x2 x3 [QualConDecl y1 Nothing Nothing $ ft t] x4)
-    where
-        f (ConDecl x1 x2 [t]) | not $ isKindHash t = Just (t, \t -> ConDecl x1 x2 [t])
-        f (RecDecl x1 x2 [FieldDecl y1 [y2] t]) = Just (t, \t -> RecDecl x1 x2 [FieldDecl y1 [y2] t])
-        f _ = Nothing
+hasAllStrategies :: HsDataDefn GhcPs -> Bool
+hasAllStrategies (HsDataDefn _ NewType _ _ _ _ (L _ xs)) = all hasStrategyClause xs
+hasAllStrategies _ = False
+
+isData :: HsDataDefn GhcPs -> Bool
+isData (HsDataDefn _ NewType _ _ _ _ _) = False
+isData (HsDataDefn _ DataType _ _ _ _ _) = True
+isData _ = False
+
+hasStrategyClause :: LHsDerivingClause GhcPs -> Bool
+hasStrategyClause (L _ (HsDerivingClause _ (Just _) _)) = True
+hasStrategyClause _ = False
+
+data WarnNewtype = WarnNewtype
+    { newDecl :: LHsDecl GhcPs
+    , insideType :: HsType GhcPs
+    }
+
+-- | Given a declaration, returns the suggested \"newtype\"ized declaration following these guidelines:
+-- * Types ending in a \"#\" are __ignored__, because they are usually unboxed primitives - @data X = X Int#@
+-- * @ExistentialQuantification@ stuff is __ignored__ - @data X = forall t. X t@
+-- * Constructors with (nonempty) constraints are __ignored__ - @data X a = (Eq a) => X a@
+-- * Single field constructors get newtyped - @data X = X Int@ -> @newtype X = X Int@
+-- * Single record field constructors get newtyped - @data X = X {getX :: Int}@ -> @newtype X = X {getX :: Int}@
+-- * All other declarations are ignored.
+singleSimpleField :: LHsDecl GhcPs -> Maybe WarnNewtype
+singleSimpleField (L loc (TyClD ext decl@(DataDecl _ _ _ _ dataDef@(HsDataDefn _ DataType _ _ _ [L _ constructor] _))))
+    | Just inType <- simpleCons constructor =
+        Just WarnNewtype
+              { newDecl = L loc $ TyClD ext decl {tcdDataDefn = dataDef
+                  { dd_ND = NewType
+                  , dd_cons = map (\(L consloc x) -> L consloc $ dropConsBang x) $ dd_cons dataDef
+                  }}
+              , insideType = inType
+              }
 singleSimpleField _ = Nothing
 
-newTypeDerivingStrategiesHintDecl :: Decl_ -> [Idea]
-newTypeDerivingStrategiesHintDecl x = [ignoreN "Use DerivingStrategies" x new | Just new <- [newtypeDecl x]]
+-- | Checks whether its argument is a \"simple constructor\" (see criteria in 'singleSimpleFieldNew')
+-- returning the type inside the constructor if it is. This is needed for strictness analysis.
+simpleCons :: ConDecl GhcPs -> Maybe (HsType GhcPs)
+simpleCons (ConDeclH98 _ _ _ [] context (PrefixCon [L _ inType]) _)
+    | emptyOrNoContext context
+    , not $ isUnboxedTuple inType
+    , not $ isHashy inType
+    = Just inType
+simpleCons (ConDeclH98 _ _ _ [] context (RecCon (L _ [L _ (ConDeclField _ [_] (L _ inType) _)])) _)
+    | emptyOrNoContext context
+    , not $ isUnboxedTuple inType
+    , not $ isHashy inType
+    = Just inType
+simpleCons _ = Nothing
 
-newtypeDecl :: Decl_ -> Maybe Decl_
-newtypeDecl (DataDecl x1 x2@(NewType _) x3 x4 x5 x6)
-    | any hasNoStrategy x6 = Just $ DataDecl x1 x2 x3 x4 x5 (withNewtype <$> x6)
-newtypeDecl (GDataDecl x1 x2@(NewType _) x3 x4 x5 x6 x7)
-    | any hasNoStrategy x7 = Just $ GDataDecl x1 x2 x3 x4 x5 x6 (withNewtype <$> x7)
-newtypeDecl _ = Nothing
+isHashy :: HsType GhcPs -> Bool
+isHashy (HsTyVar _ _ identifier) = "#" `isSuffixOf` showSDocUnsafe (ppr identifier)
+isHashy _ = False
 
-hasNoStrategy :: Deriving a -> Bool
-hasNoStrategy (Deriving _ Nothing _) = True
-hasNoStrategy _                      = False
+warnBang :: HsType GhcPs -> Bool
+warnBang (HsBangTy _ (HsSrcBang _ _ SrcStrict) _) = False
+warnBang _ = True
 
-withNewtype :: Deriving a -> Deriving a
-withNewtype (Deriving l Nothing rs)  = Deriving l (Just $ DerivNewtype l) rs
-withNewtype d                        = d
+emptyOrNoContext :: Maybe (LHsContext GhcPs) -> Bool
+emptyOrNoContext Nothing = True
+emptyOrNoContext (Just (L _ [])) = True
+emptyOrNoContext _ = False
+
+-- | The \"Bang\" here refers to 'HsSrcBang', which notably also includes @UNPACK@ pragmas!
+dropConsBang :: ConDecl GhcPs -> ConDecl GhcPs
+dropConsBang decl@(ConDeclH98 _ _ _ _ _ (PrefixCon fields) _) =
+    decl {con_args = PrefixCon $ map getBangType fields}
+dropConsBang decl@(ConDeclH98 _ _ _ _ _ (RecCon (L recloc conDeclFields)) _) =
+    decl {con_args = RecCon $ L recloc $ removeUnpacksRecords conDeclFields}
+    where
+        removeUnpacksRecords :: [LConDeclField GhcPs] -> [LConDeclField GhcPs]
+        removeUnpacksRecords = map (\(L conDeclFieldLoc x) -> L conDeclFieldLoc $ removeConDeclFieldUnpacks x)
+
+        removeConDeclFieldUnpacks :: ConDeclField GhcPs -> ConDeclField GhcPs
+        removeConDeclFieldUnpacks conDeclField@(ConDeclField _ _ fieldType _) =
+            conDeclField {cd_fld_type = getBangType fieldType}
+        removeConDeclFieldUnpacks x = x
+dropConsBang x = x
+
+isUnboxedTuple :: HsType GhcPs -> Bool
+isUnboxedTuple (HsTupleTy _ HsUnboxedTuple _) = True
+isUnboxedTuple _ = False
diff --git a/src/Hint/Pattern.hs b/src/Hint/Pattern.hs
--- a/src/Hint/Pattern.hs
+++ b/src/Hint/Pattern.hs
@@ -76,7 +76,7 @@
         noPatBind (PatBind a _ b c) = PatBind a (PWildCard a) b c
         noPatBind x = x
 
-        strict = "Strict" `elem` [n | LanguagePragma _ ns <- modulePragmas modu, Ident _ n <- ns]
+        strict = "Strict" `elem` [n | LanguagePragma _ ns <- modulePragmas (hseModule modu), Ident _ n <- ns]
 
 
 hints :: (String -> Pattern -> [Refactoring R.SrcSpan] -> Idea) -> Pattern -> [Idea]
diff --git a/src/Hint/Pragma.hs b/src/Hint/Pragma.hs
--- a/src/Hint/Pragma.hs
+++ b/src/Hint/Pragma.hs
@@ -36,9 +36,9 @@
 
 
 pragmaHint :: ModuHint
-pragmaHint _ x = languageDupes lang ++ optToPragma x lang
+pragmaHint _ x = languageDupes lang ++ optToPragma (hseModule x) lang
     where
-        lang = [x | x@LanguagePragma{} <- modulePragmas x]
+        lang = [x | x@LanguagePragma{} <- modulePragmas (hseModule x)]
 
 optToPragma :: Module_ -> [ModulePragma S] -> [Idea]
 optToPragma x lang =
diff --git a/src/Hint/Restrict.hs b/src/Hint/Restrict.hs
--- a/src/Hint/Restrict.hs
+++ b/src/Hint/Restrict.hs
@@ -26,11 +26,11 @@
 -- FIXME: The settings should be partially applied, but that's hard to orchestrate right now
 restrictHint :: [Setting] -> ModuHint
 restrictHint settings scope m =
-        checkPragmas modu (modulePragmas m) restrict ++
-        maybe [] (checkImports modu $ moduleImports m) (Map.lookup RestrictModule restrict) ++
-        maybe [] (checkFunctions modu $ moduleDecls m) (Map.lookup RestrictFunction restrict)
+        checkPragmas modu (modulePragmas (hseModule m)) restrict ++
+        maybe [] (checkImports modu $ moduleImports (hseModule m)) (Map.lookup RestrictModule restrict) ++
+        maybe [] (checkFunctions modu $ moduleDecls (hseModule m)) (Map.lookup RestrictFunction restrict)
     where
-        modu = moduleName m
+        modu = moduleName (hseModule m)
         restrict = restrictions settings
 
 ---------------------------------------------------------------------
diff --git a/src/Hint/Smell.hs b/src/Hint/Smell.hs
--- a/src/Hint/Smell.hs
+++ b/src/Hint/Smell.hs
@@ -85,7 +85,7 @@
 import qualified Data.Map as Map
 
 smellModuleHint :: [Setting] -> ModuHint
-smellModuleHint settings scope m@(moduleImports -> imports) = case Map.lookup SmellManyImports (smells settings) of
+smellModuleHint settings scope (moduleImports . hseModule -> imports) = case Map.lookup SmellManyImports (smells settings) of
   Just n | length imports >= n ->
            let span = foldl1 mergeSrcSpan $ srcInfoSpan . ann <$> imports
                displayImports = unlines $ f <$> imports
diff --git a/src/Hint/Type.hs b/src/Hint/Type.hs
--- a/src/Hint/Type.hs
+++ b/src/Hint/Type.hs
@@ -1,6 +1,7 @@
+{-# LANGUAGE PackageImports #-}
 
 module Hint.Type(
-    DeclHint, ModuHint, CrossHint, Hint(..),
+    DeclHint, DeclHint', ModuHint, CrossHint, Hint(..),
     module Export
     ) where
 
@@ -10,20 +11,22 @@
 import Idea     as Export
 import Prelude
 import Refact   as Export
-
+import "ghc-lib-parser" HsExtension
+import "ghc-lib-parser" HsDecls
 
-type DeclHint = Scope -> Module_ -> Decl_ -> [Idea]
-type ModuHint = Scope -> Module_          -> [Idea]
-type CrossHint = [(Scope, Module_)] -> [Idea]
+type DeclHint = Scope -> ModuleEx -> Decl_ -> [Idea]
+type DeclHint' = Scope -> ModuleEx -> LHsDecl GhcPs -> [Idea]
+type ModuHint = Scope -> ModuleEx -> [Idea]
+type CrossHint = [(Scope, ModuleEx)] -> [Idea]
 
 -- | Functions to generate hints, combined using the 'Monoid' instance.
 data Hint {- PUBLIC -} = Hint
-    {hintModules :: [Setting] -> [(Scope, Module SrcSpanInfo)] -> [Idea] -- ^ Given a list of modules (and their scope information) generate some 'Idea's.
-    ,hintModule :: [Setting] -> Scope -> Module SrcSpanInfo -> [Idea] -- ^ Given a single module and its scope information generate some 'Idea's.
-    ,hintDecl :: [Setting] -> Scope -> Module SrcSpanInfo -> Decl SrcSpanInfo -> [Idea]
+    { hintModules :: [Setting] -> [(Scope, ModuleEx)] -> [Idea] -- ^ Given a list of modules (and their scope information) generate some 'Idea's.
+    , hintModule :: [Setting] -> Scope -> ModuleEx -> [Idea] -- ^ Given a single module and its scope information generate some 'Idea's.
+    , hintDecl :: [Setting] -> Scope -> ModuleEx -> Decl SrcSpanInfo -> [Idea]
+    , hintDecl' :: [Setting] -> Scope -> ModuleEx -> LHsDecl GhcPs -> [Idea]
         -- ^ Given a declaration (with a module and scope) generate some 'Idea's.
         --   This function will be partially applied with one module/scope, then used on multiple 'Decl' values.
-    ,hintComment :: [Setting] -> Comment -> [Idea] -- ^ Given a comment generate some 'Idea's.
     }
 
 instance Semigroup Hint where
@@ -31,8 +34,8 @@
         (\a b -> x1 a b ++ y1 a b)
         (\a b c -> x2 a b c ++ y2 a b c)
         (\a b c d -> x3 a b c d ++ y3 a b c d)
-        (\a b -> x4 a b ++ y4 a b)
+        (\a b c d -> x4 a b c d ++ y4 a b c d)
 
 instance Monoid Hint where
-    mempty = Hint (\_ _ -> []) (\_ _ _ -> []) (\_ _ _ _ -> []) (\_ _ -> [])
+    mempty = Hint (\_ _ -> []) (\_ _ _ -> []) (\_ _ _ _ -> []) (\_ _ _ _ -> [])
     mappend = (<>)
diff --git a/src/Hint/Unsafe.hs b/src/Hint/Unsafe.hs
--- a/src/Hint/Unsafe.hs
+++ b/src/Hint/Unsafe.hs
@@ -33,7 +33,7 @@
         , isUnsafeDecl d, x `notElem_` noinline]
     where
         gen x = InlineSig an False Nothing $ UnQual an x
-        noinline = [q | InlineSig _ False Nothing (UnQual _ q) <- moduleDecls m]
+        noinline = [q | InlineSig _ False Nothing (UnQual _ q) <- moduleDecls (hseModule m)]
 
 isUnsafeDecl :: Decl_ -> Bool
 isUnsafeDecl (PatBind _ _ rhs bind) =
diff --git a/src/Idea.hs b/src/Idea.hs
--- a/src/Idea.hs
+++ b/src/Idea.hs
@@ -1,12 +1,13 @@
 {-# LANGUAGE RecordWildCards, NoMonomorphismRestriction #-}
+{-# LANGUAGE PackageImports #-}
 
 module Idea(
     Idea(..),
-    rawIdea, idea, suggest, warn, ignore,
-    rawIdeaN, suggestN, ignoreN,
+    rawIdea, idea, idea', suggest, suggest', warn, warn',ignore, ignore',
+    rawIdeaN, suggestN, suggestN', ignoreN, ignoreNoSuggestion',
     showIdeasJson, showANSI,
     Note(..), showNotes,
-    Severity(..)
+    Severity(..),
     ) where
 
 import Data.Functor
@@ -17,7 +18,9 @@
 import Refact.Types hiding (SrcSpan)
 import qualified Refact.Types as R
 import Prelude
-
+import qualified "ghc-lib-parser" SrcLoc as GHC
+import qualified "ghc-lib-parser" Outputable
+import qualified GHC.Util as GHC
 
 -- | An idea suggest by a 'Hint'.
 data Idea = Idea
@@ -79,17 +82,65 @@
             where xs = lines $ tt x
 
 
+rawIdea :: Severity -> String -> SrcSpan -> String -> Maybe String -> [Note]-> [Refactoring R.SrcSpan] -> Idea
 rawIdea = Idea [] []
+rawIdeaN :: Severity -> String -> SrcSpan -> String -> Maybe String -> [Note] -> Idea
 rawIdeaN a b c d e f = Idea [] [] a b c d e f []
 
+idea :: (Annotated ast, Pretty a, Pretty (ast SrcSpanInfo)) =>
+        Severity -> String -> ast SrcSpanInfo -> a -> [Refactoring R.SrcSpan] -> Idea
 idea severity hint from to = rawIdea severity hint (srcInfoSpan $ ann from) (f from) (Just $ f to) []
     where f = trimStart . prettyPrint
 
+idea' :: (GHC.HasSrcSpan a, Outputable.Outputable a) =>
+         Severity -> String -> a -> a -> [Refactoring R.SrcSpan] -> Idea
+idea' severity hint from to =
+  rawIdea severity hint (ghcSpanToHSE (GHC.getLoc from)) (GHC.unsafePrettyPrint from) (Just $ GHC.unsafePrettyPrint to) []
+
+suggest :: (Annotated ast, Pretty a, Pretty (ast SrcSpanInfo)) =>
+           String -> ast SrcSpanInfo -> a -> [Refactoring R.SrcSpan] -> Idea
 suggest = idea Suggestion
+
+suggest' :: (GHC.HasSrcSpan a, Outputable.Outputable a) =>
+            String -> a -> a -> [Refactoring R.SrcSpan] -> Idea
+suggest' = idea' Suggestion
+
+warn :: (Annotated ast, Pretty a, Pretty (ast SrcSpanInfo)) =>
+        String -> ast SrcSpanInfo -> a -> [Refactoring R.SrcSpan] -> Idea
 warn = idea Warning
+
+warn' :: (GHC.HasSrcSpan a, Outputable.Outputable a) =>
+         String -> a -> a -> [Refactoring R.SrcSpan] -> Idea
+warn' = idea' Warning
+
+ignoreNoSuggestion' :: (GHC.HasSrcSpan a, Outputable.Outputable a)
+                    => String -> a -> Idea
+ignoreNoSuggestion' hint x = rawIdeaN Ignore hint (ghcSpanToHSE (GHC.getLoc x)) (GHC.unsafePrettyPrint x) Nothing []
+
+ignore :: (Annotated ast, Pretty a, Pretty (ast SrcSpanInfo)) =>
+          String -> ast SrcSpanInfo -> a -> [Refactoring R.SrcSpan] -> Idea
 ignore = idea Ignore
 
+ignore' :: (GHC.HasSrcSpan a, Outputable.Outputable a) =>
+           String -> a -> a -> [Refactoring R.SrcSpan] -> Idea
+ignore' = idea' Ignore
+
+ideaN :: (Annotated ast, Pretty a, Pretty (ast SrcSpanInfo)) =>
+         Severity -> String -> ast SrcSpanInfo -> a -> Idea
 ideaN severity hint from to = idea severity hint from to []
 
+ideaN' :: (GHC.HasSrcSpan a, Outputable.Outputable a) =>
+          Severity -> String -> a -> a -> Idea
+ideaN' severity hint from to = idea' severity hint from to []
+
+suggestN :: (Annotated ast, Pretty a, Pretty (ast SrcSpanInfo)) =>
+            String -> ast SrcSpanInfo -> a -> Idea
 suggestN = ideaN Suggestion
+
+suggestN' :: (GHC.HasSrcSpan a, Outputable.Outputable a) =>
+             String -> a -> a -> Idea
+suggestN' = ideaN' Suggestion
+
+ignoreN :: (Annotated ast, Pretty a, Pretty (ast SrcSpanInfo)) =>
+           String -> ast SrcSpanInfo -> a -> Idea
 ignoreN = ideaN Ignore
diff --git a/src/Language/Haskell/HLint4.hs b/src/Language/Haskell/HLint4.hs
--- a/src/Language/Haskell/HLint4.hs
+++ b/src/Language/Haskell/HLint4.hs
@@ -23,7 +23,7 @@
     -- * Hints
     Hint, resolveHints,
     -- * Parse files
-    ModuleEx, parseModuleEx, defaultParseFlags, parseFlagsAddFixities, ParseError(..), ParseFlags(..), CppFlags(..)
+    ModuleEx, parseModuleEx, createModuleEx, defaultParseFlags, parseFlagsAddFixities, ParseError(..), ParseFlags(..), CppFlags(..)
     ) where
 
 import Config.Type
@@ -34,6 +34,9 @@
 import HSE.All
 import Hint.All hiding (resolveHints)
 import qualified Hint.All as H
+import qualified ApiAnnotation as GHC
+import qualified HsSyn as GHC
+import SrcLoc
 import CmdLine
 import Paths_hlint
 
@@ -127,8 +130,7 @@
 --   which is slower and often pointless (by default HLint passes modules singularly, using
 --   @--cross@ to pass all modules together).
 applyHints :: [Classify] -> Hint -> [ModuleEx] -> [Idea]
-applyHints a b = H.applyHints a b . map pm_hsext
-
+applyHints = H.applyHints
 
 -- | Snippet from the documentation, if this changes, update the documentation
 _docs :: IO ()
@@ -136,3 +138,10 @@
     (flags, classify, hint) <- autoSettings
     Right m <- parseModuleEx flags "MyFile.hs" Nothing
     print $ applyHints classify hint [m]
+
+
+-- | Create a 'ModuleEx' from GHC annotations and module tree.
+--   Note that any hints that work on the @haskell-src-exts@ won't work.
+createModuleEx:: GHC.ApiAnns -> Located (GHC.HsModule GHC.GhcPs) -> ModuleEx
+createModuleEx anns ast = ModuleEx empty [] ast anns
+    where empty = Module an Nothing [] [] []
diff --git a/src/Refact.hs b/src/Refact.hs
--- a/src/Refact.hs
+++ b/src/Refact.hs
@@ -1,8 +1,15 @@
-module Refact(toRefactSrcSpan, toSS) where
+{-# LANGUAGE PackageImports #-}
+module Refact
+    ( toRefactSrcSpan
+    , toSS, toSS'
+    , toSrcSpan'
+    ) where
 
 import qualified Refact.Types as R
 import HSE.All
 
+import qualified "ghc-lib-parser" SrcLoc as GHC
+
 toRefactSrcSpan :: SrcSpan -> R.SrcSpan
 toRefactSrcSpan ss = R.SrcSpan (srcSpanStartLine ss)
                                (srcSpanStartColumn ss)
@@ -11,3 +18,18 @@
 
 toSS :: Annotated a => a S -> R.SrcSpan
 toSS = toRefactSrcSpan . srcInfoSpan . ann
+
+-- | Don't crash in case ghc gives us a \"fake\" span,
+-- opting instead to show @0 0 0 0@ coordinates.
+toSrcSpan' :: GHC.HasSrcSpan a => a -> R.SrcSpan
+toSrcSpan' x = case GHC.getLoc x of
+    GHC.RealSrcSpan span ->
+        R.SrcSpan (GHC.srcSpanStartLine span)
+                  (GHC.srcSpanStartCol span)
+                  (GHC.srcSpanEndLine span)
+                  (GHC.srcSpanEndCol span)
+    GHC.UnhelpfulSpan _ ->
+        R.SrcSpan 0 0 0 0
+
+toSS' :: GHC.Located e -> R.SrcSpan
+toSS' = toRefactSrcSpan . ghcSpanToHSE . GHC.getLoc
