hlint 2.1.2 → 2.1.3
raw patch · 17 files changed
+125/−27 lines, 17 filesPVP ok
version bump matches the API change (PVP)
API changes (from Hackage documentation)
Files
- CHANGES.txt +4/−0
- hlint.cabal +2/−1
- src/Apply.hs +1/−1
- src/CmdLine.hs +3/−1
- src/Config/Yaml.hs +2/−1
- src/HLint.hs +9/−1
- src/HSE/All.hs +2/−1
- src/Hint/All.hs +9/−7
- src/Hint/Extensions.hs +1/−1
- src/Hint/Match.hs +3/−1
- src/Hint/Naming.hs +5/−5
- src/Hint/Pragma.hs +2/−2
- src/Report.hs +2/−1
- src/Test/Proof.hs +5/−2
- src/Test/Translate.hs +2/−2
- src/Timing.hs +65/−0
- src/Util.hs +8/−0
CHANGES.txt view
@@ -1,5 +1,9 @@ Changelog for HLint (* = breaking change) +2.1.3, released 2018-04-18+ Improve the performance of the camelCase hint+ Don't suggest camelCase for record fields+ Add a --timing flag to detect what is slow 2.1.2, released 2018-04-16 #407, don't error on unknown extensions on the command line Require extra-1.6.6
hlint.cabal view
@@ -1,7 +1,7 @@ cabal-version: >= 1.18 build-type: Simple name: hlint-version: 2.1.2+version: 2.1.3 license: BSD3 license-file: LICENSE category: Development@@ -84,6 +84,7 @@ Util Parallel Refact+ Timing CC Config.Compute Config.Haskell
src/Apply.hs view
@@ -87,7 +87,7 @@ -- | Find which hints a list of settings implies. allHints :: [Setting] -> Hint allHints xs = mconcat $ hintRules [x | SettingMatchExp x <- xs] : map f builtin- where builtin = nub $ concat [if x == "All" then map fst builtinHints else [x] | Builtin x <- xs]+ where builtin = nubOrd $ concat [if x == "All" then map fst builtinHints else [x] | Builtin x <- xs] f x = fromMaybe (error $ "Unknown builtin hints: HLint.Builtin." ++ x) $ lookup x builtinHints
src/CmdLine.hs view
@@ -116,6 +116,7 @@ ,cmdNoSummary :: Bool -- ^ do not show the summary info ,cmdOnly :: [String] -- ^ specify which hints explicitly ,cmdNoExitCode :: Bool+ ,cmdTiming :: Bool ,cmdSerialise :: Bool -- ^ Display hints in serialisation format ,cmdRefactor :: Bool -- ^ Run the `refactor` executable to automatically perform hints ,cmdRefactorOptions :: String -- ^ Options to pass to the `refactor` executable.@@ -178,10 +179,11 @@ ,cmdNoSummary = nam_ "no-summary" &= help "Do not show summary information" ,cmdOnly = nam "only" &= typ "HINT" &= help "Specify which hints explicitly" ,cmdNoExitCode = nam_ "no-exit-code" &= help "Do not give a negative exit if hints"+ ,cmdTiming = nam_ "timing" &= help "Display timing information" ,cmdSerialise = nam_ "serialise" &= help "Serialise hint data for consumption by apply-refact" ,cmdRefactor = nam_ "refactor" &= help "Automatically invoke `refactor` to apply hints" ,cmdRefactorOptions = nam_ "refactor-options" &= typ "OPTIONS" &= help "Options to pass to the `refactor` executable"- , cmdWithRefactor = nam_ "with-refactor" &= help "Give the path to refactor"+ ,cmdWithRefactor = nam_ "with-refactor" &= help "Give the path to refactor" } &= auto &= explicit &= name "lint" ,CmdGrep {cmdFiles = def &= args &= typ "FILE/DIR"
src/Config/Yaml.hs view
@@ -21,6 +21,7 @@ import HSE.All hiding (Rule, String) import Data.Functor import Data.Semigroup+import Timing import Util import Prelude @@ -28,7 +29,7 @@ -- | Read a config file in YAML format. Takes a filename, and optionally the contents. -- Fails if the YAML doesn't parse or isn't valid HLint YAML readFileConfigYaml :: FilePath -> Maybe String -> IO ConfigYaml-readFileConfigYaml file contents = do+readFileConfigYaml file contents = timedIO "Config" file $ do val <- case contents of Nothing -> decodeFileEither file Just src -> return $ decodeEither' $ BS.pack src
src/HLint.hs view
@@ -12,6 +12,7 @@ import GHC.Conc import System.Exit import System.IO.Extra+import System.Time.Extra import Data.Tuple.Extra import Prelude @@ -31,6 +32,7 @@ import Test.All import Hint.All import Grep+import Timing import Test.Proof import Parallel import HSE.All@@ -53,7 +55,13 @@ hlint args = do cmd <- getCmd args case cmd of- CmdMain{} -> do xs <- hlintMain args cmd; return $ if cmdNoExitCode cmd then [] else xs+ CmdMain{} -> do+ startTimings+ (time, xs) <- duration $ hlintMain args cmd+ when (cmdTiming cmd) $ do+ printTimings+ putStrLn $ "Took " ++ showDuration time+ return $ if cmdNoExitCode cmd then [] else xs CmdGrep{} -> hlintGrep cmd >> return [] CmdHSE{} -> hlintHSE cmd >> return [] CmdTest{} -> hlintTest cmd >> return []
src/HSE/All.hs view
@@ -20,6 +20,7 @@ import Data.Char import Data.List.Extra import Data.Maybe+import Timing import Language.Preprocessor.Cpphs import Data.Set (Set) import qualified Data.Map as Map@@ -143,7 +144,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 flags file str = do+parseModuleEx flags file str = timedIO "Parse" file $ do str <- case str of Just x -> return x Nothing | file == "-" -> getContents
src/Hint/All.hs view
@@ -7,8 +7,10 @@ import Data.Monoid import Config.Type import Data.Either-import Data.List+import Data.List.Extra import Hint.Type+import Timing+import Util import Prelude import Hint.Match@@ -58,11 +60,11 @@ HintNewType -> decl newtypeHint HintRestrict -> mempty{hintModule=restrictHint} where- decl x = mempty{hintDecl=const x}- modu x = mempty{hintModule=const x}- mods x = mempty{hintModules=const x}- comm x = mempty{hintComment=const x}-+ wrap = timed "Hint" (drop 4 $ show x) . forceList+ decl f = mempty{hintDecl=const $ \a b c -> wrap $ f a b c}+ modu f = mempty{hintModule=const $ \a b -> wrap $ f a b}+ mods f = mempty{hintModules=const $ \a -> wrap $ f a}+ comm f = mempty{hintComment=const $ \a -> wrap $ f a} -- | A list of builtin hints, currently including entries such as @\"List\"@ and @\"Bracket\"@. builtinHints :: [(String, Hint)]@@ -70,7 +72,7 @@ -- | Transform a list of 'HintBuiltin' or 'HintRule' into a 'Hint'. resolveHints :: [Either HintBuiltin HintRule] -> Hint-resolveHints xs = mconcat $ mempty{hintDecl=const $ readMatch rights} : map builtin (nub lefts)+resolveHints xs = mconcat $ mempty{hintDecl=const $ readMatch rights} : map builtin (nubOrd lefts) where (lefts,rights) = partitionEithers xs -- | Transform a list of 'HintRule' into a 'Hint'.
src/Hint/Extensions.hs view
@@ -144,7 +144,7 @@ minimalExtensions :: Module_ -> [Extension] -> [Extension]-minimalExtensions x es = nub $ concatMap f es+minimalExtensions x es = nubOrd $ concatMap f es where f e = [e | usedExt e x]
src/Hint/Match.hs view
@@ -47,6 +47,8 @@ import Control.Monad import Data.Tuple.Extra import HSE.Unify+import Util+import Timing import qualified Data.Set as Set import Prelude import qualified Refact.Types as R@@ -90,7 +92,7 @@ -- PERFORM THE MATCHING findIdeas :: [HintRule] -> Scope -> Module S -> Decl_ -> [Idea]-findIdeas matches s _ decl =+findIdeas matches s _ decl = timed "Hint" "Match apply" $ forceList [ (idea (hintRuleSeverity m) (hintRuleName m) x y [r]){ideaNote=notes} | decl <- case decl of InstDecl{} -> children decl; _ -> [decl] , (parent,x) <- universeParentExp decl, not $ isParen x
src/Hint/Naming.hs view
@@ -16,7 +16,7 @@ data Yes = Foo | Bar'Test -- data Yes = Foo | BarTest data Yes = Bar | Test_Bar -- data Yes = Bar | TestBar data No = a :::: b-data Yes = Foo {bar_cap :: Int} -- data Yes = Foo{barCap :: Int}+data Yes = Foo {bar_cap :: Int} data No = FOO | BarBAR | BarBBar yes_foo = yes_foo + yes_foo -- yesFoo = ... no = 1 where yes_foo = 2@@ -38,7 +38,7 @@ module Hint.Naming(namingHint) where import Hint.Type-import Data.List+import Data.List.Extra import Data.Data import Data.Char import Data.Maybe@@ -46,11 +46,11 @@ namingHint :: DeclHint-namingHint _ modu = naming $ Set.fromList [x | Ident _ x <- universeS modu]+namingHint _ modu = naming $ Set.fromList $ concatMap getNames $ moduleDecls modu naming :: Set.Set String -> Decl_ -> [Idea] naming seen x = [suggestN "Use camelCase" x2 (replaceNames res x2) | not $ null res]- where res = [(n,y) | n <- nub $ getNames x, Just y <- [suggestName n], not $ y `Set.member` seen]+ where res = [(n,y) | n <- nubOrd $ getNames x, Just y <- [suggestName n], not $ y `Set.member` seen] x2 = shorten x @@ -81,7 +81,7 @@ f (ConDecl _ x _) = [x] f (InfixConDecl _ _ x _) = [x]- f (RecDecl _ x ys) = x : concat [y | FieldDecl _ y _ <- ys]+ f (RecDecl _ x _) = [x] suggestName :: String -> Maybe String
src/Hint/Pragma.hs view
@@ -29,7 +29,7 @@ module Hint.Pragma(pragmaHint) where import Hint.Type-import Data.List+import Data.List.Extra import Data.Maybe import Refact.Types import qualified Refact.Types as R@@ -50,7 +50,7 @@ , let r = mkRefact old new ns] ls = concat [map fromNamed n | LanguagePragma _ n <- lang]- ns2 = nub (concat ns) \\ ls+ ns2 = nubOrd (concat ns) \\ ls ys = [LanguagePragma an (map toNamed ns2) | ns2 /= []] ++ catMaybes new mkRefact :: ModulePragma S -> Maybe (ModulePragma S) -> [String] -> Refactoring R.SrcSpan
src/Report.hs view
@@ -10,6 +10,7 @@ import System.FilePath import System.IO.Extra import HSE.All+import Timing import Paths_hlint import HsColour @@ -24,7 +25,7 @@ writeReport :: FilePath -> FilePath -> [Idea] -> IO ()-writeReport dataDir file ideas = writeTemplate dataDir inner file+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
src/Test/Proof.hs view
@@ -8,7 +8,7 @@ import Control.Monad import Control.Monad.Trans.State import Data.Char-import Data.List+import Data.List.Extra import Data.Maybe import Data.Function import System.FilePath@@ -26,6 +26,9 @@ instance Eq Theorem where t1 == t2 = lemma t1 == lemma t2 +instance Ord Theorem where+ compare t1 t2 = compare (lemma t1) (lemma t2)+ instance Show Theorem where show Theorem{..} = location ++ ":\n" ++ maybe "" f original ++ lemma ++ "\n" where f HintRule{..} = "(* " ++ prettyPrint hintRuleLHS ++ " ==> " ++ prettyPrint hintRuleRHS ++ " *)\n"@@ -33,7 +36,7 @@ proof :: [FilePath] -> [Setting] -> FilePath -> IO () proof reports hints thy = do got <- isabelleTheorems (takeFileName thy) <$> readFile thy- let want = nub $ hintTheorems hints+ let want = nubOrd $ hintTheorems hints let unused = got \\ want let missing = want \\ got let reasons = map (\x -> (fst $ head x, map snd x)) $ groupBy ((==) `on` fst) $
src/Test/Translate.hs view
@@ -63,7 +63,7 @@ ["{-# LINE " ++ show (startLine $ ann rhs) ++ " " ++ show (fileName $ ann rhs) ++ " #-}\n" ++ prettyPrint (PatBind an (toNamed $ "test" ++ show i) bod Nothing) | (i, HintRule _ _ _ lhs rhs side _) <- zip [1..] hints, "noTypeCheck" `notElem` vars (maybeToList side)- , let vs = map toNamed $ nub $ filter isUnifyVar $ vars lhs ++ vars rhs+ , let vs = map toNamed $ nubOrd $ filter isUnifyVar $ vars lhs ++ vars rhs , let inner = InfixApp an (Paren an lhs) (toNamed "==>") (Paren an rhs) , let bod = UnGuardedRhs an $ if null vs then inner else Lambda an vs inner] @@ -87,7 +87,7 @@ (toNamed "test" `app` str (fileName $ ann rhs) `app` int (startLine $ ann rhs) `app` str (prettyPrint lhs ++ " ==> " ++ prettyPrint rhs)) `app` toNamed "t" | (i, HintRule _ _ _ lhs rhs side note) <- zip [1..] hints, "noQuickCheck" `notElem` vars (maybeToList side)- , let vs = map (restrict side) $ nub $ filter isUnifyVar $ vars lhs ++ vars rhs+ , let vs = map (restrict side) $ nubOrd $ filter isUnifyVar $ vars lhs ++ vars rhs , let op = if any isRemovesError note then "?==>" else "==>" , let inner = InfixApp an (Paren an lhs) (toNamed op) (Paren an rhs) , let bod = if null vs then Paren an inner else Lambda an vs inner]
+ src/Timing.hs view
@@ -0,0 +1,65 @@++module Timing(+ timed, timedIO,+ startTimings,+ printTimings+ ) where++import qualified Data.HashMap.Strict as Map+import Control.Exception+import Data.IORef.Extra+import Data.Tuple.Extra+import Data.List.Extra+import System.Time.Extra+import System.IO.Unsafe+++type Category = String+type Item = String++{-# NOINLINE useTimingsRef #-}+useTimingsRef :: IORef Bool+useTimingsRef = unsafePerformIO $ newIORef False++{-# NOINLINE useTimings #-}+useTimings :: Bool+useTimings = unsafePerformIO $ readIORef useTimingsRef++{-# NOINLINE timings #-}+timings :: IORef (Map.HashMap (Category, Item) Seconds)+timings = unsafePerformIO $ newIORef Map.empty++{-# NOINLINE timed #-}+timed :: Category -> Item -> a -> a+timed c i x = if not useTimings then x else unsafePerformIO $ timedIO c i $ evaluate x+++timedIO :: Category -> Item -> IO a -> IO a+timedIO c i x = if not useTimings then x else do+ (time, x) <- duration x+ atomicModifyIORef' timings $ \mp -> (Map.insertWith (+) (c, i) time mp, ())+ return x++startTimings :: IO ()+startTimings = do+ writeIORef useTimingsRef True+ writeIORef timings Map.empty++printTimings :: IO ()+printTimings = do+ mp <- readIORef timings+ let items = sortOn (sumSnd . snd) $+ groupSort $ map (\((a,b),c) -> (a,(b,c))) $ Map.toList mp+ putStrLn $ unlines $ intercalate [""] $ map disp $ items ++ [("TOTAL", map (second sumSnd) items)]+ where+ sumSnd = sum . map snd++ disp (cat,xs) =+ ("Timing " ++ cat) :+ [" " ++ showDuration b ++ " " ++ a | (a,b) <- xs2] +++ [" " ++ showDuration (sumSnd xs2) ++ " TOTAL"]+ where+ xs2 = f $ splitAt 9 $ sortOn (negate . snd) xs+ f (xs,ys)+ | length ys <= 1 = xs ++ ys+ | otherwise = xs ++ [("Other items (" ++ show (length ys) ++ ")", sumSnd ys)]
src/Util.hs view
@@ -2,6 +2,7 @@ module Util( defaultExtensions,+ forceList, gzip, universeParentBi, exitMessage, exitMessageImpure ) where@@ -14,6 +15,13 @@ import Data.Data import Data.Generics.Uniplate.Operations import Language.Haskell.Exts.Extension+++---------------------------------------------------------------------+-- CONTROL.DEEPSEQ++forceList :: [a] -> [a]+forceList xs = length xs `seq` xs ---------------------------------------------------------------------