hlint 2.2.10 → 2.2.11
raw patch · 12 files changed
+76/−37 lines, 12 filesdep +ghc-bootdep ~ghc-lib-parser-exPVP ok
version bump matches the API change (PVP)
Dependencies added: ghc-boot
Dependency ranges changed: ghc-lib-parser-ex
API changes (from Hackage documentation)
Files
- CHANGES.txt +9/−0
- data/hlint.yaml +16/−5
- hlint.cabal +4/−3
- src/GHC/Util/FreeVars.hs +1/−1
- src/GHC/Util/HsExpr.hs +23/−12
- src/GHC/Util/Unify.hs +4/−1
- src/HSE/Util.hs +1/−0
- src/Hint/Import.hs +3/−2
- src/Hint/Match.hs +6/−6
- src/Hint/Monad.hs +2/−2
- src/Hint/Pragma.hs +5/−4
- src/Report.hs +2/−1
CHANGES.txt view
@@ -1,5 +1,14 @@ Changelog for HLint (* = breaking change) +2.2.11, released 2020-02-09+ #868, fix some brackets in refactoring suggestions+ #865, suggest biList if generalise-for-conciseness is turned on+ #859, suggest regular if instead of a simple multi-way if+ #860, improve the sortBy/sortOn hints+ #862, only suggest TupleSections for 2-tuples once+ #854, add more generalise-for-conciseness hints for Either/Maybe+ #852, change maybe to fromMaybe, when the function is duplicated+ #851, add a rule for maybe Nothing Just 2.2.10, released 2020-02-02 #846, add splitAt warnings #774, don't warn about 'Redundant compare' in == and /=
data/hlint.yaml view
@@ -99,8 +99,8 @@ - warn: {lhs: head (sortBy f x), rhs: minimumBy f x, side: isCompare f} - warn: {lhs: last (sortBy f x), rhs: maximumBy f x, side: isCompare f} - warn: {lhs: reverse (sortBy f x), rhs: sortBy (flip f) x, name: Avoid reverse, side: isCompare f, note: Stabilizes sort order}- - warn: {lhs: sortBy (comparing (flip f)), rhs: sortOn (Down . f)}- - warn: {lhs: sortBy (comparing f), rhs: sortOn f}+ - warn: {lhs: sortBy (flip (comparing f)), rhs: sortOn (Down . f)}+ - warn: {lhs: sortBy (comparing f), rhs: sortOn f, side: notEq f fst && notEq f snd} - warn: {lhs: reverse (sortOn f x), rhs: sortOn (Data.Ord.Down . f) x, name: Avoid reverse, note: Stabilizes sort order} - warn: {lhs: reverse (sort x), rhs: sortOn Data.Ord.Down x, name: Avoid reverse, note: Stabilizes sort order} - hint: {lhs: flip (g `on` h), rhs: flip g `on` h, name: Move flip}@@ -303,8 +303,6 @@ - warn: {lhs: x . id, rhs: x, name: Redundant id} - warn: {lhs: "((,) x)", rhs: "(_noParen_ x,)", name: Use tuple-section, note: RequiresExtension TupleSections} - warn: {lhs: "flip (,) x", rhs: "(,_noParen_ x)", name: Use tuple-section, note: RequiresExtension TupleSections}- - warn: {lhs: "\\y -> (x,y)", rhs: "(x,)", name: Use tuple-section, note: RequiresExtension TupleSections}- - warn: {lhs: "\\y -> (y,x)", rhs: "(,x)", name: Use tuple-section, note: RequiresExtension TupleSections} # CHAR @@ -332,6 +330,7 @@ - warn: {lhs: if a then (if b then t else f) else f, rhs: if a && b then t else f, name: Redundant if} - warn: {lhs: if x then True else y, rhs: x || y, side: notEq y False, name: Redundant if} - warn: {lhs: if x then y else False, rhs: x && y, side: notEq y True, name: Redundant if}+ - warn: {lhs: if | b -> t | otherwise -> f, rhs: if b then t else f, name: Redundant multi-way if} - hint: {lhs: "case a of {True -> t; False -> f}", rhs: if a then t else f, name: Use if} - hint: {lhs: "case a of {False -> f; True -> t}", rhs: if a then t else f, name: Use if} - hint: {lhs: "case a of {True -> t; _ -> f}", rhs: if a then t else f, name: Use if}@@ -516,6 +515,7 @@ # MAYBE - warn: {lhs: maybe x id, rhs: Data.Maybe.fromMaybe x}+ - warn: {lhs: maybe Nothing Just, rhs: id, name: Redundant maybe} - warn: {lhs: maybe False (const True), rhs: Data.Maybe.isJust} - warn: {lhs: maybe True (const False), rhs: Data.Maybe.isNothing} - warn: {lhs: maybe False (== x), rhs: (== Just x)}@@ -552,6 +552,7 @@ - hint: {lhs: maybe Nothing id, rhs: join} - hint: {lhs: maybe Nothing f x, rhs: f =<< x} - hint: {lhs: maybe (f x) (f . g), rhs: f . maybe x g, note: IncreasesLaziness, name: Too strict maybe}+ - hint: {lhs: maybe (f x) f y, rhs: f (Data.Maybe.fromMaybe x y), note: IncreasesLaziness, name: Too strict maybe} - warn: {lhs: maybe x f (fmap g y), rhs: maybe x (f . g) y, name: Redundant fmap} - warn: {lhs: isJust (fmap f x), rhs: isJust x} - warn: {lhs: isNothing (fmap f x), rhs: isNothing x}@@ -812,13 +813,23 @@ - warn: {lhs: Data.Maybe.fromMaybe mempty, rhs: Data.Foldable.fold} - warn: {lhs: Data.Maybe.fromMaybe False, rhs: or} - warn: {lhs: Data.Maybe.fromMaybe True, rhs: and}+ - warn: {lhs: Data.Maybe.fromMaybe 0, rhs: sum}+ - warn: {lhs: Data.Maybe.fromMaybe 1, rhs: product}+ - warn: {lhs: Data.Maybe.fromMaybe empty, rhs: Data.Foldable.asum}+ - warn: {lhs: Data.Maybe.fromMaybe mzero, rhs: Data.Foldable.msum} - warn: {lhs: Data.Either.fromRight mempty, rhs: Data.Foldable.fold} - warn: {lhs: Data.Either.fromRight False, rhs: or} - warn: {lhs: Data.Either.fromRight True, rhs: and}+ - warn: {lhs: Data.Either.fromRight 0, rhs: sum}+ - warn: {lhs: Data.Either.fromRight 1, rhs: product}+ - warn: {lhs: Data.Either.fromRight empty, rhs: Data.Foldable.asum}+ - warn: {lhs: Data.Either.fromRight mzero, rhs: Data.Foldable.msum} - warn: {lhs: if f x then Just x else Nothing, rhs: mfilter f (Just x)} - hint: {lhs: maybe (pure ()), rhs: traverse_, note: IncreasesLaziness} - hint: {lhs: fromMaybe (pure ()), rhs: sequenceA_, note: IncreasesLaziness} - hint: {lhs: fromRight (pure ()), rhs: sequenceA_, note: IncreasesLaziness}+ - hint: {lhs: "[fst x, snd x]", rhs: Data.Bifoldable.biList x}+ - hint: {lhs: "\\(x, y) -> [x, y]", rhs: Data.Bifoldable.biList, note: IncreasesLaziness} - group: name: dollar@@ -1029,7 +1040,7 @@ # main = hello .~ Just 12 -- hello ?~ 12 # foo = liftIO $ window `on` deleteEvent $ do a; b # no = sort <$> f input `shouldBe` sort <$> x-# sortBy (comparing snd) -- sortOn snd+# sortBy (comparing length) -- sortOn length # myJoin = on $ child ^. ChildParentId ==. parent ^. ParentId # foo = typeOf (undefined :: Foo Int) -- typeRep (Proxy :: Proxy (Foo Int)) # foo = typeOf (undefined :: a) -- typeRep (Proxy :: Proxy a)
hlint.cabal view
@@ -1,7 +1,7 @@ cabal-version: >= 1.18 build-type: Simple name: hlint-version: 2.2.10+version: 2.2.11 license: BSD3 license-file: LICENSE category: Development@@ -68,11 +68,12 @@ refact >= 0.3, aeson >= 1.1.2.0, filepattern >= 0.1.1,- ghc-lib-parser-ex == 8.8.4.*+ ghc-lib-parser-ex == 8.8.5.* if !flag(ghc-lib) && impl(ghc >= 8.8.0) && impl(ghc < 8.9.0) build-depends: ghc == 8.8.*,- ghc-boot-th+ ghc-boot-th,+ ghc-boot else build-depends: ghc-lib-parser == 8.8.*
src/GHC/Util/FreeVars.hs view
@@ -30,7 +30,7 @@ ( ^- ) :: Set OccName -> Set OccName -> Set OccName ( ^- ) = Set.difference --- See [Note : Spack leaks lurking here?] below.+-- See [Note : Space leaks lurking here?] below. data Vars' = Vars'{bound' :: Set OccName, free' :: Set OccName} -- Useful for debugging.
src/GHC/Util/HsExpr.hs view
@@ -35,6 +35,7 @@ import Data.Data import Data.Generics.Uniplate.Data import Data.List.Extra+import Data.Tuple.Extra import Refact.Types hiding (Match) import qualified Refact.Types as R (SrcSpan)@@ -211,30 +212,40 @@ replaceBranches' x = ([], \[] -> x) --- Like needBracket, but with a special case for 'a . b . b', which was+-- Like needBracket', but with a special case for 'a . b . b', which was -- removed from haskell-src-exts-util-0.2.2. needBracketOld' :: Int -> LHsExpr GhcPs -> LHsExpr GhcPs -> Bool needBracketOld' i parent child | isDotApp parent, isDotApp child, i == 2 = False | otherwise = needBracket' i parent child -transformBracketOld' :: (LHsExpr GhcPs -> Maybe (LHsExpr GhcPs)) -> LHsExpr GhcPs -> LHsExpr GhcPs-transformBracketOld' op = snd . g+transformBracketOld' :: (LHsExpr GhcPs -> Maybe (LHsExpr GhcPs)) -> LHsExpr GhcPs -> (LHsExpr GhcPs, LHsExpr GhcPs)+transformBracketOld' op = first snd . g where- g = f . descendBracketOld' g+ g = first f . descendBracketOld' g f x = maybe (False, x) (True, ) (op x) -- Descend, and if something changes then add/remove brackets--- appropriately-descendBracketOld' :: (LHsExpr GhcPs -> (Bool, LHsExpr GhcPs)) -> LHsExpr GhcPs -> LHsExpr GhcPs-descendBracketOld' op x = descendIndex' g x+-- appropriately. Returns (suggested replacement, refactor template).+-- Whenever a bracket is added to the suggested replacement, a+-- corresponding bracket is added to the refactor template.+descendBracketOld' :: (LHsExpr GhcPs -> ((Bool, LHsExpr GhcPs), LHsExpr GhcPs))+ -> LHsExpr GhcPs+ -> (LHsExpr GhcPs, LHsExpr GhcPs)+descendBracketOld' op x = (descendIndex' g1 x, descendIndex' g2 x) where- g i y = if a then f i b else b- where (a, b) = op y+ g i y = if a then (f1 i b z, f2 i b z) else (b, z)+ where ((a, b), z) = op y - f i (LL _ (HsPar _ y)) | not $ needBracketOld' i x y = y- f i y | needBracketOld' i x y = addParen' y- f _ y = y+ g1 = (fst .) . g+ g2 = (snd .) . g++ f i (LL _ (HsPar _ y)) z | not $ needBracketOld' i x y = (y, z)+ f i y z | needBracketOld' i x y = (addParen' y, addParen' z)+ f _ y z = (y, z)++ f1 = ((fst .) .) . f+ f2 = ((snd .) .) . f reduce' :: LHsExpr GhcPs -> LHsExpr GhcPs reduce' = fromParen' . transform reduce1'
src/GHC/Util/Unify.hs view
@@ -58,7 +58,10 @@ f _ = Nothing -- Peform a substition.-substitute' :: Subst' (LHsExpr GhcPs) -> LHsExpr GhcPs -> LHsExpr GhcPs+-- Returns (suggested replacement, refactor template), both with brackets added+-- as needed.+-- Example: (traverse foo (bar baz), traverse f (x))+substitute' :: Subst' (LHsExpr GhcPs) -> LHsExpr GhcPs -> (LHsExpr GhcPs, LHsExpr GhcPs) substitute' (Subst' bind) = transformBracketOld' exp . transformBi pat . transformBi typ where exp :: LHsExpr GhcPs -> Maybe (LHsExpr GhcPs)
src/HSE/Util.hs view
@@ -395,6 +395,7 @@ -- | (a, bs) means extension a implies all of bs. -- Taken from https://downloads.haskell.org/~ghc/master/users-guide/glasgow_exts.html#language-options+-- In the GHC source at DynFlags.impliedXFlags extensionImplications :: [(Extension, [Extension])] extensionImplications = map (first EnableExtension) $ (RebindableSyntax, [DisableExtension ImplicitPrelude]) :
src/Hint/Import.hs view
@@ -73,8 +73,9 @@ concatMap preferHierarchicalImports ms reduceImports :: [LImportDecl GhcPs] -> [Idea]-reduceImports ms =- [rawIdea' Hint.Type.Warning "Use fewer imports" (getLoc $ head ms) (f ms) (Just $ f x) [] rs+reduceImports [] = []+reduceImports ms@(m:_) =+ [rawIdea' Hint.Type.Warning "Use fewer imports" (getLoc m) (f ms) (Just $ f x) [] rs | Just (x, rs) <- [simplify ms]] where f = unlines . map unsafePrettyPrint
src/Hint/Match.hs view
@@ -111,8 +111,8 @@ [ (idea' (hintRuleSeverity m) (hintRuleName m) x y [r]){ideaNote=notes} | (name, expr) <- findDecls' decl , (parent,x) <- universeParentExp' expr- , m <- matches, Just (y, notes, subst) <- [matchIdea' s name m parent x]- , let r = R.Replace R.Expr (toSS' x) subst (unsafePrettyPrint $ unextendInstances (hintRuleGhcRHS m))+ , m <- matches, Just (y, tpl, notes, subst) <- [matchIdea' s name m parent x]+ , let r = R.Replace R.Expr (toSS' x) subst (unsafePrettyPrint tpl) ] -- | A list of root expressions, with their associated names@@ -127,7 +127,7 @@ -> HintRule -> Maybe (Int, LHsExpr GhcPs) -> LHsExpr GhcPs- -> Maybe (LHsExpr GhcPs, [Note], [(String, R.SrcSpan)])+ -> Maybe (LHsExpr GhcPs, LHsExpr GhcPs, [Note], [(String, R.SrcSpan)]) matchIdea' sb declName HintRule{..} parent x = do let lhs = unextendInstances hintRuleGhcLHS rhs = unextendInstances hintRuleGhcRHS@@ -138,8 +138,8 @@ -- Need to check free vars before unqualification, but after subst -- (with 'e') need to unqualify before substitution (with 'res').- let e = substitute' u rhs- res = addBracketTy' (addBracket' parent $ performSpecial' $ substitute' u $ unqualify' sa sb rhs)+ let (e, tpl) = substitute' u rhs+ res = addBracketTy' (addBracket' parent $ performSpecial' $ fst $ substitute' u $ unqualify' sa sb rhs) guard $ (freeVars' e Set.\\ Set.filter (not . isUnifyVar . occNameString) (freeVars' rhs)) `Set.isSubsetOf` freeVars' x -- Check no unexpected new free variables. @@ -152,7 +152,7 @@ guard $ checkSide' (unextendInstances <$> hintRuleGhcSide) $ ("original", x) : ("result", res) : fromSubst' u guard $ checkDefine' declName parent res - return (res, hintRuleNotes, [(s, toSS' pos) | (s, pos) <- fromSubst' u, getLoc pos /= noSrcSpan])+ return (res, tpl, hintRuleNotes, [(s, toSS' pos) | (s, pos) <- fromSubst' u, getLoc pos /= noSrcSpan]) --------------------------------------------------------------------- -- SIDE CONDITIONS
src/Hint/Monad.hs view
@@ -133,8 +133,8 @@ -> [ExprLStmt GhcPs] -> [Idea] -- Rewrite 'do return x; $2' as 'do $2'.-monadStep wrap o@(LL _ (BodyStmt _ (fromRet -> Just (ret, _)) _ _ ) : x : xs)- = [warn' ("Redundant " ++ ret) (wrap o) (wrap $ x:xs) [Delete Stmt (toSS' (head o))]]+monadStep wrap os@(o@(LL _ (BodyStmt _ (fromRet -> Just (ret, _)) _ _ )) : xs@(_:_))+ = [warn' ("Redundant " ++ ret) (wrap os) (wrap xs) [Delete Stmt (toSS' o)]] -- Rewrite 'do a <- $1; return a' as 'do $1'. monadStep wrap o@[ g@(LL _ (BindStmt _ (LL _ (VarPat _ (L _ p))) x _ _ ))
src/Hint/Pragma.hs view
@@ -32,6 +32,7 @@ import Hint.Type(ModuHint,ModuleEx(..),Idea(..),Severity(..),toSS',rawIdea',prettyExtension,glasgowExts) import Data.List.Extra+import qualified Data.List.NonEmpty as NE import Data.Maybe import Refact.Types import qualified Refact.Types as R@@ -52,7 +53,7 @@ -> [(Located AnnotationComment, [String])] -> [Idea] optToPragma flags langExts =- [pragmaIdea (OptionsToComment (map fst old) ys rs) | old /= []]+ [pragmaIdea (OptionsToComment (fst <$> old2) ys rs) | Just old2 <- [NE.nonEmpty old]] where (old, new, ns, rs) = unzip4 [(old, new, ns, r)@@ -73,7 +74,7 @@ data PragmaIdea = SingleComment (Located AnnotationComment) (Located AnnotationComment) | MultiComment (Located AnnotationComment) (Located AnnotationComment) (Located AnnotationComment)- | OptionsToComment [Located AnnotationComment] [Located AnnotationComment] [Refactoring R.SrcSpan]+ | OptionsToComment (NE.NonEmpty (Located AnnotationComment)) [Located AnnotationComment] [Refactoring R.SrcSpan] pragmaIdea :: PragmaIdea -> Idea pragmaIdea pidea =@@ -87,8 +88,8 @@ [ ModifyComment (toSS' repl) (comment new) , ModifyComment (toSS' delete) ""] OptionsToComment old new r ->- mkLanguage (getLoc . head $ old)- (f old) (Just $ f new) []+ mkLanguage (getLoc . NE.head $ old)+ (f $ NE.toList old) (Just $ f new) [] r where f = unlines . map comment
src/Report.hs view
@@ -5,6 +5,7 @@ import Idea import Data.Tuple.Extra import Data.List.Extra+import qualified Data.List.NonEmpty as NE import Data.Maybe import Data.Version import HSE.All@@ -26,7 +27,7 @@ writeReport dataDir file ideas = timedIO "Report" file $ writeTemplate dataDir inner file where generateIds :: [String] -> [(String,Int)] -- sorted by name- generateIds = map (head &&& length) . group -- must be already sorted+ generateIds = map (NE.head &&& length) . NE.group -- must be already sorted files = generateIds $ sort $ map (srcSpanFilename . ideaSpan) ideas hints = generateIds $ map hintName $ sortOn (negate . fromEnum . ideaSeverity &&& hintName) ideas hintName x = show (ideaSeverity x) ++ ": " ++ ideaHint x