hlint 3.2.3 → 3.2.4
raw patch · 30 files changed
+1000/−27 lines, 30 filesdep ~extradep ~ghc-lib-parser-exPVP ok
version bump matches the API change (PVP)
Dependency ranges changed: extra, ghc-lib-parser-ex
API changes (from Hackage documentation)
Files
- .hlint.yaml +68/−0
- CHANGES.txt +7/−0
- README.md +1/−1
- data/hlint-restrict-extensions.yaml +3/−0
- data/hlint.yaml +15/−2
- data/test-restrict.yaml +17/−0
- data/wildcard.yaml +1/−0
- hlint.cabal +8/−2
- src/GHC/Util/Brackets.hs +8/−0
- src/GHC/Util/Unify.hs +5/−3
- src/Hint/All.hs +3/−1
- src/Hint/Bracket.hs +6/−17
- src/Hint/Fixities.hs +79/−0
- src/Test/All.hs +1/−1
- tests/bracket.test +29/−0
- tests/cmdline.test +29/−0
- tests/cpp.test +131/−0
- tests/cross.test +25/−0
- tests/find.test +42/−0
- tests/flag-extension.test +31/−0
- tests/flag-no-summary.test +18/−0
- tests/flag-only.test +30/−0
- tests/flag-quiet.test +6/−0
- tests/flag-with-group.test +22/−0
- tests/hint.test +165/−0
- tests/json.test +40/−0
- tests/lhs.test +47/−0
- tests/parse-error.test +33/−0
- tests/serialise.test +85/−0
- tests/wildcard.test +45/−0
+ .hlint.yaml view
@@ -0,0 +1,68 @@+# HLint configuration file+# https://github.com/ndmitchell/hlint+##########################++# Hints that apply only to the HLint source code++#####################################################################+## RESTRICTIONS++- extensions:+ - default: false+ - name: [DeriveDataTypeable, DeriveFunctor, GeneralizedNewtypeDeriving, NoMonomorphismRestriction, OverloadedStrings]+ - name: [MultiWayIf, PatternGuards, RecordWildCards, ViewPatterns, PatternSynonyms, TupleSections, LambdaCase]+ - name: [Rank2Types, ScopedTypeVariables]+ - name: [ExistentialQuantification, MultiParamTypeClasses, NamedFieldPuns]+ - name: [FlexibleContexts, FlexibleInstances]+ - name: [PackageImports]+ - name: [ConstraintKinds, RankNTypes, TypeFamilies]+ - name: [TemplateHaskell]+ - {name: CPP, within: [HsColour, Config.Yaml, Test.Annotations]} # so it can be disabled to avoid GPL code++- flags:+ - default: false+ - {name: [-Wno-missing-fields, -fno-cse, -O0], within: CmdLine} # for cmdargs+ - {name: [-Wno-incomplete-patterns, -Wno-overlapping-patterns]} # the pattern match checker is not very good++- modules:+ - {name: [Data.Set, Data.HashSet], as: Set}+ - {name: [Data.Map, Data.HashMap.Strict, Data.HashMap.Lazy], as: Map}+ - {name: Control.Arrow, within: []}++- functions:+ - {name: unsafeInterleaveIO, within: Parallel}+ - {name: unsafePerformIO, within: [Util.exitMessageImpure, Test.Util.ref, Timing]}+ - {name: unsafeCoerce, within: [Util.gzip, GHC.Util.Refact.Utils]}+ - {name: Data.List.nub, within: []}+ - {name: Data.List.nubBy, within: []}+ - {name: Data.List.NonEmpty.nub, within: []}+ - {name: Data.List.NonEmpty.nubBy, within: []}+++#####################################################################+## OTHER HINTS++- warn: {name: Use explicit module export list}+++#####################################################################+## HINTS++- error: {lhs: idea Warning, rhs: warn}+- error: {lhs: idea Suggestion, rhs: suggest}+- error: {lhs: ideaN Warning, rhs: warnN}+- error: {lhs: ideaN Suggestion, rhs: suggestN}++- error: {lhs: occNameString (occName (unLoc x)), rhs: rdrNameStr x}+- error: {lhs: occNameString (occName x), rhs: occNameStr x}+- error: {lhs: noLoc (HsVar noExtField (noLoc (mkRdrUnqual (mkVarOcc x)))), rhs: strToVar x}++#####################################################################+## IGNORES++# doesn't fit with the other statements+- ignore: {name: Use let, within: [HLint, Test.All]}+# this const has meaingful argument names+- ignore: {name: Use const, within: Config.Yaml}+# TEMPORARY: this lint is deleted on HEAD+- ignore: {name: Use String}
CHANGES.txt view
@@ -1,5 +1,12 @@ Changelog for HLint (* = breaking change) +3.2.4, released 2020-12-30+ #1193, add warnings for redundant flip+ #1183, allow matches where users specify unnecessary brackets+ #1177, remove suggestions for fromMaybe False/fromMaybe True+ #1176, suggest use of unzip+ #1159, spot redundant brackets due to fixities, default ignored+ #1172, suggest reusing fromLeft/fromRight 3.2.3, released 2020-11-23 #1160, never consult the .hscolour file for color preferences #1171, do not refactor redundant lambda with case
README.md view
@@ -1,4 +1,4 @@-# HLint [](https://hackage.haskell.org/package/hlint) [](https://www.stackage.org/package/hlint) [](https://travis-ci.org/ndmitchell/hlint) [](https://ci.appveyor.com/project/ndmitchell/hlint)+# HLint [](https://hackage.haskell.org/package/hlint) [](https://www.stackage.org/package/hlint) [](https://github.com/ndmitchell/hlint/actions) HLint is a tool for suggesting possible improvements to Haskell code. These suggestions include ideas such as using alternative functions, simplifying code and spotting redundancies. This document is structured as follows:
+ data/hlint-restrict-extensions.yaml view
@@ -0,0 +1,3 @@+- extensions:+ - {name: DeriveFunctor, within: []}+ - {name: DeriveTraversable, within: [], message: "Custom message"}
data/hlint.yaml view
@@ -168,6 +168,7 @@ - warn: {lhs: all f (map g x), rhs: all (f . g) x} - warn: {lhs: "zipWith (,)", rhs: zip} - warn: {lhs: "zipWith3 (,,)", rhs: zip3}+ - hint: {lhs: map fst &&& map snd, rhs: unzip} - hint: {lhs: length x == 0, rhs: null x, note: IncreasesLaziness} - hint: {lhs: 0 == length x, rhs: null x, note: IncreasesLaziness} - hint: {lhs: length x < 1, rhs: null x, note: IncreasesLaziness}@@ -319,6 +320,11 @@ - 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: flip (flip f), rhs: f, note: DecreasesLaziness}+ - warn: {lhs: flip f <*> g, rhs: f =<< g, name: Redundant flip}+ - warn: {lhs: g <**> flip f, rhs: g >>= f, name: Redundant flip}+ - warn: {lhs: flip f =<< g, rhs: f <*> g, name: Redundant flip}+ - warn: {lhs: g >>= flip f, rhs: g Control.Applicative.<**> f, name: Redundant flip} # CHAR @@ -560,8 +566,10 @@ - warn: {lhs: maybe True (x /=), rhs: (Just x /=)} - warn: {lhs: maybe False (== x), rhs: (Just x ==), note: ValidInstance Eq x} - warn: {lhs: maybe True (/= x), rhs: (Just x /=), note: ValidInstance Eq x}- - warn: {lhs: fromMaybe False x, rhs: Just True == x} # Eta expanded, see https://github.com/ndmitchell/hlint/issues/970#issuecomment-643645053- - warn: {lhs: fromMaybe True x, rhs: Just False /= x}+ # The following two hints seem to be somewhat unwelcome, e.g.+ # https://github.com/ndmitchell/hlint/issues/1177+ - ignore: {lhs: fromMaybe False x, rhs: Just True == x} # Eta expanded, see https://github.com/ndmitchell/hlint/issues/970#issuecomment-643645053+ - ignore: {lhs: fromMaybe True x, rhs: Just False /= x} - warn: {lhs: not (isNothing x), rhs: isJust x} - warn: {lhs: not (isJust x), rhs: isNothing x} - warn: {lhs: "maybe [] (:[])", rhs: maybeToList}@@ -609,6 +617,8 @@ - warn: {lhs: isRight (fmap f x), rhs: isRight x} - warn: {lhs: fromLeft x (fmap f y), rhs: fromLeft x y} - warn: {lhs: fromRight x (fmap f y), rhs: either (const x) f y}+ - warn: {lhs: either (const x) id, rhs: fromRight x}+ - warn: {lhs: either id (const x), rhs: fromLeft x} # INFIX @@ -856,6 +866,7 @@ - warn: {lhs: "sequence [a]", rhs: "pure <$> a"} - warn: {lhs: "x /= []", rhs: not (null x), name: Use null} - warn: {lhs: "[] /= x", rhs: not (null x), name: Use null}+ - warn: {lhs: "maybe []", rhs: foldMap} - group: name: generalise-for-conciseness@@ -1200,6 +1211,8 @@ # issue1049 = elem True -- or # issue1062 = bar (\(f, x) -> baz () () . f $ x) -- uncurry ((.) (baz () ())) # issue1058 n = [] ++ issue1058 (n+1) -- issue1058 (n+1)+# issue1183 = (a >= 'a') && a <= 'z' -- isAsciiLower a+# issue1183 = (a >= 'a') && (a <= 'z') -- isAsciiLower a # import Prelude \ # yes = flip mapM -- Control.Monad.forM
+ data/test-restrict.yaml view
@@ -0,0 +1,17 @@+- modules:+ - {name: Restricted.Module, within: []}+ - {name: Restricted.Module.Message, within: [], message: "Custom message"}++- functions:+ - {name: restricted, within: []}+ - {name: restrictedMessage, within: [], message: "Custom message"}++- extensions:+ - {name: DeriveFunctor, within: []}+ - {name: DeriveTraversable, within: [], message: "Custom message"}+++# Test https://github.com/ndmitchell/hlint/issues/766+- warn:+ lhs: "[hamlet| |]"+ rhs: "mempty"
+ data/wildcard.yaml view
@@ -0,0 +1,1 @@+- ignore: { name: Use const, within: '**.*Spec' }
hlint.cabal view
@@ -1,7 +1,7 @@ cabal-version: >= 1.18 build-type: Simple name: hlint-version: 3.2.3+version: 3.2.4 license: BSD3 license-file: LICENSE category: Development@@ -24,6 +24,11 @@ hlint.ghci HLint_QuickCheck.hs HLint_TypeCheck.hs+extra-source-files:+ .hlint.yaml+ data/*.hs+ data/*.yaml+ tests/*.test extra-doc-files: README.md CHANGES.txt@@ -80,7 +85,7 @@ build-depends: ghc-lib-parser == 8.10.* build-depends:- ghc-lib-parser-ex >= 8.10.0.16 && < 8.10.1+ ghc-lib-parser-ex >= 8.10.0.17 && < 8.10.1 if flag(gpl) build-depends: hscolour >= 1.21@@ -140,6 +145,7 @@ Hint.Duplicate Hint.Export Hint.Extensions+ Hint.Fixities Hint.Import Hint.Lambda Hint.List
src/GHC/Util/Brackets.hs view
@@ -7,6 +7,7 @@ import SrcLoc import BasicTypes import Language.Haskell.GhclibParserEx.GHC.Hs.Expr+import Refact.Types class Brackets a where remParen :: a -> Maybe a -- Remove one paren or nothing if there is no paren.@@ -17,6 +18,7 @@ -- | Is the child safe free from brackets in the parent -- position. Err on the side of caution, True = don't know. needBracket :: Int -> a -> a -> Bool+ findType :: a -> RType instance Brackets (LHsExpr GhcPs) where -- When GHC parses a section in concrete syntax, it will produce an@@ -88,6 +90,8 @@ | L _ HsPar{} <- parent = False | otherwise = True + findType _ = Expr+ -- | Am I an HsApp such that having me in an infix doesn't require brackets. -- Before BlockArguments that was _all_ HsApps. Now, imagine: --@@ -133,6 +137,8 @@ | L _ ListPat{} <- parent = False | otherwise = True + findType _ = Pattern+ instance Brackets (LHsType GhcPs) where remParen (L _ (HsParTy _ x)) = Just x remParen _ = Nothing@@ -164,3 +170,5 @@ | L _ HsOpTy{} <- parent, L _ HsAppTy{} <- child = False | L _ HsParTy{} <- parent = False | otherwise = True++ findType _ = Type
src/GHC/Util/Unify.hs view
@@ -189,13 +189,15 @@ -- dot at the root, since otherwise you get two matches because of -- 'readRule' (Bug #570). unifyExp' :: NameMatch -> Bool -> LHsExpr GhcPs -> LHsExpr GhcPs -> Maybe (Subst (LHsExpr GhcPs))--- Brackets are not added when expanding '$' in user code, so tolerate--- them in the match even if they aren't in the user code.-unifyExp' nm root x y | not root, isPar x, not $ isPar y = unifyExp' nm root (fromParen x) y -- Don't subsitute for type apps, since no one writes rules imagining -- they exist. unifyExp' nm root (L _ (HsVar _ (rdrNameStr -> v))) y | isUnifyVar v, not $ isTypeApp y = Just $ Subst [(v, y)] unifyExp' nm root (L _ (HsVar _ x)) (L _ (HsVar _ y)) | nm x y = Just mempty++-- Brackets are not added when expanding '$' in user code, so tolerate+-- them in the match even if they aren't in the user code.+-- Also, allow the user to put in more brackets than they strictly need (e.g. with infix).+unifyExp' nm root x y | not root, isPar x || isPar y = unifyExp' nm root (fromParen x) (fromParen y) unifyExp' nm root x@(L _ (OpApp _ lhs1 (L _ (HsVar _ (rdrNameStr -> v))) rhs1)) y@(L _ (OpApp _ lhs2 (L _ (HsVar _ op2)) rhs2)) =
src/Hint/All.hs view
@@ -19,6 +19,7 @@ import Hint.Monad import Hint.Lambda import Hint.Bracket+import Hint.Fixities import Hint.Naming import Hint.Pattern import Hint.Import@@ -35,7 +36,7 @@ -- | A list of the builtin hints wired into HLint. -- This list is likely to grow over time. data HintBuiltin =- HintList | HintListRec | HintMonad | HintLambda |+ HintList | HintListRec | HintMonad | HintLambda | HintFixities | HintBracket | HintNaming | HintPattern | HintImport | HintExport | HintPragma | HintExtensions | HintUnsafe | HintDuplicate | HintRestrict | HintComment | HintNewType | HintSmell@@ -61,6 +62,7 @@ HintListRec -> decl listRecHint HintNaming -> decl namingHint HintBracket -> decl bracketHint+ HintFixities -> mempty{hintDecl=fixitiesHint} HintSmell -> mempty{hintDecl=smellHint,hintModule=smellModuleHint} HintPattern -> decl patternHint HintMonad -> decl monadHint
src/Hint/Bracket.hs view
@@ -138,17 +138,6 @@ prettyExpr s@(L _ SectionR{}) = unsafePrettyPrint (noLoc (HsPar noExtField s) :: LHsExpr GhcPs) prettyExpr x = unsafePrettyPrint x --- Dirty, should add to Brackets type class I think-tyConToRtype :: String -> RType-tyConToRtype "Exp" = Expr-tyConToRtype "Type" = Type-tyConToRtype "HsType" = Type-tyConToRtype "Pat" = Pattern-tyConToRtype _ = Expr--findType :: (Data a) => a -> RType-findType = tyConToRtype . dataTypeName . dataTypeOf- -- 'Just _' if at least one set of parens were removed. 'Nothing' if -- zero parens were removed. remParens' :: Brackets a => a -> Maybe a@@ -162,7 +151,7 @@ isPartialAtom (L _ (HsSpliceE _ (HsUntypedSplice _ HasDollar _ _) )) = True isPartialAtom x = isRecConstr x || isRecUpdate x -bracket :: forall a . (Data a, Data (SrcSpanLess a), HasSrcSpan a, Outputable a, Brackets a) => (a -> String) -> (a -> Bool) -> Bool -> a -> [Idea]+bracket :: forall a . (Data a, HasSrcSpan a, Outputable a, Brackets a) => (a -> String) -> (a -> Bool) -> Bool -> a -> [Idea] bracket pretty isPartialAtom root = f Nothing where msg = "Redundant bracket"@@ -188,7 +177,7 @@ | not $ needBracket i o x, not $ isPartialAtom x = rawIdea Suggestion msg (getLoc v) (pretty o) (Just (pretty (gen x))) [] [r] : g x where- typ = findType (unLoc v)+ typ = findType v r = Replace typ (toSS v) [("x", toSS x)] "x" -- Regardless of the context, there are no parentheses to remove -- from 'x'.@@ -199,13 +188,13 @@ -- redundant parentheses in each. g o = concat [f (Just (i, o, gen)) x | (i, (x, gen)) <- zipFrom 0 $ holes o] -bracketWarning :: (HasSrcSpan a, HasSrcSpan b, Data (SrcSpanLess b), Outputable a, Outputable b) => String -> a -> b -> Idea+bracketWarning :: (HasSrcSpan a, HasSrcSpan b, Outputable a, Outputable b, Brackets b) => String -> a -> b -> Idea bracketWarning msg o x =- suggest msg o x [Replace (findType (unLoc x)) (toSS o) [("x", toSS x)] "x"]+ suggest msg o x [Replace (findType x) (toSS o) [("x", toSS x)] "x"] -bracketError :: (HasSrcSpan a, HasSrcSpan b, Data (SrcSpanLess b), Outputable a, Outputable b ) => String -> a -> b -> Idea+bracketError :: (HasSrcSpan a, HasSrcSpan b, Outputable a, Outputable b, Brackets b) => String -> a -> b -> Idea bracketError msg o x =- warn msg o x [Replace (findType (unLoc x)) (toSS o) [("x", toSS x)] "x"]+ warn msg o x [Replace (findType x) (toSS o) [("x", toSS x)] "x"] fieldDecl :: LConDeclField GhcPs -> [Idea] fieldDecl o@(L loc f@ConDeclField{cd_fld_type=v@(L l (HsParTy _ c))}) =
+ src/Hint/Fixities.hs view
@@ -0,0 +1,79 @@+{-++Raise a warning if you have redundant brackets in nested infix expressions.++<TEST>+yes = 1 + (2 * 3) -- @Ignore 1 + 2 * 3+yes = (2 * 3) + 1 -- @Ignore 2 * 3 + 1+no = (1 + 2) * 3+no = 3 * (1 + 2)+no = 1 + 2 * 3+no = 2 * 3 + 1+yes = (a >>= f) >>= g -- @Ignore a >>= f >>= g+no = (a >>= \x -> b) >>= g+</TEST>+-}++module Hint.Fixities(fixitiesHint) where++import Hint.Type(DeclHint,Idea(..),rawIdea,toSS)+import Config.Type+import Control.Monad+import Data.List.Extra+import Data.Map+import Data.Generics.Uniplate.DataOnly+import Refact.Types++import BasicTypes (compareFixity)+import Fixity+import GHC.Hs+import GHC.Util+import Language.Haskell.GhclibParserEx.GHC.Utils.Outputable+import OccName+import SrcLoc+import RdrName++fixitiesHint :: [Setting] -> DeclHint+fixitiesHint settings _ _ x =+ concatMap (infixBracket fixities) (childrenBi x :: [LHsExpr GhcPs])+ where+ fixities = foldMap getFixity settings `mappend` fromList (toFixity <$> defaultFixities)+ getFixity (Infix x) = uncurry singleton (toFixity x)+ getFixity _ = mempty++infixBracket :: Map String Fixity -> LHsExpr GhcPs -> [Idea]+infixBracket fixities = f Nothing+ where+ msg = "Redundant bracket due to operator fixities"+ f p o = cur p o <> concat [f (Just (i, o, gen)) x | (i, (x, gen)) <- zipFrom 0 $ holes o]+ cur p v = do+ Just (i, o, gen) <- [p]+ Just x <- [remParen v]+ guard $ redundantInfixBracket fixities i o x+ pure $+ rawIdea Ignore msg (getLoc v) (unsafePrettyPrint o)+ (Just (unsafePrettyPrint (gen x))) [] [Replace (findType v) (toSS v) [("x", toSS x)] "x"]++redundantInfixBracket :: Map String Fixity -> Int -> LHsExpr GhcPs -> LHsExpr GhcPs -> Bool+redundantInfixBracket fixities i parent child+ | L _ (OpApp _ _ (L _ (HsVar _ (L _ (Unqual p)))) _) <- parent+ , L _ (OpApp _ _ (L _ (HsVar _ (L _ (Unqual c)))) (L _ cr)) <- child =+ let (lop, rop)+ | i == 0 = (c, p)+ | otherwise = (p, c)+ in+ case compareFixity <$> (fixities !? occNameString lop) <*> (fixities !? occNameString rop) of+ Just (False, r)+ | i == 0 -> not (needParenAsChild cr || r)+ | otherwise -> r+ _ -> False+ | otherwise = False++needParenAsChild :: HsExpr p -> Bool+needParenAsChild HsLet{} = True+needParenAsChild HsDo{} = True+needParenAsChild HsLam{} = True+needParenAsChild HsLamCase{} = True+needParenAsChild HsCase{} = True+needParenAsChild HsIf{} = True+needParenAsChild _ = False
src/Test/All.hs view
@@ -43,7 +43,7 @@ pure (file, hints ++ (if takeBaseName file /= "Test" then [] else map (Builtin . fst) builtinHints)) let wrap msg act = do liftIO $ putStr (msg ++ " "); act; liftIO $ putStrLn "" - liftIO $ putStrLn "Testing"+ liftIO $ putStrLn $ "Testing (" ++ (if isRight rpath then "with" else "WITHOUT") ++ " refactoring)" liftIO $ checkCommentedYaml $ dataDir </> "default.yaml" when useSrc $ wrap "Source annotations" $ do config <- liftIO $ readFilesConfig [(".hlint.yaml",Nothing)]
+ tests/bracket.test view
@@ -0,0 +1,29 @@+---------------------------------------------------------------------+RUN tests/bracket-slice.hs+FILE tests/bracket-slice.hs++fAnd :: [a -> Bool] -> a -> Bool+fAnd fs x = all ($x) fs++OUTPUT+No hints++---------------------------------------------------------------------+RUN tests/bracket-slice-spaced.hs+FILE tests/bracket-slice-spaced.hs++fAnd :: [a -> Bool] -> a -> Bool+fAnd fs x = all ($ x) fs++OUTPUT+No hints++---------------------------------------------------------------------+RUN tests/bracket-slice-plus.hs+FILE tests/bracket-slice-plus.hs++incAll :: [Int] -> Int -> [Int]+incAll ys x = map (+x) fs++OUTPUT+No hints
+ tests/cmdline.test view
@@ -0,0 +1,29 @@+---------------------------------------------------------------------+RUN lint tests/cmdline-lint.hs+FILE tests/cmdline-lint.hs++foo = map f (map g xs)++OUTPUT+tests/cmdline-lint.hs:2:7-22: Suggestion: Use map once+Found:+ map f (map g xs)+Perhaps:+ map (f . g) xs++1 hint++---------------------------------------------------------------------+RUN tests/cmdline-bare.hs+FILE tests/cmdline-bare.hs++foo = map f (map g xs)++OUTPUT+tests/cmdline-bare.hs:2:7-22: Suggestion: Use map once+Found:+ map f (map g xs)+Perhaps:+ map (f . g) xs++1 hint
+ tests/cpp.test view
@@ -0,0 +1,131 @@+---------------------------------------------------------------------+RUN tests/cpp-full.hs+FILE tests/cpp-full.hs+/* this is a test */++main = putStrLn $ show "Hello"+OUTPUT+tests/cpp-full.hs:3:8-30: Warning: Use print+Found:+ putStrLn $ show "Hello"+Perhaps:+ print "Hello"++1 hint++---------------------------------------------------------------------+RUN -XNoCPP tests/cpp-none.hs+FILE tests/cpp-none.hs+#include "Any/File.h"++main = print "Hello"+EXIT 0+OUTPUT+No hints++---------------------------------------------------------------------+RUN -XNoCPP tests/cpp-must-not-run.hs+FILE tests/cpp-must-not-run.hs+{-+#error Cpp has run+-}+main = undefined+EXIT 0+OUTPUT+No hints++---------------------------------------------------------------------+RUN --cpp-define FOO tests/cpp-ext-enable.hs+FILE tests/cpp-ext-enable.hs+{-# LANGUAGE CPP #-}+#if defined(FOO)+{-# LANGUAGE Foo #-}+#endif+main = undefined+EXIT 1+OUTPUT+tests/cpp-ext-enable.hs:1:1: Error: Parse error: Unsupported extension: Foo++Found:+ {-# LANGUAGE CPP #-}++ {-# LANGUAGE Foo #-}++ main = undefined++1 hint++---------------------------------------------------------------------+RUN tests/cpp-ext-disable.hs+FILE tests/cpp-ext-disable.hs+{-# LANGUAGE CPP #-}+#if defined(FOO)+{-# LANGUAGE Foo #-}+#endif+main = undefined+EXIT 1+OUTPUT+tests/cpp-ext-disable.hs:1:1-20: Warning: Avoid restricted extensions+Found:+ {-# LANGUAGE CPP #-}+Note: may break the code++1 hint++---------------------------------------------------------------------+RUN --cpp-simple tests/cpp-simple.hs+FILE tests/cpp-simple.hs+#include "Any/File.h"++main = print "Hello"+OUTPUT+No hints++---------------------------------------------------------------------+RUN tests/cpp-file1.hs+FILE tests/cpp-file1.hs+import Network.Wai+#if MIN_VERSION_wai(2, 0, 0)+import Network.Wai.Internal+#endif+OUTPUT+No hints++---------------------------------------------------------------------+RUN --cpp-file=tests/cabal_macros.h tests/cpp-file2.hs+FILE tests/cabal_macros.h+#define MIN_VERSION_wai(a,b,c) 1+FILE tests/cpp-file2.hs+import Network.Wai+#if MIN_VERSION_wai(2, 0, 0)+import Network.Wai.Internal+#endif+foo = map f . map g+OUTPUT+tests/cpp-file2.hs:5:7-19: Suggestion: Use map once+Found:+ map f . map g+Perhaps:+ map (f . g)++1 hint++---------------------------------------------------------------------+RUN --cpp-simple tests/cpp-file3.hs+FILE tests/cpp-file3.hs+import Network.Wai+#if MIN_VERSION_wai(2, 0, 0)+import Network.Wai.Internal+#endif+OUTPUT+No hints++---------------------------------------------------------------------+RUN --cpp-simple tests/cpp-file4.hs+FILE tests/cpp-file4.hs+import Network.Wai+#if defined(MIN_VERSION_wai) && MIN_VERSION_wai(2, 0, 0)+import Network.Wai.Internal+#endif+OUTPUT+No hints
+ tests/cross.test view
@@ -0,0 +1,25 @@+---------------------------------------------------------------------+RUN tests/cross.hs1 tests/cross.hs2 --cross+FILE tests/cross.hs1++module B(bar) where+++bar = 1+ where+ a = 1+ b = 2+ c = 3++FILE tests/cross.hs2++module A(foo) where++foo = 1+ where+ a = 1+ b = 2+ c = 3++OUTPUT+No hints
+ tests/find.test view
@@ -0,0 +1,42 @@+---------------------------------------------------------------------+RUN --find=tests/find.hs+FILE tests/find.hs+{-# LANGUAGE CPP, ExistentialQuantification, Rank2Types #-}++module Util where++import Control.Arrow+import Control.Monad.Trans.State+import Data.Char+import Data.Function++infixr 4 %^&, `wheeeee`++instance Foo a where+ bar = baz + qux++listM' :: Monad m => [a] -> m [a]+listM' x = length x `seq` return x++notNull = not . null++headDef :: a -> [a] -> a+headDef x [] = x+headDef x (y:ys) = y++isLeft Left{} = True; isLeft _ = False+isRight = not . isLeft++swap :: (a,b) -> (b,a)+swap (a,b) = (b,a)++defaultExtensions = knownExtensions \\ badExtensions+OUTPUT+# hints found in tests/find.hs+- fixity: "infixr 4 %^&"+- fixity: "infixr 4 `wheeeee`"+- warn: {lhs: "baz + qux", rhs: "bar"}+- warn: {lhs: "length a `seq` return a", rhs: "listM' a"}+- warn: {lhs: "not (null a)", rhs: "notNull a"}+- warn: {lhs: "not (isLeft a)", rhs: "isRight a"}+- warn: {lhs: "knownExtensions \\\\ badExtensions", rhs: "defaultExtensions"}
+ tests/flag-extension.test view
@@ -0,0 +1,31 @@+---------------------------------------------------------------------+RUN tests/directory+FILE tests/directory/File1.hs+ foo = map f . map g+FILE tests/directory/File2.lhs+> foo = map f . map g+OUTPUT+tests/directory/File1.hs:1:8-20: Suggestion: Use map once+Found:+ map f . map g+Perhaps:+ map (f . g)++tests/directory/File2.lhs:1:9-21: Suggestion: Use map once+Found:+ map f . map g+Perhaps:+ map (f . g)++2 hints++---------------------------------------------------------------------+RUN tests/directory --extension=lhs+OUTPUT+tests/directory/File2.lhs:1:9-21: Suggestion: Use map once+Found:+ map f . map g+Perhaps:+ map (f . g)++1 hint
+ tests/flag-no-summary.test view
@@ -0,0 +1,18 @@+---------------------------------------------------------------------+RUN tests/flag-no-summary1.hs --no-summary+FILE tests/flag-no-summary1.hs+main = map f $ map g xs+OUTPUT+tests/flag-no-summary1.hs:1:8-23: Suggestion: Use map once+Found:+ map f $ map g xs+Perhaps:+ map (f . g) xs++EXIT 1+---------------------------------------------------------------------+RUN tests/flag-no-summary2.hs --no-summary+FILE tests/flag-no-summary2.hs+main = return ()+OUTPUT+EXIT 0
+ tests/flag-only.test view
@@ -0,0 +1,30 @@+---------------------------------------------------------------------+RUN tests/flag-only-one-arg.hs --only="Redundant bracket"+FILE tests/flag-only-one-arg.hs+foo xs = if length xs == 0 then 42 else (666) -- should result in 2 suggestions+OUTPUT+tests/flag-only-one-arg.hs:1:41-45: Warning: Redundant bracket+Found:+ (666)+Perhaps:+ 666++1 hint+---------------------------------------------------------------------+RUN tests/flag-only-many-args.hs --only="Redundant bracket" --only="Use ++"+FILE tests/flag-only-many-args.hs+foo xs = if length xs == 0 then concat ["foo", "bar"] else (666) -- should result in 3 suggestions+OUTPUT+tests/flag-only-many-args.hs:1:33-53: Suggestion: Use +++Found:+ concat ["foo", "bar"]+Perhaps:+ "foo" ++ "bar"++tests/flag-only-many-args.hs:1:60-64: Warning: Redundant bracket+Found:+ (666)+Perhaps:+ 666++2 hints
+ tests/flag-quiet.test view
@@ -0,0 +1,6 @@+---------------------------------------------------------------------+RUN tests/flag-quiet.hs --quiet+FILE tests/flag-quiet.hs+main = map f $ map g xs+OUTPUT+EXIT 1
+ tests/flag-with-group.test view
@@ -0,0 +1,22 @@+---------------------------------------------------------------------+FILE tests/flag-with-group.hs+foo = map (+1) . maybe mempty reverse+RUN "--with-group=generalise" tests/flag-with-group.hs+OUTPUT+tests/flag-with-group.hs:1:7-9: Warning: Use fmap+Found:+ map+Perhaps:+ fmap++1 hint+---------------------------------------------------------------------+RUN "--with-group=generalise-for-conciseness" tests/flag-with-group.hs+OUTPUT+tests/flag-with-group.hs:1:18-29: Warning: Use foldMap+Found:+ maybe mempty+Perhaps:+ foldMap++1 hint
+ tests/hint.test view
@@ -0,0 +1,165 @@+---------------------------------------------------------------------+RUN tests/newtype-derive.hs --hint=data/hlint.yaml+FILE tests/newtype-derive.hs+{-# LANGUAGE DeriveTraversable #-} -- Implies DeriveFoldable and DeriveFunctor+{-# LANGUAGE DeriveDataTypeable #-}+module Test(A) where++import Data.Foldable (Foldable)+import Data.Traversable (Traversable)+import Data.Typeable (Typeable)++newtype A f = A f+ deriving (Foldable, Functor, Traversable, Typeable)+OUTPUT+No hints++---------------------------------------------------------------------+RUN tests/note.hs+FILE tests/note.hs+{-# LANGUAGE RecordWildCards #-}++module Sample(test) where++test xs = length xs == 0+OUTPUT+tests/note.hs:1:1-32: Warning: Unused LANGUAGE pragma+Found:+ {-# LANGUAGE RecordWildCards #-}+Perhaps you should remove it.+Note: may require `{-# LANGUAGE DisambiguateRecordFields #-}` adding to the top of the file++tests/note.hs:5:11-24: Suggestion: Use null+Found:+ length xs == 0+Perhaps:+ null xs+Note: increases laziness++2 hints++---------------------------------------------------------------------+RUN tests/brackets.hs+FILE tests/brackets.hs+test = if isNothing x then (-1.0) else fromJust x+OUTPUT+tests/brackets.hs:1:8-49: Warning: Use fromMaybe+Found:+ if isNothing x then (- 1.0) else fromJust x+Perhaps:+ fromMaybe (- 1.0) x++1 hint++---------------------------------------------------------------------+RUN tests/typesig-ignore.hs+FILE tests/typesig-ignore.hs+-- Bug #563+module Foo(foobar) where+{-# ANN foobar "HLint: ignore Use String" #-}++foobar :: [Char]+foobar = []+OUTPUT+No hints++---------------------------------------------------------------------+RUN tests/typesig-ignore2.hs+FILE tests/typesig-ignore2.hs+-- Bug #563+module Foo(foobar) where+{-# HLINT ignore foobar "Use String" #-}++foobar :: [Char]+foobar = []+OUTPUT+No hints++---------------------------------------------------------------------+RUN tests/restricted-module.lhs --hint=data/test-restrict.yaml+FILE tests/restricted-module.lhs+> import Restricted.Module+OUTPUT+tests/restricted-module.lhs:1:3-26: Warning: Avoid restricted module+Found:+ import Restricted.Module+Note: may break the code++1 hint++---------------------------------------------------------------------+RUN tests/restricted-module-message.lhs --hint=data/test-restrict.yaml+FILE tests/restricted-module-message.lhs+> import Restricted.Module.Message+OUTPUT+tests/restricted-module-message.lhs:1:3-34: Warning: Avoid restricted module+Found:+ import Restricted.Module.Message+Note: Custom message++1 hint++---------------------------------------------------------------------+RUN tests/restricted-function.lhs --hint=data/test-restrict.yaml+FILE tests/restricted-function.lhs+> main = restricted ()+OUTPUT+tests/restricted-function.lhs:1:10-19: Warning: Avoid restricted function+Found:+ restricted+Note: may break the code++1 hint++---------------------------------------------------------------------+RUN tests/restricted-function-message.lhs --hint=data/test-restrict.yaml+FILE tests/restricted-function-message.lhs+> main = restrictedMessage ()+OUTPUT+tests/restricted-function-message.lhs:1:10-26: Warning: Avoid restricted function+Found:+ restrictedMessage+Note: Custom message++1 hint++---------------------------------------------------------------------+RUN tests/restricted-extension.hs --hint=data/test-restrict.yaml+FILE tests/restricted-extension.hs+{-# LANGUAGE DeriveFoldable #-}+{-# LANGUAGE DeriveFunctor #-}+{-# LANGUAGE DeriveTraversable #-}+{-# LANGUAGE DeriveDataTypeable #-}+module Test(A) where++import Data.Foldable (Foldable)+import Data.Traversable (Traversable)+import Data.Typeable (Typeable)++newtype A f = A f+ deriving (Foldable, Functor, Traversable, Typeable)+OUTPUT+OUTPUT+tests/restricted-extension.hs:1:1-31: Warning: Unused LANGUAGE pragma+Found:+ {-# LANGUAGE DeriveFoldable #-}+Perhaps you should remove it.+Note: Extension DeriveFoldable is implied by DeriveTraversable++tests/restricted-extension.hs:2:1-30: Warning: Unused LANGUAGE pragma+Found:+ {-# LANGUAGE DeriveFunctor #-}+Perhaps you should remove it.+Note: Extension DeriveFunctor is implied by DeriveTraversable++tests/restricted-extension.hs:2:1-30: Warning: Avoid restricted extensions+Found:+ {-# LANGUAGE DeriveFunctor #-}+Note: may break the code++tests/restricted-extension.hs:3:1-34: Warning: Avoid restricted extensions+Found:+ {-# LANGUAGE DeriveTraversable #-}+Note: Custom message++4 hints
+ tests/json.test view
@@ -0,0 +1,40 @@+---------------------------------------------------------------------+RUN tests/json-none.hs --json+FILE tests/json-none.hs+foo = (+1)+OUTPUT+[]++---------------------------------------------------------------------+RUN tests/json-one.hs --json+FILE tests/json-one.hs+foo = (+1)+bar x = foo x+OUTPUT+[{"module":["Main"],"decl":["bar"],"severity":"Warning","hint":"Eta reduce","file":"tests/json-one.hs","startLine":2,"startColumn":1,"endLine":2,"endColumn":14,"from":"bar x = foo x","to":"bar = foo","note":[],"refactorings":"[Replace {rtype = Decl, pos = SrcSpan {startLine = 2, startCol = 1, endLine = 2, endCol = 14}, subts = [(\"body\",SrcSpan {startLine = 2, startCol = 9, endLine = 2, endCol = 12})], orig = \"bar = body\"}]"}]++---------------------------------------------------------------------+RUN tests/json-two.hs --json+FILE tests/json-two.hs+foo = (+1)+bar x = foo x+baz = getLine >>= return . map toUpper+OUTPUT+[{"module":["Main"],"decl":["bar"],"severity":"Warning","hint":"Eta reduce","file":"tests/json-two.hs","startLine":2,"startColumn":1,"endLine":2,"endColumn":14,"from":"bar x = foo x","to":"bar = foo","note":[],"refactorings":"[Replace {rtype = Decl, pos = SrcSpan {startLine = 2, startCol = 1, endLine = 2, endCol = 14}, subts = [(\"body\",SrcSpan {startLine = 2, startCol = 9, endLine = 2, endCol = 12})], orig = \"bar = body\"}]"}+,{"module":["Main"],"decl":["baz"],"severity":"Suggestion","hint":"Use <&>","file":"tests/json-two.hs","startLine":3,"startColumn":7,"endLine":3,"endColumn":39,"from":"getLine >>= return . map toUpper","to":"getLine Data.Functor.<&> map toUpper","note":[],"refactorings":"[Replace {rtype = Expr, pos = SrcSpan {startLine = 3, startCol = 7, endLine = 3, endCol = 39}, subts = [(\"f\",SrcSpan {startLine = 3, startCol = 28, endLine = 3, endCol = 39}),(\"m\",SrcSpan {startLine = 3, startCol = 7, endLine = 3, endCol = 14})], orig = \"m Data.Functor.<&> f\"}]"}]++---------------------------------------------------------------------+RUN tests/json-parse-error.hs --json+FILE tests/json-parse-error.hs+@+OUTPUT+[{"module":[],"decl":[],"severity":"Error","hint":"Parse error: on input `@'","file":"tests/json-parse-error.hs","startLine":1,"startColumn":1,"endLine":1,"endColumn":2,"from":"> @\n","to":null,"note":[],"refactorings":"[]"}]++---------------------------------------------------------------------+RUN tests/json-note.hs --json+FILE tests/json-note.hs+foo = any (a ==)+bar = foldl (&&) True+OUTPUT+[{"module":["Main"],"decl":["foo"],"severity":"Warning","hint":"Use elem","file":"tests/json-note.hs","startLine":1,"startColumn":7,"endLine":1,"endColumn":17,"from":"any (a ==)","to":"elem a","note":["requires a valid `Eq` instance for `a`"],"refactorings":"[Replace {rtype = Expr, pos = SrcSpan {startLine = 1, startCol = 7, endLine = 1, endCol = 17}, subts = [(\"a\",SrcSpan {startLine = 1, startCol = 12, endLine = 1, endCol = 13})], orig = \"elem a\"}]"}+,{"module":["Main"],"decl":["bar"],"severity":"Warning","hint":"Use and","file":"tests/json-note.hs","startLine":2,"startColumn":7,"endLine":2,"endColumn":22,"from":"foldl (&&) True","to":"and","note":["increases laziness"],"refactorings":"[Replace {rtype = Expr, pos = SrcSpan {startLine = 2, startCol = 7, endLine = 2, endCol = 22}, subts = [], orig = \"and\"}]"}]
+ tests/lhs.test view
@@ -0,0 +1,47 @@+---------------------------------------------------------------------+RUN tests/lhs-line-numbers.lhs+FILE tests/lhs-line-numbers.lhs+BUG 331++> main = print ([1] ++ [2, 3])+OUTPUT+tests/lhs-line-numbers.lhs:3:17-29: Suggestion: Use :+Found:+ [1] ++ [2, 3]+Perhaps:+ 1 : [2, 3]++1 hint++---------------------------------------------------------------------+RUN tests/lhs-line-numbers2.lhs+FILE tests/lhs-line-numbers2.lhs+BUG 331+% Blah1+% Blah2+% Blah3++\begin{code}+module+\end{code}+OUTPUT+tests/lhs-line-numbers2.lhs:10:1: Error: Parse error: possibly incorrect indentation or mismatched brackets+Found:+ \end{code}++ >++1 hint++---------------------------------------------------------------------+RUN tests/lhs-first-line.lhs+FILE tests/lhs-first-line.lhs+> main = print ([1] ++ [2, 3])+OUTPUT+tests/lhs-first-line.lhs:1:17-29: Suggestion: Use :+Found:+ [1] ++ [2, 3]+Perhaps:+ 1 : [2, 3]++1 hint
+ tests/parse-error.test view
@@ -0,0 +1,33 @@+---------------------------------------------------------------------+RUN "--ignore=Parse error" tests/ignore-parse-error.hs+FILE tests/ignore-parse-error.hs+where+OUTPUT+No hints++---------------------------------------------------------------------+RUN tests/ignore-parse-error2.hs+FILE tests/ignore-parse-error2.hs+module Foo where++where+OUTPUT+tests/ignore-parse-error2.hs:3:1-5: Error: Parse error: on input `where'+Found:+ module Foo where++ > where++1 hint++---------------------------------------------------------------------+RUN tests/ignore-parse-error3.hs+FILE tests/ignore-parse-error3.hs+{-# LANGUAGE InvalidExtension #-}+OUTPUT+tests/ignore-parse-error3.hs:1:1: Error: Parse error: Unsupported extension: InvalidExtension++Found:+ {-# LANGUAGE InvalidExtension #-}++1 hint
+ tests/serialise.test view
@@ -0,0 +1,85 @@+---------------------------------------------------------------------+RUN tests/serialise-none.hs --serialise+FILE tests/serialise-none.hs+foo = (+1)+OUTPUT+[]++---------------------------------------------------------------------+RUN tests/serialise-one.hs --serialise+FILE tests/serialise-one.hs+foo = (+1)+bar x = foo x+OUTPUT+[("tests/serialise-one.hs:2:1-13: Warning: Eta reduce\nFound:\n bar x = foo x\nPerhaps:\n bar = foo\n",[Replace {rtype = Decl, pos = SrcSpan {startLine = 2, startCol = 1, endLine = 2, endCol = 14}, subts = [("body",SrcSpan {startLine = 2, startCol = 9, endLine = 2, endCol = 12})], orig = "bar = body"}])]++---------------------------------------------------------------------+RUN tests/serialise-two.hs --serialise+FILE tests/serialise-two.hs+foo = (+1)+bar x = foo x+baz = getLine >>= return . map toUpper+OUTPUT+[("tests/serialise-two.hs:2:1-13: Warning: Eta reduce\nFound:\n bar x = foo x\nPerhaps:\n bar = foo\n",[Replace {rtype = Decl, pos = SrcSpan {startLine = 2, startCol = 1, endLine = 2, endCol = 14}, subts = [("body",SrcSpan {startLine = 2, startCol = 9, endLine = 2, endCol = 12})], orig = "bar = body"}]),("tests/serialise-two.hs:3:7-38: Suggestion: Use <&>\nFound:\n getLine >>= return . map toUpper\nPerhaps:\n getLine Data.Functor.<&> map toUpper\n",[Replace {rtype = Expr, pos = SrcSpan {startLine = 3, startCol = 7, endLine = 3, endCol = 39}, subts = [("f",SrcSpan {startLine = 3, startCol = 28, endLine = 3, endCol = 39}),("m",SrcSpan {startLine = 3, startCol = 7, endLine = 3, endCol = 14})], orig = "m Data.Functor.<&> f"}])]+++---------------------------------------------------------------------+RUN tests/serialise-three.hs --serialise+FILE tests/serialise-three.hs+foo = concat (map f (let x = x in x))+OUTPUT+[("tests/serialise-three.hs:1:7-37: Warning: Use concatMap\nFound:\n concat (map f (let x = x in x))\nPerhaps:\n concatMap f (let x = x in x)\n",[Replace {rtype = Expr, pos = SrcSpan {startLine = 1, startCol = 7, endLine = 1, endCol = 38}, subts = [("f",SrcSpan {startLine = 1, startCol = 19, endLine = 1, endCol = 20}),("x",SrcSpan {startLine = 1, startCol = 21, endLine = 1, endCol = 37})], orig = "concatMap f x"}])]+++---------------------------------------------------------------------+RUN tests/serialise-four.hs --serialise --hint=data/hlint.yaml+FILE tests/serialise-four.hs+{-# LANGUAGE CPP #-}+{-# LANGUAGE CPP #-}+OUTPUT+[("tests/serialise-four.hs:1:1-20: Warning: Use fewer LANGUAGE pragmas\nFound:\n {-# LANGUAGE CPP #-}\n {-# LANGUAGE CPP #-}\nPerhaps:\n {-# LANGUAGE CPP #-}\n",[ModifyComment {pos = SrcSpan {startLine = 1, startCol = 1, endLine = 1, endCol = 21}, newComment = "{-# LANGUAGE CPP #-}"},ModifyComment {pos = SrcSpan {startLine = 2, startCol = 1, endLine = 2, endCol = 21}, newComment = ""}])]+++---------------------------------------------------------------------+RUN tests/serialise-five.hs --serialise+FILE tests/serialise-five.hs+import qualified GHC as GHC+OUTPUT+[("tests/serialise-five.hs:1:1-27: Suggestion: Redundant as\nFound:\n import qualified GHC as GHC\nPerhaps:\n import qualified GHC\n",[RemoveAsKeyword {pos = SrcSpan {startLine = 1, startCol = 1, endLine = 1, endCol = 28}}])]+++---------------------------------------------------------------------+RUN tests/serialise-six.hs --serialise+FILE tests/serialise-six.hs+foo = qux (\x -> f (g x))+OUTPUT+[("tests/serialise-six.hs:1:12-24: Suggestion: Avoid lambda\nFound:\n / x -> f (g x)\nPerhaps:\n f . g\n",[Replace {rtype = Expr, pos = SrcSpan {startLine = 1, startCol = 12, endLine = 1, endCol = 25}, subts = [("a",SrcSpan {startLine = 1, startCol = 18, endLine = 1, endCol = 19}),("b",SrcSpan {startLine = 1, startCol = 21, endLine = 1, endCol = 22})], orig = "a . b"}])]+++---------------------------------------------------------------------+RUN tests/serialise-seven.hs --serialise+FILE tests/serialise-seven.hs+foo = if baz+ then qux+ else if baz'+ then qux'+ else qux''+OUTPUT+[("tests/serialise-seven.hs:(1,1)-(5,23): Suggestion: Use guards\nFound:\n foo = if baz then qux else if baz' then qux' else qux''\nPerhaps:\n foo\n | baz = qux\n | baz' = qux'\n | otherwise = qux''\n",[Replace {rtype = Match, pos = SrcSpan {startLine = 1, startCol = 1, endLine = 5, endCol = 24}, subts = [("g1001",SrcSpan {startLine = 1, startCol = 10, endLine = 1, endCol = 13}),("g1002",SrcSpan {startLine = 3, startCol = 17, endLine = 3, endCol = 21}),("e1001",SrcSpan {startLine = 2, startCol = 14, endLine = 2, endCol = 17}),("e1002",SrcSpan {startLine = 4, startCol = 19, endLine = 4, endCol = 23}),("e1003",SrcSpan {startLine = 5, startCol = 19, endLine = 5, endCol = 24})], orig = "foo\n | g1001 = e1001\n | g1002 = e1002\n | otherwise = e1003"}])]+++---------------------------------------------------------------------+RUN tests/serialise-eight.hs --serialise+FILE tests/serialise-eight.hs+foo = do x <- baz+ x+OUTPUT+[("tests/serialise-eight.hs:(1,7)-(2,10): Warning: Use join\nFound:\n do x <- baz\n x\nPerhaps:\n do join baz\n",[Replace {rtype = Stmt, pos = SrcSpan {startLine = 1, startCol = 10, endLine = 1, endCol = 18}, subts = [("x",SrcSpan {startLine = 1, startCol = 15, endLine = 1, endCol = 18})], orig = "join x"},Delete {rtype = Stmt, pos = SrcSpan {startLine = 2, startCol = 10, endLine = 2, endCol = 11}}])]+++---------------------------------------------------------------------+RUN tests/serialise-nine.hs --serialise+FILE tests/serialise-nine.hs+foo | True = baz+OUTPUT+[("tests/serialise-nine.hs:1:1-16: Suggestion: Redundant guard\nFound:\n foo | True = baz\nPerhaps:\n foo = baz\n",[Delete {rtype = Stmt, pos = SrcSpan {startLine = 1, startCol = 7, endLine = 1, endCol = 11}}])]
+ tests/wildcard.test view
@@ -0,0 +1,45 @@+---------------------------------------------------------------------+RUN tests/wildcard-1.hs --hint=data/wildcard.yaml+FILE tests/wildcard-1.hs+module SpecNoMatch where+main = print $ (\ _ -> 'a') ()+OUTPUT+tests/wildcard-1.hs:2:17-26: Suggestion: Use const+Found:+ \ _ -> 'a'+Perhaps:+ const 'a'++1 hint++---------------------------------------------------------------------+RUN tests/wildcard-2.hs --hint=data/wildcard.yaml+FILE tests/wildcard-2.hs+module Spec where+main = print $ (\ _ -> 'a') ()+OUTPUT+No hints++---------------------------------------------------------------------+RUN tests/wildcard-3.hs --hint=data/wildcard.yaml+FILE tests/wildcard-3.hs+module PrefixedSpec where+main = print $ (\ _ -> 'a') ()+OUTPUT+No hints++---------------------------------------------------------------------+RUN tests/wildcard-4.hs --hint=data/wildcard.yaml+FILE tests/wildcard-4.hs+module Namespaced.Spec where+main = print $ (\ _ -> 'a') ()+OUTPUT+No hints++---------------------------------------------------------------------+RUN tests/wildcard-5.hs --hint=data/wildcard.yaml+FILE tests/wildcard-5.hs+module Deeply.Nested.Spec where+main = print $ (\ _ -> 'a') ()+OUTPUT+No hints