packages feed

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 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