packages feed

weeder 2.1.3 → 2.2.0

raw patch · 5 files changed

+110/−93 lines, 5 filesdep ~basedep ~bytestringdep ~containers

Dependency ranges changed: base, bytestring, containers, dhall, directory, filepath, generic-lens, ghc, lens, optparse-applicative, transformers

Files

CHANGELOG.md view
@@ -1,5 +1,15 @@ ## Changelog for Weeder +### [`2.2.0`][v2.2.0] - *2021-08-28*++This will likely be the last Weeder release before GHC 9.++- Allow configuration of the HIE file extension using the `--hie-extension` command-line flag+- Add `--require-hs-files` switch. If supplied, this switch means Weeder will only consider `.hie` files where a corresponding `.hs` file can be found. ([#50](https://github.com/ocharles/weeder/pull/50))+- Pattern synonyms are now considered ([#79](https://github.com/ocharles/weeder/pull/79))+- Weeder's output format is now one-line-per-weed ([#62](https://github.com/ocharles/weeder/pull/62))+- `--hie-extension` can be used to change the extension used for `.hie` files ([#64](https://github.com/ocharles/weeder/pull/64))+ ### [`2.1.3`][v2.1.3] - *2020-12-11*  - Support `dhall-1.35`, `dhall-1.36` and `dhall-1.37`.@@ -187,8 +197,13 @@ - Initial version  -[v2.0.1 ]: https://github.com/ocharles/weeder/tree/v2.0.1-[v2.0.0 ]: https://github.com/ocharles/weeder/tree/v2.0.0+[v2.2.0 ]: https://github.com/ocharles/weeder/releases/tag/2.2.0+[v2.1.3 ]: https://github.com/ocharles/weeder/releases/tag/2.1.3+[v2.1.2 ]: https://github.com/ocharles/weeder/releases/tag/2.1.2+[v2.1.1 ]: https://github.com/ocharles/weeder/releases/tag/2.1.1+[v2.1.0 ]: https://github.com/ocharles/weeder/releases/tag/2.1.0+[v2.0.1 ]: https://github.com/ocharles/weeder/releases/tag/2.0.1+[v2.0.0 ]: https://github.com/ocharles/weeder/releases/tag/2.0.0 [v1.0.8 ]: https://github.com/ndmitchell/weeder/tree/v1.0.8 [v1.0.7 ]: https://github.com/ndmitchell/weeder/tree/v1.0.7 [v1.0.6 ]: https://github.com/ndmitchell/weeder/tree/v1.0.6
README.md view
@@ -66,7 +66,7 @@ `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 good starting point - specifying that `main` is a root. Weeder currently doesn't-add all exported functions a roots automatically but in many cases `main` from a+add all exported functions as roots automatically but in many cases `main` from a test suite could be a good workaround for that  `type-class-roots` configures whether or not Weeder should consider anything in
src/Weeder.hs view
@@ -7,7 +7,6 @@ {-# language NoImplicitPrelude #-} {-# language OverloadedLabels #-} {-# language OverloadedStrings #-}-{-# language PackageImports #-}  module Weeder   ( -- * Analysis@@ -38,9 +37,6 @@ import GHC.Generics ( Generic ) import Prelude hiding ( span ) --- bytestring-import Data.ByteString ( ByteString )- -- containers import Data.Map.Strict ( Map ) import qualified Data.Map.Strict as Map@@ -60,7 +56,7 @@   , DeclType( DataDec, ClassDec, ConDec )   , HieAST( Node, nodeInfo, nodeChildren, nodeSpan )   , HieASTs( HieASTs )-  , HieFile( HieFile, hie_asts, hie_exports, hie_module, hie_hs_file, hie_hs_src )+  , HieFile( HieFile, hie_asts, hie_exports, hie_module, hie_hs_file )   , IdentifierDetails( IdentifierDetails, identInfo )   , NodeInfo( NodeInfo, nodeIdentifiers, nodeAnnotations )   , Scope( ModuleScope )@@ -139,7 +135,6 @@       -- ^ All exports for a given module.     , modulePaths :: Map Module FilePath       -- ^ A map from modules to the file path to the .hs file defining them.-    , moduleSource :: Map Module ByteString     }   deriving     ( Generic )@@ -147,8 +142,7 @@  -- | The empty analysis - the result of analysing zero @.hie@ files. emptyAnalysis :: Analysis-emptyAnalysis =-  Analysis empty mempty mempty mempty mempty mempty+emptyAnalysis = Analysis empty mempty mempty mempty mempty   -- | A root for reachability analysis.@@ -181,9 +175,8 @@  -- | Incrementally update 'Analysis' with information in a 'HieFile'. analyseHieFile :: MonadState Analysis m => HieFile -> m ()-analyseHieFile HieFile{ hie_asts = HieASTs hieASTs, hie_exports, hie_module, hie_hs_file, hie_hs_src } = do+analyseHieFile HieFile{ hie_asts = HieASTs hieASTs, hie_exports, hie_module, hie_hs_file } = do   #modulePaths %= Map.insert hie_module hie_hs_file-  #moduleSource %= Map.insert hie_module hie_hs_src    for_ hieASTs \ast -> do     addAllDeclarations ast@@ -253,6 +246,7 @@           , analyseRewriteRule n           , analyseClassDeclaration n           , analyseDataDeclaration n+          , analysePatternSynonyms n           ]       ) @@ -274,8 +268,7 @@   for_ ( findDeclarations n ) \d -> do     define d nodeSpan -    for_ ( uses n ) \use ->-      addDependency d use+    for_ ( uses n ) $ addDependency d   analyseRewriteRule :: ( Alternative m, MonadState Analysis m ) => HieAST a -> m ()@@ -296,8 +289,8 @@ analyseClassDeclaration n@Node{ nodeInfo = NodeInfo{ nodeAnnotations } } = do   guard ( ( "ClassDecl", "TyClDecl" ) `Set.member` nodeAnnotations ) -  for_ ( findIdentifiers isClassDeclaration n ) \d ->-    for_ ( findIdentifiers ( const True ) n ) ( addDependency d )+  for_ ( findIdentifiers isClassDeclaration n ) $+    for_ ( findIdentifiers ( const True ) n ) . addDependency    where @@ -345,6 +338,11 @@   else     foldMap constructors nodeChildren +analysePatternSynonyms :: ( Alternative m, MonadState Analysis m ) => HieAST a -> m ()+analysePatternSynonyms n@Node{ nodeInfo = NodeInfo{ nodeAnnotations } } = do+  guard $ ( "PatSynBind", "HsBindLR" ) `Set.member` nodeAnnotations++  for_ ( findDeclarations n ) $ for_ ( uses n ) . addDependency  findDeclarations :: HieAST a -> Seq Declaration findDeclarations =
src/Weeder/Main.hs view
@@ -10,17 +10,14 @@ module Weeder.Main ( main, mainWithConfig ) where  -- base-import Control.Monad ( guard, unless )+import Control.Monad ( guard, unless, when ) import Control.Monad.IO.Class ( liftIO ) import Data.Bool import Data.Foldable+import Data.List ( isSuffixOf ) import Data.Version ( showVersion )-import Text.Printf ( printf ) import System.Exit ( exitFailure ) --- bytestring-import qualified Data.ByteString.Char8 as BS- -- containers import qualified Data.Map.Strict as Map import qualified Data.Set as Set@@ -39,11 +36,11 @@  -- ghc import HieBin ( HieFileResult( HieFileResult, hie_file_result ), readHieFileWithVersion )-import HieTypes ( HieFile, hieVersion )+import HieTypes ( HieFile( hie_hs_file ), hieVersion ) import Module ( moduleName, moduleNameString ) import NameCache ( initNameCache, NameCache ) import OccName ( occNameString )-import SrcLoc ( realSrcSpanStart, srcLocCol, srcLocLine )+import SrcLoc ( RealSrcLoc, realSrcSpanStart, srcLocLine ) import UniqSupply ( mkSplitUniqSupply )  -- regex-tdfa@@ -64,13 +61,14 @@ -- | Parse command line arguments and into a 'Config' and run 'mainWithConfig'. main :: IO () main = do-  (configExpr, hieDirectories) <-+  (configExpr, hieExt, hieDirectories, requireHsFiles) <-     execParser $       info (optsP <**> helper <**> versionP) mempty -  Dhall.input config configExpr >>= mainWithConfig hieDirectories+  Dhall.input config configExpr+    >>= mainWithConfig hieExt hieDirectories requireHsFiles   where-    optsP = (,)+    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."@@ -78,31 +76,49 @@                 <> metavar "<weeder.dhall>"                 <> showDefaultWith T.unpack             )+        <*> strOption+            ( long "hie-extension"+                <> value ".hie"+                <> help "Extension of HIE files"+                <> showDefault+            )         <*> many (             strOption                 ( long "hie-directory"                     <> help "A directory to look for .hie files in. Maybe specified multiple times. Default ./."                 )             )+        <*> switch+              ( long "require-hs-files"+                  <> help "Requries that all .hie files have matching .hs files. This can help deal with skipping .hie files for Haskell modules that have since been removed"+              ) -    versionP = infoOption (showVersion version)+    versionP = infoOption ( "weeder version "+                            <> showVersion version+                            <> "\nhie version "+                            <> show hieVersion )         ( long "version" <> help "Show version" )   -- | Run Weeder in the current working directory with a given 'Config'. ----- This will recursively find all @.hie@ files in the current directory, perform+-- 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 :: [FilePath] -> Config -> IO ()-mainWithConfig hieDirectories Config{ rootPatterns, typeClassRoots } = do+mainWithConfig :: String -> [FilePath] -> Bool -> Config -> IO ()+mainWithConfig hieExt hieDirectories requireHsFiles Config{ rootPatterns, typeClassRoots } = do   hieFilePaths <-     concat <$>-      traverse getHieFilesIn+      traverse ( getFilesIn hieExt )         ( if null hieDirectories           then ["./."]           else hieDirectories         ) +  hsFilePaths <-+    if requireHsFiles+      then getFilesIn ".hs" "./."+      else pure []+   nameCache <- do     uniqSupply <- mkSplitUniqSupply 'z'     return ( initNameCache uniqSupply [] )@@ -111,7 +127,9 @@     flip execStateT emptyAnalysis do       for_ hieFilePaths \hieFilePath -> do         hieFileResult <- liftIO ( readCompatibleHieFileOrExit nameCache hieFilePath )-        analyseHieFile hieFileResult+        let hsFileExists = any ( hie_hs_file hieFileResult `isSuffixOf` ) hsFilePaths+        when (requireHsFiles ==> hsFileExists) do+          analyseHieFile hieFileResult    let     roots =@@ -137,60 +155,33 @@         ( \d ->             fold $ do               moduleFilePath <- Map.lookup ( declModule d ) ( modulePaths analysis )-              moduleSource <- Map.lookup ( declModule d ) ( moduleSource analysis )-               spans <- Map.lookup d ( declarationSites analysis )               guard $ not $ null spans--              let snippets = do-                    srcSpan <- Set.toList spans--                    let start = realSrcSpanStart srcSpan-                    let firstLine = max 0 ( srcLocLine start - 3 )--                    return ( start, take 5 $ drop firstLine $ zip [1..] $ BS.lines moduleSource )--              return [ Map.singleton moduleFilePath ( liftA2 (,) snippets (pure d) ) ]+              let starts = map realSrcSpanStart $ Set.toList spans+              return [ Map.singleton moduleFilePath ( liftA2 (,) starts (pure d) ) ]         )         dead    for_ ( Map.toList warnings ) \( path, declarations ) ->-    for_ declarations \( ( start, snippet ), d ) -> do-      putStrLn $-        unwords-          [ foldMap ( <> ":" ) [ path, show ( srcLocLine start ), show ( srcLocCol start ) ]-          , "error:"-          , occNameString ( declOccName d )-          , "is unused"-          ]--      putStrLn ""-      for_ snippet \( n, line ) ->-        putStrLn $-             replicate 4 ' '-          <> printf "% 4d" ( n :: Int )-          <> " ┃ "-          <> BS.unpack line-      putStrLn ""--      putStrLn $-           replicate 4 ' '-        <> "Delete this definition or add ‘"-        <> moduleNameString ( moduleName ( declModule d ) )-        <> "."-        <> occNameString ( declOccName d )-        <> "’ as a root to fix this error."-      putStrLn ""-      putStrLn ""--  putStrLn $ "Weeds detected: " <> show ( sum ( length <$> warnings ) )+    for_ declarations \( start, d ) ->+      putStrLn $ showWeed path start d    unless ( null warnings ) exitFailure +showWeed :: FilePath -> RealSrcLoc -> Declaration -> String+showWeed path start d =+  path <> ":" <> show ( srcLocLine start ) <> ": "+    <> occNameString ( declOccName d) --- | Recursively search for .hie files in given directory-getHieFilesIn :: FilePath -> IO [FilePath]-getHieFilesIn path = do++-- | Recursively search for files with the given extension in given directory+getFilesIn+  :: String+  -- ^ Only files with this extension are considered+  -> FilePath+  -- ^ Directory to look in+  -> IO [FilePath]+getFilesIn ext path = do   exists <-     doesPathExist path @@ -199,7 +190,7 @@       isFile <-         doesFileExist path -      if isFile && "hie" `isExtensionOf` path+      if isFile && ext `isExtensionOf` path         then do           path' <-             canonicalizePath path@@ -215,7 +206,7 @@               cnts <-                 listDirectory path -              withCurrentDirectory path ( foldMap getHieFilesIn cnts )+              withCurrentDirectory path ( foldMap ( getFilesIn ext ) cnts )              else               return []@@ -233,7 +224,20 @@       return hie_file_result     Left ( v, _ghcVersion ) -> do       putStrLn $ "incompatible hie file: " <> path-      putStrLn $ "    expected .hie file version " <> show hieVersion <> " but got " <> show v+      putStrLn $ "    this version of weeder was compiled with GHC version "+               <> show hieVersion+      putStrLn $ "    the hie files in this project were generated with GHC version "+               <> show v       putStrLn $ "    weeder must be built with the same GHC version"                <> " as the project it is used on"       exitFailure++++infixr 5 ==>+++-- | An infix operator for logical implication+(==>) :: Bool -> Bool -> Bool+True  ==> x = x+False ==> _ = True
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.1.3+version:       2.2.0 copyright:     Neil Mitchell 2017-2020, Oliver Charles 2020 synopsis:      Detect dead code description:   Find declarations.@@ -22,17 +22,17 @@     , base                 ^>= 4.13.0.0 || ^>= 4.14.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+    , 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     , directory            ^>= 1.3.3.2     , filepath             ^>= 1.4.2.1-    , generic-lens         ^>= 1.1.0.0 || ^>= 1.2.0.0 || ^>= 2.0.0.0+    , generic-lens         ^>= 1.1.0.0 || ^>= 1.2.0.0 || ^>= 2.0.0.0 || ^>= 2.2.0.0     , ghc                  ^>= 8.8.1 || ^>= 8.10-    , lens                 ^>= 4.18.1 || ^>= 4.19+    , lens                 ^>= 4.18.1 || ^>= 4.19 || ^>= 5.0.1     , mtl                  ^>= 2.2.2     , optparse-applicative ^>= 0.14.3.0 || ^>= 0.15.1.0 || ^>= 0.16.0.0     , regex-tdfa           ^>= 1.2.0.0 || ^>= 1.3.1.0     , text                 ^>= 1.2.3.0-    , transformers         ^>= 0.5.6.2+    , transformers         ^>= 0.5.6.2 || ^>= 0.6   hs-source-dirs: src   exposed-modules:     Weeder@@ -48,14 +48,14 @@  executable weeder   build-depends:-    , base                 ^>= 4.13.0.0 || ^>= 4.14-    , bytestring           ^>= 0.10.9.0 || ^>= 0.11.0.0-    , containers           ^>= 0.6.2.1-    , directory            ^>= 1.3.3.2-    , filepath             ^>= 1.4.2.1-    , ghc                  ^>= 8.8.1 || ^>= 8.10-    , optparse-applicative ^>= 0.14.3.0 || ^>= 0.15.1.0 || ^>= 0.16.0.0-    , transformers         ^>= 0.5.6.2+    , base+    , bytestring+    , containers+    , directory+    , filepath+    , ghc+    , optparse-applicative+    , transformers     , weeder   main-is: Main.hs   hs-source-dirs: exe-weeder