packages feed

hlint 1.9.21 → 1.9.22

raw patch · 32 files changed

+637/−226 lines, 32 filesdep +refactPVP: major bump suggested

API removals or changes: PVP suggests a major version bump

Dependencies added: refact

API changes (from Hackage documentation)

- Language.Haskell.HLint2: classifyDecl :: Classify -> String
- Language.Haskell.HLint2: classifyHint :: Classify -> String
- Language.Haskell.HLint2: classifyModule :: Classify -> String
- Language.Haskell.HLint2: classifySeverity :: Classify -> Severity
- Language.Haskell.HLint2: cppFlags :: ParseFlags -> CppFlags
- Language.Haskell.HLint2: encoding :: ParseFlags -> TextEncoding
- Language.Haskell.HLint2: hintComment :: Hint -> Comment -> [Idea]
- Language.Haskell.HLint2: hintDecl :: Hint -> Scope -> Module SrcSpanInfo -> Decl SrcSpanInfo -> [Idea]
- Language.Haskell.HLint2: hintModule :: Hint -> Scope -> Module SrcSpanInfo -> [Idea]
- Language.Haskell.HLint2: hintModules :: Hint -> [(Scope, Module SrcSpanInfo)] -> [Idea]
- Language.Haskell.HLint2: hintRuleLHS :: HintRule -> Exp SrcSpanInfo
- Language.Haskell.HLint2: hintRuleName :: HintRule -> String
- Language.Haskell.HLint2: hintRuleNotes :: HintRule -> [Note]
- Language.Haskell.HLint2: hintRuleRHS :: HintRule -> Exp SrcSpanInfo
- Language.Haskell.HLint2: hintRuleScope :: HintRule -> Scope
- Language.Haskell.HLint2: hintRuleSeverity :: HintRule -> Severity
- Language.Haskell.HLint2: hintRuleSide :: HintRule -> Maybe (Exp SrcSpanInfo)
- Language.Haskell.HLint2: hseFlags :: ParseFlags -> ParseMode
- Language.Haskell.HLint2: ideaDecl :: Idea -> String
- Language.Haskell.HLint2: ideaFrom :: Idea -> String
- Language.Haskell.HLint2: ideaHint :: Idea -> String
- Language.Haskell.HLint2: ideaModule :: Idea -> String
- Language.Haskell.HLint2: ideaNote :: Idea -> [Note]
- Language.Haskell.HLint2: ideaSeverity :: Idea -> Severity
- Language.Haskell.HLint2: ideaSpan :: Idea -> SrcSpan
- Language.Haskell.HLint2: ideaTo :: Idea -> Maybe String
- Language.Haskell.HLint2: parseErrorContents :: ParseError -> String
- Language.Haskell.HLint2: parseErrorLocation :: ParseError -> SrcLoc
- Language.Haskell.HLint2: parseErrorMessage :: ParseError -> String
- Language.Haskell.HLint3: classifyDecl :: Classify -> String
- Language.Haskell.HLint3: classifyHint :: Classify -> String
- Language.Haskell.HLint3: classifyModule :: Classify -> String
- Language.Haskell.HLint3: classifySeverity :: Classify -> Severity
- Language.Haskell.HLint3: cppFlags :: ParseFlags -> CppFlags
- Language.Haskell.HLint3: encoding :: ParseFlags -> TextEncoding
- Language.Haskell.HLint3: hintComment :: Hint -> Comment -> [Idea]
- Language.Haskell.HLint3: hintDecl :: Hint -> Scope -> Module SrcSpanInfo -> Decl SrcSpanInfo -> [Idea]
- Language.Haskell.HLint3: hintModule :: Hint -> Scope -> Module SrcSpanInfo -> [Idea]
- Language.Haskell.HLint3: hintModules :: Hint -> [(Scope, Module SrcSpanInfo)] -> [Idea]
- Language.Haskell.HLint3: hintRuleLHS :: HintRule -> Exp SrcSpanInfo
- Language.Haskell.HLint3: hintRuleName :: HintRule -> String
- Language.Haskell.HLint3: hintRuleNotes :: HintRule -> [Note]
- Language.Haskell.HLint3: hintRuleRHS :: HintRule -> Exp SrcSpanInfo
- Language.Haskell.HLint3: hintRuleScope :: HintRule -> Scope
- Language.Haskell.HLint3: hintRuleSeverity :: HintRule -> Severity
- Language.Haskell.HLint3: hintRuleSide :: HintRule -> Maybe (Exp SrcSpanInfo)
- Language.Haskell.HLint3: hseFlags :: ParseFlags -> ParseMode
- Language.Haskell.HLint3: ideaDecl :: Idea -> String
- Language.Haskell.HLint3: ideaFrom :: Idea -> String
- Language.Haskell.HLint3: ideaHint :: Idea -> String
- Language.Haskell.HLint3: ideaModule :: Idea -> String
- Language.Haskell.HLint3: ideaNote :: Idea -> [Note]
- Language.Haskell.HLint3: ideaSeverity :: Idea -> Severity
- Language.Haskell.HLint3: ideaSpan :: Idea -> SrcSpan
- Language.Haskell.HLint3: ideaTo :: Idea -> Maybe String
- Language.Haskell.HLint3: parseErrorContents :: ParseError -> String
- Language.Haskell.HLint3: parseErrorLocation :: ParseError -> SrcLoc
- Language.Haskell.HLint3: parseErrorMessage :: ParseError -> String
+ Language.Haskell.HLint2: [classifyDecl] :: Classify -> String
+ Language.Haskell.HLint2: [classifyHint] :: Classify -> String
+ Language.Haskell.HLint2: [classifyModule] :: Classify -> String
+ Language.Haskell.HLint2: [classifySeverity] :: Classify -> Severity
+ Language.Haskell.HLint2: [cppFlags] :: ParseFlags -> CppFlags
+ Language.Haskell.HLint2: [encoding] :: ParseFlags -> TextEncoding
+ Language.Haskell.HLint2: [hintComment] :: Hint -> Comment -> [Idea]
+ Language.Haskell.HLint2: [hintDecl] :: Hint -> Scope -> Module SrcSpanInfo -> Decl SrcSpanInfo -> [Idea]
+ Language.Haskell.HLint2: [hintModule] :: Hint -> Scope -> Module SrcSpanInfo -> [Idea]
+ Language.Haskell.HLint2: [hintModules] :: Hint -> [(Scope, Module SrcSpanInfo)] -> [Idea]
+ Language.Haskell.HLint2: [hintRuleLHS] :: HintRule -> Exp SrcSpanInfo
+ Language.Haskell.HLint2: [hintRuleName] :: HintRule -> String
+ Language.Haskell.HLint2: [hintRuleNotes] :: HintRule -> [Note]
+ Language.Haskell.HLint2: [hintRuleRHS] :: HintRule -> Exp SrcSpanInfo
+ Language.Haskell.HLint2: [hintRuleScope] :: HintRule -> Scope
+ Language.Haskell.HLint2: [hintRuleSeverity] :: HintRule -> Severity
+ Language.Haskell.HLint2: [hintRuleSide] :: HintRule -> Maybe (Exp SrcSpanInfo)
+ Language.Haskell.HLint2: [hseFlags] :: ParseFlags -> ParseMode
+ Language.Haskell.HLint2: [ideaDecl] :: Idea -> String
+ Language.Haskell.HLint2: [ideaFrom] :: Idea -> String
+ Language.Haskell.HLint2: [ideaHint] :: Idea -> String
+ Language.Haskell.HLint2: [ideaModule] :: Idea -> String
+ Language.Haskell.HLint2: [ideaNote] :: Idea -> [Note]
+ Language.Haskell.HLint2: [ideaRefactoring] :: Idea -> [Refactoring SrcSpan]
+ Language.Haskell.HLint2: [ideaSeverity] :: Idea -> Severity
+ Language.Haskell.HLint2: [ideaSpan] :: Idea -> SrcSpan
+ Language.Haskell.HLint2: [ideaTo] :: Idea -> Maybe String
+ Language.Haskell.HLint2: [parseErrorContents] :: ParseError -> String
+ Language.Haskell.HLint2: [parseErrorLocation] :: ParseError -> SrcLoc
+ Language.Haskell.HLint2: [parseErrorMessage] :: ParseError -> String
+ Language.Haskell.HLint3: [classifyDecl] :: Classify -> String
+ Language.Haskell.HLint3: [classifyHint] :: Classify -> String
+ Language.Haskell.HLint3: [classifyModule] :: Classify -> String
+ Language.Haskell.HLint3: [classifySeverity] :: Classify -> Severity
+ Language.Haskell.HLint3: [cppFlags] :: ParseFlags -> CppFlags
+ Language.Haskell.HLint3: [encoding] :: ParseFlags -> TextEncoding
+ Language.Haskell.HLint3: [hintComment] :: Hint -> Comment -> [Idea]
+ Language.Haskell.HLint3: [hintDecl] :: Hint -> Scope -> Module SrcSpanInfo -> Decl SrcSpanInfo -> [Idea]
+ Language.Haskell.HLint3: [hintModule] :: Hint -> Scope -> Module SrcSpanInfo -> [Idea]
+ Language.Haskell.HLint3: [hintModules] :: Hint -> [(Scope, Module SrcSpanInfo)] -> [Idea]
+ Language.Haskell.HLint3: [hintRuleLHS] :: HintRule -> Exp SrcSpanInfo
+ Language.Haskell.HLint3: [hintRuleName] :: HintRule -> String
+ Language.Haskell.HLint3: [hintRuleNotes] :: HintRule -> [Note]
+ Language.Haskell.HLint3: [hintRuleRHS] :: HintRule -> Exp SrcSpanInfo
+ Language.Haskell.HLint3: [hintRuleScope] :: HintRule -> Scope
+ Language.Haskell.HLint3: [hintRuleSeverity] :: HintRule -> Severity
+ Language.Haskell.HLint3: [hintRuleSide] :: HintRule -> Maybe (Exp SrcSpanInfo)
+ Language.Haskell.HLint3: [hseFlags] :: ParseFlags -> ParseMode
+ Language.Haskell.HLint3: [ideaDecl] :: Idea -> String
+ Language.Haskell.HLint3: [ideaFrom] :: Idea -> String
+ Language.Haskell.HLint3: [ideaHint] :: Idea -> String
+ Language.Haskell.HLint3: [ideaModule] :: Idea -> String
+ Language.Haskell.HLint3: [ideaNote] :: Idea -> [Note]
+ Language.Haskell.HLint3: [ideaRefactoring] :: Idea -> [Refactoring SrcSpan]
+ Language.Haskell.HLint3: [ideaSeverity] :: Idea -> Severity
+ Language.Haskell.HLint3: [ideaSpan] :: Idea -> SrcSpan
+ Language.Haskell.HLint3: [ideaTo] :: Idea -> Maybe String
+ Language.Haskell.HLint3: [parseErrorContents] :: ParseError -> String
+ Language.Haskell.HLint3: [parseErrorLocation] :: ParseError -> SrcLoc
+ Language.Haskell.HLint3: [parseErrorMessage] :: ParseError -> String
- Language.Haskell.HLint2: Idea :: String -> String -> Severity -> String -> SrcSpan -> String -> Maybe String -> [Note] -> Idea
+ Language.Haskell.HLint2: Idea :: String -> String -> Severity -> String -> SrcSpan -> String -> Maybe String -> [Note] -> [Refactoring SrcSpan] -> Idea
- Language.Haskell.HLint3: Idea :: String -> String -> Severity -> String -> SrcSpan -> String -> Maybe String -> [Note] -> Idea
+ Language.Haskell.HLint3: Idea :: String -> String -> Severity -> String -> SrcSpan -> String -> Maybe String -> [Note] -> [Refactoring SrcSpan] -> Idea

Files

