weeder 2.5.0 → 2.6.0
raw patch · 10 files changed
+165/−49 lines, 10 filesdep +aesondep +hspecdep +processdep −dhalldep ~algebraic-graphsdep ~basedep ~directory
Dependencies added: aeson, hspec, process, silently, toml-reader
Dependencies removed: dhall
Dependency ranges changed: algebraic-graphs, base, directory, filepath, ghc, mtl, text
Files
- CHANGELOG.md +15/−1
- README.md +8/−12
- src/Weeder.hs +2/−4
- src/Weeder/Config.hs +7/−13
- src/Weeder/Main.hs +20/−13
- test/Spec.hs +64/−0
- test/Spec/BasicExample.stdout +1/−0
- test/Spec/BasicExample.toml +3/−0
- test/Spec/BasicExample/BasicExample.hs +10/−0
- weeder.cabal +35/−6
CHANGELOG.md view
@@ -1,4 +1,18 @@-## Changelog for Weeder+# Changelog+All notable changes to this project will be documented in this file.++The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),+and is generated by [Changie](https://github.com/miniscruff/changie).+++## 2.6.0 - 2023-07-07+### Added+* Weeder now supports GHC 9.6.+### Changed+* Weeder now uses [TOML](https://toml.io) as a configuration format. Please see Weeder's `README` for more information on the configuration format.+* Weeder now sorts weeds in a single file by ascending line number+### Fixed+* Weeder now internally adds declarations once. This shouldn't result in any user visible changes, but may improve performance. ### [`2.5.0`][v2.5.0] - *2023-01-23*
README.md view
@@ -18,9 +18,9 @@ ## Preparing Your Code for Weeder -To use Weeder, you will need to generate `.hie` files from your source code. +To use Weeder, you will need to generate `.hie` files from your source code. -### Cabal +### Cabal If you use Cabal, this is easily done by adding one line to your `cabal.project.local` file:@@ -55,13 +55,8 @@ ## Calling Weeder -To call Weeder, you first need to provide a configuration file, `weeder.dhall`. Weeder uses-[Dhall](https://dhall-lang.org) as its configuration format, and configuration-files have the type:--``` dhall-{ roots : List Text, type-class-roots : Bool }-```+To call Weeder, you first need to provide a configuration file, `weeder.toml`. Weeder uses+[TOML](https://toml.io/en/) as its configuration format. `roots` is a list of regular expressions of symbols that are considered as alive. If you're building an executable, the pattern `^Main.main$` is a@@ -74,8 +69,9 @@ edges into type class instances, and without this flag may produce false positives. It's recommended to initially set this to `True`: -``` dhall-{ roots = [ "^Main.main$" ], type-class-roots = True }+``` toml+roots = [ "^Main.main$" ]+type-class-roots = true ``` Now invoke the `weeder` executable, and - if your project has weeds - you will@@ -93,7 +89,7 @@ # Tips -- You may want to add `^Paths_.*` to the roots in `weeder.dhall` to ignore the+- You may want to add `^Paths_.*` to the roots in `weeder.toml` to ignore the `Paths_packageName` module automatically generated by Cabal. # Limitations
src/Weeder.hs view
@@ -164,7 +164,7 @@ -- | Determine the set of all declaration reachable from a set of roots. reachable :: Analysis -> Set Root -> Set Declaration reachable Analysis{ dependencyGraph, exports } roots =- Set.fromList ( dfs ( foldMap rootDeclarations roots ) dependencyGraph )+ Set.fromList ( dfs dependencyGraph ( foldMap rootDeclarations roots ) ) where @@ -241,10 +241,8 @@ -- | Try and add vertices for all declarations in an AST - both -- those declared here, and those referred to from here. addAllDeclarations :: ( MonadState Analysis m ) => HieAST a -> m ()-addAllDeclarations n@Node{ nodeChildren } = do+addAllDeclarations n = do for_ ( findIdentifiers ( const True ) n ) addDeclaration-- for_ nodeChildren addAllDeclarations topLevelAnalysis :: MonadState Analysis m => HieAST a -> m ()
src/Weeder/Config.hs view
@@ -3,14 +3,13 @@ {-# language OverloadedStrings #-} {-# language RecordWildCards #-} -module Weeder.Config ( Config(..), config ) where+module Weeder.Config ( Config(..) ) where -- containers import Data.Set ( Set )-import qualified Data.Set as Set --- dhall-import qualified Dhall+-- toml-reader+import qualified TOML -- | Configuration for Weeder analysis.@@ -24,14 +23,9 @@ -- instance is used - enabling this option can prevent false positives. } ---- | A Dhall expression decoder for 'Config'.------ This parses Dhall expressions of the type @{ roots : List Text, type-class-roots : Bool }@.-config :: Dhall.Decoder Config-config =- Dhall.record do- rootPatterns <- Set.fromList <$> Dhall.field "roots" ( Dhall.list Dhall.string )- typeClassRoots <- Dhall.field "type-class-roots" Dhall.bool+instance TOML.DecodeTOML Config where+ tomlDecoder = do+ rootPatterns <- TOML.getField "roots"+ typeClassRoots <- TOML.getField "type-class-roots" return Config{..}
src/Weeder/Main.hs view
@@ -9,13 +9,14 @@ module Weeder.Main ( main, mainWithConfig ) where -- base-import Control.Monad ( guard, unless, when )+import Control.Exception ( throwIO )+import Control.Monad ( guard, when ) import Control.Monad.IO.Class ( liftIO ) import Data.Bool import Data.Foldable-import Data.List ( isSuffixOf )+import Data.List ( isSuffixOf, sortOn ) import Data.Version ( showVersion )-import System.Exit ( exitFailure )+import System.Exit ( exitFailure, ExitCode(..), exitWith ) -- containers import qualified Data.Map.Strict as Map@@ -24,8 +25,8 @@ -- text import qualified Data.Text as T --- dhall-import qualified Dhall+-- toml-reader+import qualified TOML -- directory import System.Directory ( canonicalizePath, doesDirectoryExist, doesFileExist, doesPathExist, listDirectory, withCurrentDirectory )@@ -63,15 +64,19 @@ execParser $ info (optsP <**> helper <**> versionP) mempty - Dhall.input config configExpr- >>= mainWithConfig hieExt hieDirectories requireHsFiles+ (exitCode, _) <- + TOML.decodeFile (T.unpack configExpr)+ >>= either throwIO pure+ >>= mainWithConfig hieExt hieDirectories requireHsFiles+ + exitWith exitCode where optsP = (,,,) <$> strOption ( long "config"- <> help "A Dhall expression for Weeder's configuration. Can either be a file path (a Dhall import) or a literal Dhall expression."- <> value "./weeder.dhall"- <> metavar "<weeder.dhall>"+ <> help "A file path for Weeder's configuration."+ <> value "./weeder.toml"+ <> metavar "<weeder.toml>" <> showDefaultWith T.unpack ) <*> strOption@@ -102,7 +107,7 @@ -- -- This will recursively find all files with the given extension in the given directories, perform -- analysis, and report all unused definitions according to the 'Config'.-mainWithConfig :: String -> [FilePath] -> Bool -> Config -> IO ()+mainWithConfig :: String -> [FilePath] -> Bool -> Config -> IO (ExitCode, Analysis) mainWithConfig hieExt hieDirectories requireHsFiles Config{ rootPatterns, typeClassRoots } = do hieFilePaths <- concat <$>@@ -160,10 +165,12 @@ dead for_ ( Map.toList warnings ) \( path, declarations ) ->- for_ declarations \( start, d ) ->+ for_ (sortOn (srcLocLine . fst) declarations) \( start, d ) -> putStrLn $ showWeed path start d - unless ( null warnings ) exitFailure+ let exitCode = if null warnings then ExitSuccess else ExitFailure 1++ pure (exitCode, analysis) showWeed :: FilePath -> RealSrcLoc -> Declaration -> String showWeed path start d =
+ test/Spec.hs view
@@ -0,0 +1,64 @@+import qualified Weeder.Main+import qualified Weeder+import qualified TOML++import Algebra.Graph.Export.Dot+import GHC.Types.Name.Occurrence (occNameString)+import System.Directory+import System.Environment (getArgs, withArgs)+import System.FilePath+import System.Process+import System.IO.Silently (hCapture_)+import System.IO (stdout, stderr, hPrint)+import Test.Hspec+import Control.Monad (zipWithM_, when)+import Control.Exception ( throwIO, IOException, handle )++main :: IO ()+main = do+ args <- getArgs+ stdoutFiles <- discoverIntegrationTests+ let hieDirectories = map dropExtension stdoutFiles+ drawDots = mapM_ (drawDot . (<.> ".dot")) hieDirectories+ graphviz = "--graphviz" `elem` args+ withArgs (filter (/="--graphviz") args) $+ hspec $ afterAll_ (when graphviz drawDots) $ do+ describe "Weeder.Main" $+ describe "mainWithConfig" $+ zipWithM_ integrationTestSpec stdoutFiles hieDirectories+ where+ -- Draw a dotfile via graphviz+ drawDot f = callCommand $ "dot -Tpng " ++ f ++ " -o " ++ (f -<.> ".png")++-- | Run weeder on hieDirectory, comparing the output to stdoutFile+-- The directory containing hieDirectory must also have a .toml file+-- with the same name as hieDirectory+integrationTestSpec :: FilePath -> FilePath -> Spec+integrationTestSpec stdoutFile hieDirectory = do+ it ("produces the expected output for " ++ hieDirectory) $ do+ expectedOutput <- readFile stdoutFile+ actualOutput <- integrationTestOutput hieDirectory+ actualOutput `shouldBe` expectedOutput++-- | Returns detected .stdout files in ./test/Spec+discoverIntegrationTests :: IO [FilePath]+discoverIntegrationTests = do+ contents <- listDirectory "./test/Spec"+ pure . map ("./test/Spec" </>) $ filter (".stdout" `isExtensionOf`) contents++-- | Run weeder on the given directory for .hie files, returning stdout+-- Also creates a dotfile containing the dependency graph as seen by Weeder+integrationTestOutput :: FilePath -> IO String+integrationTestOutput hieDirectory = hCapture_ [stdout] $ do+ isEmpty <- not . any (".hie" `isExtensionOf`) <$> listDirectory hieDirectory+ when isEmpty $ fail "No .hie files found in directory, this is probably unintended"+ (_, analysis) <-+ TOML.decodeFile configExpr+ >>= either throwIO pure+ >>= Weeder.Main.mainWithConfig ".hie" [hieDirectory] True+ let graph = Weeder.dependencyGraph analysis+ graph' = export (defaultStyle (occNameString . Weeder.declOccName)) graph+ handle (\e -> hPrint stderr (e :: IOException)) $+ writeFile (hieDirectory <.> ".dot") graph'+ where+ configExpr = hieDirectory <.> ".toml"
+ test/Spec/BasicExample.stdout view
@@ -0,0 +1,1 @@+test/Spec/BasicExample/BasicExample.hs:4: unrelated
+ test/Spec/BasicExample.toml view
@@ -0,0 +1,3 @@+roots = [ "Spec.BasicExample.BasicExample.root" ]++type-class-roots = true
+ test/Spec/BasicExample/BasicExample.hs view
@@ -0,0 +1,10 @@+module Spec.BasicExample.BasicExample where++unrelated :: Int+unrelated = 3++dependency :: Int+dependency = 1++root :: Int+root = dependency + 1
weeder.cabal view
@@ -5,7 +5,7 @@ author: Ollie Charles <ollie@ocharles.org.uk> maintainer: Ollie Charles <ollie@ocharles.org.uk> build-type: Simple-version: 2.5.0+version: 2.6.0 copyright: Neil Mitchell 2017-2020, Oliver Charles 2020-2023 synopsis: Detect dead code description: Find declarations.@@ -15,23 +15,26 @@ extra-doc-files: README.md CHANGELOG.md+extra-source-files:+ test/Spec/*.toml+ test/Spec/*.stdout library build-depends:- , algebraic-graphs ^>= 0.4 || ^>= 0.5 || ^>= 0.6- , base ^>= 4.17.0.0+ , algebraic-graphs ^>= 0.7+ , base ^>= 4.17.0.0 || ^>= 4.18.0.0 , bytestring ^>= 0.10.9.0 || ^>= 0.11.0.0 , containers ^>= 0.6.2.1- , dhall ^>= 1.30.0 || ^>= 1.31.0 || ^>= 1.32.0 || ^>= 1.33.0 || ^>= 1.34.0 || ^>= 1.35.0 || ^>= 1.36.0 || ^>= 1.37.0 || ^>= 1.40.0 || ^>= 1.41 , directory ^>= 1.3.3.2 , filepath ^>= 1.4.2.1 , generic-lens ^>= 2.2.0.0- , ghc ^>= 9.4+ , ghc ^>= 9.4 || ^>= 9.6 , lens ^>= 5.1 || ^>= 5.2- , mtl ^>= 2.2.2+ , mtl ^>= 2.2.2 || ^>= 2.3 , optparse-applicative ^>= 0.14.3.0 || ^>= 0.15.1.0 || ^>= 0.16.0.0 || ^>= 0.17 , regex-tdfa ^>= 1.2.0.0 || ^>= 1.3.1.0 , text ^>= 2.0.1+ , toml-reader ^>= 0.2.0.0 , transformers ^>= 0.5.6.2 || ^>= 0.6 hs-source-dirs: src exposed-modules:@@ -60,4 +63,30 @@ main-is: Main.hs hs-source-dirs: exe-weeder ghc-options: -Wall -fwarn-incomplete-uni-patterns+ default-language: Haskell2010++test-suite weeder-test+ build-depends:+ , aeson+ , algebraic-graphs+ , base+ , directory+ , filepath+ , ghc+ , hspec+ , process+ , silently+ , text+ , toml-reader+ , weeder+ type: exitcode-stdio-1.0+ main-is: Spec.hs+ hs-source-dirs: test+ autogen-modules:+ Paths_weeder+ other-modules:+ Paths_weeder+ -- Tests+ Spec.BasicExample.BasicExample+ ghc-options: -Wall -fwarn-incomplete-uni-patterns -fwrite-ide-info -hiedir ./test default-language: Haskell2010