hlint 2.1.23 → 2.1.24
raw patch · 14 files changed
+203/−58 lines, 14 filesdep −semigroupsPVP ok
version bump matches the API change (PVP)
Dependencies removed: semigroups
API changes (from Hackage documentation)
+ Language.Haskell.HLint4: Classify :: Severity -> String -> String -> String -> Classify
+ Language.Haskell.HLint4: CppSimple :: CppFlags
+ Language.Haskell.HLint4: Cpphs :: CpphsOptions -> CppFlags
+ Language.Haskell.HLint4: DecreasesLaziness :: Note
+ Language.Haskell.HLint4: Error :: Severity
+ Language.Haskell.HLint4: Idea :: [String] -> [String] -> Severity -> String -> SrcSpan -> String -> Maybe String -> [Note] -> [Refactoring SrcSpan] -> Idea
+ Language.Haskell.HLint4: Ignore :: Severity
+ Language.Haskell.HLint4: IncreasesLaziness :: Note
+ Language.Haskell.HLint4: NoCpp :: CppFlags
+ Language.Haskell.HLint4: Note :: String -> Note
+ Language.Haskell.HLint4: ParseError :: SrcLoc -> String -> String -> ParseError
+ Language.Haskell.HLint4: ParseFlags :: CppFlags -> ParseMode -> ParseFlags
+ Language.Haskell.HLint4: RemovesError :: String -> Note
+ Language.Haskell.HLint4: RequiresExtension :: String -> Note
+ Language.Haskell.HLint4: Suggestion :: Severity
+ Language.Haskell.HLint4: ValidInstance :: String -> String -> Note
+ Language.Haskell.HLint4: Warning :: Severity
+ Language.Haskell.HLint4: [classifyDecl] :: Classify -> String
+ Language.Haskell.HLint4: [classifyHint] :: Classify -> String
+ Language.Haskell.HLint4: [classifyModule] :: Classify -> String
+ Language.Haskell.HLint4: [classifySeverity] :: Classify -> Severity
+ Language.Haskell.HLint4: [cppFlags] :: ParseFlags -> CppFlags
+ Language.Haskell.HLint4: [hseFlags] :: ParseFlags -> ParseMode
+ Language.Haskell.HLint4: [ideaDecl] :: Idea -> [String]
+ Language.Haskell.HLint4: [ideaFrom] :: Idea -> String
+ Language.Haskell.HLint4: [ideaHint] :: Idea -> String
+ Language.Haskell.HLint4: [ideaModule] :: Idea -> [String]
+ Language.Haskell.HLint4: [ideaNote] :: Idea -> [Note]
+ Language.Haskell.HLint4: [ideaRefactoring] :: Idea -> [Refactoring SrcSpan]
+ Language.Haskell.HLint4: [ideaSeverity] :: Idea -> Severity
+ Language.Haskell.HLint4: [ideaSpan] :: Idea -> SrcSpan
+ Language.Haskell.HLint4: [ideaTo] :: Idea -> Maybe String
+ Language.Haskell.HLint4: [parseErrorContents] :: ParseError -> String
+ Language.Haskell.HLint4: [parseErrorLocation] :: ParseError -> SrcLoc
+ Language.Haskell.HLint4: [parseErrorMessage] :: ParseError -> String
+ Language.Haskell.HLint4: applyHints :: [Classify] -> Hint -> [ModuleEx] -> [Idea]
+ Language.Haskell.HLint4: argsSettings :: [String] -> IO (ParseFlags, [Classify], Hint)
+ Language.Haskell.HLint4: autoSettings :: IO (ParseFlags, [Classify], Hint)
+ Language.Haskell.HLint4: data Classify
+ Language.Haskell.HLint4: data CppFlags
+ Language.Haskell.HLint4: data Hint
+ Language.Haskell.HLint4: data Idea
+ Language.Haskell.HLint4: data ModuleEx
+ Language.Haskell.HLint4: data Note
+ Language.Haskell.HLint4: data ParseError
+ Language.Haskell.HLint4: data ParseFlags
+ Language.Haskell.HLint4: data Severity
+ Language.Haskell.HLint4: defaultParseFlags :: ParseFlags
+ Language.Haskell.HLint4: findSettings :: (String -> IO (FilePath, Maybe String)) -> Maybe String -> IO ([Fixity], [Classify], Hint)
+ Language.Haskell.HLint4: getHLintDataDir :: IO FilePath
+ Language.Haskell.HLint4: hlint :: [String] -> IO [Idea]
+ Language.Haskell.HLint4: parseFlagsAddFixities :: [Fixity] -> ParseFlags -> ParseFlags
+ Language.Haskell.HLint4: parseModuleEx :: ParseFlags -> FilePath -> Maybe String -> IO (Either ParseError ModuleEx)
+ Language.Haskell.HLint4: readSettingsFile :: Maybe FilePath -> String -> IO (FilePath, Maybe String)
+ Language.Haskell.HLint4: resolveHints :: Hint -> Hint
Files
- CHANGES.txt +7/−0
- README.md +3/−1
- data/hlint.yaml +9/−10
- hlint.cabal +2/−3
- src/Apply.hs +6/−4
- src/Config/Compute.hs +2/−2
- src/Config/Haskell.hs +3/−3
- src/Grep.hs +2/−2
- src/HLint.hs +1/−1
- src/HSE/All.hs +11/−13
- src/HSE/Unify.hs +4/−5
- src/Hint/Match.hs +1/−1
- src/Language/Haskell/HLint3.hs +14/−13
- src/Language/Haskell/HLint4.hs +138/−0
CHANGES.txt view
@@ -1,5 +1,12 @@ Changelog for HLint (* = breaking change) +2.1.24, released 2019-06-10+ Add Language.Haskell.HLint4+ #658, ignore the previously undocumented {- LINT -} comments+ #658, force parsing of all pragmas and comments eagerly+ #665, make different fromMaybe hints have different names+ #664, better name for the Use uncurry hint+ #659, make hints with brackets at the root work 2.1.23, released 2019-06-09 Make it an error if your code does not parse with GHC #662, don't warn on ($x), since it might not really be TH
README.md view
@@ -1,6 +1,6 @@ # 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 is a tool for suggesting possible improvements to Haskell code. These suggestions include ideas such as using alternative functions, simplifying code and spotting redundancies. You can try HLint online at [lpaste.net](http://lpaste.net/) - suggestions are shown at the bottom. This document is structured as follows:+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: * [Installing and running HLint](#installing-and-running-hlint) * [FAQ](#faq)@@ -86,6 +86,8 @@ * [Code Climate](https://docs.codeclimate.com/v1.0/docs/hlint) is a CI for analysis which integrates HLint. * [Danger](http://allocinit.io/haskell/danger-and-hlint/) can be used to automatically comment on pull requests with HLint suggestions. * [Restyled](https://restyled.io) includes an HLint Restyler to automatically run `hlint --refactor` on files changed in GitHub Pull Requests.+* [lpaste](http://lpaste.net/) integrates with HLint - suggestions are shown at the bottom.+* [hlint-test](https://hackage.haskell.org/package/hlint-test) helps you write a small test runner with HLint. ### Automatically Applying Hints
data/hlint.yaml view
@@ -261,15 +261,14 @@ - warn: {lhs: \x y -> x, rhs: const} - warn: {lhs: "\\(x,y) -> y", rhs: snd} - warn: {lhs: "\\(x,y) -> x", rhs: fst}- - hint: {lhs: "\\x y -> f (x,y)", rhs: curry f, name: Use curry}- - hint: {lhs: "\\(x,y) -> f x y", rhs: uncurry f, note: IncreasesLaziness, name: Use uncurry}- - warn: {lhs: ($) . f, rhs: f, name: Redundant $}- - warn: {lhs: (f $), rhs: f, name: Redundant $}+ - hint: {lhs: "\\x y -> f (x,y)", rhs: curry f}+ - hint: {lhs: "\\(x,y) -> f x y", rhs: uncurry f, note: IncreasesLaziness}+ - warn: {lhs: f (fst p) (snd p), rhs: uncurry f p} - warn: {lhs: (Data.Function.& f), rhs: f, name: Redundant Data.Function.&} - hint: {lhs: \x -> y, rhs: const y, side: isAtom y && not (isWildcard y)} # If any isWildcard recursively then x may be used but not mentioned explicitly- - warn: {lhs: flip f x y, rhs: f y x, side: isApp original, name: Redundant flip}- - warn: {lhs: id x, rhs: x, side: not (isTypeApp x), name: Redundant id}+ - warn: {lhs: flip f x y, rhs: f y x, side: isApp original}+ - warn: {lhs: id x, rhs: x, side: not (isTypeApp x)} - warn: {lhs: id . x, rhs: x, name: Redundant id} - warn: {lhs: x . id, rhs: x, name: Redundant id} - warn: {lhs: "((,) x)", rhs: "(_noParen_ x,)", name: Use tuple-section, note: RequiresExtension TupleSections}@@ -473,7 +472,8 @@ - warn: {lhs: "maybe [] (:[])", rhs: maybeToList} - warn: {lhs: catMaybes (map f x), rhs: mapMaybe f x} - warn: {lhs: catMaybes (fmap f x), rhs: mapMaybe f x}- - hint: {lhs: case x of Nothing -> y; Just a -> a , rhs: fromMaybe y x}+ - hint: {lhs: case x of Nothing -> y; Just a -> a , rhs: Data.Maybe.fromMaybe y x, name: Replace case with fromMaybe}+ - hint: {lhs: case x of Just a -> a; Nothing -> y, rhs: Data.Maybe.fromMaybe y x, name: Replace case with fromMaybe} - warn: {lhs: if isNothing x then y else f (fromJust x), rhs: maybe y f x} - warn: {lhs: if isJust x then f (fromJust x) else y, rhs: maybe y f x} - warn: {lhs: maybe Nothing (Just . f), rhs: fmap f}@@ -485,7 +485,6 @@ - warn: {lhs: concatMap (maybeToList . f), rhs: Data.Maybe.mapMaybe f} - warn: {lhs: concatMap maybeToList, rhs: catMaybes} - warn: {lhs: maybe n Just x, rhs: x Control.Applicative.<|> n}- - hint: {lhs: case x of Just a -> a; Nothing -> y, rhs: fromMaybe y x} - warn: {lhs: if isNothing x then y else fromJust x, rhs: fromMaybe y x} - warn: {lhs: if isJust x then fromJust x else y, rhs: fromMaybe y x} - warn: {lhs: isJust x && (fromJust x == y), rhs: x == Just y}@@ -606,7 +605,6 @@ - warn: {lhs: either f g (Right y), rhs: g y, name: Evaluate} - warn: {lhs: "fst (x,y)", rhs: x, name: Evaluate} - warn: {lhs: "snd (x,y)", rhs: "y", name: Evaluate}- - warn: {lhs: f (fst p) (snd p), rhs: uncurry f p, name: Evaluate} - warn: {lhs: "init [x]", rhs: "[]", name: Evaluate} - warn: {lhs: "null []", rhs: "True", name: Evaluate} - warn: {lhs: "length []", rhs: "0", name: Evaluate}@@ -835,7 +833,7 @@ # yes x = case x of {True -> a ; False -> b} -- if x then a else b # yes x = case x of {False -> a ; _ -> b} -- if x then b else a # no = const . ok . toResponse $ "saved"-# yes = case x z of Nothing -> y z; Just pat -> pat -- fromMaybe (y z) (x z)+# yes = case x z of Nothing -> y z; Just pat -> pat -- Data.Maybe.fromMaybe (y z) (x z) # yes = if p then s else return () -- Control.Monad.when p s # warn = a $$$$ b $$$$ c ==> a . b $$$$$ c # yes = when (not . null $ asdf) -- unless (null asdf)@@ -913,6 +911,7 @@ # no = sequence (return x) # no = sequenceA (pure a) # {-# LANGUAGE QuasiQuotes #-}; no = f (\url -> [hamlet|foo @{url}|])+# yes = f ((,) x) -- (x,) # import Prelude \ # yes = flip mapM -- Control.Monad.forM
hlint.cabal view
@@ -1,7 +1,7 @@ cabal-version: >= 1.18 build-type: Simple name: hlint-version: 2.1.23+version: 2.1.24 license: BSD3 license-file: LICENSE category: Development@@ -66,13 +66,12 @@ build-depends: hscolour >= 1.21 else cpp-options: -DGPL_SCARES_ME- if impl(ghc < 8.0)- build-depends: semigroups >= 0.18 hs-source-dirs: src exposed-modules: Language.Haskell.HLint Language.Haskell.HLint3+ Language.Haskell.HLint4 other-modules: Paths_hlint Apply
src/Apply.hs view
@@ -36,7 +36,7 @@ -- | Given a way of classifying results, and a 'Hint', apply to a set of modules generating a list of 'Idea's. -- The 'Idea' values will be ordered within a file. ----- Given a set of modules, it may be faster pass each to 'applyHints' in a singleton list.+-- Given a set of modules, it may be faster to pass each to 'applyHints' in a singleton list. -- 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).@@ -45,11 +45,13 @@ applyHintsReal :: [Setting] -> Hint -> [(Module_, [Comment])] -> [Idea] applyHintsReal settings hints_ ms = concat $- [ map (classify (cls ++ mapMaybe readPragma (universeBi m) ++ concatMap readComment cs) . removeRequiresExtensionNotes m) $+ [ map (classify classifiers . removeRequiresExtensionNotes m) $ order [] (hintModule hints settings nm m) `merge` concat [order [fromNamed d] $ decHints d | d <- moduleDecls m] `merge` concat [order [] $ hintComment hints settings c | c <- cs] | (nm,(m,cs)) <- mns+ , let classifiers = cls ++ mapMaybe readPragma (universeBi m) ++ concatMap readComment cs+ , seq (length classifiers) True -- to force any errors from readPragma or readComment , let decHints = hintDecl hints settings nm m -- partially apply , let order n = map (\i -> i{ideaModule= f $ moduleName m : ideaModule i, ideaDecl= f $ n ++ ideaDecl i}) . sortOn ideaSpan , let merge = mergeBy (comparing ideaSpan)] ++@@ -77,9 +79,9 @@ -- | Return either an idea (a parse error) or the module. In IO because might call the C pre processor. parseModuleApply :: ParseFlags -> [Setting] -> FilePath -> Maybe String -> IO (Either Idea (Module_, [Comment])) parseModuleApply flags s file src = do- res <- parseModuleExInternal (parseFlagsAddFixities [x | Infix x <- s] flags) file src+ res <- parseModuleEx (parseFlagsAddFixities [x | Infix x <- s] flags) file src case res of- Right (ParsedModuleResults (m, c) _) -> return $ Right (m,c)+ Right (ModuleEx (m, c) _) -> return $ Right (m,c) Left (ParseError sl msg ctxt) -> return $ Left $ classify [x | SettingClassify x <- s] $ rawIdeaN Error "Parse error" (mkSrcSpan sl sl) ctxt Nothing []
src/Config/Compute.hs view
@@ -14,11 +14,11 @@ -- Returns the text of the hints (if you want to save it down) along with the settings to be used. computeSettings :: ParseFlags -> FilePath -> IO (String, [Setting]) computeSettings flags file = do- x <- parseModuleExInternal flags file Nothing+ x <- parseModuleEx flags file Nothing case x of Left (ParseError sl msg _) -> return ("# Parse error " ++ showSrcLoc sl ++ ": " ++ msg, [])- Right (ParsedModuleResults (m, _) _) -> do+ Right (ModuleEx (m, _) _) -> do let xs = concatMap (findSetting $ UnQual an) (moduleDecls m) r = concatMap (readSetting mempty) xs s = unlines $ ["# hints found in " ++ file] ++ concatMap renderSetting r ++ ["# no hints found" | null xs]
src/Config/Haskell.hs view
@@ -27,11 +27,11 @@ readFileConfigHaskell :: FilePath -> Maybe String -> IO [Setting] readFileConfigHaskell file contents = do let flags = addInfix defaultParseFlags- res <- parseModuleExInternal flags file contents+ res <- parseModuleEx flags file contents case res of Left (ParseError sl msg err) -> error $ "Config parse failure at " ++ showSrcLoc sl ++ ": " ++ msg ++ "\n" ++ err- Right (ParsedModuleResults (m, cs) _) -> return $ readSettings m ++ map SettingClassify (concatMap readComment cs)+ Right (ModuleEx (m, cs) _) -> return $ readSettings m ++ map SettingClassify (concatMap readComment cs) -- | Given a module containing HLint settings information return the 'Classify' rules and the 'HintRule' expressions.@@ -83,7 +83,7 @@ | (hash, x) <- maybe (False, x) (True,) $ stripPrefix "#" x , x <- trim x , (hlint, x) <- word1 x- , lower hlint `elem` ["lint","hlint"]+ , lower hlint == "hlint" = f hash x where f hash x
src/Grep.hs view
@@ -22,10 +22,10 @@ let scope = scopeCreate $ Module an Nothing [] [] [] let rule = hintRules [HintRule Suggestion "grep" scope exp (Tuple an Boxed []) Nothing []] forM_ files $ \file -> do- res <- parseModuleExInternal flags file Nothing+ res <- parseModuleEx flags file Nothing case res of Left (ParseError sl msg ctxt) -> print $ rawIdeaN Error (if "Parse error" `isPrefixOf` msg then msg else "Parse error: " ++ msg) (mkSrcSpan sl sl) ctxt Nothing []- Right (ParsedModuleResults (m, c) _) ->+ Right (ModuleEx (m, c) _) -> forM_ (applyHints [] rule [(m, c)]) $ \i -> print i{ideaHint="", ideaTo=Nothing}
src/HLint.hs view
@@ -50,7 +50,7 @@ -- -- /Warning:/ The flags provided by HLint are relatively stable, but do not have the same -- API stability guarantees as the rest of the strongly-typed API. Do not run this function--- on a your server with untrusted input.+-- on your server with untrusted input. hlint :: [String] -> IO [Idea] hlint args = do cmd <- getCmd args
src/HSE/All.hs view
@@ -6,8 +6,8 @@ module X, CppFlags(..), ParseFlags(..), defaultParseFlags, parseFlagsAddFixities, parseFlagsSetLanguage,- parseModuleEx, ParseError(..), ParsedModuleResults(..),- parseModuleExInternal,+ ParseError(..), ModuleEx(..),+ parseModuleEx, freeVars, vars, varss, pvars ) where @@ -158,8 +158,9 @@ , parseErrorContents :: String -- ^ Snippet of several lines (typically 5) including a @>@ character pointing at the faulty line. } --- | Combined 'hs-src-ext' and 'ghc-lib-parser' parse trees.-data ParsedModuleResults = ParsedModuleResults {+-- | Result of 'parseModuleEx', representing a parsed module.+data ModuleEx = ModuleEx {+ -- Combined 'hs-src-ext' and 'ghc-lib-parser' parse trees. pm_hsext :: (Module SrcSpanInfo, [Comment]) -- hs-src-ext result , pm_ghclib :: Located (HsSyn.HsModule HsSyn.GhcPs) -- ghc-lib-parser result }@@ -176,7 +177,7 @@ -> SrcLoc -> String -> Maybe (GHC.SrcSpan, ErrUtils.MsgDoc)- -> IO (Either ParseError ParsedModuleResults)+ -> IO (Either ParseError ModuleEx) failOpParseModuleEx ppstr flags file str sl msg ghc = case ghc of Just err ->@@ -197,7 +198,7 @@ -> String -> SrcLoc -> String- -> IO (Either ParseError ParsedModuleResults)+ -> IO (Either ParseError ModuleEx) hseFailOpParseModuleEx ppstr flags file str sl msg = do flags <- return $ parseFlagsNoLocations flags ppstr2 <- runCpp (cppFlags flags) file str@@ -211,7 +212,7 @@ -> FilePath -> String -> (GHC.SrcSpan, ErrUtils.MsgDoc)- -> IO (Either ParseError ParsedModuleResults)+ -> IO (Either ParseError ModuleEx) ghcFailOpParseModuleEx ppstr file str (loc, err) = do let sl = case loc of@@ -233,11 +234,8 @@ -- filename @-@ is treated as @stdin@. Requires some flags (often -- 'defaultParseFlags'), the filename, and optionally the contents of -- that file. This version uses both hs-src-exts AND ghc-lib.-parseModuleEx :: ParseFlags -> FilePath -> Maybe String -> IO (Either ParseError (Module SrcSpanInfo, [Comment]))-parseModuleEx flags file str = fmap pm_hsext <$> parseModuleExInternal flags file str--parseModuleExInternal :: ParseFlags -> FilePath -> Maybe String -> IO (Either ParseError ParsedModuleResults)-parseModuleExInternal flags file str = timedIO "Parse" file $ do+parseModuleEx :: ParseFlags -> FilePath -> Maybe String -> IO (Either ParseError ModuleEx)+parseModuleEx flags file str = timedIO "Parse" file $ do str <- case str of Just x -> return x Nothing | file == "-" -> getContentsUTF8@@ -249,7 +247,7 @@ Right ghcFlags -> case (parseFileContentsWithComments (mkMode flags file) ppstr, parseFileGhcLib file ppstr ghcFlags) of (ParseOk (x, cs), POk _ a) ->- return $ Right (ParsedModuleResults (applyFixity fixity x, cs) a)+ return $ Right (ModuleEx (applyFixity fixity x, cs) a) -- Parse error if GHC parsing fails (see -- https://github.com/ndmitchell/hlint/issues/645). (ParseOk _, PFailed _ loc err) ->
src/HSE/Unify.hs view
@@ -97,11 +97,10 @@ -- root = True, this is the outside of the expr -- do not expand out a dot at the root, since otherwise you get two matches because of readRule (Bug #570) unifyExp :: NameMatch -> Bool -> Exp_ -> Exp_ -> Maybe (Subst Exp_)-unifyExp nm root x y | not root, isParen x || isParen y =- fmap (rebracket y) <$> unifyExp nm root (fromParen x) (fromParen y)- where- rebracket (Paren l e') e | e' == e = Paren l e- rebracket e e' = e'++-- brackets are not added when expanding $ in the users code, so tolerate them+-- in the match even if they aren't in the users code+unifyExp nm root x y | not root, isParen x, not $ isParen y = unifyExp nm root (fromParen x) y unifyExp nm root (Var _ (fromNamed -> v)) y | isUnifyVar v = Just $ Subst [(v,y)] unifyExp nm root (Var _ x) (Var _ y) | nm x y = Just mempty
src/Hint/Match.hs view
@@ -95,7 +95,7 @@ findIdeas matches s _ decl = timed "Hint" "Match apply" $ forceList [ (idea (hintRuleSeverity m) (hintRuleName m) x y [r]){ideaNote=notes} | decl <- findDecls decl- , (parent,x) <- universeParentExp decl, not $ isParen x+ , (parent,x) <- universeParentExp decl , m <- matches, Just (y,notes, subst) <- [matchIdea s decl m parent x] , let r = R.Replace R.Expr (toSS x) subst (prettyPrint $ hintRuleRHS m) ]
src/Language/Haskell/HLint3.hs view
@@ -1,17 +1,7 @@ {-# LANGUAGE PatternGuards, RecordWildCards #-} --- | /WARNING: This module represents the evolving second version of the HLint API./--- /It will be renamed to drop the "3" in the next major version./------ This module provides a way to apply HLint hints. If you want to just run @hlint@ in-process--- and collect the results see 'hlint'. If you want to approximate the @hlint@ experience with--- a more structured API try:------ @--- (flags, classify, hint) <- 'autoSettings'--- Right m <- 'parseModuleEx' flags \"MyFile.hs\" Nothing--- print $ 'applyHints' classify hint [m]--- @+-- | /WARNING: This module represents a previous version of the HLint API./+-- /Please use "Language.Haskell.HLint4" instead./ module Language.Haskell.HLint3( hlint, applyHints, -- * Idea data type@@ -34,7 +24,8 @@ import Idea import Apply import HLint-import HSE.All+import HSE.All hiding (parseModuleEx)+import qualified HSE.All as H import Hint.All import CmdLine import Paths_hlint@@ -115,6 +106,16 @@ ([x | Infix x <- xs] ,[x | SettingClassify x <- xs] ,[Right x | SettingMatchExp x <- xs] ++ map Left [minBound..maxBound])+++-- | 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. This version uses both hs-src-exts AND ghc-lib.+parseModuleEx :: ParseFlags -> FilePath -> Maybe String -> IO (Either ParseError (Module SrcSpanInfo, [Comment]))+parseModuleEx flags file str = fmap pm_hsext <$> H.parseModuleEx flags file str+ -- | Snippet from the documentation, if this changes, update the documentation _docs :: IO ()
+ src/Language/Haskell/HLint4.hs view
@@ -0,0 +1,138 @@+{-# LANGUAGE PatternGuards, RecordWildCards #-}++-- | /WARNING: This module represents the evolving version of the HLint API./+-- /It will be renamed to drop the "4" in the next major version./+--+-- This module provides a way to apply HLint hints. If you want to just run @hlint@ in-process+-- and collect the results see 'hlint'. If you want to approximate the @hlint@ experience with+-- a more structured API try:+--+-- @+-- (flags, classify, hint) <- 'autoSettings'+-- Right m <- 'parseModuleEx' flags \"MyFile.hs\" Nothing+-- print $ 'applyHints' classify hint [m]+-- @+module Language.Haskell.HLint4(+ hlint, applyHints,+ -- * Idea data type+ Idea(..), Severity(..), Note(..),+ -- * Settings+ Classify(..),+ getHLintDataDir, autoSettings, argsSettings,+ findSettings, readSettingsFile,+ -- * Hints+ Hint, resolveHints,+ -- * Parse files+ ModuleEx, parseModuleEx, defaultParseFlags, parseFlagsAddFixities, ParseError(..), ParseFlags(..), CppFlags(..)+ ) where++import Config.Type+import Config.Read+import Idea+import qualified Apply as H+import HLint+import HSE.All+import Hint.All hiding (resolveHints)+import qualified Hint.All as H+import CmdLine+import Paths_hlint++import Data.List.Extra+import Data.Maybe+import System.FilePath+import Data.Functor+import Prelude+++-- | Get the Cabal configured data directory of HLint.+getHLintDataDir :: IO FilePath+getHLintDataDir = getDataDir+++-- | The function produces a tuple containg 'ParseFlags' (for 'parseModuleEx'),+-- and 'Classify' and 'Hint' for 'applyHints'.+-- It approximates the normal HLint configuration steps, roughly:+--+-- 1. Use 'findSettings' with 'readSettingsFile' to find and load the HLint settings files.+--+-- 1. Use 'parseFlagsAddFixities' and 'resolveHints' to transform the outputs of 'findSettings'.+--+-- If you want to do anything custom (e.g. using a different data directory, storing intermediate outputs,+-- loading hints from a database) you are expected to copy and paste this function, then change it to your needs.+autoSettings :: IO (ParseFlags, [Classify], Hint)+autoSettings = do+ (fixities, classify, hints) <- findSettings (readSettingsFile Nothing) Nothing+ return (parseFlagsAddFixities fixities defaultParseFlags, classify, hints)+++-- | The identity function. In previous versions of HLint this function was useful. Now, it isn't.+resolveHints :: Hint -> Hint+resolveHints = id++-- | A version of 'autoSettings' which respects some of the arguments supported by HLint.+-- If arguments unrecognised by HLint are used it will result in an error.+-- Arguments which have no representation in the return type are silently ignored.+argsSettings :: [String] -> IO (ParseFlags, [Classify], Hint)+argsSettings args = do+ cmd <- getCmd args+ case cmd of+ CmdMain{..} -> do+ -- FIXME: Two things that could be supported (but aren't) are 'cmdGivenHints' and 'cmdWithHints'.+ (_,settings) <- readAllSettings args cmd+ let (fixities, classify, hints) = splitSettings settings+ let flags = parseFlagsSetLanguage (cmdExtensions cmd) $ parseFlagsAddFixities fixities $+ defaultParseFlags{cppFlags = cmdCpp cmd}+ let ignore = [Classify Ignore x "" "" | x <- cmdIgnore]+ return (flags, classify ++ ignore, hints)+ _ -> error "Can only invoke autoSettingsArgs with the root process"+++-- | Given a directory (or 'Nothing' to imply 'getHLintDataDir'), and a module name+-- (e.g. @HLint.Default@), find the settings file associated with it, returning the+-- name of the file, and (optionally) the contents.+--+-- This function looks for all settings files starting with @HLint.@ in the directory+-- argument, and all other files relative to the current directory.+readSettingsFile :: Maybe FilePath -> String -> IO (FilePath, Maybe String)+readSettingsFile dir x+ | takeExtension x `elem` [".yml",".yaml"] = do+ dir <- maybe getHLintDataDir return dir+ return (dir </> x, Nothing)+ | Just x <- "HLint." `stripPrefix` x = do+ dir <- maybe getHLintDataDir return dir+ return (dir </> x <.> "hs", Nothing)+ | otherwise = return (x <.> "hs", Nothing)+++-- | Given a function to load a module (typically 'readSettingsFile'), and a module to start from+-- (defaults to @hlint.yaml@) find the information from all settings files.+findSettings :: (String -> IO (FilePath, Maybe String)) -> Maybe String -> IO ([Fixity], [Classify], Hint)+findSettings load start = do+ (file,contents) <- load $ fromMaybe "hlint.yaml" start+ splitSettings <$> readFilesConfig [(file,contents)]++-- | Split a list of 'Setting' for separate use in parsing and hint resolution+splitSettings :: [Setting] -> ([Fixity], [Classify], Hint)+splitSettings xs =+ ([x | Infix x <- xs]+ ,[x | SettingClassify x <- xs]+ ,H.resolveHints $ [Right x | SettingMatchExp x <- xs] ++ map Left [minBound..maxBound])+++-- | Given a way of classifying results, and a 'Hint', apply to a set of modules generating a list of 'Idea's.+-- The 'Idea' values will be ordered within a file.+--+-- Given a set of modules, it may be faster to pass each to 'applyHints' in a singleton list.+-- 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 -> [ModuleEx] -> [Idea]+applyHints a b = H.applyHints a b . map pm_hsext+++-- | Snippet from the documentation, if this changes, update the documentation+_docs :: IO ()+_docs = do+ (flags, classify, hint) <- autoSettings+ Right m <- parseModuleEx flags "MyFile.hs" Nothing+ print $ applyHints classify hint [m]