CHANGES.txt view
@@ -1,5 +1,17 @@ Changelog for HLint +1.9.22+    Don't suggest redundant lambda on view patterns+    Add --no-exit-code flag+    #174, don't suggest string literals+    #175, disable 'rec' stealing extensions by default+    #170, add hints for eta-reduced operators+    #149, integrate a --refactor flag+    #147, fix the -fglasgow-exts hint+    #140, better name for moving brackets to eliminate $+    Extra hints for <$>+    Remove a redundant fmap hint+    #131, add =<< rules in addition to >>= 1.9.21     #130, ignore a BOM if it exists     #128, don't find files starting with . when searching directories
README.md view
@@ -55,6 +55,29 @@  **Bug reports:** The suggested replacement should be equivalent - please report all incorrect suggestions not mentioned as known limitations. +### Automatically Applying Hints++By supplying the `--refactor` flag hlint can automatically apply most+suggestions. Instead of a list of hints, hlint will instead output the+refactored file on stdout. In order to do this, it is necessary to have the+`refactor` executable on you path. `refactor` is provided by the+[`apply-refact`](https://github.com/mpickering/apply-refact) package,+it uses the GHC API in order to transform source files given a list of+refactorings to apply. Hlint directly calls the executable to apply the+suggestions.++Additional configuration can be passed to `refactor` with the+`--refactor-options` flag. Some useful flags include `-i` which replaces the+original file and `-s` which asks for confirmation before performing a hint.++An alternative location for `refactor` can be specified with the+`--with-refactor` flag.++Simple bindings for [vim](https://github.com/mpickering/hlint-refactor-vim),+[emacs](https://github.com/mpickering/hlint-refactor-mode) and [atom](https://github.com/mpickering/hlint-refactor-atom) are provided.++There are no plans to support the duplication nor the renaming hints.+ ### Reports  HLint can generate a lot of information, making it difficult to search for particular types of errors. The `--report` flag will cause HLint to generate a report file in HTML, which can be viewed interactively. Reports are recommended when there are more than a handful of hints.@@ -109,16 +132,6 @@ * Some people only make use of some of the suggestions. In the above example using concatMap is a good idea, but sometimes eta reduction isn't. By suggesting them separately, people can pick and choose. * Sometimes a transformed expression will be large, and a further hint will apply to some small part of the result, which appears confusing. * Consider `f $ (a b)`. There are two valid hints, either remove the $ or remove the brackets, but only one can be applied.--### Why aren't the suggestions automatically applied?--If you want to automatically apply suggestions, the [Emacs integration](https://rawgithub.com/ndmitchell/hlint/master/hlint.htm#emacs) offers such a feature. However, there are a number of reasons that HLint itself doesn't have an option to automatically apply suggestions:--* The underlying Haskell parser library makes it hard to modify the code, then print it similarly to the original.-* Sometimes multiple transformations may apply.-* After applying one transformation, others that were otherwise suggested may become inappropriate.--I am intending to develop such a feature, but the above reasons mean it is likely to take some time.  ### Why doesn't the compiler automatically apply the optimisations? 
data/Default.hs view
@@ -89,6 +89,7 @@ -- LIST  error = concat (map f x) ==> concatMap f x+error = concat (fmap f x) ==> concatMap f x warn = concat [a, b] ==> a ++ b warn "Use map once" = map f (map g x) ==> map (f . g) x warn "Fuse concatMap/map" = concatMap f (map g x) ==> concatMap (f . g) x@@ -283,15 +284,20 @@ -- FUNCTOR  error "Functor law" = fmap f (fmap g x) ==> fmap (f . g) x where _ = noQuickCheck+error "Functor law" = f <$> g <$> x ==> f . g <$> x where _ = noQuickCheck error "Functor law" = fmap id ==> id where _ = noQuickCheck+error "Functor law" = id <$> x ==> x where _ = noQuickCheck warn = fmap f $ x ==> f Control.Applicative.<$> x     where _ = (isApp x || isAtom x) && noQuickCheck  -- MONAD  error "Monad law, left identity" = return a >>= f ==> f a where _ = noQuickCheck+error "Monad law, left identity" = f =<< return a ==> f a where _ = noQuickCheck error "Monad law, right identity" = m >>= return ==> m where _ = noQuickCheck+error "Monad law, right identity" = return =<< m ==> m where _ = noQuickCheck warn  = m >>= return . f ==> Control.Monad.liftM f m where _ = noQuickCheck -- cannot be fmap, because is in Functor not Monad+warn  = return . f =<< m ==> Control.Monad.liftM f m where _ = noQuickCheck error = (if x then y else return ()) ==> Control.Monad.when x $ _noParen_ y where _ = not (isAtom y) && noQuickCheck error = (if x then y else return ()) ==> Control.Monad.when x y where _ = isAtom y && noQuickCheck error = (if x then return () else y) ==> Control.Monad.unless x $ _noParen_ y where _ = not (isAtom y) && noQuickCheck@@ -304,11 +310,12 @@ warn  = flip forM_ ==> mapM_ where _ = noQuickCheck error = when (not x) ==> unless x where _ = noQuickCheck error = x >>= id ==> Control.Monad.join x where _ = noQuickCheck+error = id =<< x ==> Control.Monad.join x where _ = noQuickCheck error = liftM f (liftM g x) ==> liftM (f . g) x where _ = noQuickCheck-error = fmap f (fmap g x) ==> fmap (f . g) x where _ = noQuickCheck warn  = a >> return () ==> Control.Monad.void a     where _ = (isAtom a || isApp a) && noQuickCheck error = fmap (const ()) ==> Control.Monad.void where _ = noQuickCheck+error = const () <$> x ==> Control.Monad.void x where _ = noQuickCheck error = flip (>=>) ==> (<=<) where _ = noQuickCheck error = flip (<=<) ==> (>=>) where _ = noQuickCheck error = flip (>>=) ==> (=<<) where _ = noQuickCheck@@ -469,6 +476,7 @@ error "Evaluate" = not True ==> False error "Evaluate" = not False ==> True error "Evaluate" = Nothing >>= k ==> Nothing+error "Evaluate" = k =<< Nothing ==> Nothing error "Evaluate" = either f g (Left x) ==> f x error "Evaluate" = either f g (Right y) ==> g y error "Evaluate" = fst (x,y) ==> x@@ -530,6 +538,10 @@ yes = if f a then True else b -- f a || b yes = not (a == b) -- a /= b yes = not (a /= b) -- a == b+yes = not . (a ==) -- (a /=)+yes = not . (== a) -- (/= a)+yes = not . (a /=) -- (a ==)+yes = not . (/= a) -- (== a) yes = if a then 1 else if b then 1 else 2 -- if a || b then 1 else 2 no  = if a then 1 else if b then 3 else 2 yes = a >>= return . bob -- Control.Monad.liftM bob a@@ -637,6 +649,7 @@ foo = maybe Bar{..} id -- Data.Maybe.fromMaybe Bar{..} foo = (\a -> Foo {..}) 1 foo = zipWith SymInfo [0 ..] (repeat ty) -- map (\ x -> SymInfo x ty) [0 ..]+f rec = rec  import Prelude \ yes = flip mapM -- Control.Monad.forM
hlint.cabal view
@@ -1,7 +1,7 @@ cabal-version:      >= 1.6 build-type:         Simple name:               hlint-version:            1.9.21+version:            1.9.22 license:            BSD3 license-file:       LICENSE category:           Development@@ -26,7 +26,7 @@     hlint.ghci     HLint_QuickCheck.hs     HLint_TypeCheck.hs-extra-source-files:+extra-doc-files:     README.md     CHANGES.txt tested-with:        GHC==7.10.1, GHC==7.8.4, GHC==7.6.3, GHC==7.4.2@@ -52,7 +52,8 @@         haskell-src-exts >= 1.16 && < 1.17,         uniplate >= 1.5,         ansi-terminal >= 0.6.2,-        extra >= 0.5+        extra >= 0.5,+        refact >= 0.3      if flag(gpl)         build-depends: hscolour >= 1.21@@ -76,6 +77,7 @@         Report         Util         Parallel+        Refact         HSE.All         HSE.Bracket         HSE.Evaluate@@ -113,8 +115,8 @@     build-depends:      base     hs-source-dirs:     src     main-is:            Main.hs- -    ghc-options:        -fno-warn-overlapping-patterns++    ghc-options:        -fno-warn-overlapping-patterns -rtsopts     if flag(threaded)         ghc-options:    -threaded 
src/Apply.hs view
@@ -62,7 +62,7 @@     case res of         Right m -> return $ Right m         Left (ParseError sl msg ctxt) -> do-            i <- return $ rawIdea Warning "Parse error" (mkSrcSpan sl sl) ctxt Nothing []+            i <- return $ rawIdeaN Warning "Parse error" (mkSrcSpan sl sl) ctxt Nothing []             i <- return $ classify [x | SettingClassify x <- s] i             return $ Left i{ideaHint = if "Parse error" `isPrefixOf` msg then msg else "Parse error: " ++ msg} 
src/CmdLine.hs view
@@ -1,5 +1,5 @@ {-# LANGUAGE PatternGuards, RecordWildCards, DeriveDataTypeable #-}-{-# OPTIONS_GHC -fno-warn-missing-fields -fno-cse #-}+{-# OPTIONS_GHC -fno-warn-missing-fields -fno-cse -O0 #-}  module CmdLine(Cmd(..), cmdCpp, CppFlags(..), getCmd, cmdExtensions, cmdHintFiles, cmdUseColour, exitWithHelp, resolveFile) where @@ -91,6 +91,11 @@         ,cmdCppAnsi :: Bool         ,cmdJson :: Bool                -- ^ display hint data as JSON         ,cmdNoSummary :: Bool           -- ^ do not show the summary info+        ,cmdNoExitCode :: Bool+        ,cmdSerialise :: Bool           -- ^ Display hints in serialisation format+        ,cmdRefactor :: Bool            -- ^ Run the `refactor` executable to automatically perform hints+        ,cmdRefactorOptions :: String   -- ^ Options to pass to the `refactor` executable.+        ,cmdWithRefactor :: FilePath    -- ^ Path to refactor tool         }     | CmdGrep         {cmdFiles :: [FilePath]    -- ^ which files to run it on, nothing = none given@@ -146,6 +151,11 @@         ,cmdCppAnsi = nam_ "cpp-ansi" &= help "Use CPP in ANSI compatibility mode"         ,cmdJson = nam_ "json" &= help "Display hint data as JSON"         ,cmdNoSummary = nam_ "no-summary" &= help "Do not show summary information"+        ,cmdNoExitCode = nam_ "no-exit-code" &= help "Do not give a negative exit if hints"+        ,cmdSerialise = nam_ "serialise" &= help "Serialise hint data for consumption by apply-refact"+        ,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"         } &= auto &= explicit &= name "lint"     ,CmdGrep         {cmdFiles = def &= args &= typ "FILE/DIR"@@ -200,14 +210,16 @@ x <\> y = x </> y  -resolveFile :: Cmd -> FilePath -> IO [FilePath]+resolveFile :: Cmd -> Maybe FilePath -> FilePath -> IO [FilePath] resolveFile cmd = getFile (cmdPath cmd) (cmdExtension cmd)  -getFile :: [FilePath] -> [String] -> FilePath -> IO [FilePath]-getFile path _ "-" = return ["-"]-getFile [] exts file = error $ "Couldn't find file: " ++ file-getFile (p:ath) exts file = do+getFile :: [FilePath] -> [String] -> Maybe FilePath -> FilePath -> IO [FilePath]+getFile path _ (Just tmpfile) "-" =+  getContents >>= writeFile tmpfile >> return [tmpfile]+getFile path _ Nothing "-" = return ["-"]+getFile [] exts _ file = error $ "Couldn't find file: " ++ file+getFile (p:ath) exts t file = do     isDir <- doesDirectoryExist $ p <\> file     if isDir then do         let avoidDir x = let y = takeFileName x in "_" `isPrefixOf` y || ("." `isPrefixOf` y && not (all (== '.') y))@@ -221,7 +233,7 @@             res <- getModule p exts file             case res of                 Just x -> return [x]-                Nothing -> getFile ath exts file+                Nothing -> getFile ath exts t file   getModule :: FilePath -> [String] -> FilePath -> IO (Maybe FilePath)
src/Grep.hs view
@@ -23,7 +23,7 @@         res <- parseModuleEx flags file Nothing         case res of             Left (ParseError sl msg ctxt) ->-                print $ rawIdea Warning (if "Parse error" `isPrefixOf` msg then msg else "Parse error: " ++ msg) (mkSrcSpan sl sl) ctxt Nothing []+                print $ rawIdeaN Warning (if "Parse error" `isPrefixOf` msg then msg else "Parse error: " ++ msg) (mkSrcSpan sl sl) ctxt Nothing []             Right m ->                 forM_ (applyHints [] rule [m]) $ \i ->                     print i{ideaHint="", ideaTo=Nothing}
src/HLint.hs view
@@ -1,4 +1,4 @@-{-# LANGUAGE RecordWildCards #-}+{-# LANGUAGE RecordWildCards, ViewPatterns #-} {-# OPTIONS_GHC -fno-warn-incomplete-patterns #-}  module HLint(hlint, Suggestion, suggestionLocation, suggestionSeverity, Severity(..)) where@@ -9,8 +9,15 @@ import Data.List import System.Exit import System.IO+import System.IO.Extra import Prelude +import Data.Version+import System.Process.Extra+import Data.Maybe+import System.Directory+import Text.ParserCombinators.ReadP+ import CmdLine import Settings import Report@@ -23,7 +30,6 @@ import Parallel import HSE.All - -- | A suggestion - the @Show@ instance is of particular use. newtype Suggestion = Suggestion {fromSuggestion :: Idea}                      deriving (Eq,Ord)@@ -54,7 +60,7 @@ hlint args = do     cmd <- getCmd args     case cmd of-        CmdMain{} -> hlintMain cmd+        CmdMain{} -> do xs <- hlintMain cmd; return $ if cmdNoExitCode cmd then [] else xs         CmdGrep{} -> hlintGrep cmd >> return []         CmdHSE{}  -> hlintHSE  cmd >> return []         CmdTest{} -> hlintTest cmd >> return []@@ -91,7 +97,7 @@     if null cmdFiles then         exitWithHelp      else do-        files <- concatMapM (resolveFile cmd) cmdFiles+        files <- concatMapM (resolveFile cmd Nothing) cmdFiles         if null files then             error "No files found"          else@@ -102,17 +108,21 @@     encoding <- if cmdUtf8 then return utf8 else readEncoding cmdEncoding     let flags = parseFlagsSetExtensions (cmdExtensions cmd) $ defaultParseFlags{cppFlags=cmdCpp cmd, encoding=encoding}     if null cmdFiles && not (null cmdFindHints) then do-        hints <- concatMapM (resolveFile cmd) cmdFindHints+        hints <- concatMapM (resolveFile cmd Nothing) cmdFindHints         mapM_ (\x -> putStrLn . fst =<< findSettings2 flags x) hints >> return []      else if null cmdFiles then         exitWithHelp-     else do-        files <- concatMapM (resolveFile cmd) cmdFiles-        if null files then-            error "No files found"-         else-            runHints cmd{cmdFiles=files} flags+     else if cmdRefactor then do+         withTempFile (\t ->  runHlintMain cmd (Just t) flags)+     else runHlintMain cmd Nothing flags +runHlintMain :: Cmd -> Maybe FilePath -> ParseFlags -> IO [Suggestion]+runHlintMain cmd@(CmdMain{..}) fp flags = do+  files <- concatMapM (resolveFile cmd fp) cmdFiles+  if null files+    then error "No files found"+    else runHints cmd{cmdFiles=files} flags+ readAllSettings :: Cmd -> ParseFlags -> IO [Setting] readAllSettings cmd@CmdMain{..} flags = do     files <- cmdHintFiles cmd@@ -131,11 +141,28 @@         then applyHintFiles flags settings cmdFiles         else concat <$> parallel [evaluateList =<< applyHintFile flags settings x Nothing | x <- cmdFiles]     let (showideas,hideideas) = partition (\i -> cmdShowAll || ideaSeverity i /= Ignore) ideas+    usecolour <- cmdUseColour cmd+    showItem <- if usecolour then showANSI else return show     if cmdJson         then putStrLn . showIdeasJson $ showideas+        else if cmdSerialise then do+          hSetBuffering stdout NoBuffering+          print $ map (\i -> (show i, ideaRefactoring i)) showideas+        else if cmdRefactor then do+          case cmdFiles of+            [file] -> do+              -- Ensure that we can find the executable+              path <- checkRefactor (if cmdWithRefactor == "" then Nothing else Just cmdWithRefactor)+              -- writeFile "hlint.refact"+              let hints =  show $ map (\i -> (show i, ideaRefactoring i)) showideas+              withTempFile $ \f -> do+                writeFile f hints+                runRefactoring path file f cmdRefactorOptions+                -- Exit with the exit code from 'refactor'+                >>= exitWith+            _ -> error "Refactor flag can only be used with an individual file"+         else do-            usecolour <- cmdUseColour cmd-            showItem <- if usecolour then showANSI else return show             mapM_ (outStrLn . showItem) showideas             if null showideas then                 when (cmdReports /= []) $ outStrLn "Skipping writing reports"@@ -148,6 +175,30 @@                     (let i = length showideas in if i == 0 then "No suggestions" else show i ++ " suggestion" ++ ['s' | i/=1]) ++                     (let i = length hideideas in if i == 0 then "" else " (" ++ show i ++ " ignored)")     return $ map Suggestion showideas++runRefactoring :: FilePath -> FilePath -> FilePath -> String -> IO ExitCode+runRefactoring rpath fin hints opts =  do+  let args = [fin, "-v0"] ++ words opts ++ ["--refact-file", hints]+  (_, _, _, phand) <- createProcess $ proc rpath args+  hSetBuffering stdin LineBuffering+  hSetBuffering stdout LineBuffering+  -- Propagate the exit code from the spawn process+  waitForProcess phand++checkRefactor :: Maybe FilePath -> IO FilePath+checkRefactor rpath = do+  let excPath = (fromMaybe "refactor" rpath)+  mexc <- findExecutable excPath+  case mexc of+    Just exc ->  do+      vers <- readP_to_S parseVersion . tail <$> (readProcess exc ["--version"] "")+      case vers of+        [] -> putStrLn "Unabled to determine version of refactor" >> (return exc)+        (last -> (version, _)) -> if versionBranch version >= [0,1,0,0]+                                    then return exc+                                    else error "Your version of refactor is too old, please upgrade to the latest version"+    Nothing ->  error $ unlines ["Could not find refactor"+                                , "Tried with: " ++ excPath ]  evaluateList :: [a] -> IO [a] evaluateList xs = length xs `seq` return xs
src/HSE/All.hs view
@@ -117,7 +117,7 @@ cheapFixities fixs = descendBi (transform f)     where         ask = askFixity fixs-    +         f o@(InfixApp s1 (InfixApp s2 x op1 y) op2 z)                 | p1 == p2 && (a1 /= a2 || a1 == AssocNone) = o -- Ambiguous infix expression!                 | p1 > p2 || p1 == p2 && (a1 == AssocLeft || a2 == AssocNone) = o
src/HSE/Bracket.hs view
@@ -41,7 +41,7 @@         _ -> isLexeme x      -- note: i is the index in children, not in the AST-    needBracket i parent child +    needBracket i parent child         | isAtom child = False         | InfixApp{} <- parent, App{} <- child = False         | isSection parent, App{} <- child = False
src/HSE/Match.hs view
@@ -69,7 +69,7 @@     fromNamed (Con _ x) = fromNamed x     fromNamed (List _ []) = "[]"     fromNamed _ = ""-    +     toNamed "[]" = List an []     toNamed x | isCtor x = Con an $ toNamed x               | otherwise = Var an $ toNamed x
src/HSE/Scope.hs view
@@ -95,13 +95,13 @@     where         f (ImportSpecList _ hide xs) = if hide then Just True `notElem` ms else Nothing `elem` ms || Just True `elem` ms             where ms = map g xs-        +         g :: ImportSpec S -> Maybe Bool -- does this import cover the name x         g (IVar _ _ y) = Just $ x =~= y         g (IAbs _ y) = Just $ x =~= y         g (IThingAll _ y) = if x =~= y then Just True else Nothing         g (IThingWith _ y ys) = Just $ x `elem_` (y : map fromCName ys)-        +         fromCName :: CName S -> Name S         fromCName (VarName _ x) = x         fromCName (ConName _ x) = x
src/HSE/Util.hs view
@@ -202,10 +202,12 @@ apps :: [Exp_] -> Exp_ apps = foldl1 (App an) - fromApps :: Exp_ -> [Exp_]-fromApps (App _ x y) = fromApps x ++ [y]-fromApps x = [x]+fromApps = map fst . fromAppsWithLoc++fromAppsWithLoc :: Exp_ -> [(Exp_, S)]+fromAppsWithLoc (App l x y) = fromAppsWithLoc x ++ [(y, l)]+fromAppsWithLoc x = [(x, ann x)]   -- Rule for the Uniplate Apps functions
src/Hint/Bracket.hs view
@@ -1,4 +1,5 @@ {-# LANGUAGE ViewPatterns #-}+{-# LANGUAGE RecordWildCards #-} {- Raise an error if you are bracketing an atom, or are enclosed be a list bracket @@ -61,6 +62,8 @@ module Hint.Bracket(bracketHint) where  import Hint.Type+import Data.Data+import Refact.Types   bracketHint :: DeclHint@@ -76,39 +79,62 @@             Paren _ x -> x             x -> x +-- Dirty, should add to Brackets type class I think+tyConToRtype :: String -> RType+tyConToRtype "Exp" = Expr+tyConToRtype "Type" = Type+tyConToRtype "Pat"  = Pattern+tyConToRtype _      = Expr -bracket :: (Annotated a, Uniplate (a S), ExactP a, Pretty (a S), Brackets (a S)) => Bool -> a S -> [Idea]+findType :: (Data a) => a -> RType+findType = tyConToRtype . dataTypeName . dataTypeOf++++bracket :: (Data (a S), Annotated a, Uniplate (a S), ExactP a, Pretty (a S), Brackets (a S)) => Bool -> a S -> [Idea] bracket bad = f Nothing     where         msg = "Redundant bracket"          -- f (Maybe (index, parent, gen)) child-        f :: (Annotated a, Uniplate (a S), ExactP a, Pretty (a S), Brackets (a S)) => Maybe (Int,a S,a S -> a S) -> a S -> [Idea]-        f Just{} o@(remParen -> Just x) | isAtom x = err msg o x : g x-        f Nothing o@(remParen -> Just x) | bad = warn msg o x : g x-        f (Just (i,o,gen)) (remParen -> Just x) | not $ needBracket i o x = warn msg o (gen x) : g x+        f :: (Data (a S), Annotated a, Uniplate (a S), ExactP a, Pretty (a S), Brackets (a S)) => Maybe (Int,a S,a S -> a S) -> a S -> [Idea]+        f Just{} o@(remParen -> Just x) | isAtom x = bracketError msg o x : g x+        f Nothing o@(remParen -> Just x) | bad = bracketWarning msg o x : g x+        f (Just (i,o,gen)) v@(remParen -> Just x) | not $ needBracket i o x =+          warn msg o (gen x) [r] : g x+          where+            typ = findType v+            r = Replace typ (toSS v) [("x", toSS x)] "x"         f _ x = g x -        g :: (Annotated a, Uniplate (a S), ExactP a, Pretty (a S), Brackets (a S)) => a S -> [Idea]+        g :: (Data (a S), Annotated a, Uniplate (a S), ExactP a, Pretty (a S), Brackets (a S)) => a S -> [Idea]         g o = concat [f (Just (i,o,gen)) x | (i,(x,gen)) <- zip [0..] $ holes o] +bracketWarning msg o x =+  idea Warning msg o x [Replace (findType x) (toSS o) [("x", toSS x)] "x"]+bracketError msg o x =+  idea Error msg o x [Replace (findType x) (toSS o) [("x", toSS x)] "x"] + fieldDecl :: FieldDecl S -> [Idea]-fieldDecl o@(FieldDecl a b (TyParen _ c))-    = [warn "Redundant bracket" o (FieldDecl a b c)]+fieldDecl o@(FieldDecl a b v@(TyParen _ c))+    = [warn "Redundant bracket" o (FieldDecl a b c)  [Replace Type (toSS v) [("x", toSS c)] "x"]] fieldDecl _ = []   dollar :: Exp_ -> [Idea] dollar = concatMap f . universe     where-        msg = warn "Redundant $"-        f x = [msg x y | InfixApp _ a d b <- [x], opExp d ~= "$"-              ,let y = App an a b, not $ needBracket 0 y a, not $ needBracket 1 y b]+        f x = [warn "Redundant $" x y [r] | InfixApp _ a d b <- [x], opExp d ~= "$"+              ,let y = App an a b, not $ needBracket 0 y a, not $ needBracket 1 y b+              ,let r = Replace Expr (toSS x) [("a", toSS a), ("b", toSS b)] "a b"+                ]+               ++-              [msg x (t y) |(t, Paren _ (InfixApp _ a1 op1 a2)) <- splitInfix x+              [warn "Move brackets to avoid $" x (t y) [r] |(t, e@(Paren _ (InfixApp _ a1 op1 a2))) <- splitInfix x               ,opExp op1 ~= "$", isVar a1 || isApp a1 || isParen a1, not $ isAtom a2-              ,let y = App an a1 (Paren an a2)]+              , let y = App an a1 (Paren an a2)+              , let r = Replace Expr (toSS e) [("a", toSS a1), ("b", toSS a2)] "a (b)" ]   -- return both sides, and a way to put them together again
src/Hint/Comment.hs view
@@ -17,6 +17,7 @@ import Hint.Type import Data.Char import Data.List.Extra+import Refact.Types(Refactoring(ModifyComment))   pragmas = words $@@ -32,5 +33,6 @@ commentHint _ = []  suggest :: String -> Comment -> String -> Idea-suggest msg (Comment typ pos s1) s2 = rawIdea Warning msg pos (f s1) (Just $ f s2) []+suggest msg (Comment typ pos s1) s2 = rawIdea Warning msg pos (f s1) (Just $ f s2) [] refact     where f s = if typ then "{-" ++ s ++ "-}" else "--" ++ s+          refact = [ModifyComment (toRefactSrcSpan pos) (f s2)]
src/Hint/Duplicate.hs view
@@ -32,7 +32,7 @@   dupes ys =-    [rawIdea+    [rawIdeaN         (if length xs >= 5 then Error else Warning)         "Reduce duplication" p1         (unlines $ map (prettyPrint . fmap (const p1)) xs)
src/Hint/Extensions.hs view
@@ -72,16 +72,19 @@ import Hint.Type import Data.Maybe import Data.List.Extra+import Refact.Types   extensionsHint :: ModuHint extensionsHint _ x = [rawIdea Error "Unused LANGUAGE pragma" (toSrcSpan sl)-          (prettyPrint o) (Just $ if null new then "" else prettyPrint $ LanguagePragma sl $ map (toNamed . prettyExtension) new)-          (warnings old new)+          (prettyPrint o) (Just newPragma)+          (warnings old new) [refact]     | not $ used TemplateHaskell x -- if TH is on, can use all other extensions programmatically     , o@(LanguagePragma sl exts) <- modulePragmas x     , let old = map (parseExtension . prettyPrint) exts     , let new = minimalExtensions x old+    , let newPragma = if null new then "" else prettyPrint $ LanguagePragma sl $ map (toNamed . prettyExtension) new+    , let refact = ModifyComment (toSS o) newPragma     , sort new /= sort old]  
src/Hint/Import.hs view
@@ -3,7 +3,7 @@     Reduce the number of import declarations.     Two import declarations can be combined if:       (note, A[] is A with whatever import list, or none)-    +     import A[]; import A[] = import A[]     import A(B); import A(C) = import A(B,C)     import A; import A(C) = import A@@ -14,6 +14,7 @@ import A; import A -- import A import A; import A; import A -- import A import A(Foo) ; import A -- import A+import A ;import A(Foo) -- import A import A(Bar(..)); import {-# SOURCE #-} A import A; import B import A(B) ; import A(C) -- import A(B,C)@@ -43,7 +44,10 @@ module Hint.Import(importHint) where  import Control.Applicative+import Control.Arrow import Hint.Type+import Refact.Types hiding (ModuleName)+import qualified Refact.Types as R import Data.List.Extra import Data.Maybe import Prelude@@ -57,31 +61,38 @@   wrap :: [ImportDecl S] -> [Idea]-wrap o = [ rawIdea Error "Use fewer imports" (toSrcSpan $ ann $ head o) (f o) (Just $ f x) []-         | Just x <- [simplify o]]+wrap o = [ rawIdea Error "Use fewer imports" (toSrcSpan $ ann $ head o) (f o) (Just $ f x) [] rs+         | Just (x, rs) <- [simplify o]]     where f = unlines . map prettyPrint  -simplify :: [ImportDecl S] -> Maybe [ImportDecl S]+simplify :: [ImportDecl S] -> Maybe ([ImportDecl S], [Refactoring R.SrcSpan]) simplify [] = Nothing simplify (x:xs) = case simplifyHead x xs of-    Nothing -> (x:) <$> simplify xs-    Just xs -> Just $ fromMaybe xs $ simplify xs+    Nothing -> first (x:) <$> simplify xs+    Just (xs, rs) -> Just $ fromMaybe (xs, rs) $ (second (++ rs) <$> simplify xs)  -simplifyHead :: ImportDecl S -> [ImportDecl S] -> Maybe [ImportDecl S]+simplifyHead :: ImportDecl S -> [ImportDecl S] -> Maybe ([ImportDecl S], [Refactoring R.SrcSpan]) simplifyHead x [] = Nothing simplifyHead x (y:ys) = case reduce x y of-    Nothing -> (y:) <$> simplifyHead x ys-    Just xy -> Just $ xy : ys+    Nothing -> first (y:) <$> simplifyHead x ys+    Just (xy, rs) -> Just $ (xy : ys, rs)  -reduce :: ImportDecl S -> ImportDecl S -> Maybe (ImportDecl S)-reduce x y | qual, as, specs = Just x-           | qual, as, Just (ImportSpecList _ False xs) <- importSpecs x, Just (ImportSpecList _ False ys) <- importSpecs y =-                Just x{importSpecs = Just $ ImportSpecList an False $ nub_ $ xs ++ ys}-           | qual, as, isNothing (importSpecs x) || isNothing (importSpecs y) = Just x{importSpecs=Nothing}-           | not (importQualified x), qual, specs, length ass == 1 = Just x{importAs=Just $ head ass}+reduce :: ImportDecl S -> ImportDecl S -> Maybe ((ImportDecl S), [Refactoring R.SrcSpan])+reduce 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)])+     where         qual = importQualified x == importQualified y         as = importAs x `eqMaybe` importAs y@@ -94,7 +105,7 @@ reduce1 :: ImportDecl S -> [Idea] reduce1 i@ImportDecl{..}     | Just (dropAnn importModule) == fmap dropAnn importAs-    = [warn "Redundant as" i i{importAs=Nothing}]+    = [warn "Redundant as" i i{importAs=Nothing} [RemoveAsKeyword (toSS i)]] reduce1 _ = []  @@ -116,13 +127,16 @@   hierarchy :: ImportDecl S -> [Idea]-hierarchy i@ImportDecl{importModule=ModuleName _ x,importPkg=Nothing} | Just y <- lookup x newNames-    = [warn "Use hierarchical imports" i (desugarQual i){importModule=ModuleName an $ y ++ "." ++ x}]+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+    [warn "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}-    = [rawIdea Warning "Use hierarchical imports" (toSrcSpan $ ann i) (trimStart $ prettyPrint i) (+    = [rawIdeaN Warning "Use hierarchical imports" (toSrcSpan $ 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 (NoNamespace an) $ toNamed x | x <- ["bracket","bracket_"]]]) []]@@ -139,7 +153,7 @@  multiExport :: Module S -> [Idea] multiExport x =-    [ rawIdea Warning "Use import/export shortcut" (toSrcSpan $ ann hd)+    [ rawIdeaN Warning "Use import/export shortcut" (toSrcSpan $ ann hd)         (unlines $ prettyPrint hd : map prettyPrint imps)         (Just $ unlines $ prettyPrint newhd : map prettyPrint newimps)         []
src/Hint/Lambda.hs view
@@ -13,8 +13,8 @@     (flip op x) ==> (`op` x)  -- rotate operators     \x y -> x + y ==> (+)  -- insert operator     \x y -> op y x ==> flip op-    \x -> x + y ==> (+ y)  -- insert section, -    \x -> op x y ==> (`op` y)  -- insert section +    \x -> x + y ==> (+ y)  -- insert section,+    \x -> op x y ==> (`op` y)  -- insert section     \x -> y + x ==> (y +)  -- insert section     \x -> \y -> ... ==> \x y -- lambda compression     \x -> (x +) ==> (+) -- operator reduction@@ -23,6 +23,7 @@ f a = \x -> x + x -- f a x = x + x f a = \a -> a + a -- f _ a = a + a f a = \x -> x + x where _ = test+f (test -> a) = \x -> x + x f = \x -> x + x -- f x = x + x fun x y z = f x y z -- fun = f fun x y z = f x x y z -- fun x = f x x@@ -40,6 +41,7 @@ f = foo (\x -> x # y) f = foo (\x -> \y -> x x y y) -- \x y -> x x y y f = foo (\x -> \x -> foo x x) -- \_ x -> foo x x+f = foo (\(foo -> x) -> \y -> x x y y) f = foo (\(x:xs) -> \x -> foo x x) -- \(_:xs) x -> foo x x f = foo (\x -> \y -> \z -> x x y y z z) -- \x y z -> x x y y z z x ! y = fromJust $ lookup x y@@ -73,6 +75,7 @@ import Util import Data.List.Extra import Data.Maybe+import Refact.Types hiding (RType(Match))   lambdaHint :: DeclHint@@ -81,10 +84,27 @@  lambdaDecl :: Decl_ -> [Idea] lambdaDecl (toFunBind -> o@(FunBind loc [Match _ name pats (UnGuardedRhs _ bod) bind]))-    | isNothing bind, isLambda $ fromParen bod = [err "Redundant lambda" o $ uncurry reform $ fromLambda $ Lambda an pats bod]-    | (pats2,bod2) <- etaReduce pats bod, length pats2 < length pats, pvars (drop (length pats2) pats) `disjoint` varss bind-        = [err "Eta reduce" (reform pats bod) (reform pats2 bod2)]+    | isNothing bind, isLambda $ fromParen bod, null (universeBi pats :: [Exp_]) =+      [err "Redundant lambda" o (gen pats bod) [Replace Decl (toSS o) s1 t1]]+    | length pats2 < length pats, pvars (drop (length pats2) pats) `disjoint` varss bind+        = [err "Eta reduce" (reform pats bod) (reform pats2 bod2)+            [ -- Disabled, see apply-refact #3+              -- Replace Decl (toSS $ reform pats bod) s2 t2]]+            ]]         where reform p b = FunBind loc [Match an name p (UnGuardedRhs an b) Nothing]+              gen ps b = uncurry reform . fromLambda . Lambda an ps $ b+              (finalpats, body) = fromLambda . Lambda an pats $ bod+              (pats2, bod2) = etaReduce pats bod+              template fps b = prettyPrint $ reform (zipWith munge ['a'..'z'] fps) (Var (ann b) (UnQual (ann b) (Ident (ann b) "body")))+              munge :: Char -> Pat_ -> Pat_+              munge ident p@(PWildCard _) = p+              munge ident p = PVar (ann p) (Ident (ann p) [ident])+              subts fps b = ("body", toSS b) : zipWith (\x y -> ([x],y)) ['a'..'z'] (map toSS fps)+              s1 = subts finalpats body+              --s2 = subts pats2 bod2+              t1 = template finalpats body+              --t2 = template pats2 bod2+ lambdaDecl _ = []  @@ -95,15 +115,27 @@ etaReduce ps x = (ps,x)  +--Section refactoring is not currently implemented. lambdaExp :: Maybe Exp_ -> Exp_ -> [Idea]-lambdaExp p o@(Paren _ (App _ (Var _ (UnQual _ (Symbol _ x))) y)) | isAtom y, allowLeftSection x =-    [warn "Use section" o $ LeftSection an y (toNamed x)]+lambdaExp p o@(Paren _ (App _ v@(Var l (UnQual _ (Symbol _ x))) y)) | isAtom y, allowLeftSection x =+    [warnN "Use section" o (exp y x)] -- [Replace Expr (toSS o) subts template]]+    where+      exp op rhs = LeftSection an op (toNamed rhs)+--      template = prettyPrint (exp (toNamed "a") "*")+--      subts = [("a", toSS y), ("*", toSS v)] lambdaExp p o@(Paren _ (App _ (App _ (view -> Var_ "flip") (Var _ x)) y)) | allowRightSection $ fromNamed x =-    [warn "Use section" o $ RightSection an (QVarOp an x) y]-lambdaExp p o@Lambda{} | maybe True (not . isInfixApp) p, res <- niceLambda [] o, not $ isLambda res =-    [(if isVar res || isCon res then err else warn) "Avoid lambda" o res]-lambdaExp p o@(Lambda _ _ x) | isLambda (fromParen x) && maybe True (not . isLambda) p =-    [warn "Collapse lambdas" o $ uncurry (Lambda an) $ fromLambda o]+    [warnN "Use section" o $ RightSection an (QVarOp an x) y]+lambdaExp p o@Lambda{} | maybe True (not . isInfixApp) p, (res, refact) <- niceLambdaR [] o, not $ isLambda res =+    [(if isVar res || isCon res then err else warn) "Avoid lambda" o res (refact $ toSS o)]+lambdaExp p o@(Lambda _ pats x) | isLambda (fromParen x), null (universeBi pats :: [Exp_]), maybe True (not . isLambda) p =+    [warn "Collapse lambdas" o (Lambda an pats body) [Replace Expr (toSS o) subts template]]+    where+      (pats, body) = fromLambda o+      template = prettyPrint $  Lambda an (zipWith munge ['a'..'z'] pats) (toNamed "body")+      munge :: Char -> Pat_ -> Pat_+      munge ident p@(PWildCard _) = p+      munge ident p = PVar (ann p) (Ident (ann p) [ident])+      subts = ("body", toSS body) : zipWith (\x y -> ([x],y)) ['a'..'z'] (map toSS pats) lambdaExp _ _ = []  
src/Hint/List.hs view
@@ -1,13 +1,13 @@-{-# LANGUAGE ViewPatterns, PatternGuards, FlexibleContexts #-}+{-# LANGUAGE ViewPatterns, PatternGuards, FlexibleContexts, TupleSections #-}  {-     Find and match:  <TEST> yes = 1:2:[] -- [1,2]-yes = ['h','e','l','l','o'] -- "hello"+yes = ['h','e','l','l','o'] yes (1:2:[]) = 1 -- [1,2]-yes ['h','e'] = 1 -- "he"+yes ['h','e'] = 1  -- [a]++b -> a : b, but only if not in a chain of ++'s yes = [x] ++ xs -- x : xs@@ -30,6 +30,7 @@ import Control.Applicative import Hint.Type import Prelude+import Refact.Types   listHint :: DeclHint@@ -43,51 +44,77 @@ listExp b (fromParen -> x) =         if null res then concatMap (listExp $ isAppend x) $ children x else [head res]     where-        res = [warn name x x2 | (name,f) <- checks, Just x2 <- [f b x]]+        res = [warn name x x2 [r] | (name,f) <- checks+                                  , Just (x2, subts, temp) <- [f b x]+                                  , let r = Replace Expr (toSS x) subts temp ]  listPat :: Pat_ -> [Idea] listPat x = if null res then concatMap listPat $ children x else [head res]-    where res = [warn name x x2 | (name,f) <- pchecks, Just x2 <- [f x]]+    where res = [warn name x x2 [r]+                  | (name,f) <- pchecks+                  , Just (x2, subts, temp) <- [f x]+                  , let r = Replace Pattern (toSS x) subts temp ]  isAppend (view -> App2 op _ _) = op ~= "++" isAppend _ = False  -checks = let (*) = (,) in+checks = let (*) = (,) in drop 1 -- see #174          ["Use string literal" * useString          ,"Use list literal" * useList          ,"Use :" * useCons          ] -pchecks = let (*) = (,) in+pchecks = let (*) = (,) in drop 1 -- see #174           ["Use string literal pattern" * usePString           ,"Use list literal pattern" * usePList           ]  -usePString (PList _ xs) | xs /= [], Just s <- mapM fromPChar xs = Just $ PLit an (Signless an) $ String an s (show s)+usePString (PList _ xs) | xs /= [], Just s <- mapM fromPChar xs =+  let literal = PLit an (Signless an) $ String an s (show s)+  in Just (literal, [], prettyPrint literal) usePString _ = Nothing -usePList = fmap (PList an) . f True+usePList = fmap (\(e, s) -> (PList an e, map (fmap toSS) s, prettyPrint (PList an (map snd s))))+    . fmap unzip . f True ['a'..'z']     where-        f first x | x ~= "[]" = if first then Nothing else Just []-        f first (view -> PApp_ ":" [a,b]) = (a:) <$> f False b-        f first _ = Nothing+        f first _ x | x ~= "[]" = if first then Nothing else Just []+        f first (ident: cs) (view -> PApp_ ":" [a,b]) =+          ((a, g ident a) :) <$> f False cs b+        f first _ _ = Nothing -useString b (List _ xs) | xs /= [], Just s <- mapM fromChar xs = Just $ Lit an $ String an s (show s)+        g :: Char -> Pat_ -> (String, Pat_)+        g c p = ([c], PVar (ann p) (toNamed [c]))++useString b (List _ xs) | xs /= [], Just s <- mapM fromChar xs =+  let literal = Lit an $ String an s (show s)+  in Just (literal , [], prettyPrint literal) useString b _ = Nothing -useList b = fmap (List an) . f True+useList b = fmap (\(e, s) -> (List an e, map (fmap toSS) s, prettyPrint (List an (map snd s))))+              . fmap unzip . f True ['a'..'z']     where-        f first x | x ~= "[]" = if first then Nothing else Just []-        f first (view -> App2 c a b) | c ~= ":" = (a:) <$> f False b-        f first _ = Nothing+        f first _ x | x ~= "[]" = if first then Nothing else Just []+        f first (ident:cs) (view -> App2 c a b) | c ~= ":" =+          ((a, g ident a) :) <$> f False cs b+        f first _ _ = Nothing -useCons False (view -> App2 op x y) | op ~= "++", Just x2 <- f x, not $ isAppend y =-        Just $ InfixApp an x2 (QConOp an $ list_cons_name an) y+        g :: Char -> Exp_ -> (String, Exp_)+        g c p = ([c], Var (ann p) (toNamed [c]))++useCons False (view -> App2 op x y) | op ~= "++"+                                    , Just (x2, build) <- f x+                                    , not $ isAppend y =+  Just (gen (build x2) y+       , [("x", toSS x2), ("xs", toSS y)]+       , prettyPrint $ gen (build $ toNamed "x") (toNamed "xs"))     where-        f (List _ [x]) = Just $ if isApp x then x else paren x+        f (List _ [x]) = Just $ (x, \v -> if isApp x then v else paren v)         f _ = Nothing+++        gen x xs = InfixApp an x (QConOp an $ list_cons_name an) xs useCons _ _ = Nothing  @@ -104,5 +131,8 @@         f x = concatMap g $ childrenBi x          g :: Type_ -> [Idea]-        g (fromTyParen -> x) = [warn "Use String" x (transform f x) | any (=~= typeListChar) $ universe x]+        g e@(fromTyParen -> x) = [warn "Use String" x (transform f x)+                                    rs | not . null $ rs]             where f x = if x =~= typeListChar then typeString else x+                  toSS = toRefactSrcSpan . toSrcSpan . ann+                  rs = [Replace Type (toSS t) [] (prettyPrint typeString) | t <- universe x, t =~= typeListChar]
src/Hint/ListRec.hs view
@@ -35,6 +35,7 @@ import Data.Ord import Data.Either.Extra import Control.Monad+import Refact.Types hiding (RType(Match))   listRecHint :: DeclHint@@ -46,7 +47,8 @@             (use,severity,x) <- matchListRec x             let y = addCase x             guard $ recursiveStr `notElem` varss y-            return $ idea severity ("Use " ++ use) o y+            -- Maybe we can do better here maintaining source formatting?+            return $ (idea severity ("Use " ++ use) o y [Replace Decl (toSS o) [] (prettyPrint y)])   recursiveStr = "_recursive_"@@ -74,7 +76,7 @@  matchListRec :: ListCase -> Maybe (String,Severity,Exp_) matchListRec o@(ListCase vs nil (x,xs,cons))-    +     | [] <- vs, nil ~= "[]", InfixApp _ lhs c rhs <- cons, opExp c ~= ":"     , fromParen rhs =~= recursive, xs `notElem` vars lhs     = Just $ (,,) "map" Error $ appsBracket
src/Hint/Match.hs view
@@ -1,4 +1,4 @@-{-# LANGUAGE PatternGuards, ViewPatterns, RelaxedPolyRec, RecordWildCards, FlexibleContexts #-}+{-# LANGUAGE PatternGuards, ViewPatterns, RelaxedPolyRec, RecordWildCards, FlexibleContexts, ScopedTypeVariables, TupleSections #-}  {- The matching does a fairly simple unification between the two terms, treating@@ -51,6 +51,7 @@ import Util import qualified Data.Set as Set import Prelude+import qualified Refact.Types as R   fmapAn = fmap (const an)@@ -65,25 +66,26 @@  readRule :: HintRule -> [HintRule] readRule (m@HintRule{hintRuleLHS=(fmapAn -> hintRuleLHS), hintRuleRHS=(fmapAn -> hintRuleRHS), hintRuleSide=(fmap fmapAn -> hintRuleSide)}) =-    (:) m{hintRuleLHS=hintRuleLHS,hintRuleSide=hintRuleSide,hintRuleRHS=hintRuleRHS} $ fromMaybe [] $ do+    (:) m{hintRuleLHS=hintRuleLHS,hintRuleSide=hintRuleSide,hintRuleRHS=hintRuleRHS} $ do         (l,v1) <- dotVersion hintRuleLHS         (r,v2) <- dotVersion hintRuleRHS         guard $ v1 == v2 && l /= [] && (length l > 1 || length r > 1) && Set.notMember v1 (freeVars $ maybeToList hintRuleSide ++ l ++ r)-        if r /= [] then return+        if r /= [] then             [m{hintRuleLHS=dotApps l, hintRuleRHS=dotApps r, hintRuleSide=hintRuleSide}             ,m{hintRuleLHS=dotApps (l++[toNamed v1]), hintRuleRHS=dotApps (r++[toNamed v1]), hintRuleSide=hintRuleSide}]-         else if length l > 1 then return+         else if length l > 1 then             [m{hintRuleLHS=dotApps l, hintRuleRHS=toNamed "id", hintRuleSide=hintRuleSide}             ,m{hintRuleLHS=dotApps (l++[toNamed v1]), hintRuleRHS=toNamed v1, hintRuleSide=hintRuleSide}]-         else-            Nothing+         else []   -- find a dot version of this rule, return the sequence of app prefixes, and the var-dotVersion :: Exp_ -> Maybe ([Exp_], String)-dotVersion (view -> Var_ v) | isUnifyVar v = Just ([], v)-dotVersion (fromApps -> xs) | length xs > 1 = first (apps (init xs) :) <$> dotVersion (fromParen $ last xs)-dotVersion _ = Nothing+dotVersion :: Exp_ -> [([Exp_], String)]+dotVersion (view -> Var_ v) | isUnifyVar v = [([], v)]+dotVersion (App l ls rs) = first (ls :) <$> dotVersion (fromParen $ rs)+dotVersion (InfixApp l x op y) = (first (LeftSection l x op :) <$> dotVersion y) +++                                 (first (RightSection l op y:) <$> dotVersion x)+dotVersion _ = []   ---------------------------------------------------------------------@@ -91,27 +93,61 @@  findIdeas :: [HintRule] -> Scope -> Module S -> Decl_ -> [Idea] findIdeas matches s _ decl =-  [ (idea (hintRuleSeverity m) (hintRuleName m) x y){ideaNote=notes}+  [ (idea (hintRuleSeverity m) (hintRuleName m) x y [r]){ideaNote=notes}   | decl <- case decl of InstDecl{} -> children decl; _ -> [decl]-  , (parent,x) <- universeParentExp decl, not $ isParen x, let x2 = fmapAn x-  , m <- matches, Just (y,notes) <- [matchIdea s decl m parent x2]]-+  , (parent,x) <- universeParentExp decl, not $ isParen x+  , m <- matches, Just (y,notes, subst, rule) <- [matchIdea s decl m parent x]+  , let r = R.Replace R.Expr (toSS x) subst (prettyPrint rule) ] -matchIdea :: Scope -> Decl_ -> HintRule -> Maybe (Int, Exp_) -> Exp_ -> Maybe (Exp_,[Note])+matchIdea :: Scope -> Decl_ -> HintRule -> Maybe (Int, Exp_) -> Exp_ -> Maybe (Exp_,[Note], [(String, R.SrcSpan)], Exp_) matchIdea s decl HintRule{..} parent x = do     let nm a b = scopeMatch (hintRuleScope,a) (s,b)     u <- unifyExp nm True hintRuleLHS x     u <- check u     let e = subst u hintRuleRHS+        template = substT u hintRuleRHS     let res = addBracket parent $ unqualify hintRuleScope s u $ performEval e     guard $ (freeVars e Set.\\ Set.filter (not . isUnifyVar) (freeVars hintRuleRHS))             `Set.isSubsetOf` freeVars x         -- check no unexpected new free variables     guard $ checkSide hintRuleSide $ ("original",x) : ("result",res) : u     guard $ checkDefine decl parent res-    return (res,hintRuleNotes)+    return (res,hintRuleNotes, [(s, toSS pos) | (s, pos) <- u, ann pos /= an], template)  +-- | Descend, and if something changes then add/remove brackets appropriately in both the template+-- and the original expression.+descendBracketTemplate :: (Exp_ -> (Bool, (Exp_, Exp_))) -> Exp_ -> Exp_+descendBracketTemplate op x = descendIndex g x+    where+        g i y = if a then f i b else (fst b)+            where (a,b) = op y++        f i (v, y) | needBracket i x y = addParen v+        f i (v, y) = v++transformBracketTemplate :: (Exp_ -> Maybe (Exp_, Exp_)) -> Exp_ -> Exp_+transformBracketTemplate op = fst . snd . g+    where+        g :: Exp_ -> (Bool, (Exp_, Exp_))+        g = f . descendBracketTemplate g+        f :: Exp_ -> (Bool, (Exp_, Exp_))+        f x = maybe (False,(x, x)) ((,) True) (op x)++-- perform a substitution+substT :: [(String,Exp_)] -> Exp_ -> Exp_+substT bind = transform g . transformBracketTemplate f+    where+        f v@(Var _ (fromNamed -> x)) | isUnifyVar x = case lookup x bind of+                                                        Just x -> if ann x == an then  Just (x, x)+                                                                                 else  Just (v, x)+                                                        Nothing -> Nothing+        f _ = Nothing++        g (App _ np x) | np ~= "_noParen_" = fromParen x+        g x = x++ --------------------------------------------------------------------- -- UNIFICATION @@ -127,6 +163,7 @@ unify :: Data a => NameMatch -> Bool -> a -> a -> Maybe [(String,Exp_)] unify nm root x y | Just x <- cast x = unifyExp nm root x (unsafeCoerce y)                   | Just x <- cast x = unifyPat nm x (unsafeCoerce y)+                  | Just (x :: SrcSpanInfo) <- cast x = Just []                   | otherwise = unifyDef nm x y  @@ -138,7 +175,8 @@ -- root = True, this is the outside of the expr -- do not expand out a dot at the root, since otherwise you get two matches because of readRule (Bug #570) unifyExp :: NameMatch -> Bool -> Exp_ -> Exp_ -> Maybe [(String,Exp_)]-unifyExp nm root x y | isParen x || isParen y = unifyExp nm root (fromParen x) (fromParen y)+unifyExp nm root x y | isParen x || isParen y =+  map (rebracket y) <$> (unifyExp nm root (fromParen x) (fromParen y)) unifyExp nm root (Var _ (fromNamed -> v)) y | isUnifyVar v = Just [(v,y)] unifyExp nm root (Var _ x) (Var _ y) | nm x y = Just [] unifyExp nm root x@(App _ x1 x2) (App _ y1 y2) =@@ -151,11 +189,15 @@ unifyExp nm root x y | isOther x, isOther y = unifyDef nm x y unifyExp nm root _ _ = Nothing +rebracket (Paren l e') (v, e)+  | e' == e = (v, Paren l e)+rebracket e (v, e') = (v, e') + unifyPat :: NameMatch -> Pat_ -> Pat_ -> Maybe [(String,Exp_)] unifyPat nm (PVar _ x) (PVar _ y) = Just [(fromNamed x, toNamed $ fromNamed y)] unifyPat nm (PVar _ x) PWildCard{} = Just [(fromNamed x, toNamed $ "_" ++ fromNamed x)]-unifyPat nm x y = unifyDef nm x y +unifyPat nm x y = unifyDef nm x y   -- types that are not already handled in unify@@ -172,7 +214,9 @@ -- check the unification is valid check :: [(String,Exp_)] -> Maybe [(String,Exp_)] check = mapM f . groupSort-    where f (x,ys) = if allSame ys then Just (x,head ys) else Nothing+    where f (x,ys) = if checkSame ys then Just (x,head ys) else Nothing+          checkSame [] = True+          checkSame (x:xs) = all (x =~=) xs   -- perform a substitution@@ -203,7 +247,7 @@             = isType typ y         f (App _ (App _ cond (sub -> x)) (sub -> y))             | cond ~= "notIn" = and [x `notElem` universe y | x <- list x, y <- list y]-            | cond ~= "notEq" = x /= y+            | cond ~= "notEq" = x /=~= y         f x | x ~= "noTypeCheck" = True         f x | x ~= "noQuickCheck" = True         f x = error $ "Hint.Match.checkSide, unknown side condition: " ++ prettyPrint x
src/Hint/Monad.hs view
@@ -1,4 +1,4 @@-{-# LANGUAGE ViewPatterns, PatternGuards #-}+{-# LANGUAGE ViewPatterns, PatternGuards, FlexibleContexts #-}  {-     Find and match:@@ -19,7 +19,7 @@ no = do bar; a <- foo; return b yes = do x <- bar; x -- do join bar no = do x <- bar; x; x-no = mdo hook <- mkTrigger pat (act >> rmHook hook) ; return hook+{-# LANGUAGE RecursiveDo #-}; no = mdo hook <- mkTrigger pat (act >> rmHook hook) ; return hook yes = do x <- return y; foo x -- @Warning do let x = y; foo x yes = do x <- return $ y + z; foo x -- do let x = y + z; foo x no = do x <- return x; foo x@@ -41,6 +41,8 @@ import Data.Maybe import Data.List import Hint.Type+import Refact.Types+import qualified Refact.Types as R import Prelude  @@ -53,52 +55,68 @@ monadExp :: Decl_ -> Exp_ -> [Idea] monadExp decl x = case x of         (view -> App2 op x1 x2) | op ~= ">>" -> f x1-        Do _ xs -> [err "Redundant return" x y | Just y <- [monadReturn xs]] ++-                   [err "Use join" x (Do an y) | Just y <- [monadJoin xs]] ++-                   [err "Redundant do" x y | [Qualifier _ y] <- [xs]] ++-                   [warn "Use let" x (Do an y) | Just y <- [monadLet xs]] +++        Do _ xs -> [err "Redundant return" x (Do an y) rs | Just (y, rs) <- [monadReturn xs]] +++                   [err "Use join" x (Do an y) rs | Just (y, rs) <- [monadJoin xs ['a'..'z']]] +++                   [err "Redundant do" x y [Replace Expr (toSS x) [("y", toSS y)] "y"] | [Qualifier _ y] <- [xs]] +++                   [warn "Use let" x (Do an y) rs | Just (y, rs) <- [monadLet xs]] ++                    concat [f x | Qualifier _ x <- init xs]         _ -> []     where-        f x = [err ("Use " ++ name) x y | Just (name,y) <- [monadCall x], fromNamed decl /= name]+        f x = [err ("Use " ++ name) x y r  | Just (name,y, r) <- [monadCall x], fromNamed decl /= name] +middle :: (b -> d) -> (a, b, c) -> (a, d, c)+middle f (a,b,c) = (a, f b, c) + -- see through Paren and down if/case etc -- return the name to use in the hint, and the revised expression-monadCall :: Exp_ -> Maybe (String,Exp_)-monadCall (Paren _ x) = second (Paren an) <$> monadCall x-monadCall (App _ x y) = second (\x -> App an x y) <$> monadCall x-monadCall (InfixApp _ x op y)-    | isDol op = second (\x -> InfixApp an x op y) <$> monadCall x-    | op ~= ">>=" = second (\y -> InfixApp an x op y) <$> monadCall y+monadCall :: Exp_ -> Maybe (String,Exp_, [Refactoring R.SrcSpan])+monadCall (Paren l x) = middle (Paren l) <$> monadCall x+monadCall (App l x y) = middle (\x -> App l x y) <$> monadCall x+monadCall (InfixApp l x op y)+    | isDol op = middle (\x -> InfixApp l x op y) <$> monadCall x+    | op ~= ">>=" = middle (\y -> InfixApp l x op y) <$> monadCall y monadCall (replaceBranches -> (bs@(_:_), gen)) | all isJust res-    = Just (fst $ fromJust $ head res, gen $ map (snd . fromJust) res)+    = Just ("Use simple functions", gen $ map (\(Just (a,b,c)) -> b) res, rs)     where res = map monadCall bs-monadCall x | x:_ <- filter (x ~=) badFuncs = let x2 = x ++ "_" in  Just (x2, toNamed x2)+          rs  = concatMap (\(Just (a,b,c)) -> c) res+monadCall x | x2:_ <- filter (x ~=) badFuncs = let x3 = x2 ++ "_" in  Just (x3, toNamed x3, [Replace Expr (toSS x) [] x3]) monadCall _ = Nothing --monadReturn (reverse -> Qualifier _ (App _ ret (Var _ v)):Generator _ (PVar _ p) x:rest)+monadReturn :: [Stmt S] -> Maybe ([Stmt S], [Refactoring R.SrcSpan])+monadReturn (reverse -> q@(Qualifier _ (App _ ret (Var _ v))):g@(Generator _ (PVar _ p) x):rest)     | ret ~= "return", fromNamed v == fromNamed p-    = Just $ Do an $ reverse $ Qualifier an x : rest+    = Just (reverse (Qualifier an x : rest),+            [Replace Stmt (toSS g) [("x", toSS x)] "x", Delete Stmt (toSS q)]) monadReturn _ = Nothing --monadJoin (Generator _ (view -> PVar_ p) x:Qualifier _ (view -> Var_ v):xs)+monadJoin :: [Stmt S] -> [Char] -> Maybe ([Stmt S], [Refactoring R.SrcSpan])+monadJoin (g@(Generator _ (view -> PVar_ p) x):q@(Qualifier _ (view -> Var_ v)):xs) (c:cs)     | p == v && v `notElem` varss xs-    = Just $ Qualifier an (rebracket1 $ App an (toNamed "join") x) : fromMaybe xs (monadJoin xs)-monadJoin (x:xs) = (x:) <$> monadJoin xs-monadJoin [] = Nothing+    = Just . f $ fromMaybe def (monadJoin xs cs)+    where+      gen expr = Qualifier (ann x) (rebracket1 $ App an (toNamed "join") expr)+      def = (xs, [])+      f (ss, rs) = (s:ss, r ++ rs)+      s = gen x+      r = [Replace Stmt (toSS g) [("x", toSS x)] "join x", Delete Stmt (toSS q)] +monadJoin (x:xs) cs = first (x:)   <$> monadJoin xs cs+monadJoin [] _ = Nothing -monadLet xs = if xs == ys then Nothing else Just ys+monadLet :: [Stmt S] -> Maybe ([Stmt S], [Refactoring R.SrcSpan])+monadLet xs = if null rs then Nothing else Just (ys, rs)     where-        ys = map mkLet xs+        (ys, catMaybes -> rs) = unzip $ map mkLet xs         vs = concatMap pvars [p | Generator _ p _ <- xs]-        mkLet (Generator _ (view -> PVar_ p) (fromRet -> Just y))+        mkLet g@(Generator _ v@(view -> PVar_ p) (fromRet -> Just y))             | p `notElem` vars y, p `notElem` delete p vs-            = LetStmt an $ BDecls an [PatBind an (toNamed p) (UnGuardedRhs an y) Nothing]-        mkLet x = x+            = (template (toNamed p) y, Just refact)+         where+            refact = Replace Stmt (toSS g) [("lhs", toSS v), ("rhs", toSS y)]+                      (prettyPrint $ template (toNamed "lhs") (toNamed "rhs"))+        mkLet x = (x, Nothing)+        template lhs rhs = LetStmt an $ BDecls an [PatBind an lhs (UnGuardedRhs an rhs) Nothing]  fromRet (Paren _ x) = fromRet x fromRet (InfixApp _ x y z) | opExp y ~= "$" = fromRet $ App an x z
src/Hint/Naming.hs view
@@ -46,7 +46,7 @@ namingHint _ modu = naming $ Set.fromList [x | Ident _ x <- universeS modu]  naming :: Set.Set String -> Decl_ -> [Idea]-naming seen x = [warn "Use camelCase" x2 (replaceNames res x2) | not $ null res]+naming seen x = [(warnN "Use camelCase" x2 (replaceNames res x2)) | not $ null res]     where res = [(n,y) | n <- nub $ getNames x, Just y <- [suggestName n], not $ y `Set.member` seen]           x2 = shorten x 
src/Hint/Pragma.hs view
@@ -1,3 +1,5 @@+{-# LANGUAGE ViewPatterns #-}+{-# LANGUAGE FlexibleContexts #-} {-     Suggest better pragmas     OPTIONS_GHC -cpp => LANGUAGE CPP@@ -29,26 +31,65 @@ import Hint.Type import Data.List import Data.Maybe+import Refact.Types+import qualified Refact.Types as R   pragmaHint :: ModuHint-pragmaHint _ x = languageDupes lang ++ [pragmaIdea old $ [LanguagePragma an (map toNamed ns2) | ns2 /= []] ++ catMaybes new | old /= []]+pragmaHint _ x = languageDupes lang ++ optToPragma x lang     where         lang = [x | x@LanguagePragma{} <- modulePragmas x]-        (old,new,ns) = unzip3 [(old,new,ns) | old <- modulePragmas x, Just (new,ns) <- [optToLanguage old]]-        ns2 = nub (concat ns) \\ concat [map fromNamed n | LanguagePragma _ n <- lang] +optToPragma :: Module_ -> [ModulePragma S] -> [Idea]+optToPragma x lang =+  [pragmaIdea (OptionsToComment old ys rs) | old /= []]+  where+        (old,new,ns, rs) =+          unzip4 [(old,new,ns, r)+                 | old <- modulePragmas x, Just (new,ns) <- [optToLanguage old ls]+                 , let r = mkRefact old new ns] -pragmaIdea :: [ModulePragma S] -> [ModulePragma S] -> Idea-pragmaIdea xs ys = rawIdea Error "Use better pragmas" (toSrcSpan $ ann $ head xs) (f xs) (Just $ f ys) []-    where f = unlines . map prettyPrint+        ls = concat [map fromNamed n | LanguagePragma _ n <- lang]+        ns2 = nub (concat ns) \\ ls +        ys = [LanguagePragma an (map toNamed ns2) | ns2 /= []] ++ catMaybes new+        mkRefact :: ModulePragma S -> Maybe (ModulePragma S) -> [String] -> Refactoring R.SrcSpan+        mkRefact old (maybe "" prettyPrint -> new) ns =+          let ns' = map (\n -> prettyPrint $ LanguagePragma an [toNamed n]) ns+          in+          ModifyComment (toSS old) (intercalate "\n" (filter (not . null) (new: ns'))) +data PragmaIdea = SingleComment (ModulePragma S) (ModulePragma S)+                | MultiComment (ModulePragma S) (ModulePragma S) (ModulePragma S)+                | OptionsToComment [ModulePragma S] [ModulePragma S] [Refactoring R.SrcSpan]+++pragmaIdea :: PragmaIdea -> Idea+pragmaIdea pidea =+  case pidea of+    SingleComment old new ->+      mkIdea (toSrcSpan . ann $ old)+        (prettyPrint old) (Just $ prettyPrint new) []+        [ModifyComment (toSS old) (prettyPrint new)]+    MultiComment repl delete new ->+      mkIdea (toSrcSpan . ann $ repl)+        (f [repl, delete]) (Just $ prettyPrint new) []+        [ ModifyComment (toSS repl) (prettyPrint new)+        , ModifyComment (toSS delete) ""]+    OptionsToComment old new r ->+      mkIdea (toSrcSpan . ann . head $ old)+        (f old) (Just $ f new) []+        r+    where+          f = unlines . map prettyPrint+          mkIdea = rawIdea Error "Use better pragmas"++ languageDupes :: [ModulePragma S] -> [Idea] languageDupes (a@(LanguagePragma _ x):xs) =     (if nub_ x `neqList` x-        then [pragmaIdea [a] [LanguagePragma an $ nub_ x]]-        else [pragmaIdea [a,b] [LanguagePragma an (nub_ $ x ++ y)] | b@(LanguagePragma _ y) <- xs, not $ null $ intersect_ x y]) +++        then [pragmaIdea (SingleComment a (LanguagePragma (ann a) $ nub_ x))]+        else [pragmaIdea (MultiComment a b (LanguagePragma (ann a) (nub_ $ x ++ y))) | b@(LanguagePragma _ y) <- xs, not $ null $ intersect_ x y]) ++     languageDupes xs languageDupes _ = [] @@ -57,16 +98,17 @@ strToLanguage :: String -> Maybe [String] strToLanguage "-cpp" = Just ["CPP"] strToLanguage x | "-X" `isPrefixOf` x = Just [drop 2 x]-strToLanguage "-fglasgow-exts" = Just $ map show glasgowExts+strToLanguage "-fglasgow-exts" = Just $ map prettyExtension glasgowExts strToLanguage _ = Nothing  -optToLanguage :: ModulePragma S -> Maybe (Maybe (ModulePragma S), [String])-optToLanguage (OptionsPragma sl tool val)-    | maybe True (== GHC) tool && any isJust vs = Just (res, concat $ catMaybes vs)+optToLanguage :: ModulePragma S -> [String] -> Maybe (Maybe (ModulePragma S), [String])+optToLanguage (OptionsPragma sl tool val) ls+    | maybe True (== GHC) tool && any isJust vs =+      Just (res, filter (not . (`elem` ls)) (concat $ catMaybes vs))     where         strs = words val         vs = map strToLanguage strs         keep = concat $ zipWith (\v s -> [s | isNothing v]) vs strs         res = if null keep then Nothing else Just $ OptionsPragma sl tool (unwords keep)-optToLanguage _ = Nothing+optToLanguage _ _ = Nothing
src/Hint/Structure.hs view
@@ -41,6 +41,10 @@ import Hint.Type import Data.List.Extra import Data.Tuple+import Data.Maybe+import Data.Either+import Refact.Types hiding (RType(Pattern, Match))+import qualified Refact.Types as R (RType(Pattern, Match), SrcSpan)   structureHint :: DeclHint@@ -50,10 +54,32 @@     concatMap expHint (universeBi x)  -hints :: (String -> Pattern -> Idea) -> Pattern -> [Idea]-hints gen (Pattern pat (UnGuardedRhs d bod) bind)-    | length guards > 2 = [gen "Use guards" $ Pattern pat (GuardedRhss d guards) bind]-    where guards = asGuards bod+hints :: (String -> Pattern -> [Refactoring R.SrcSpan] -> Idea) -> Pattern -> [Idea]+hints gen (Pattern l rtype pat (UnGuardedRhs d bod) bind)+    | length guards > 2 = [gen "Use guards" (Pattern l rtype pat (GuardedRhss d guards) bind) [refactoring]]+    where rawGuards = asGuards bod+          mkGuard a b = GuardedRhs an [Qualifier an a] b+          guards = map (uncurry mkGuard) rawGuards+          (lhs, rhs) = unzip rawGuards+          mkTemplate c ps =+            -- Check if the expression has been injected or is natural+            let checkAn p v = if ann p == an then Left p else Right ( c ++ [v], toSS p)+            in zipWith checkAn ps ['1' .. '9']+          patSubts = case pat of+                       [p] -> [Left p] -- Substitution doesn't work properly for PatBinds+                                       -- This will probably produce+                                       -- unexpected results if the pattern+                                       -- contains any template variables+                       ps  -> mkTemplate "p100" ps+          guardSubts = mkTemplate "g100" lhs+          exprSubts  = mkTemplate "e100" rhs+          templateGuards = zipWith (\a b -> mkGuard (toString a) (toString b)) guardSubts exprSubts+          toString (Left e) = e+          toString (Right (v, _)) = toNamed v+          template = fromMaybe "" $ ideaTo (gen "" (Pattern l rtype (map toString patSubts) (GuardedRhss d templateGuards) bind) [])+          f :: [Either a (String, R.SrcSpan)] -> [(String, R.SrcSpan)]+          f = rights+          refactoring = Replace rtype (toRefactSrcSpan . toSrcSpan $ l) (f patSubts ++ f guardSubts ++ f exprSubts) template  {- -- Do not suggest view patterns, they aren't something everyone likes sufficiently@@ -67,66 +93,76 @@         decsBind = nub $ concatMap declBind $ childrenBi bind -} -hints gen (Pattern pats (GuardedRhss _ [GuardedRhs _ [test] bod]) bind)+hints gen (Pattern l t pats (GuardedRhss _ [GuardedRhs _ [test] bod]) bind)     | prettyPrint test `elem` ["otherwise","True"]-    = [gen "Redundant guard" $ Pattern pats (UnGuardedRhs an bod) bind]+    = [gen "Redundant guard" (Pattern l t pats (UnGuardedRhs an bod) bind) [Delete Stmt (toSS test)]] -hints gen (Pattern pats bod (Just bind)) | f bind && False -- disabled due to bug 358-    = [gen "Redundant where" $ Pattern pats bod Nothing]+hints gen (Pattern l t pats bod (Just bind)) | f bind && False -- disabled due to bug #138+    = [gen "Redundant where" (Pattern l t pats bod Nothing) []]     where         f (BDecls _ x) = null x         f (IPBinds _ x) = null x -hints gen (Pattern pats (GuardedRhss _ (unsnoc -> Just (gs, GuardedRhs _ [test] bod))) bind)+hints gen (Pattern l t pats (GuardedRhss _ (unsnoc -> Just (gs, GuardedRhs _ [test] bod))) bind)     | prettyPrint test == "True"-    = [gen "Use otherwise" $ Pattern pats (GuardedRhss an $ gs ++ [GuardedRhs an [Qualifier an $ toNamed "otherwise"] bod]) bind]+    = [gen "Use otherwise" (Pattern l t pats (GuardedRhss an $ gs ++ [GuardedRhs an [Qualifier an $ toNamed "otherwise"] bod]) bind) [Replace Expr (toSS test) [] "otherwise"]]  hints _ _ = []  -asGuards :: Exp_ -> [GuardedRhs S]+asGuards :: Exp_ -> [(Exp S, Exp S)] asGuards (Paren _ x) = asGuards x-asGuards (If _ a b c) = GuardedRhs an [Qualifier an a] b : asGuards c-asGuards x = [GuardedRhs an [Qualifier an $ toNamed "otherwise"] x]+asGuards (If _ a b c) = (a, b) : asGuards c+asGuards x = [(toNamed "otherwise", x)]  -data Pattern = Pattern [Pat_] (Rhs S) (Maybe (Binds S))+data Pattern = Pattern SrcSpanInfo R.RType [Pat_] (Rhs S) (Maybe (Binds S))  -- Invariant: Number of patterns may not change-asPattern :: Decl_ -> [(Pattern, String -> Pattern -> Idea)]+asPattern :: Decl_ -> [(Pattern, String -> Pattern -> [Refactoring R.SrcSpan] -> Idea)] asPattern x = concatMap decl (universeBi x) ++ concatMap alt (universeBi x)     where-        decl o@(PatBind a pat rhs bind) = [(Pattern [pat] rhs bind, \msg (Pattern [pat] rhs bind) -> warn msg o $ PatBind a pat rhs bind)]+        decl o@(PatBind a pat rhs bind) = [(Pattern a Bind [pat] rhs bind, \msg (Pattern _ _ [pat] rhs bind) rs -> warn msg o (PatBind a pat rhs bind) rs)]         decl (FunBind _ xs) = map match xs         decl _ = []-        match o@(Match a b pat rhs bind) = (Pattern pat rhs bind, \msg (Pattern pat rhs bind) -> warn msg o $ Match a b pat rhs bind)-        match o@(InfixMatch a p b ps rhs bind) = (Pattern (p:ps) rhs bind, \msg (Pattern (p:ps) rhs bind) -> warn msg o $ InfixMatch a p b ps rhs bind)-        alt o@(Alt a pat rhs bind) = [(Pattern [pat] rhs bind, \msg (Pattern [pat] rhs bind) -> warn msg o $ Alt a pat rhs bind)]+        match o@(Match a b pat rhs bind) = (Pattern a R.Match pat rhs bind, \msg (Pattern _ _ pat rhs bind) rs -> warn msg o (Match a b pat rhs bind) rs)+        match o@(InfixMatch a p b ps rhs bind) = (Pattern a R.Match (p:ps) rhs bind, \msg (Pattern _ _ (p:ps) rhs bind) rs -> warn msg o (InfixMatch a p b ps rhs bind) rs)+        alt o@(Alt a pat rhs bind) = [(Pattern a R.Match [pat] rhs bind, \msg (Pattern _ _ [pat] rhs bind) rs -> warn msg o (Alt a pat rhs bind) [])]    -- Should these hints be in the same module? They are less structure, and more about pattern matching -- Or perhaps the entire module should be renamed Pattern, since it's all about patterns patHint :: Pat_ -> [Idea]-patHint o@(PApp _ name args) | length args >= 3 && all isPWildCard args = [warn "Use record patterns" o $ PRec an name []]-patHint o@(PBangPat _ x) | f x = [err "Redundant bang pattern" o x]+patHint o@(PApp _ name args) | length args >= 3 && all isPWildCard args =+  [warn "Use record patterns" o (PRec an name []) [Replace R.Pattern (toSS o) [] (prettyPrint $ PRec an name [])] ]++patHint o@(PBangPat _ x) | f x = [err "Redundant bang pattern" o x [r]]     where f (PParen _ x) = f x           f (PAsPat _ _ x) = f x           f PLit{} = True           f PApp{} = True           f PInfixApp{} = True           f _ = False-patHint o@(PIrrPat _ x) | f x = [err "Redundant irrefutable pattern" o x]+          r = Replace R.Pattern (toSS o) [("x", toSS x)] "x"+patHint o@(PIrrPat _ x) | f x = [err "Redundant irrefutable pattern" o x [r]]     where f (PParen _ x) = f x           f (PAsPat _ _ x) = f x           f PWildCard{} = True           f PVar{} = True           f _ = False+          r = Replace R.Pattern (toSS o) [("x", toSS x)] "x" patHint _ = []   expHint :: Exp_ -> [Idea]-expHint o@(Case _ _ [Alt _ PWildCard{} (UnGuardedRhs _ e) Nothing]) = [warn "Redundant case" o e]+expHint o@(Case _ _ [Alt _ PWildCard{} (UnGuardedRhs _ e) Nothing]) =+  [warn "Redundant case" o e [r]]+  where+    r = Replace Expr (toSS o) [("x", toSS e)] "x" expHint o@(Case _ (Var _ x) [Alt _ (PVar _ y) (UnGuardedRhs _ e) Nothing])-    | x =~= UnQual an y = [warn "Redundant case" o e]+    | x =~= UnQual an y =+      [warn "Redundant case" o e [r]]+  where+    r = Replace Expr (toSS o) [("x", toSS e)] "x" expHint _ = []
src/Hint/Type.hs view
@@ -1,10 +1,11 @@ -module Hint.Type(module Hint.Type, module Idea, module HSE.All) where+module Hint.Type(module Hint.Type, module Idea, module HSE.All, module Refact) where  import Data.Monoid import HSE.All import Idea import Prelude+import Refact   type DeclHint = Scope -> Module_ -> Decl_ -> [Idea]
src/Hint/Unsafe.hs view
@@ -20,6 +20,7 @@  import Hint.Type import Data.Char+import Refact.Types   unsafeHint :: ModuHint@@ -27,7 +28,7 @@         [ rawIdea Error "Missing NOINLINE pragma" (toSrcSpan $ ann d)             (prettyPrint d)             (Just $ dropWhile isSpace (prettyPrint $ gen x) ++ "\n" ++ prettyPrint d)-            []+            [] [InsertComment (toSS d) (prettyPrint $ gen x)]         | d@(PatBind _ (PVar _ x) _ _) <- moduleDecls m         , isUnsafeDecl d, x `notElem_` noinline]     where
src/Hint/Util.hs view
@@ -4,55 +4,81 @@  import HSE.All import Data.List.Extra+import Refact.Types+import Refact+import qualified Refact.Types as R (SrcSpan) +niceLambda :: [String] -> Exp_ -> Exp_+niceLambda ss e = fst (niceLambdaR ss e) + -- | Generate a lambda, but prettier (if possible). --   Generally no lambda is good, but removing just some arguments isn't so useful.-niceLambda :: [String] -> Exp_ -> Exp_+niceLambdaR :: [String] -> Exp_ -> (Exp_, R.SrcSpan -> [Refactoring R.SrcSpan])  -- \xs -> (e) ==> \xs -> e-niceLambda xs (Paren _ x) = niceLambda xs x+niceLambdaR xs (Paren l x) = niceLambdaR xs x  -- \xs -> \v vs -> e ==> \xs v -> \vs -> e -- \xs -> \ -> e ==> \xs -> e-niceLambda xs (Lambda _ ((view -> PVar_ v):vs) x) | v `notElem` xs = niceLambda (xs++[v]) (Lambda an vs x)-niceLambda xs (Lambda _ [] x) = niceLambda xs x+niceLambdaR xs (Lambda _ ((view -> PVar_ v):vs) x) | v `notElem` xs = niceLambdaR (xs++[v]) (Lambda an vs x)+niceLambdaR xs (Lambda _ [] x) = niceLambdaR xs x  -- \ -> e ==> e-niceLambda [] x = x+niceLambdaR [] x = (x, const [])  -- \xs -> e xs ==> e-niceLambda xs (fromApps -> e) | map view xs2 == map Var_ xs, vars e2 `disjoint` xs, not $ null e2 = apps e2-    where (e2,xs2) = splitAt (length e - length xs) e+niceLambdaR xs (fromAppsWithLoc -> e) | map view xs2 == map Var_ xs, vars e2 `disjoint` xs, not $ null e2 =+    (apps e2, \s -> [Replace Expr s [("x", pos)] "x"])+    where (e',xs') = splitAt (length e - length xs) e+          (e2, xs2) = (map fst e', map fst xs')+          pos      = toRefactSrcSpan . toSrcSpan $ snd (last e')  -- \x y -> x + y ==> (+)-niceLambda [x,y] (InfixApp _ (view -> Var_ x1) (opExp -> op) (view -> Var_ y1))-    | x == x1, y == y1, vars op `disjoint` [x,y] = op+niceLambdaR [x,y] (InfixApp _ (view -> Var_ x1) (opExp -> op) (view -> Var_ y1))+    | x == x1, y == y1, vars op `disjoint` [x,y] = (op, \s -> [Replace Expr s [] (prettyPrint op)])  -- \x -> x + b ==> (+ b) [heuristic, b must be a single lexeme, or gets too complex]-niceLambda [x] (view -> App2 (expOp -> Just op) a b)-    | isLexeme b, view a == Var_ x, x `notElem` vars b, allowRightSection (fromNamed op) = rebracket1 $ RightSection an op b+niceLambdaR [x] (view -> App2 (expOp -> Just op) a b)+    | isLexeme b, view a == Var_ x, x `notElem` vars b, allowRightSection (fromNamed op) =+      let e = rebracket1 $ RightSection an op b+      in (e, \s -> [Replace Expr s [] (prettyPrint e)])  -- \x y -> f y x = flip f-niceLambda [x,y] (view -> App2 op (view -> Var_ y1) (view -> Var_ x1))-    | x == x1, y == y1, vars op `disjoint` [x,y] = App an (toNamed "flip") op+niceLambdaR [x,y] (view -> App2 op (view -> Var_ y1) (view -> Var_ x1))+    | x == x1, y == y1, vars op `disjoint` [x,y] = (gen op, \s -> [Replace Expr s [("x", toSS op)] (prettyPrint $ gen (toNamed "x"))])+    where+      gen x = App an (toNamed "flip") x  -- \x -> f (b x) ==> f . b -- \x -> f $ b x ==> f . b-niceLambda [x] y | Just z <- factor y, x `notElem` vars z = z+niceLambdaR [x] y | Just (z, subts) <- factor y, x `notElem` vars z = (z, \s -> [mkRefact subts s])     where         -- factor the expression with respect to x-        factor y@App{} | Just (ini,lst) <- unsnoc $ fromApps y, view lst == Var_ x = Just $ apps ini-        factor y@App{} | Just (ini,lst) <- unsnoc $ fromApps y, Just z <- factor lst = Just $ niceDotApp (apps ini) z-        factor (InfixApp _ y op (factor -> Just z)) | isDol op = Just $ niceDotApp y z+        factor y@(App _ ini lst) | view lst == Var_ x = Just $ (ini, [ann ini])+        factor y@(App _ ini lst) | Just (z, ss) <- factor lst = let r = niceDotApp ini z+                                                           in if r == z then Just (r, ss)+                                                                        else Just (r, ann ini : ss)+        factor (InfixApp _ y op (factor -> Just (z, ss))) | isDol op = let r = niceDotApp y z+                                                                 in if r == z then Just (r, ss)+                                                                              else Just (r, ann y : ss)         factor (Paren _ y@App{}) = factor y         factor _ = Nothing+        mkRefact :: [S] -> R.SrcSpan -> Refactoring R.SrcSpan+        mkRefact subts s =+          let tempSubts = zipWith (\a b -> ([a], toRefactSrcSpan . toSrcSpan $ b)) ['a' .. 'z'] subts+              template = dotApps (map (toNamed . fst) tempSubts)+          in Replace Expr s tempSubts (prettyPrint template) + -- \x -> (x +) ==> (+)-niceLambda [x] (LeftSection _ (view -> Var_ x1) op) | x == x1 = opExp op+-- Section handling is not yet supported for refactoring+niceLambdaR [x] (LeftSection _ (view -> Var_ x1) op) | x == x1 =+  let e = opExp op+  in (e, \s -> [Replace Expr s [] (prettyPrint e)])  -- base case-niceLambda ps x = Lambda an (map toNamed ps) x+niceLambdaR ps x = (Lambda an (map toNamed ps) x, const [])   
src/Idea.hs view
@@ -6,6 +6,8 @@ import HSE.All import Settings import HsColour+import Refact.Types hiding (SrcSpan)+import qualified Refact.Types as R   -- | An idea suggest by a 'Hint'.@@ -18,6 +20,7 @@     ,ideaFrom :: String -- ^ The contents of the source code the idea relates to.     ,ideaTo :: Maybe String -- ^ The suggested replacement, or 'Nothing' for no replacement (e.g. on parse errors).     ,ideaNote :: [Note] -- ^ Notes about the effect of applying the replacement.+    , ideaRefactoring :: [Refactoring R.SrcSpan] -- ^ How to perform this idea     }     deriving (Eq,Ord) @@ -65,7 +68,17 @@   rawIdea = Idea "" ""-idea severity hint from to = rawIdea severity hint (toSrcSpan $ ann from) (f from) (Just $ f to) []+rawIdeaN a b c d e f = Idea "" "" a b c d e f []++idea severity hint from to rs = rawIdea severity hint (toSrcSpan $ ann from) (f from) (Just $ f to) [] rs     where f = trimStart . prettyPrint warn = idea Warning err = idea Error+++ideaN severity hint from to = rawIdea severity hint (toSrcSpan $ ann from) (f from) (Just $ f to) [] []+    where f = trimStart . prettyPrint++warnN = ideaN Warning+errN  = ideaN Error+
+ src/Refact.hs view
@@ -0,0 +1,15 @@+module Refact where++import qualified Refact.Types as R+import HSE.All++toRefactSrcSpan :: SrcSpan -> R.SrcSpan+toRefactSrcSpan ss = R.SrcSpan (srcSpanStartLine ss)+                               (srcSpanStartColumn ss)+                               (srcSpanEndLine ss)+                               (srcSpanEndColumn ss)++toSS :: Annotated a => a S -> R.SrcSpan+toSS = toRefactSrcSpan . toSrcSpan . ann++
src/Util.hs view
@@ -128,4 +128,5 @@     ,XmlSyntax, RegularPatterns -- steals a-b     ,UnboxedTuples -- breaks (#) lens operator     ,QuasiQuotes -- breaks [x| ...], making whitespace free list comps break+    ,DoRec, RecursiveDo -- breaks rec     ]