weeder 1.0.8 → 1.0.9
raw patch · 9 files changed
+44/−31 lines, 9 files
Files
- CHANGES.txt +2/−0
- LICENSE +1/−1
- README.md +14/−11
- src/Cabal.hs +14/−9
- src/Check.hs +2/−1
- src/CmdLine.hs +1/−1
- src/Hi.hs +6/−4
- src/Warning.hs +1/−1
- weeder.cabal +3/−3
CHANGES.txt view
@@ -1,5 +1,7 @@ Changelog for Weeder +1.0.9, released 2020-06-11+ #55, fix handling of internal libraries 1.0.8, released 2018-08-26 #42, make paths case-insensitive on MacOS 1.0.7, released 2018-08-23
LICENSE view
@@ -1,4 +1,4 @@-Copyright Neil Mitchell 2017-2018.+Copyright Neil Mitchell 2017-2020. All rights reserved. Redistribution and use in source and binary forms, with or without
README.md view
@@ -1,9 +1,16 @@-# Weeder [](https://hackage.haskell.org/package/weeder) [](https://www.stackage.org/package/weeder) [](https://travis-ci.org/ndmitchell/weeder) [](https://ci.appveyor.com/project/ndmitchell/weeder)+# Weeder [](https://hackage.haskell.org/package/weeder) [](https://www.stackage.org/package/weeder) [](https://travis-ci.org/ndmitchell/weeder) [](https://ci.appveyor.com/project/ndmitchell/weeder) +# Weeder has moved!++Weeder 2.0 is being developed at https://github.com/ocharles/weeder on different foundations. This repo is for historical reference only.++-------------------+ Most projects accumulate code over time. Weeder detects unused Haskell exports, allowing dead code to be removed (pulling up the weeds). Weeder piggy-backs off files generated by [`stack`](https://www.haskellstack.org), so first obtain stack, then: * Install `weeder` by running `stack install weeder --resolver=nightly`. * Ensure your project has a `stack.yaml` file. If you don't normally build with `stack` then run `stack init` to generate one.+* Make sure you have `ghc-options: {"$locals": -ddump-to-file -ddump-hi}` in your `stack.yaml`. * Run `weeder . --build`, which builds your project with `stack` and reports any weeds. ## What does Weeder detect?@@ -47,19 +54,15 @@ Before running Weeder on your continuous integration (CI) server, you should first ensure there are no existing weeds. One way to achieve that is to ignore existing hints by running `weeder . --yaml > .weeder.yaml` and checking in the resulting `.weeder.yaml`. -On the CI you should then run `weeder .` (or `weeder . --build` to compile as well). To avoid the cost of compilation you may wish to fetch the [latest Weeder binary release](https://github.com/ndmitchell/weeder/releases/latest). For certain CI environments there are helper scripts to do that.--**Travis:** Execute the following command:-- curl -sSL https://raw.github.com/ndmitchell/weeder/master/misc/travis.sh | sh -s .--The arguments after `-s` are passed to `weeder`, so modify the final `.` if you want other arguments.+On the CI you should then run `weeder .` (or `weeder . --build` to compile as well). To avoid the cost of compilation you may wish to fetch the [latest Weeder binary release](https://github.com/ndmitchell/weeder/releases/latest). -**Appveyor:** Add the following statement to `.appveyor.yml`:+For the CI systems [Travis](https://travis-ci.org/), [Appveyor](https://www.appveyor.com/) and [Azure Pipelines](https://azure.microsoft.com/en-gb/services/devops/pipelines/) add the line: - - ps: Invoke-Command ([Scriptblock]::Create((Invoke-WebRequest 'https://raw.githubusercontent.com/ndmitchell/weeder/master/misc/appveyor.ps1').Content)) -ArgumentList @('.')+```sh+curl -sSL https://raw.github.com/ndmitchell/weeder/master/misc/run.sh | sh -s .+``` -The arguments inside `@()` are passed to `weeder`, so add new arguments surrounded by `'`, space separated - e.g. `@('.' '--build')`.+The arguments after `-s` are passed to `weeder`, so modify the final `.` if you want other arguments. This command works on Windows, Mac and Linux. ## What about Cabal users?
src/Cabal.hs view
@@ -54,6 +54,7 @@ [ joinPath (root : x : components) <.> "dump-hi" | extra <- [".",distDir] , root <- concat [["build" </> extra </> x </> (x ++ "-tmp")+ ,"build" </> extra </> x </> x ,"build" </> extra </> x </> (x ++ "-tmp") </> distDir </> "build" </> x </> (x ++ "-tmp")] | Just x <- [cabalSectionTypeName sectionType]] ++ ["build", "build" </> distDir </> "build"]@@ -72,27 +73,29 @@ mempty = Cabal "" [] mappend = (<>) -data CabalSectionType = Library | Executable String | TestSuite String | Benchmark String+data CabalSectionType = Library (Maybe String) | Executable String | TestSuite String | Benchmark String deriving (Eq,Ord) cabalSectionTypeName :: CabalSectionType -> Maybe String-cabalSectionTypeName Library = Nothing+cabalSectionTypeName (Library x) = x cabalSectionTypeName (Executable x) = Just x cabalSectionTypeName (TestSuite x) = Just x cabalSectionTypeName (Benchmark x) = Just x instance Show CabalSectionType where- show Library = "library"+ show (Library Nothing) = "library"+ show (Library (Just x)) = "library:" ++ x show (Executable x) = "exe:" ++ x show (TestSuite x) = "test:" ++ x show (Benchmark x) = "bench:" ++ x instance Read CabalSectionType where- readsPrec _ "library" = [(Library,"")]+ readsPrec _ "library" = [(Library Nothing,"")] readsPrec _ x | Just x <- stripPrefix "exe:" x = [(Executable x, "")] | Just x <- stripPrefix "test:" x = [(TestSuite x, "")] | Just x <- stripPrefix "bench:" x = [(Benchmark x, "")]+ | Just x <- stripPrefix "library:" x = [(Library (Just x), "")] readsPrec _ _ = [] data CabalSection = CabalSection@@ -109,20 +112,22 @@ CabalSection x1 (x2?:y2) (x3<>y3) (x4<>y4) (x5<>y5) (x6<>y6) instance Monoid CabalSection where- mempty = CabalSection Library "" [] [] [] []+ mempty = CabalSection (Library Nothing) "" [] [] [] [] mappend = (<>) parseCabal :: FilePath -> IO Cabal parseCabal = fmap parseTop . readFile' -parseTop = mconcat . map f . parseHanging . filter (not . isComment) . lines+parseTop = mconcatMap f . parseHanging . filter (not . isComment) . lines where isComment = isPrefixOf "--" . trimStart keyName = (lower *** fst . word1) . word1 f (keyName -> (key, name), xs) = case key of "name:" -> mempty{cabalName=name}- "library" -> mempty{cabalSections=[parseSection Library xs]}+ "library" -> case name of+ "" -> mempty{cabalSections=[parseSection (Library Nothing) xs]}+ x -> mempty{cabalSections=[parseSection (Library (Just x)) xs]} "executable" -> mempty{cabalSections=[parseSection (Executable name) xs]} "test-suite" -> mempty{cabalSections=[parseSection (TestSuite name) xs]} "benchmark" -> mempty{cabalSections=[parseSection (Benchmark name) xs]}@@ -130,7 +135,7 @@ parseSection typ xs = mempty{cabalSectionType=typ} <> parse xs where- parse = mconcat . map f . parseHanging+ parse = mconcatMap f . parseHanging keyValues (x,xs) = let (x1,x2) = word1 x in (lower x1, trimEqual $ filter (not . null) $ x2:xs) trimEqual xs = map (drop n) xs where n = minimum $ 0 : map (length . takeWhile isSpace) xs@@ -145,5 +150,5 @@ "hs-source-dirs:" -> mempty{cabalSourceDirs=listSplit vs} "exposed-modules:" -> mempty{cabalExposedModules=listSplit vs} "other-modules:" -> mempty{cabalOtherModules=listSplit vs}- "main-is:" -> mempty{cabalMainIs=head $ vs ++ [""]}+ "main-is:" -> mempty{cabalMainIs=headDef "" vs} _ -> mempty
src/Check.hs view
@@ -1,4 +1,4 @@-{-# LANGUAGE RecordWildCards, NamedFieldPuns, ScopedTypeVariables #-}+{-# LANGUAGE RecordWildCards, ScopedTypeVariables #-} module Check(check) where @@ -51,6 +51,7 @@ [ Warning pkg [cabalSectionType] "Redundant build-depends entry" (Just p) Nothing Nothing | (CabalSection{..}, (x1,x2,_)) <- sections , let usedPackages = Set.unions $ map (Set.map fst . hiImportPackageModule . hi) $ x1 ++ x2+ , not $ "" `Set.member` usedPackages -- Sometimes we don't get the package name at all, e.g. https://gitlab.haskell.org/ghc/ghc/issues/16886 , p <- Set.toList $ Set.fromList cabalPackages `Set.difference` usedPackages , p /= if isWindows then "unix" else "Win32" -- ignore packages that must be conditional on the other platform , p /= "semigroups" -- ignore packages that are often conditional
src/CmdLine.hs view
@@ -44,6 +44,6 @@ ,cmdShowAll = nam "show-all" &= help "Show even ignored warnings" ,cmdDistDir = nam "dist-dir" &= typDir &= help "Stack dist-dir, defaults to 'stack path --dist-dir'" } &= explicit &= verbosity- &= name "weeder" &= program "weeder" &= summary ("Weeder v" ++ showVersion version ++ ", (C) Neil Mitchell 2017-2018")+ &= name "weeder" &= program "weeder" &= summary ("Weeder v" ++ showVersion version ++ ", (C) Neil Mitchell 2017-2020") where nam xs = def &= explicit &= name xs &= name [head xs]
src/Hi.hs view
@@ -13,6 +13,7 @@ import System.Time.Extra import GHC.Generics import Data.Tuple.Extra+import Data.Maybe import Control.Monad import Control.Exception import Control.DeepSeq@@ -106,16 +107,17 @@ where names = Set.fromList [s | Ident m s <- Set.toList hiExportIdent, m == hiModuleName] hiParseContents :: Str -> Hi-hiParseContents = mconcat . map f . parseHanging2 . S.linesCR+hiParseContents = mconcatMap f . parseHanging2 . S.linesCR where f (x,xs) | Just x <- S.stripPrefix "interface " x = mempty{hiModuleName = parseInterface $ S.toList x}- | Just x <- S.stripPrefix "exports:" x = mconcat $ map (parseExports . S.toList) $ unindent2 xs+ | Just x <- S.stripPrefix "exports:" x = mconcatMap (parseExports . S.toList) $ unindent2 xs | Just x <- S.stripPrefix "orphans:" x = mempty{hiImportOrphan = Set.fromList $ map parseInterface $ concatMap (words . S.toList) $ x:xs} | Just x <- S.stripPrefix "package dependencies:" x = mempty{hiImportPackage = Set.fromList $ map parsePackDep $ concatMap (words . S.toList) $ x:xs} | Just x <- S.stripPrefix "import " x = case unindent2 xs of- [] | (pkg, mod) <- breakOn ":" $ words (S.toList x) !! 1 -> mempty- {hiImportPackageModule = Set.singleton (parsePackDep pkg, drop 1 mod)}+ [] | let s = words (S.toList x) !! 1+ , (pkg, mod) <- fromMaybe ("", s) $ stripInfix ":" s -> mempty+ {hiImportPackageModule = Set.singleton (parsePackDep pkg, mod)} xs -> let m = words (S.toList x) !! 1 in mempty {hiImportModule = Set.singleton m ,hiImportIdent = Set.fromList $ map (Ident m . fst . word1 . S.toList) $ dropWhile ("exports:" `S.isPrefixOf`) xs}
src/Warning.hs view
@@ -124,7 +124,7 @@ readWarningsFile :: FilePath -> IO [Warning] readWarningsFile file = do- x <- either throwIO return =<< Yaml.decodeFileEither file+ x <- eitherM throwIO return $ Yaml.decodeFileEither file let res = map warningUnpath $ concatMap (f warningLabels) $ valueToVal x mapM_ evaluate res -- ensure exceptions happen immediately return res
weeder.cabal view
@@ -1,13 +1,13 @@ cabal-version: >= 1.18 build-type: Simple name: weeder-version: 1.0.8+version: 1.0.9 license: BSD3 license-file: LICENSE category: Development author: Neil Mitchell <ndmitchell@gmail.com> maintainer: Neil Mitchell <ndmitchell@gmail.com>-copyright: Neil Mitchell 2017-2018+copyright: Neil Mitchell 2017-2020 synopsis: Detect dead code description: Find redundant package dependencies or redundant module exports.@@ -16,7 +16,7 @@ extra-doc-files: README.md CHANGES.txt-tested-with: GHC==8.4.3, GHC==8.2.2, GHC==8.0.2, GHC==7.10.3+tested-with: GHC==8.10.1, GHC==8.8.3, GHC==8.6.5, GHC==8.4.4, GHC==8.2.2, GHC==8.0.2 source-repository head type: git