packages feed

hlint 1.9.40 → 1.9.41

raw patch · 32 files changed

+511/−473 lines, 32 filesdep ~extraPVP: major bump suggested

API removals or changes: PVP suggests a major version bump

Dependency ranges changed: extra

API changes (from Hackage documentation)

- Language.Haskell.HLint2: Hint :: ([(Scope, Module SrcSpanInfo)] -> [Idea]) -> (Scope -> Module SrcSpanInfo -> [Idea]) -> (Scope -> Module SrcSpanInfo -> Decl SrcSpanInfo -> [Idea]) -> (Comment -> [Idea]) -> Hint
+ Language.Haskell.HLint2: Hint :: ([(Scope, Module_)] -> [Idea]) -> (Scope -> Module_ -> [Idea]) -> (Scope -> Module_ -> Decl_ -> [Idea]) -> (Comment -> [Idea]) -> Hint
- Language.Haskell.HLint2: HintRule :: Severity -> String -> Scope -> Exp SrcSpanInfo -> Exp SrcSpanInfo -> Maybe (Exp SrcSpanInfo) -> [Note] -> HintRule
+ Language.Haskell.HLint2: HintRule :: Severity -> String -> Scope -> Exp_ -> Exp_ -> Maybe Exp_ -> [Note] -> HintRule
- Language.Haskell.HLint2: [hintDecl] :: Hint -> Scope -> Module SrcSpanInfo -> Decl SrcSpanInfo -> [Idea]
+ Language.Haskell.HLint2: [hintDecl] :: Hint -> Scope -> Module_ -> Decl_ -> [Idea]
- Language.Haskell.HLint2: [hintModule] :: Hint -> Scope -> Module SrcSpanInfo -> [Idea]
+ Language.Haskell.HLint2: [hintModule] :: Hint -> Scope -> Module_ -> [Idea]
- Language.Haskell.HLint2: [hintModules] :: Hint -> [(Scope, Module SrcSpanInfo)] -> [Idea]
+ Language.Haskell.HLint2: [hintModules] :: Hint -> [(Scope, Module_)] -> [Idea]
- Language.Haskell.HLint2: [hintRuleLHS] :: HintRule -> Exp SrcSpanInfo
+ Language.Haskell.HLint2: [hintRuleLHS] :: HintRule -> Exp_
- Language.Haskell.HLint2: [hintRuleRHS] :: HintRule -> Exp SrcSpanInfo
+ Language.Haskell.HLint2: [hintRuleRHS] :: HintRule -> Exp_
- Language.Haskell.HLint2: [hintRuleSide] :: HintRule -> Maybe (Exp SrcSpanInfo)
+ Language.Haskell.HLint2: [hintRuleSide] :: HintRule -> Maybe Exp_
- Language.Haskell.HLint2: applyHints :: [Classify] -> Hint -> [(Module SrcSpanInfo, [Comment])] -> [Idea]
+ Language.Haskell.HLint2: applyHints :: [Classify] -> Hint -> [(Module_, [Comment])] -> [Idea]
- Language.Haskell.HLint2: findSettings :: FilePath -> FilePath -> Maybe String -> IO ([String], [Module SrcSpanInfo])
+ Language.Haskell.HLint2: findSettings :: FilePath -> FilePath -> Maybe String -> IO ([String], [Module_])
- Language.Haskell.HLint2: parseModuleEx :: ParseFlags -> FilePath -> Maybe String -> IO (Either ParseError (Module SrcSpanInfo, [Comment]))
+ Language.Haskell.HLint2: parseModuleEx :: ParseFlags -> FilePath -> Maybe String -> IO (Either ParseError (Module_, [Comment]))
- Language.Haskell.HLint2: readSettings :: Module SrcSpanInfo -> ([Classify], [HintRule])
+ Language.Haskell.HLint2: readSettings :: Module_ -> ([Classify], [HintRule])
- Language.Haskell.HLint2: scopeCreate :: Module SrcSpanInfo -> Scope
+ Language.Haskell.HLint2: scopeCreate :: Module_ -> Scope
- Language.Haskell.HLint2: scopeMatch :: (Scope, QName SrcSpanInfo) -> (Scope, QName SrcSpanInfo) -> Bool
+ Language.Haskell.HLint2: scopeMatch :: (Scope, QName S) -> (Scope, QName S) -> Bool
- Language.Haskell.HLint2: scopeMove :: (Scope, QName SrcSpanInfo) -> Scope -> QName SrcSpanInfo
+ Language.Haskell.HLint2: scopeMove :: (Scope, QName S) -> Scope -> QName S
- Language.Haskell.HLint3: Hint :: ([(Scope, Module SrcSpanInfo)] -> [Idea]) -> (Scope -> Module SrcSpanInfo -> [Idea]) -> (Scope -> Module SrcSpanInfo -> Decl SrcSpanInfo -> [Idea]) -> (Comment -> [Idea]) -> Hint
+ Language.Haskell.HLint3: Hint :: ([(Scope, Module_)] -> [Idea]) -> (Scope -> Module_ -> [Idea]) -> (Scope -> Module_ -> Decl_ -> [Idea]) -> (Comment -> [Idea]) -> Hint
- Language.Haskell.HLint3: HintRule :: Severity -> String -> Scope -> Exp SrcSpanInfo -> Exp SrcSpanInfo -> Maybe (Exp SrcSpanInfo) -> [Note] -> HintRule
+ Language.Haskell.HLint3: HintRule :: Severity -> String -> Scope -> Exp_ -> Exp_ -> Maybe Exp_ -> [Note] -> HintRule
- Language.Haskell.HLint3: [hintDecl] :: Hint -> Scope -> Module SrcSpanInfo -> Decl SrcSpanInfo -> [Idea]
+ Language.Haskell.HLint3: [hintDecl] :: Hint -> Scope -> Module_ -> Decl_ -> [Idea]
- Language.Haskell.HLint3: [hintModule] :: Hint -> Scope -> Module SrcSpanInfo -> [Idea]
+ Language.Haskell.HLint3: [hintModule] :: Hint -> Scope -> Module_ -> [Idea]
- Language.Haskell.HLint3: [hintModules] :: Hint -> [(Scope, Module SrcSpanInfo)] -> [Idea]
+ Language.Haskell.HLint3: [hintModules] :: Hint -> [(Scope, Module_)] -> [Idea]
- Language.Haskell.HLint3: [hintRuleLHS] :: HintRule -> Exp SrcSpanInfo
+ Language.Haskell.HLint3: [hintRuleLHS] :: HintRule -> Exp_
- Language.Haskell.HLint3: [hintRuleRHS] :: HintRule -> Exp SrcSpanInfo
+ Language.Haskell.HLint3: [hintRuleRHS] :: HintRule -> Exp_
- Language.Haskell.HLint3: [hintRuleSide] :: HintRule -> Maybe (Exp SrcSpanInfo)
+ Language.Haskell.HLint3: [hintRuleSide] :: HintRule -> Maybe Exp_
- Language.Haskell.HLint3: applyHints :: [Classify] -> Hint -> [(Module SrcSpanInfo, [Comment])] -> [Idea]
+ Language.Haskell.HLint3: applyHints :: [Classify] -> Hint -> [(Module_, [Comment])] -> [Idea]
- Language.Haskell.HLint3: parseModuleEx :: ParseFlags -> FilePath -> Maybe String -> IO (Either ParseError (Module SrcSpanInfo, [Comment]))
+ Language.Haskell.HLint3: parseModuleEx :: ParseFlags -> FilePath -> Maybe String -> IO (Either ParseError (Module_, [Comment]))
- Language.Haskell.HLint3: scopeCreate :: Module SrcSpanInfo -> Scope
+ Language.Haskell.HLint3: scopeCreate :: Module_ -> Scope
- Language.Haskell.HLint3: scopeMatch :: (Scope, QName SrcSpanInfo) -> (Scope, QName SrcSpanInfo) -> Bool
+ Language.Haskell.HLint3: scopeMatch :: (Scope, QName S) -> (Scope, QName S) -> Bool
- Language.Haskell.HLint3: scopeMove :: (Scope, QName SrcSpanInfo) -> Scope -> QName SrcSpanInfo
+ Language.Haskell.HLint3: scopeMove :: (Scope, QName S) -> Scope -> QName S

Files

