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 +17/−2
- README.md +1/−1
- src/Weeder.hs +12/−14
- src/Weeder/Main.hs +67/−63
- weeder.cabal +13/−13
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