CHANGES.txt view
@@ -1,5 +1,13 @@ Changelog for HLint +1.9.41+    #299, warn in some cases when NumDecimals extension is unused+    #300, warn when LambdaCase extension is unused+    #301, when suggesting newtype remove strictness annotations+    #297, better testing that there isn't a performance regression+    #167, add -j flag for number of threads+    #292, add fst/snd . unzip ==> map fst/snd+    Don't suggest module export trick, breaks Haddock 1.9.40     #293, fix the JSON format of the output 1.9.39
README.md view
@@ -17,7 +17,7 @@  * HLint operates on each module at a time in isolation, as a result HLint does not know about types or which names are in scope. * The presence of `seq` may cause some hints (i.e. eta-reduction) to change the semantics of a program.-* Either the monomorphism restriction, or rank-2 types, may cause transformed programs to require type signatures to be manually inserted.+* Some transformed programs may require additional type signatures, particularly if the transformations trigger the monomorphism restriction or involve rank-2 types. * The `RebindableSyntax` extension can cause HLint to suggest incorrect changes. * HLint turns on many language extensions so it can parse more documents, occasionally some break otherwise legal syntax - e.g. `{-#INLINE foo#-}` doesn't work with `MagicHash`. These extensions can be disabled with `-XNoMagicHash`. @@ -102,7 +102,7 @@  ### Parallel Operation -To run HLint on n processors append the flags `+RTS -Nn`, as described in the [GHC user manual](http://www.haskell.org/ghc/docs/latest/html/users_guide/runtime-control.html). HLint will usually perform fastest if n is equal to the number of physical processors.+To run HLint on 4 processors append the flags `-j4`. HLint will usually perform fastest if n is equal to the number of physical processors, which can be done with `-j` alone.  If your version of GHC does not support the GHC threaded runtime then install with the command: `cabal install --flags="-threaded"` 
data/Default.hs view
@@ -226,6 +226,7 @@ warn "Redundant $" = (($) . f) ==> f warn "Redundant $" = (f $) ==> f hint = (\x -> y) ==> const y where _ = isAtom y && not (isWildcard y)+    -- isWildcard because some people like to put brackets round them even though they are atomic warn "Redundant flip" = flip f x y ==> f y x where _ = isApp original warn "Evaluate" = id x ==> x     where _ = not (isTypeApp x)@@ -383,6 +384,11 @@ warn "Redundant $!" = f $! x ==> f x where _ = isWHNF x warn "Redundant evaluate" = evaluate x ==> return x where _ = isWHNF x +-- TUPLE++warn = fst (unzip x) ==> map fst x+warn = snd (unzip x) ==> map snd x+ -- MAYBE  warn = maybe x id ==> Data.Maybe.fromMaybe x@@ -646,6 +652,8 @@ no  = x `elem` y no  = elem 1 [] : [] test a = foo (\x -> True) -- const True+test a = foo (\_ -> True) -- const True+test a = foo (\x -> x) -- id h a = flip f x (y z) -- f (y z) x h a = flip f x $ y z yes x = case x of {True -> a ; False -> b} -- if x then a else b
data/Test.hs view
@@ -105,10 +105,6 @@ {-# ANN module "HLint: ignore Unused LANGUAGE pragma" #-} \ {-# LANGUAGE RecordWildCards #-} -- @Ignore ??? -{-# ANN module "HLint: ignore Use import/export shortcut" #-} \-module ABCD(module A, module B, module C) where \-import A; import B; import C -- @Ignore ???- {-# ANN lam "HLint: ignore Redundant lambda" #-} \ lam = \x -> x x x -- @Ignore ??? 
hlint.cabal view
@@ -1,7 +1,7 @@ cabal-version:      >= 1.18 build-type:         Simple name:               hlint-version:            1.9.40+version:            1.9.41 license:            BSD3 license-file:       LICENSE category:           Development@@ -29,7 +29,7 @@ extra-doc-files:     README.md     CHANGES.txt-tested-with:        GHC==8.0.1, GHC==7.10.3, GHC==7.8.4, GHC==7.6.3, GHC==7.4.2+tested-with:        GHC==8.0.2, GHC==7.10.3, GHC==7.8.4, GHC==7.6.3, GHC==7.4.2  source-repository head     type:     git@@ -76,16 +76,17 @@         HLint         HsColour         Idea-        Settings         Report         Util         Parallel         Refact+        Config.Haskell+        Config.Type         HSE.All         HSE.Bracket-        HSE.Evaluate         HSE.FreeVars         HSE.Match+        HSE.Reduce         HSE.Scope         HSE.Type         HSE.Util
src/Apply.hs view
@@ -10,7 +10,8 @@ import Data.List.Extra import Data.Maybe import Data.Ord-import Settings+import Config.Type+import Config.Haskell import Idea import Prelude @@ -38,7 +39,7 @@ --   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 :: [Classify] -> Hint -> [(Module SrcSpanInfo, [Comment])] -> [Idea]+applyHints :: [Classify] -> Hint -> [(Module_, [Comment])] -> [Idea] applyHints cls hints_ ms = concat $     [ map (classify $ cls ++ mapMaybe readPragma (universeBi m)) $         order "" (hintModule hints nm m) `merge`
src/CmdLine.hs view
@@ -76,6 +76,7 @@         ,cmdGivenHints :: [FilePath]     -- ^ which settignsfiles were explicitly given         ,cmdWithHints :: [String]        -- ^ hints that are given on the command line         ,cmdColor :: ColorMode           -- ^ color the result+        ,cmdThreads :: Int              -- ^ Numbmer of threads to use, 0 = whatever GHC has         ,cmdIgnore :: [String]           -- ^ the hints to ignore         ,cmdShowAll :: Bool              -- ^ display all skipped items         ,cmdExtension :: [String]        -- ^ extensions@@ -137,6 +138,7 @@         ,cmdGivenHints = nam "hint" &= typFile &= help "Hint/ignore file to use"         ,cmdWithHints = nam "with" &= typ "HINT" &= help "Extra hints to use"         ,cmdColor = nam "colour" &= name "color" &= opt Always &= typ "always/never/auto" &= help "Color output (requires ANSI terminal; auto means on when $TERM is supported; by itself, selects always)"+        ,cmdThreads = 1 &= name "threads" &= name "j" &= opt (0 :: Int) &= help "Number of threads to use (-j for all)"         ,cmdIgnore = nam "ignore" &= typ "HINT" &= help "Ignore a particular hint"         ,cmdShowAll = nam "show" &= help "Show all ignored ideas"         ,cmdExtension = nam "extension" &= typ "EXT" &= help "File extensions to search (default hs/lhs)"
+ src/Config/Haskell.hs view
@@ -0,0 +1,219 @@+{-# LANGUAGE PatternGuards, ViewPatterns #-}++module Config.Haskell(+    findSettings, readSettings,+    readSettings2, readPragma, findSettings2, addInfix+    ) where++import Data.Monoid+import HSE.All+import Control.Monad.Extra+import Data.Char+import Data.Either+import Data.List.Extra+import System.FilePath+import Config.Type+import Util+import Prelude+++getSeverity :: String -> Maybe Severity+getSeverity "ignore" = Just Ignore+getSeverity "warn" = Just Warning+getSeverity "warning" = Just Warning+getSeverity "suggest" = Just Suggestion+getSeverity "suggestion" = Just Suggestion+getSeverity "error" = Just Error+getSeverity "hint" = Just Suggestion+getSeverity _ = Nothing+++addInfix = parseFlagsAddFixities $ infix_ (-1) ["==>"]+++---------------------------------------------------------------------+-- READ A SETTINGS FILE++-- Given a list of hint files to start from+-- Return the list of settings commands+readSettings2 :: FilePath -> [FilePath] -> [String] -> IO [Setting]+readSettings2 dataDir files hints = do+    (builtin,mods) <- fmap partitionEithers $ concatMapM (readHints dataDir) $ map Right files ++ map Left hints+    return $ map Builtin builtin ++ concatMap moduleSettings_ mods++moduleSettings_ :: Module_ -> [Setting]+moduleSettings_ m = concatMap (readSetting $ scopeCreate m) $ concatMap getEquations $+                       [AnnPragma l x | AnnModulePragma l x <- modulePragmas m] ++ moduleDecls m++-- | Given a module containing HLint settings information return the 'Classify' rules and the 'HintRule' expressions.+--   Any fixity declarations will be discarded, but any other unrecognised elements will result in an exception.+readSettings :: Module_ -> ([Classify], [HintRule])+readSettings m = ([x | SettingClassify x <- xs], [x | SettingMatchExp x <- xs])+    where xs = moduleSettings_ m+++readHints :: FilePath -> Either String FilePath -> IO [Either String Module_]+readHints datadir x = do+    (builtin,ms) <- case x of+        Left src -> findSettings datadir "CommandLine" (Just src)+        Right file -> findSettings datadir file Nothing+    return $ map Left builtin ++ map Right ms+++-- | Given the data directory (where the @hlint@ data files reside, see 'getHLintDataDir'),+--   and a filename to read, and optionally that file's contents, produce a pair containing:+--+-- 1. Builtin hints to use, e.g. @"List"@, which should be resolved using 'builtinHints'.+--+-- 1. A list of modules containing hints, suitable for processing with 'readSettings'.+--+--   Any parse failures will result in an exception.+findSettings :: FilePath -> FilePath -> Maybe String -> IO ([String], [Module_])+findSettings dataDir file contents = do+    let flags = addInfix defaultParseFlags+    res <- parseModuleEx flags file contents+    case res of+        Left (ParseError sl msg err) -> exitMessage $ "Parse failure at " ++ showSrcLoc sl ++ ": " ++ msg ++ "\n" ++ err+        Right (m, _) -> do+            ys <- sequence [f $ fromNamed $ importModule i | i <- moduleImports m, importPkg i `elem` [Just "hint", Just "hlint"]]+            return $ concatUnzip $ ([],[m]) : ys+    where+        f x | Just x <- "HLint.Builtin." `stripPrefix` x = return ([x],[])+            | Just x <- "HLint." `stripPrefix` x = findSettings dataDir (dataDir </> x <.> "hs") Nothing+            | otherwise = findSettings dataDir (x <.> "hs") Nothing+++readSetting :: Scope -> Decl_ -> [Setting]+readSetting s (FunBind _ [Match _ (Ident _ (getSeverity -> Just severity)) pats (UnGuardedRhs _ bod) bind])+    | InfixApp _ lhs op rhs <- bod, opExp op ~= "==>" =+        let (a,b) = readSide $ childrenBi bind in+        [SettingMatchExp $ HintRule severity (head $ snoc names defaultHintName) s (fromParen lhs) (fromParen rhs) a b]+    | otherwise = [SettingClassify $ Classify severity n a b | n <- names2, (a,b) <- readFuncs bod]+    where+        names = filter (not . null) $ getNames pats bod+        names2 = ["" | null names] ++ names++readSetting s x | "test" `isPrefixOf` map toLower (fromNamed x) = []+readSetting s (AnnPragma _ x) | Just y <- readPragma x = [SettingClassify y]+readSetting s (PatBind an (PVar _ name) bod bind) = readSetting s $ FunBind an [Match an name [] bod bind]+readSetting s (FunBind an xs) | length xs /= 1 = concatMap (readSetting s . FunBind an . return) xs+readSetting s (SpliceDecl an (App _ (Var _ x) (Lit _ y))) = readSetting s $ FunBind an [Match an (toNamed $ fromNamed x) [PLit an (Signless an) y] (UnGuardedRhs an $ Lit an $ String an "" "") Nothing]+readSetting s x@InfixDecl{} = map Infix $ getFixity x+readSetting s x = errorOn x "bad hint"+++-- return Nothing if it is not an HLint pragma, otherwise all the settings+readPragma :: Annotation S -> Maybe Classify+readPragma o = case o of+    Ann _ name x -> f (fromNamed name) x+    TypeAnn _ name x -> f (fromNamed name) x+    ModuleAnn _ x -> f "" x+    where+        f name (Lit _ (String _ s _)) | "hlint:" `isPrefixOf` map toLower s =+                case getSeverity a of+                    Nothing -> errorOn o "bad classify pragma"+                    Just severity -> Just $ Classify severity (trimStart b) "" name+            where (a,b) = break isSpace $ trimStart $ drop 6 s+        f name (Paren _ x) = f name x+        f name (ExpTypeSig _ x _) = f name x+        f _ _ = Nothing+++readSide :: [Decl_] -> (Maybe Exp_, [Note])+readSide = foldl f (Nothing,[])+    where f (Nothing,notes) (PatBind _ PWildCard{} (UnGuardedRhs _ side) Nothing) = (Just side, notes)+          f (Nothing,notes) (PatBind _ (fromNamed -> "side") (UnGuardedRhs _ side) Nothing) = (Just side, notes)+          f (side,[]) (PatBind _ (fromNamed -> "note") (UnGuardedRhs _ note) Nothing) = (side,g note)+          f _ x = errorOn x "bad side condition"++          g (Lit _ (String _ x _)) = [Note x]+          g (List _ xs) = concatMap g xs+          g x = case fromApps x of+              [con -> Just "IncreasesLaziness"] -> [IncreasesLaziness]+              [con -> Just "DecreasesLaziness"] -> [DecreasesLaziness]+              [con -> Just "RemovesError",fromString -> Just a] -> [RemovesError a]+              [con -> Just "ValidInstance",fromString -> Just a,var -> Just b] -> [ValidInstance a b]+              _ -> errorOn x "bad note"++          con :: Exp_ -> Maybe String+          con c@Con{} = Just $ prettyPrint c; con _ = Nothing+          var c@Var{} = Just $ prettyPrint c; var _ = Nothing+++-- Note: Foo may be ("","Foo") or ("Foo",""), return both+readFuncs :: Exp_ -> [(String, String)]+readFuncs (App _ x y) = readFuncs x ++ readFuncs y+readFuncs (Lit _ (String _ "" _)) = [("","")]+readFuncs (Var _ (UnQual _ name)) = [("",fromNamed name)]+readFuncs (Var _ (Qual _ (ModuleName _ mod) name)) = [(mod, fromNamed name)]+readFuncs (Con _ (UnQual _ name)) = [(fromNamed name,""),("",fromNamed name)]+readFuncs (Con _ (Qual _ (ModuleName _ mod) name)) = [(mod ++ "." ++ fromNamed name,""),(mod,fromNamed name)]+readFuncs x = errorOn x "bad classification rule"+++getNames :: [Pat_] -> Exp_ -> [String]+getNames ps _ | ps /= [], Just ps <- mapM fromPString ps = ps+getNames [] (InfixApp _ lhs op rhs) | opExp op ~= "==>" = map ("Use "++) names+    where+        lnames = map f $ childrenS lhs+        rnames = map f $ childrenS rhs+        names = filter (not . isUnifyVar) $ (rnames \\ lnames) ++ rnames+        f (Ident _ x) = x+        f (Symbol _ x) = x+getNames _ _ = []+++errorOn :: (Annotated ast, Pretty (ast S)) => ast S -> String -> b+errorOn val msg = exitMessage $+    showSrcLoc (getPointLoc $ ann val) +++    ": Error while reading hint file, " ++ msg ++ "\n" +++    prettyPrint val+++---------------------------------------------------------------------+-- FIND SETTINGS IN A SOURCE FILE++-- find definitions in a source file+findSettings2 :: ParseFlags -> FilePath -> IO (String, [Setting])+findSettings2 flags file = do+    x <- parseModuleEx flags file Nothing+    case x of+        Left (ParseError sl msg _) ->+            return ("-- Parse error " ++ showSrcLoc sl ++ ": " ++ msg, [])+        Right (m, _) -> do+            let xs = concatMap (findSetting $ UnQual an) (moduleDecls m)+                s = unlines $ ["-- hints found in " ++ file] ++ map prettyPrint xs ++ ["-- no hints found" | null xs]+                r = concatMap (readSetting mempty) xs+            return (s,r)+++findSetting :: (Name S -> QName S) -> Decl_ -> [Decl_]+findSetting qual (InstDecl _ _ _ (Just xs)) = concatMap (findSetting qual) [x | InsDecl _ x <- xs]+findSetting qual (PatBind _ (PVar _ name) (UnGuardedRhs _ bod) Nothing) = findExp (qual name) [] bod+findSetting qual (FunBind _ [InfixMatch _ p1 name ps rhs bind]) = findSetting qual $ FunBind an [Match an name (p1:ps) rhs bind]+findSetting qual (FunBind _ [Match _ name ps (UnGuardedRhs _ bod) Nothing]) = findExp (qual name) [] $ Lambda an ps bod+findSetting _ x@InfixDecl{} = [x]+findSetting _ _ = []+++-- given a result function name, a list of variables, a body expression, give some hints+findExp :: QName S -> [String] -> Exp_ -> [Decl_]+findExp name vs (Lambda _ ps bod) | length ps2 == length ps = findExp name (vs++ps2) bod+                                  | otherwise = []+    where ps2 = [x | PVar_ x <- map view ps]+findExp name vs Var{} = []+findExp name vs (InfixApp _ x dot y) | isDot dot = findExp name (vs++["_hlint"]) $ App an x $ Paren an $ App an y (toNamed "_hlint")++findExp name vs bod = [PatBind an (toNamed "warn") (UnGuardedRhs an $ InfixApp an lhs (toNamed "==>") rhs) Nothing]+    where+        lhs = g $ transform f bod+        rhs = apps $ Var an name : map snd rep++        rep = zip vs $ map (toNamed . return) ['a'..]+        f xx | Var_ x <- view xx, Just y <- lookup x rep = y+        f (InfixApp _ x dol y) | isDol dol = App an x (paren y)+        f x = x++        g o@(InfixApp _ _ _ x) | isAnyApp x || isAtom x = o+        g o@App{} = o+        g o = paren o
+ src/Config/Type.hs view
@@ -0,0 +1,85 @@++module Config.Type(+    Severity(..), Classify(..), HintRule(..), Note(..), Setting(..),+    defaultHintName, isUnifyVar, showNotes,+    ) where++import HSE.All+import Data.Char+import Data.List.Extra+import Prelude+++defaultHintName :: String+defaultHintName = "Use alternative"+++-- | How severe an issue is.+data Severity+    = Ignore -- ^ The issue has been explicitly ignored and will usually be hidden (pass @--show@ on the command line to see ignored ideas).+    | Suggestion -- ^ Suggestions are things that some people may consider improvements, but some may not.+    | Warning -- ^ Warnings are suggestions that are nearly always a good idea to apply.+    | Error -- ^ Available as a setting for the user.+      deriving (Eq,Ord,Show,Read,Bounded,Enum)+++-- Any 1-letter variable names are assumed to be unification variables+isUnifyVar :: String -> Bool+isUnifyVar [x] = x == '?' || isAlpha x+isUnifyVar _ = False+++---------------------------------------------------------------------+-- TYPE++-- | A note describing the impact of the replacement.+data Note+    = IncreasesLaziness -- ^ The replacement is increases laziness, for example replacing @reverse (reverse x)@ with @x@ makes the code lazier.+    | DecreasesLaziness -- ^ The replacement is decreases laziness, for example replacing @(fst x, snd x)@ with @x@ makes the code stricter.+    | RemovesError String -- ^ The replacement removes errors, for example replacing @foldr1 (+)@ with @sum@ removes an error on @[]@, and might contain the text @\"on []\"@.+    | ValidInstance String String -- ^ The replacement assumes standard type class lemmas, a hint with the note @ValidInstance \"Eq\" \"x\"@ might only be valid if+                                  --   the @x@ variable has a reflexive @Eq@ instance.+    | Note String -- ^ An arbitrary note.+      deriving (Eq,Ord)++instance Show Note where+    show IncreasesLaziness = "increases laziness"+    show DecreasesLaziness = "decreases laziness"+    show (RemovesError x) = "removes error " ++ x+    show (ValidInstance x y) = "requires a valid " ++ x ++ " instance for " ++ y+    show (Note x) = x+++showNotes :: [Note] -> String+showNotes = intercalate ", " . map show . filter use+    where use ValidInstance{} = False -- Not important enough to tell an end user+          use _ = True++-- | How to classify an 'Idea'. If any matching field is @\"\"@ then it matches everything.+data Classify = Classify+    {classifySeverity :: Severity -- ^ Severity to set the 'Idea' to.+    ,classifyHint :: String -- ^ Match on 'Idea' field 'ideaHint'.+    ,classifyModule :: String -- ^ Match on 'Idea' field 'ideaModule'.+    ,classifyDecl :: String -- ^ Match on 'Idea' field 'ideaDecl'.+    }+    deriving Show++-- | A @LHS ==> RHS@ style hint rule.+data HintRule = HintRule+    {hintRuleSeverity :: Severity -- ^ Default severity for the hint.+    ,hintRuleName :: String -- ^ Name for the hint.+    ,hintRuleScope :: Scope -- ^ Module scope in which the hint operates.+    ,hintRuleLHS :: Exp_ -- ^ LHS+    ,hintRuleRHS :: Exp_ -- ^ RHS+    ,hintRuleSide :: Maybe Exp_ -- ^ Side condition, typically specified with @where _ = ...@.+    ,hintRuleNotes :: [Note] -- ^ Notes about application of the hint.+    }+    deriving Show++data Setting+    = SettingClassify Classify+    | SettingMatchExp HintRule+    | Builtin String -- use a builtin hint set+    | Infix Fixity+      deriving Show+
src/HLint.hs view
@@ -6,8 +6,10 @@ import Control.Applicative import Control.Monad.Extra import Control.Exception+import Control.Concurrent.Extra import System.Console.CmdArgs.Verbosity import Data.List+import GHC.Conc import System.Exit import System.IO.Extra import Data.Tuple.Extra@@ -20,7 +22,8 @@ import Text.ParserCombinators.ReadP  import CmdLine-import Settings+import Config.Type+import Config.Haskell import Report import Idea import Apply@@ -111,8 +114,8 @@ 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+      then error "No files found"+      else runHints cmd{cmdFiles=files} flags  {-# ANN readAllSettings "HLint: ignore Use let" #-} readAllSettings :: Cmd -> ParseFlags -> IO [Setting]@@ -126,86 +129,88 @@  runHints :: Cmd -> ParseFlags -> IO [Idea] runHints cmd@CmdMain{..} flags = do-    let outStrLn = whenNormal . putStrLn-    settings <- readAllSettings cmd flags-    ideas <- getIdeas cmd settings flags-    let (showideas,hideideas) = partition (\i -> cmdShowAll || ideaSeverity i /= Ignore) ideas-    if cmdJson then-        putStrLn $ showIdeasJson showideas-     else if cmdSerialise then do-        hSetBuffering stdout NoBuffering-        print $ map (show &&& ideaRefactoring) showideas-     else if cmdRefactor then-        handleRefactoring showideas cmdFiles cmd-     else do-        usecolour <- cmdUseColour cmd-        showItem <- if usecolour then showANSI else return show-        mapM_ (outStrLn . showItem) showideas-        handleReporting showideas hideideas cmd-    return showideas+    j <- if cmdThreads == 0 then getNumProcessors else return cmdThreads+    withNumCapabilities j $ do+        let outStrLn = whenNormal . putStrLn+        settings <- readAllSettings cmd flags+        ideas <- getIdeas cmd settings flags+        let (showideas,hideideas) = partition (\i -> cmdShowAll || ideaSeverity i /= Ignore) ideas+        if cmdJson then+            putStrLn $ showIdeasJson showideas+         else if cmdSerialise then do+            hSetBuffering stdout NoBuffering+            print $ map (show &&& ideaRefactoring) showideas+         else if cmdRefactor then+            handleRefactoring showideas cmdFiles cmd+         else do+            usecolour <- cmdUseColour cmd+            showItem <- if usecolour then showANSI else return show+            mapM_ (outStrLn . showItem) showideas+            handleReporting showideas hideideas cmd+        return showideas  getIdeas :: Cmd -> [Setting] -> ParseFlags -> IO [Idea] getIdeas cmd@CmdMain{..} settings flags = do-  ideas <- if cmdCross-    then applyHintFiles flags settings cmdFiles-    else concat <$> parallel [evaluateList =<< applyHintFile flags settings x Nothing | x <- cmdFiles]-  return $ if not (null cmdOnly)-    then [i | i <- ideas, ideaHint i `elem` cmdOnly]-    else ideas+    ideas <- if cmdCross+        then applyHintFiles flags settings cmdFiles+        else concat <$> parallel cmdThreads [evaluateList =<< applyHintFile flags settings x Nothing | x <- cmdFiles]+    return $ if not (null cmdOnly)+        then [i | i <- ideas, ideaHint i `elem` cmdOnly]+        else ideas  handleRefactoring :: [Idea] -> [String] -> Cmd -> IO () handleRefactoring showideas files cmd@CmdMain{..} =-  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 (show &&& ideaRefactoring) 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"+    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 (show &&& ideaRefactoring) showideas+            withTempFile $ \f -> do+                writeFile f hints+                exitWith =<< runRefactoring path file f cmdRefactorOptions+        _ -> error "Refactor flag can only be used with an individual file"   handleReporting :: [Idea] -> [Idea] -> Cmd -> IO () handleReporting showideas hideideas cmd@CmdMain{..} = do-  let outStrLn = whenNormal . putStrLn-  if null showideas then-    when (cmdReports /= []) $ outStrLn "Skipping writing reports"-  else-    forM_ cmdReports $ \x -> do-      outStrLn $ "Writing report to " ++ x ++ " ..."-      writeReport cmdDataDir x showideas-  unless cmdNoSummary $-    outStrLn $-      (let i = length showideas in if i == 0 then "No hints" else show i ++ " hint" ++ ['s' | i/=1]) ++-      (let i = length hideideas in if i == 0 then "" else " (" ++ show i ++ " ignored)")+    let outStrLn = whenNormal . putStrLn+    if null showideas then+        when (cmdReports /= []) $ outStrLn "Skipping writing reports"+     else+        forM_ cmdReports $ \x -> do+            outStrLn $ "Writing report to " ++ x ++ " ..."+            writeReport cmdDataDir x showideas+    unless cmdNoSummary $+        outStrLn $+            (let i = length showideas in if i == 0 then "No hints" else show i ++ " hint" ++ ['s' | i/=1]) +++            (let i = length hideideas in if i == 0 then "" else " (" ++ show i ++ " ignored)")  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-  try $ hSetBuffering stdin LineBuffering :: IO (Either IOException ())-  hSetBuffering stdout LineBuffering-  -- Propagate the exit code from the spawn process-  waitForProcess phand+    let args = [fin, "-v0"] ++ words opts ++ ["--refact-file", hints]+    (_, _, _, phand) <- createProcess $ proc rpath args+    try $ hSetBuffering stdin LineBuffering :: IO (Either IOException ())+    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 ]+    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+evaluateList xs = do+    evaluate $ length xs+    return xs
src/HSE/All.hs view
@@ -8,7 +8,7 @@     ) where  import HSE.Util as X-import HSE.Evaluate as X+import HSE.Reduce as X import HSE.Type as X import HSE.Bracket as X import HSE.Match as X@@ -73,7 +73,7 @@  -- | Parse a Haskell module. Applies the C pre processor, and uses best-guess fixity resolution if there are ambiguities. --   The filename @-@ is treated as @stdin@. Requires some flags (often 'defaultParseFlags'), the filename, and optionally the contents of that file.-parseModuleEx :: ParseFlags -> FilePath -> Maybe String -> IO (Either ParseError (Module SrcSpanInfo, [Comment]))+parseModuleEx :: ParseFlags -> FilePath -> Maybe String -> IO (Either ParseError (Module_, [Comment])) parseModuleEx flags file str = do         str <- maybe (readFileEncoding' (encoding flags) file) return str         str <- return $ fromMaybe str $ stripPrefix "\65279" str -- remove the BOM if it exists, see #130
− src/HSE/Evaluate.hs
@@ -1,28 +0,0 @@-{-# LANGUAGE ViewPatterns #-}---- Evaluate a HSE Exp as much as possible-module HSE.Evaluate(evaluate) where--import HSE.Match-import HSE.Util-import HSE.Type-import HSE.Bracket---evaluate :: Exp_ -> Exp_-evaluate = fromParen . transform evaluate1---evaluate1 :: Exp_ -> Exp_-evaluate1 (App s len (Lit _ (String _ xs _))) | len ~= "length" = Lit s $ Int s n (show n)-    where n = fromIntegral $ length xs-evaluate1 (App s len (List _ xs)) | len ~= "length" = Lit s $ Int s n (show n)-    where n = fromIntegral $ length xs-evaluate1 (view -> App2 op (Lit _ x) (Lit _ y)) | op ~= "==" = toNamed $ show $ x =~= y-evaluate1 (view -> App2 op (Lit _ (Int _ x _)) (Lit _ (Int _ y _)))-    | op ~= ">=" = toNamed $ show $ x >= y-evaluate1 (view -> App2 op x y)-    | op ~= "&&" && x ~= "True"  = y-    | op ~= "&&" && x ~= "False" = x-evaluate1 (Paren _ x) | isAtom x = x-evaluate1 x = x
+ src/HSE/Reduce.hs view
@@ -0,0 +1,28 @@+{-# LANGUAGE ViewPatterns #-}++-- Evaluate/reduce a HSE Exp as much as possible+module HSE.Reduce(reduce) where++import HSE.Match+import HSE.Util+import HSE.Type+import HSE.Bracket+++reduce :: Exp_ -> Exp_+reduce = fromParen . transform reduce1+++reduce1 :: Exp_ -> Exp_+reduce1 (App s len (Lit _ (String _ xs _))) | len ~= "length" = Lit s $ Int s n (show n)+    where n = fromIntegral $ length xs+reduce1 (App s len (List _ xs)) | len ~= "length" = Lit s $ Int s n (show n)+    where n = fromIntegral $ length xs+reduce1 (view -> App2 op (Lit _ x) (Lit _ y)) | op ~= "==" = toNamed $ show $ x =~= y+reduce1 (view -> App2 op (Lit _ (Int _ x _)) (Lit _ (Int _ y _)))+    | op ~= ">=" = toNamed $ show $ x >= y+reduce1 (view -> App2 op x y)+    | op ~= "&&" && x ~= "True"  = y+    | op ~= "&&" && x ~= "False" = x+reduce1 (Paren _ x) | isAtom x = x+reduce1 x = x
src/HSE/Scope.hs view
@@ -40,7 +40,7 @@     mappend (Scope xs) (Scope ys) = Scope $ xs ++ ys  -- | Create a 'Scope' value from a module, based on the modules imports.-scopeCreate :: Module SrcSpanInfo -> Scope+scopeCreate :: Module_ -> Scope scopeCreate xs = Scope $ [prelude | not $ any isPrelude res] ++ res     where         res = [x | x <- moduleImports xs, importPkg x /= Just "hint"]@@ -55,7 +55,7 @@  -- | Given a two names in scopes, could they possibly refer to the same thing. --   This property is reflexive.-scopeMatch :: (Scope, QName SrcSpanInfo) -> (Scope, QName SrcSpanInfo) -> Bool+scopeMatch :: (Scope, QName S) -> (Scope, QName S) -> Bool scopeMatch (a, x@Special{}) (b, y@Special{}) = x =~= y scopeMatch (a, x) (b, y) | isSpecial x || isSpecial y = False scopeMatch (a, x) (b, y) = unqual x =~= unqual y && not (null $ possModules a x `intersect` possModules b y)@@ -63,7 +63,7 @@  -- | Given a name in a scope, and a new scope, create a name for the new scope that will refer --   to the same thing. If the resulting name is ambiguous, it picks a plausible candidate.-scopeMove :: (Scope, QName SrcSpanInfo) -> Scope -> QName SrcSpanInfo+scopeMove :: (Scope, QName S) -> Scope -> QName S scopeMove (a, x) (Scope b)     | isSpecial x = x     | null imps = head $ real ++ [x]
src/HSE/Util.hs view
@@ -70,6 +70,10 @@ fromTyParen (TyParen _ x) = fromTyParen x fromTyParen x = x +fromTyBang :: Type s -> Type s+fromTyBang (TyBang _ _ _ x) = x+fromTyBang x = x+ fromDeriving :: Deriving s -> [InstRule s] fromDeriving (Deriving _ x) = x @@ -103,6 +107,8 @@ isNewType NewType{} = True; isNewType _ = False isRecStmt RecStmt{} = True; isRecStmt _ = False isClsDefSig ClsDefSig{} = True; isClsDefSig _ = False+isTyBang TyBang{} = True; isTyBang _ = False+isLCase LCase{} = True; isLCase _ = False  isSection LeftSection{} = True isSection RightSection{} = True
src/Hint/All.hs view
@@ -5,7 +5,7 @@     ) where  import Data.Monoid-import Settings+import Config.Type import Data.Either import Data.List import Hint.Type
src/Hint/Extensions.hs view
@@ -73,6 +73,16 @@ foo = id -- {-# LANGUAGE TypeApplications #-} \ foo = id @Int+{-# LANGUAGE LambdaCase #-} \+foo = id --+{-# LANGUAGE LambdaCase #-} \+foo = \case () -> ()+{-# LANGUAGE NumDecimals #-} \+foo = 12.3e2+{-# LANGUAGE NumDecimals #-} \+foo = id --+{-# LANGUAGE NumDecimals #-} \+foo = 12.345e2 -- </TEST> -} @@ -82,6 +92,7 @@ import Hint.Type import Data.Maybe import Data.List.Extra+import Data.Ratio import Refact.Types  @@ -116,6 +127,7 @@  usedExt :: Extension -> Module_ -> Bool usedExt (EnableExtension x) = used x+usedExt (UnknownExtension "NumDecimals") = hasS isWholeFrac usedExt _ = const True  @@ -156,6 +168,7 @@ used DeriveTraversable = hasDerive ["Traversable"] used DeriveGeneric = hasDerive ["Generic","Generic1"] used GeneralizedNewtypeDeriving = any (`notElem` noNewtypeDeriving) . fst . derives+used LambdaCase = hasS isLCase used Arrows = hasS f     where f Proc{} = True           f LeftArrApp{} = True@@ -218,3 +231,8 @@  has f = any f . universeBi +-- Only whole number fractions are permitted by NumDecimals extension.+-- Anything not-whole raises an error.+isWholeFrac :: Literal S -> Bool+isWholeFrac (Frac _ v _) = denominator v == 1+isWholeFrac _ = False
src/Hint/Import.hs view
@@ -33,10 +33,6 @@ import Char(foo) -- import Data.Char(foo) import IO(foo) import IO as X -- import System.IO as X; import System.IO.Error as X; import Control.Exception  as X (bracket,bracket_)-module Foo(module A, baz, module B, module C) where; import A; import D; import B(map,filter); import C \-    -- module Foo(baz, module X) where; import A as X; import B as X(map, filter); import C as X-module Foo(module A, baz, module B, module X) where; import A; import B; import X \-    -- module Foo(baz, module Y) where; import A as Y; import B as Y; import X as Y </TEST> -} @@ -56,8 +52,7 @@ importHint :: ModuHint importHint _ x = concatMap (wrap . snd) (groupSort                  [((fromNamed $ importModule i,importPkg i),i) | i <- universeBi x, not $ importSrc i]) ++-                 concatMap (\x -> hierarchy x ++ reduce1 x) (universeBi x) ++-                 multiExport x+                 concatMap (\x -> hierarchy x ++ combine1 x) (universeBi x)   wrap :: [ImportDecl S] -> [Idea]@@ -75,13 +70,13 @@  simplifyHead :: ImportDecl S -> [ImportDecl S] -> Maybe ([ImportDecl S], [Refactoring R.SrcSpan]) simplifyHead x [] = Nothing-simplifyHead x (y:ys) = case reduce x y of+simplifyHead x (y:ys) = case combine x y of     Nothing -> first (y:) <$> simplifyHead x ys     Just (xy, rs) -> Just (xy : ys, rs)  -reduce :: ImportDecl S -> ImportDecl S -> Maybe (ImportDecl S, [Refactoring R.SrcSpan])-reduce x y | qual, as, specs = Just (x, [Delete Import (toSS y)])+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) ] )@@ -99,14 +94,14 @@         ass = mapMaybe importAs [x,y]         specs = importSpecs x `eqMaybe` importSpecs y -reduce _ _ = Nothing+combine _ _ = Nothing  -reduce1 :: ImportDecl S -> [Idea]-reduce1 i@ImportDecl{..}+combine1 :: ImportDecl S -> [Idea]+combine1 i@ImportDecl{..}     | Just (dropAnn importModule) == fmap dropAnn importAs     = [suggest "Redundant as" i i{importAs=Nothing} [RemoveAsKeyword (toSS i)]]-reduce1 _ = []+combine1 _ = []   newNames = let (*) = flip (,) in@@ -149,24 +144,3 @@ desugarQual :: ImportDecl S -> ImportDecl S desugarQual x | importQualified x && isNothing (importAs x) = x{importAs=Just (importModule x)}               | otherwise = x---multiExport :: Module S -> [Idea]-multiExport x =-    [ rawIdeaN Suggestion "Use import/export shortcut" (toSrcSpan $ ann hd)-        (unlines $ prettyPrint hd : map prettyPrint imps)-        (Just $ unlines $ prettyPrint newhd : map prettyPrint newimps)-        []-    | Module l (Just hd) _ imp _ <- [x]-    , let asNames = mapMaybe importAs imp-    , let expNames = [x | EModuleContents _ x <- childrenBi hd]-    , let imps = [i | i@ImportDecl{importAs=Nothing,importQualified=False,importModule=name} <- imp-                 ,name `notElem_` asNames, name `elem_` expNames]-    , length imps >= 3-    , let newname = ModuleName an $ head $ map return ("XYZ" ++ ['A'..]) \\-                                           [x | ModuleName (_ :: S) x <- universeBi hd ++ universeBi imp]-    , let reexport (EModuleContents _ x) = x `notElem_` map importModule imps-          reexport x = True-    , let newhd = descendBi (\xs -> filter reexport xs ++ [EModuleContents an newname]) hd-    , let newimps = [i{importAs=Just newname} | i <- imps]-    ]
src/Hint/Match.hs view
@@ -44,7 +44,7 @@ import Data.Maybe import Data.Data import Unsafe.Coerce-import Settings+import Config.Type import Hint.Type import Control.Monad import Data.Tuple.Extra@@ -165,7 +165,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 []+                  | Just (x :: S) <- cast x = Just []                   | otherwise = unifyDef nm x y  @@ -292,7 +292,7 @@  -- if it has _eval_ do evaluation on it performEval :: Exp_ -> Exp_-performEval (App _ e x) | e ~= "_eval_" = evaluate x+performEval (App _ e x) | e ~= "_eval_" = reduce x performEval x = x  
src/Hint/NewType.hs view
@@ -13,6 +13,8 @@ data Color a = Red a | Green a | Blue a data Pair a b = Pair a b data Foo = Bar+data X = Y {-# UNPACK #-} !Int -- newtype X = Y Int+data A = A {b :: !C} -- newtype A = A {b :: C} </TEST> -} module Hint.NewType (newtypeHint) where@@ -23,14 +25,18 @@ newtypeHint _ _ = newtypeHintDecl  newtypeHintDecl :: Decl_ -> [Idea]-newtypeHintDecl d@(DataDecl sp (DataType dtA) ctx dclH-                   [qcD@(QualConDecl _ tvb _ cd)] der) =-  case tvb of-    Just _  -> []-    Nothing -> case cd of-                 ConDecl _ _ [_] -> wrn-                 RecDecl _ _ [FieldDecl _ [_] _] -> wrn-                 _ -> []-  where suggestion = DataDecl sp (NewType dtA) ctx dclH [qcD] der-        wrn = [(suggestN "Use newtype instead of data" d suggestion){ideaNote = [DecreasesLaziness]}]+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 _ = []+++singleSimpleField :: Decl_ -> Maybe (DataOrNew S, Type_, DataOrNew S -> Type_ -> Decl_)+singleSimpleField (DataDecl x1 dt x2 x3 [QualConDecl y1 Nothing y2 ctor] x4)+    | Just (t, ft) <- f ctor = Just (dt, t, \dt t -> DataDecl x1 dt x2 x3 [QualConDecl y1 Nothing y2 $ ft t] x4)+    where+        f (ConDecl x1 x2 [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+singleSimpleField _ = Nothing
src/Hint/Pattern.hs view
@@ -139,7 +139,7 @@ asGuards x = [(toNamed "otherwise", x)]  -data Pattern = Pattern SrcSpanInfo R.RType [Pat_] (Rhs S) (Maybe (Binds S))+data Pattern = Pattern S R.RType [Pat_] (Rhs S) (Maybe (Binds S))  -- Invariant: Number of patterns may not change asPattern :: Decl_ -> [(Pattern, String -> Pattern -> [Refactoring R.SrcSpan] -> Idea)]
src/Hint/Type.hs view
@@ -13,9 +13,9 @@  -- | Functions to generate hints, combined using the 'Monoid' instance. data Hint = Hint-    {hintModules :: [(Scope, Module SrcSpanInfo)] -> [Idea] -- ^ Given a list of modules (and their scope information) generate some 'Idea's.-    ,hintModule :: Scope -> Module SrcSpanInfo -> [Idea] -- ^ Given a single module and its scope information generate some 'Idea's.-    ,hintDecl :: Scope -> Module SrcSpanInfo -> Decl SrcSpanInfo -> [Idea]+    {hintModules :: [(Scope, Module_)] -> [Idea] -- ^ Given a list of modules (and their scope information) generate some 'Idea's.+    ,hintModule :: Scope -> Module_ -> [Idea] -- ^ Given a single module and its scope information generate some 'Idea's.+    ,hintDecl :: Scope -> Module_ -> Decl_ -> [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 :: Comment -> [Idea] -- ^ Given a comment generate some 'Idea's.
src/Idea.hs view
@@ -6,7 +6,7 @@ import Data.Char import Numeric import HSE.All-import Settings+import Config.Type import HsColour import Refact.Types hiding (SrcSpan) import qualified Refact.Types as R
src/Language/Haskell/HLint.hs view
@@ -8,7 +8,7 @@ module Language.Haskell.HLint(hlint, Suggestion, suggestionLocation, suggestionSeverity, Severity(..)) where  import qualified HLint-import Settings+import Config.Type import Idea import HSE.All 
src/Language/Haskell/HLint2.hs view
@@ -28,7 +28,8 @@     Encoding, defaultEncoding, readEncoding, useEncoding     ) where -import Settings+import Config.Type+import Config.Haskell import Idea import Apply import Hint.Type
src/Language/Haskell/HLint3.hs view
@@ -29,7 +29,8 @@     parseModuleEx, defaultParseFlags, parseFlagsAddFixities, ParseError(..), ParseFlags(..), CppFlags(..)     ) where -import Settings hiding (findSettings)+import Config.Type+import Config.Haskell as Settings hiding (findSettings) import Idea import Apply import HLint
src/Parallel.hs view
@@ -11,14 +11,13 @@ module Parallel(parallel) where  import System.IO.Unsafe-import GHC.Conc(numCapabilities) import Control.Concurrent import Control.Exception import Control.Monad  -parallel :: [IO a] -> IO [a]-parallel = if numCapabilities <= 1 then parallel1 else parallelN+parallel :: Int -> [IO a] -> IO [a]+parallel j = if j <= 1 then parallel1 else parallelN j   parallel1 :: [IO a] -> IO [a]@@ -29,12 +28,12 @@     return $ x2:xs2  -parallelN :: [IO a] -> IO [a]-parallelN xs = do+parallelN :: Int -> [IO a] -> IO [a]+parallelN j xs = do     ms <- mapM (const newEmptyMVar) xs     chan <- newChan     mapM_ (writeChan chan . Just) $ zip ms xs-    replicateM_ numCapabilities (writeChan chan Nothing >> forkIO (f chan))+    replicateM_ j (writeChan chan Nothing >> forkIO (f chan))     let throwE x = throw (x :: SomeException)     parallel1 $ map (fmap (either throwE id) . takeMVar) ms     where
− src/Settings.hs
@@ -1,293 +0,0 @@-{-# LANGUAGE PatternGuards, ViewPatterns #-}--module Settings(-    Severity(..), Classify(..), HintRule(..), Note(..), showNotes, Setting(..),-    defaultHintName, isUnifyVar,-    findSettings, readSettings,-    readSettings2, readPragma, findSettings2, addInfix-    ) where--import Data.Monoid-import HSE.All-import Control.Monad.Extra-import Data.Char-import Data.Either-import Data.List.Extra-import System.FilePath-import Util-import Prelude---defaultHintName :: String-defaultHintName = "Use alternative"----- | How severe an issue is.-data Severity-    = Ignore -- ^ The issue has been explicitly ignored and will usually be hidden (pass @--show@ on the command line to see ignored ideas).-    | Suggestion -- ^ Suggestions are things that some people may consider improvements, but some may not.-    | Warning -- ^ Warnings are suggestions that are nearly always a good idea to apply.-    | Error -- ^ Available as a setting for the user.-      deriving (Eq,Ord,Show,Read,Bounded,Enum)--getSeverity :: String -> Maybe Severity-getSeverity "ignore" = Just Ignore-getSeverity "warn" = Just Warning-getSeverity "warning" = Just Warning-getSeverity "suggest" = Just Suggestion-getSeverity "suggestion" = Just Suggestion-getSeverity "error" = Just Error-getSeverity "hint" = Just Suggestion-getSeverity _ = Nothing----- Any 1-letter variable names are assumed to be unification variables-isUnifyVar :: String -> Bool-isUnifyVar [x] = x == '?' || isAlpha x-isUnifyVar _ = False---addInfix = parseFlagsAddFixities $ infix_ (-1) ["==>"]--------------------------------------------------------------------------- TYPE---- | A note describing the impact of the replacement.-data Note-    = IncreasesLaziness -- ^ The replacement is increases laziness, for example replacing @reverse (reverse x)@ with @x@ makes the code lazier.-    | DecreasesLaziness -- ^ The replacement is decreases laziness, for example replacing @(fst x, snd x)@ with @x@ makes the code stricter.-    | RemovesError String -- ^ The replacement removes errors, for example replacing @foldr1 (+)@ with @sum@ removes an error on @[]@, and might contain the text @\"on []\"@.-    | ValidInstance String String -- ^ The replacement assumes standard type class lemmas, a hint with the note @ValidInstance \"Eq\" \"x\"@ might only be valid if-                                  --   the @x@ variable has a reflexive @Eq@ instance.-    | Note String -- ^ An arbitrary note.-      deriving (Eq,Ord)--instance Show Note where-    show IncreasesLaziness = "increases laziness"-    show DecreasesLaziness = "decreases laziness"-    show (RemovesError x) = "removes error " ++ x-    show (ValidInstance x y) = "requires a valid " ++ x ++ " instance for " ++ y-    show (Note x) = x---showNotes :: [Note] -> String-showNotes = intercalate ", " . map show . filter use-    where use ValidInstance{} = False -- Not important enough to tell an end user-          use _ = True---- | How to classify an 'Idea'. If any matching field is @\"\"@ then it matches everything.-data Classify = Classify-    {classifySeverity :: Severity -- ^ Severity to set the 'Idea' to.-    ,classifyHint :: String -- ^ Match on 'Idea' field 'ideaHint'.-    ,classifyModule :: String -- ^ Match on 'Idea' field 'ideaModule'.-    ,classifyDecl :: String -- ^ Match on 'Idea' field 'ideaDecl'.-    }-    deriving Show---- | A @LHS ==> RHS@ style hint rule.-data HintRule = HintRule-    {hintRuleSeverity :: Severity -- ^ Default severity for the hint.-    ,hintRuleName :: String -- ^ Name for the hint.-    ,hintRuleScope :: Scope -- ^ Module scope in which the hint operates.-    ,hintRuleLHS :: Exp SrcSpanInfo -- ^ LHS-    ,hintRuleRHS :: Exp SrcSpanInfo -- ^ RHS-    ,hintRuleSide :: Maybe (Exp SrcSpanInfo) -- ^ Side condition, typically specified with @where _ = ...@.-    ,hintRuleNotes :: [Note] -- ^ Notes about application of the hint.-    }-    deriving Show--data Setting-    = SettingClassify Classify-    | SettingMatchExp HintRule-    | Builtin String -- use a builtin hint set-    | Infix Fixity-      deriving Show--------------------------------------------------------------------------- READ A SETTINGS FILE---- Given a list of hint files to start from--- Return the list of settings commands-readSettings2 :: FilePath -> [FilePath] -> [String] -> IO [Setting]-readSettings2 dataDir files hints = do-    (builtin,mods) <- fmap partitionEithers $ concatMapM (readHints dataDir) $ map Right files ++ map Left hints-    return $ map Builtin builtin ++ concatMap moduleSettings_ mods--moduleSettings_ :: Module SrcSpanInfo -> [Setting]-moduleSettings_ m = concatMap (readSetting $ scopeCreate m) $ concatMap getEquations $-                       [AnnPragma l x | AnnModulePragma l x <- modulePragmas m] ++ moduleDecls m---- | Given a module containing HLint settings information return the 'Classify' rules and the 'HintRule' expressions.---   Any fixity declarations will be discarded, but any other unrecognised elements will result in an exception.-readSettings :: Module SrcSpanInfo -> ([Classify], [HintRule])-readSettings m = ([x | SettingClassify x <- xs], [x | SettingMatchExp x <- xs])-    where xs = moduleSettings_ m---readHints :: FilePath -> Either String FilePath -> IO [Either String Module_]-readHints datadir x = do-    (builtin,ms) <- case x of-        Left src -> findSettings datadir "CommandLine" (Just src)-        Right file -> findSettings datadir file Nothing-    return $ map Left builtin ++ map Right ms----- | Given the data directory (where the @hlint@ data files reside, see 'getHLintDataDir'),---   and a filename to read, and optionally that file's contents, produce a pair containing:------ 1. Builtin hints to use, e.g. @"List"@, which should be resolved using 'builtinHints'.------ 1. A list of modules containing hints, suitable for processing with 'readSettings'.------   Any parse failures will result in an exception.-findSettings :: FilePath -> FilePath -> Maybe String -> IO ([String], [Module SrcSpanInfo])-findSettings dataDir file contents = do-    let flags = addInfix defaultParseFlags-    res <- parseModuleEx flags file contents-    case res of-        Left (ParseError sl msg err) -> exitMessage $ "Parse failure at " ++ showSrcLoc sl ++ ": " ++ msg ++ "\n" ++ err-        Right (m, _) -> do-            ys <- sequence [f $ fromNamed $ importModule i | i <- moduleImports m, importPkg i `elem` [Just "hint", Just "hlint"]]-            return $ concatUnzip $ ([],[m]) : ys-    where-        f x | Just x <- "HLint.Builtin." `stripPrefix` x = return ([x],[])-            | Just x <- "HLint." `stripPrefix` x = findSettings dataDir (dataDir </> x <.> "hs") Nothing-            | otherwise = findSettings dataDir (x <.> "hs") Nothing---readSetting :: Scope -> Decl_ -> [Setting]-readSetting s (FunBind _ [Match _ (Ident _ (getSeverity -> Just severity)) pats (UnGuardedRhs _ bod) bind])-    | InfixApp _ lhs op rhs <- bod, opExp op ~= "==>" =-        let (a,b) = readSide $ childrenBi bind in-        [SettingMatchExp $ HintRule severity (head $ snoc names defaultHintName) s (fromParen lhs) (fromParen rhs) a b]-    | otherwise = [SettingClassify $ Classify severity n a b | n <- names2, (a,b) <- readFuncs bod]-    where-        names = filter (not . null) $ getNames pats bod-        names2 = ["" | null names] ++ names--readSetting s x | "test" `isPrefixOf` map toLower (fromNamed x) = []-readSetting s (AnnPragma _ x) | Just y <- readPragma x = [SettingClassify y]-readSetting s (PatBind an (PVar _ name) bod bind) = readSetting s $ FunBind an [Match an name [] bod bind]-readSetting s (FunBind an xs) | length xs /= 1 = concatMap (readSetting s . FunBind an . return) xs-readSetting s (SpliceDecl an (App _ (Var _ x) (Lit _ y))) = readSetting s $ FunBind an [Match an (toNamed $ fromNamed x) [PLit an (Signless an) y] (UnGuardedRhs an $ Lit an $ String an "" "") Nothing]-readSetting s x@InfixDecl{} = map Infix $ getFixity x-readSetting s x = errorOn x "bad hint"----- return Nothing if it is not an HLint pragma, otherwise all the settings-readPragma :: Annotation S -> Maybe Classify-readPragma o = case o of-    Ann _ name x -> f (fromNamed name) x-    TypeAnn _ name x -> f (fromNamed name) x-    ModuleAnn _ x -> f "" x-    where-        f name (Lit _ (String _ s _)) | "hlint:" `isPrefixOf` map toLower s =-                case getSeverity a of-                    Nothing -> errorOn o "bad classify pragma"-                    Just severity -> Just $ Classify severity (trimStart b) "" name-            where (a,b) = break isSpace $ trimStart $ drop 6 s-        f name (Paren _ x) = f name x-        f name (ExpTypeSig _ x _) = f name x-        f _ _ = Nothing---readSide :: [Decl_] -> (Maybe Exp_, [Note])-readSide = foldl f (Nothing,[])-    where f (Nothing,notes) (PatBind _ PWildCard{} (UnGuardedRhs _ side) Nothing) = (Just side, notes)-          f (Nothing,notes) (PatBind _ (fromNamed -> "side") (UnGuardedRhs _ side) Nothing) = (Just side, notes)-          f (side,[]) (PatBind _ (fromNamed -> "note") (UnGuardedRhs _ note) Nothing) = (side,g note)-          f _ x = errorOn x "bad side condition"--          g (Lit _ (String _ x _)) = [Note x]-          g (List _ xs) = concatMap g xs-          g x = case fromApps x of-              [con -> Just "IncreasesLaziness"] -> [IncreasesLaziness]-              [con -> Just "DecreasesLaziness"] -> [DecreasesLaziness]-              [con -> Just "RemovesError",fromString -> Just a] -> [RemovesError a]-              [con -> Just "ValidInstance",fromString -> Just a,var -> Just b] -> [ValidInstance a b]-              _ -> errorOn x "bad note"--          con :: Exp_ -> Maybe String-          con c@Con{} = Just $ prettyPrint c; con _ = Nothing-          var c@Var{} = Just $ prettyPrint c; var _ = Nothing----- Note: Foo may be ("","Foo") or ("Foo",""), return both-readFuncs :: Exp_ -> [(String, String)]-readFuncs (App _ x y) = readFuncs x ++ readFuncs y-readFuncs (Lit _ (String _ "" _)) = [("","")]-readFuncs (Var _ (UnQual _ name)) = [("",fromNamed name)]-readFuncs (Var _ (Qual _ (ModuleName _ mod) name)) = [(mod, fromNamed name)]-readFuncs (Con _ (UnQual _ name)) = [(fromNamed name,""),("",fromNamed name)]-readFuncs (Con _ (Qual _ (ModuleName _ mod) name)) = [(mod ++ "." ++ fromNamed name,""),(mod,fromNamed name)]-readFuncs x = errorOn x "bad classification rule"---getNames :: [Pat_] -> Exp_ -> [String]-getNames ps _ | ps /= [], Just ps <- mapM fromPString ps = ps-getNames [] (InfixApp _ lhs op rhs) | opExp op ~= "==>" = map ("Use "++) names-    where-        lnames = map f $ childrenS lhs-        rnames = map f $ childrenS rhs-        names = filter (not . isUnifyVar) $ (rnames \\ lnames) ++ rnames-        f (Ident _ x) = x-        f (Symbol _ x) = x-getNames _ _ = []---errorOn :: (Annotated ast, Pretty (ast S)) => ast S -> String -> b-errorOn val msg = exitMessage $-    showSrcLoc (getPointLoc $ ann val) ++-    ": Error while reading hint file, " ++ msg ++ "\n" ++-    prettyPrint val--------------------------------------------------------------------------- FIND SETTINGS IN A SOURCE FILE---- find definitions in a source file-findSettings2 :: ParseFlags -> FilePath -> IO (String, [Setting])-findSettings2 flags file = do-    x <- parseModuleEx flags file Nothing-    case x of-        Left (ParseError sl msg _) ->-            return ("-- Parse error " ++ showSrcLoc sl ++ ": " ++ msg, [])-        Right (m, _) -> do-            let xs = concatMap (findSetting $ UnQual an) (moduleDecls m)-                s = unlines $ ["-- hints found in " ++ file] ++ map prettyPrint xs ++ ["-- no hints found" | null xs]-                r = concatMap (readSetting mempty) xs-            return (s,r)---findSetting :: (Name S -> QName S) -> Decl_ -> [Decl_]-findSetting qual (InstDecl _ _ _ (Just xs)) = concatMap (findSetting qual) [x | InsDecl _ x <- xs]-findSetting qual (PatBind _ (PVar _ name) (UnGuardedRhs _ bod) Nothing) = findExp (qual name) [] bod-findSetting qual (FunBind _ [InfixMatch _ p1 name ps rhs bind]) = findSetting qual $ FunBind an [Match an name (p1:ps) rhs bind]-findSetting qual (FunBind _ [Match _ name ps (UnGuardedRhs _ bod) Nothing]) = findExp (qual name) [] $ Lambda an ps bod-findSetting _ x@InfixDecl{} = [x]-findSetting _ _ = []----- given a result function name, a list of variables, a body expression, give some hints-findExp :: QName S -> [String] -> Exp_ -> [Decl_]-findExp name vs (Lambda _ ps bod) | length ps2 == length ps = findExp name (vs++ps2) bod-                                  | otherwise = []-    where ps2 = [x | PVar_ x <- map view ps]-findExp name vs Var{} = []-findExp name vs (InfixApp _ x dot y) | isDot dot = findExp name (vs++["_hlint"]) $ App an x $ Paren an $ App an y (toNamed "_hlint")--findExp name vs bod = [PatBind an (toNamed "warn") (UnGuardedRhs an $ InfixApp an lhs (toNamed "==>") rhs) Nothing]-    where-        lhs = g $ transform f bod-        rhs = apps $ Var an name : map snd rep--        rep = zip vs $ map (toNamed . return) ['a'..]-        f xx | Var_ x <- view xx, Just y <- lookup x rep = y-        f (InfixApp _ x dol y) | isDol dol = App an x (paren y)-        f x = x--        g o@(InfixApp _ _ _ x) | isAnyApp x || isAtom x = o-        g o@App{} = o-        g o = paren o
src/Test/All.hs view
@@ -10,7 +10,8 @@ import Data.Functor import Prelude -import Settings+import Config.Type+import Config.Haskell import CmdLine import HSE.All import Hint.All
src/Test/Annotations.hs view
@@ -9,7 +9,7 @@ import Data.Maybe import Data.Function -import Settings+import Config.Type import Idea import Apply import HSE.All
src/Test/Proof.hs view
@@ -12,7 +12,7 @@ import Data.Maybe import Data.Function import System.FilePath-import Settings+import Config.Type import HSE.All import Prelude @@ -79,7 +79,7 @@ classifyMissing Theorem{original = Just HintRule{..}}     | _:_ <- [v :: Exp_ | v@Case{} <- universeBi (hintRuleLHS,hintRuleRHS)] = "case"     | _:_ <- [v :: Exp_ | v@ListComp{} <- universeBi (hintRuleLHS,hintRuleRHS)] = "list-comp"-    | v:_ <- mapMaybe (`lookup` missingFuncs) [prettyPrint (v :: Name SrcSpanInfo) | v <- universeBi (hintRuleLHS,hintRuleRHS)] = v+    | v:_ <- mapMaybe (`lookup` missingFuncs) [prettyPrint (v :: Name S) | v <- universeBi (hintRuleLHS,hintRuleRHS)] = v classifyMissing _ = "?unknown"  
src/Test/Translate.hs view
@@ -11,7 +11,7 @@ import System.FilePath  import Paths_hlint-import Settings+import Config.Type import HSE.All import Test.Util