mirror of
https://github.com/ocharles/weeder.git
synced 2024-11-27 02:33:03 +03:00
Looking better
This commit is contained in:
parent
d4e1d84472
commit
6a1f35573d
17
atomic-write.nix
Normal file
17
atomic-write.nix
Normal file
@ -0,0 +1,17 @@
|
||||
{ mkDerivation, base, bytestring, directory, filepath, hspec
|
||||
, stdenv, temporary, text, unix-compat
|
||||
}:
|
||||
mkDerivation {
|
||||
pname = "atomic-write";
|
||||
version = "0.2.0.7";
|
||||
sha256 = "b5f5c77884bc0332306fab89acf1c8a8582d76eabaa303c91b1c4072621c960d";
|
||||
libraryHaskellDepends = [
|
||||
base bytestring directory filepath temporary text unix-compat
|
||||
];
|
||||
testHaskellDepends = [
|
||||
base bytestring filepath hspec temporary text unix-compat
|
||||
];
|
||||
homepage = "https://github.com/stackbuilders/atomic-write";
|
||||
description = "Atomically write to a file";
|
||||
license = stdenv.lib.licenses.mit;
|
||||
}
|
21
cborg-json.nix
Normal file
21
cborg-json.nix
Normal file
@ -0,0 +1,21 @@
|
||||
{ mkDerivation, aeson, aeson-pretty, base, bytestring, cborg
|
||||
, criterion, deepseq, directory, process, scientific, stdenv, text
|
||||
, unordered-containers, vector, zlib
|
||||
}:
|
||||
mkDerivation {
|
||||
pname = "cborg-json";
|
||||
version = "0.2.2.0";
|
||||
sha256 = "ab68a2457cb71a76699d7a8df07a880ea70c51d2c1a891b12669ca9ccfa7517b";
|
||||
libraryHaskellDepends = [
|
||||
aeson aeson-pretty base cborg scientific text unordered-containers
|
||||
vector
|
||||
];
|
||||
benchmarkHaskellDepends = [
|
||||
aeson base bytestring cborg criterion deepseq directory process
|
||||
zlib
|
||||
];
|
||||
doCheck = false;
|
||||
homepage = "https://github.com/well-typed/cborg";
|
||||
description = "A library for encoding JSON as CBOR";
|
||||
license = stdenv.lib.licenses.bsd3;
|
||||
}
|
22
cborg.nix
Normal file
22
cborg.nix
Normal file
@ -0,0 +1,22 @@
|
||||
{ mkDerivation, aeson, array, base, base-orphans, base16-bytestring
|
||||
, base64-bytestring, bytestring, containers, deepseq, ghc-prim
|
||||
, half, integer-gmp, primitive, QuickCheck, random, scientific
|
||||
, stdenv, tasty, tasty-hunit, tasty-quickcheck, text, vector
|
||||
}:
|
||||
mkDerivation {
|
||||
pname = "cborg";
|
||||
version = "0.2.2.1";
|
||||
sha256 = "ba920d368892fe14e048cd6ac4270ce4ea1aea0fb6a4998c5c97fe106e6c6183";
|
||||
libraryHaskellDepends = [
|
||||
array base bytestring containers deepseq ghc-prim half integer-gmp
|
||||
primitive text
|
||||
];
|
||||
testHaskellDepends = [
|
||||
aeson array base base-orphans base16-bytestring base64-bytestring
|
||||
bytestring deepseq half QuickCheck random scientific tasty
|
||||
tasty-hunit tasty-quickcheck text vector
|
||||
];
|
||||
doCheck = false;
|
||||
description = "Concise Binary Object Representation (CBOR)";
|
||||
license = stdenv.lib.licenses.bsd3;
|
||||
}
|
51
dhall.nix
Normal file
51
dhall.nix
Normal file
@ -0,0 +1,51 @@
|
||||
{ mkDerivation, aeson, aeson-pretty, ansi-terminal, atomic-write
|
||||
, base, bytestring, case-insensitive, cborg, cborg-json, containers
|
||||
, contravariant, cryptonite, data-fix, deepseq, Diff, directory
|
||||
, doctest, dotgen, either, exceptions, filepath, foldl, gauge
|
||||
, generic-random, hashable, haskeline, http-client, http-client-tls
|
||||
, http-types, lens-family-core, megaparsec, memory, mockery, mtl
|
||||
, network-uri, optparse-applicative, parser-combinators, parsers
|
||||
, pretty-simple, prettyprinter, prettyprinter-ansi-terminal
|
||||
, profunctors, QuickCheck, quickcheck-instances, repline
|
||||
, scientific, semigroups, serialise, special-values, spoon, stdenv
|
||||
, tasty, tasty-expected-failure, tasty-hunit, tasty-quickcheck
|
||||
, template-haskell, text, th-lift-instances, transformers
|
||||
, transformers-compat, turtle, unordered-containers, uri-encode
|
||||
, vector
|
||||
}:
|
||||
mkDerivation {
|
||||
pname = "dhall";
|
||||
version = "1.30.0";
|
||||
sha256 = "f2be9599ddd88602c1577b0ca57849c9827c9e700e105102cecc17c56b7c4a81";
|
||||
revision = "1";
|
||||
editedCabalFile = "1pazhb3h1rabb80wxh29k5yfp915zqp1gmhcv4mx7ibzv9zw7miq";
|
||||
isLibrary = true;
|
||||
isExecutable = true;
|
||||
libraryHaskellDepends = [
|
||||
aeson aeson-pretty ansi-terminal atomic-write base bytestring
|
||||
case-insensitive cborg cborg-json containers contravariant
|
||||
cryptonite data-fix deepseq Diff directory dotgen either exceptions
|
||||
filepath hashable haskeline http-client http-client-tls http-types
|
||||
lens-family-core megaparsec memory mtl network-uri
|
||||
optparse-applicative parser-combinators parsers pretty-simple
|
||||
prettyprinter prettyprinter-ansi-terminal profunctors repline
|
||||
scientific serialise template-haskell text th-lift-instances
|
||||
transformers transformers-compat unordered-containers uri-encode
|
||||
vector
|
||||
];
|
||||
executableHaskellDepends = [ base ];
|
||||
testHaskellDepends = [
|
||||
base bytestring cborg containers data-fix deepseq directory doctest
|
||||
either filepath foldl generic-random lens-family-core megaparsec
|
||||
mockery prettyprinter QuickCheck quickcheck-instances scientific
|
||||
semigroups serialise special-values spoon tasty
|
||||
tasty-expected-failure tasty-hunit tasty-quickcheck text
|
||||
transformers turtle unordered-containers vector
|
||||
];
|
||||
benchmarkHaskellDepends = [
|
||||
base bytestring containers directory gauge serialise text
|
||||
];
|
||||
doCheck = false;
|
||||
description = "A configuration language guaranteed to terminate";
|
||||
license = stdenv.lib.licenses.bsd3;
|
||||
}
|
@ -1,235 +1,2 @@
|
||||
{-# language ApplicativeDo #-}
|
||||
{-# language BlockArguments #-}
|
||||
{-# language FlexibleContexts #-}
|
||||
{-# language LambdaCase #-}
|
||||
{-# language NamedFieldPuns #-}
|
||||
{-# language OverloadedStrings #-}
|
||||
{-# language PackageImports #-}
|
||||
|
||||
module Main ( main ) where
|
||||
|
||||
import "base" Control.Applicative ( (<**>), liftA2, many, some )
|
||||
import "base" Control.Monad ( guard )
|
||||
import "base" Control.Monad.IO.Class ( liftIO )
|
||||
import "base" Data.Foldable ( fold, for_ )
|
||||
|
||||
import qualified "containers" Data.Map.Strict as Map
|
||||
import "containers" Data.Set ( Set )
|
||||
import qualified "containers" Data.Set as Set
|
||||
|
||||
import "directory" System.Directory ( doesPathExist, withCurrentDirectory, canonicalizePath, listDirectory, doesFileExist, doesDirectoryExist )
|
||||
|
||||
import "filepath" System.FilePath ( isExtensionOf )
|
||||
|
||||
import "ghc" FastString ( unpackFS )
|
||||
import "ghc" HieBin ( HieFileResult( HieFileResult, hie_file_result ) )
|
||||
import "ghc" HieBin ( readHieFile )
|
||||
import "ghc" Module
|
||||
( DefUnitId( DefUnitId )
|
||||
, Module( Module )
|
||||
, UnitId( DefiniteUnitId )
|
||||
, mkModuleName
|
||||
, moduleUnitId
|
||||
, stringToInstalledUnitId
|
||||
, unitIdFS
|
||||
)
|
||||
import "ghc" NameCache ( initNameCache )
|
||||
import "ghc" OccName
|
||||
( mkOccName
|
||||
, occNameString
|
||||
, varName
|
||||
)
|
||||
import "ghc" SrcLoc ( srcLocLine, srcLocCol, realSrcSpanStart )
|
||||
import "ghc" UniqSupply ( mkSplitUniqSupply )
|
||||
|
||||
import "optparse-applicative" Options.Applicative
|
||||
( Parser
|
||||
, execParser
|
||||
, fullDesc
|
||||
, header
|
||||
, help
|
||||
, helper
|
||||
, info
|
||||
, long
|
||||
, maybeReader
|
||||
, metavar
|
||||
, option
|
||||
, strArgument
|
||||
, strOption
|
||||
)
|
||||
|
||||
import "transformers" Control.Monad.Trans.State.Strict ( execStateT )
|
||||
|
||||
import Weeder
|
||||
|
||||
|
||||
data CommandLineArguments =
|
||||
CommandLineArguments
|
||||
{ hiePaths :: [ FilePath ]
|
||||
, roots :: Set Root
|
||||
, units :: Set String
|
||||
}
|
||||
|
||||
|
||||
commandLineArgumentsParser :: Parser CommandLineArguments
|
||||
commandLineArgumentsParser = do
|
||||
hiePaths <-
|
||||
some
|
||||
( strArgument
|
||||
( metavar "HIE"
|
||||
<> help "A path to a .hie file, or a directory containing .hie files"
|
||||
)
|
||||
)
|
||||
|
||||
roots <-
|
||||
many
|
||||
( option
|
||||
( maybeReader \str ->
|
||||
case words str of
|
||||
[ unitId, moduleName, sym ] ->
|
||||
return $
|
||||
DeclarationRoot $
|
||||
Declaration
|
||||
{ declModule =
|
||||
Module
|
||||
( DefiniteUnitId ( DefUnitId ( stringToInstalledUnitId unitId ) ) )
|
||||
( mkModuleName moduleName )
|
||||
, declOccName =
|
||||
mkOccName varName sym
|
||||
}
|
||||
|
||||
[ unitId, moduleName ] ->
|
||||
return $
|
||||
ModuleRoot $
|
||||
Module
|
||||
( DefiniteUnitId ( DefUnitId ( stringToInstalledUnitId unitId ) ) )
|
||||
( mkModuleName moduleName )
|
||||
|
||||
_ ->
|
||||
Nothing
|
||||
)
|
||||
( long "root"
|
||||
<> help "A symbol that should be added to the root set. Symbols are of the form unit$Module.symbol"
|
||||
)
|
||||
)
|
||||
|
||||
units <-
|
||||
many
|
||||
( strOption
|
||||
( long "report-unit"
|
||||
<> help "Report unused declarations in this unit. If ommitted, all units will be reported. Can be supplied multiple times."
|
||||
)
|
||||
)
|
||||
|
||||
return
|
||||
CommandLineArguments
|
||||
{ hiePaths
|
||||
, roots = Set.fromList roots
|
||||
, units = Set.fromList units
|
||||
}
|
||||
|
||||
|
||||
main :: IO ()
|
||||
main = do
|
||||
CommandLineArguments{ hiePaths, roots, units } <-
|
||||
execParser
|
||||
( info
|
||||
( commandLineArgumentsParser <**> helper )
|
||||
( fullDesc
|
||||
<> header "Find unused declarations in Haskell projects"
|
||||
)
|
||||
)
|
||||
|
||||
hieFilePaths <-
|
||||
foldMap getHieFilesIn hiePaths
|
||||
|
||||
nameCache <- do
|
||||
uniqSupply <- mkSplitUniqSupply 'z'
|
||||
return ( initNameCache uniqSupply [] )
|
||||
|
||||
analysis <-
|
||||
flip execStateT emptyAnalysis do
|
||||
for_ hieFilePaths \hieFilePath -> do
|
||||
( HieFileResult{ hie_file_result }, _ ) <-
|
||||
liftIO ( readHieFile nameCache hieFilePath )
|
||||
|
||||
analyseHieFile hie_file_result
|
||||
|
||||
let
|
||||
reachableSet =
|
||||
reachable
|
||||
analysis
|
||||
( roots <> Set.map DeclarationRoot ( implicitRoots analysis ) )
|
||||
|
||||
dead =
|
||||
Set.filter
|
||||
( \d ->
|
||||
if Set.null units then
|
||||
True
|
||||
|
||||
else
|
||||
Set.member
|
||||
( unpackFS ( unitIdFS ( moduleUnitId ( declModule d ) ) ) )
|
||||
units
|
||||
)
|
||||
( allDeclarations analysis Set.\\ reachableSet )
|
||||
|
||||
warnings =
|
||||
Map.unionsWith (++) $
|
||||
foldMap
|
||||
( \d ->
|
||||
fold $ do
|
||||
moduleFilePath <- Map.lookup ( declModule d ) ( modulePaths analysis )
|
||||
|
||||
spans <- Map.lookup d ( declarationSites analysis )
|
||||
guard $ not $ null spans
|
||||
|
||||
return [ Map.singleton moduleFilePath ( liftA2 (,) (Set.toList spans) (pure d) ) ]
|
||||
)
|
||||
dead
|
||||
|
||||
for_ ( Map.toList warnings ) \( path, declarations ) ->
|
||||
for_ declarations \( srcSpan, d ) -> do
|
||||
let start = realSrcSpanStart srcSpan
|
||||
|
||||
putStrLn $
|
||||
unwords
|
||||
[ foldMap ( <> ":" ) [ path, show ( srcLocLine start ), show ( srcLocCol start ) ]
|
||||
, occNameString ( declOccName d )
|
||||
]
|
||||
|
||||
|
||||
-- | Recursively search for .hie files in given directory
|
||||
getHieFilesIn :: FilePath -> IO [FilePath]
|
||||
getHieFilesIn path = do
|
||||
exists <-
|
||||
doesPathExist path
|
||||
|
||||
if exists
|
||||
then do
|
||||
isFile <-
|
||||
doesFileExist path
|
||||
|
||||
if isFile && "hie" `isExtensionOf` path
|
||||
then do
|
||||
path' <-
|
||||
canonicalizePath path
|
||||
|
||||
return [ path' ]
|
||||
|
||||
else do
|
||||
isDir <-
|
||||
doesDirectoryExist path
|
||||
|
||||
if isDir
|
||||
then do
|
||||
cnts <-
|
||||
listDirectory path
|
||||
|
||||
withCurrentDirectory path ( foldMap getHieFilesIn cnts )
|
||||
|
||||
else
|
||||
return []
|
||||
|
||||
else
|
||||
return []
|
||||
import Weeder.Main
|
||||
|
24
prettyprinter.nix
Normal file
24
prettyprinter.nix
Normal file
@ -0,0 +1,24 @@
|
||||
{ mkDerivation, ansi-wl-pprint, base, base-compat, bytestring
|
||||
, containers, deepseq, doctest, gauge, mtl, pgp-wordlist
|
||||
, QuickCheck, quickcheck-instances, random, stdenv, tasty
|
||||
, tasty-hunit, tasty-quickcheck, text, transformers
|
||||
}:
|
||||
mkDerivation {
|
||||
pname = "prettyprinter";
|
||||
version = "1.6.1";
|
||||
sha256 = "3f9765764db7b55db8fb29b92ad26a02f08364e5947a89e2e1aa6d8a6087d781";
|
||||
isLibrary = true;
|
||||
isExecutable = true;
|
||||
libraryHaskellDepends = [ base text ];
|
||||
testHaskellDepends = [
|
||||
base bytestring doctest pgp-wordlist QuickCheck
|
||||
quickcheck-instances tasty tasty-hunit tasty-quickcheck text
|
||||
];
|
||||
benchmarkHaskellDepends = [
|
||||
ansi-wl-pprint base base-compat containers deepseq gauge mtl
|
||||
QuickCheck random text transformers
|
||||
];
|
||||
homepage = "http://github.com/quchen/prettyprinter";
|
||||
description = "A modern, easy to use, well-documented, extensible pretty-printer";
|
||||
license = stdenv.lib.licenses.bsd2;
|
||||
}
|
23
shell.nix
23
shell.nix
@ -14,18 +14,31 @@ let
|
||||
)
|
||||
( self:
|
||||
super:
|
||||
{ weeder =
|
||||
{ dhall =
|
||||
self.callPackage ./dhall.nix {};
|
||||
|
||||
weeder =
|
||||
self.callCabal2nix
|
||||
"weeder"
|
||||
( cleanSource ./. )
|
||||
{};
|
||||
|
||||
ghcide =
|
||||
haskell.lib.dontCheck super.ghcide;
|
||||
cborg =
|
||||
self.callPackage ./cborg.nix {};
|
||||
|
||||
cborg-json =
|
||||
self.callPackage ./cborg-json.nix {};
|
||||
|
||||
prettyprinter =
|
||||
self.callPackage ./prettyprinter.nix {};
|
||||
|
||||
atomic-write =
|
||||
self.callPackage ./atomic-write.nix {};
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
in
|
||||
haskellPackages.weeder.env.overrideAttrs
|
||||
( old: { buildInputs = old.buildInputs or [] ++ [ haskellPackages.ghcide ]; } )
|
||||
haskellPackages.weeder.env
|
||||
# .overrideAttrs
|
||||
# ( old: { buildInputs = old.buildInputs or [] ++ [ haskellPackages.ghcide ]; } )
|
||||
|
@ -25,26 +25,26 @@ module Weeder
|
||||
)
|
||||
where
|
||||
|
||||
import "algebraic-graphs" Algebra.Graph ( Graph, edge, empty, overlay, vertex, vertexList )
|
||||
import "algebraic-graphs" Algebra.Graph.ToGraph ( dfs )
|
||||
import Algebra.Graph ( Graph, edge, empty, overlay, vertex, vertexList )
|
||||
import Algebra.Graph.ToGraph ( dfs )
|
||||
|
||||
import "base" Control.Applicative ( Alternative )
|
||||
import "base" Control.Monad ( guard, msum, when )
|
||||
import "base" Data.Foldable ( for_, traverse_ )
|
||||
import "base" Data.List ( intercalate )
|
||||
import "base" Data.Monoid ( First( First ) )
|
||||
import "base" GHC.Generics ( Generic )
|
||||
import "base" Prelude hiding ( span )
|
||||
import Control.Applicative ( Alternative )
|
||||
import Control.Monad ( guard, msum, when )
|
||||
import Data.Foldable ( for_, traverse_ )
|
||||
import Data.List ( intercalate )
|
||||
import Data.Monoid ( First( First ) )
|
||||
import GHC.Generics ( Generic )
|
||||
import Prelude hiding ( span )
|
||||
|
||||
import "containers" Data.Map.Strict ( Map )
|
||||
import qualified "containers" Data.Map.Strict as Map
|
||||
import "containers" Data.Sequence ( Seq )
|
||||
import "containers" Data.Set ( Set )
|
||||
import qualified "containers" Data.Set as Set
|
||||
import Data.Map.Strict ( Map )
|
||||
import qualified Data.Map.Strict as Map
|
||||
import Data.Sequence ( Seq )
|
||||
import Data.Set ( Set )
|
||||
import qualified Data.Set as Set
|
||||
|
||||
import "ghc" Avail ( AvailInfo( Avail, AvailTC ) )
|
||||
import "ghc" FieldLabel ( FieldLbl( FieldLabel, flSelector ) )
|
||||
import "ghc" HieTypes
|
||||
import Avail ( AvailInfo( Avail, AvailTC ) )
|
||||
import FieldLabel ( FieldLbl( FieldLabel, flSelector ) )
|
||||
import HieTypes
|
||||
( BindType( RegularBind )
|
||||
, DeclType( DataDec, ClassDec, ConDec )
|
||||
, ContextInfo( Decl, ValBind, PatternBind, Use, TyDecl, ClassTyDecl )
|
||||
@ -55,9 +55,9 @@ import "ghc" HieTypes
|
||||
, NodeInfo( NodeInfo, nodeIdentifiers, nodeAnnotations )
|
||||
, Scope( ModuleScope )
|
||||
)
|
||||
import "ghc" Module ( Module, moduleStableString )
|
||||
import "ghc" Name ( Name, nameOccName, nameModule_maybe )
|
||||
import "ghc" OccName
|
||||
import Module ( Module, moduleStableString )
|
||||
import Name ( Name, nameOccName, nameModule_maybe )
|
||||
import OccName
|
||||
( OccName
|
||||
, isDataOcc
|
||||
, isDataSymOcc
|
||||
@ -66,15 +66,15 @@ import "ghc" OccName
|
||||
, isVarOcc
|
||||
, occNameString
|
||||
)
|
||||
import "ghc" SrcLoc ( RealSrcSpan, realSrcSpanStart, realSrcSpanEnd )
|
||||
import SrcLoc ( RealSrcSpan, realSrcSpanStart, realSrcSpanEnd )
|
||||
|
||||
import "generic-lens" Data.Generics.Labels ()
|
||||
import Data.Generics.Labels ()
|
||||
|
||||
import "lens" Control.Lens ( (%=) )
|
||||
import Control.Lens ( (%=) )
|
||||
|
||||
import "mtl" Control.Monad.State.Class ( MonadState )
|
||||
import Control.Monad.State.Class ( MonadState )
|
||||
|
||||
import "transformers" Control.Monad.Trans.Maybe ( runMaybeT )
|
||||
import Control.Monad.Trans.Maybe ( runMaybeT )
|
||||
|
||||
|
||||
data Declaration =
|
||||
|
@ -22,25 +22,39 @@ import Data.Set ( Set )
|
||||
import qualified Data.Set as Set
|
||||
|
||||
|
||||
-- | Configuration for Weeder analysis.
|
||||
data Config = Config
|
||||
{ ignore :: Set Declaration
|
||||
-- ^ The set of declarations that should not be reported to be weeds.
|
||||
, roots :: Set Root
|
||||
-- ^ The set of roots to consider always alive.
|
||||
, strict :: Bool
|
||||
-- ^ Enable strict analysis. Strict analysis means:
|
||||
--
|
||||
-- * The set of ignored declarations must be a subset of reported weeds
|
||||
-- (i.e., you are not trying to ignore something that is not considered
|
||||
-- a weed)
|
||||
-- * The set of roots only mentions declarations that Weeder can find
|
||||
-- (i.e., all mentioned roots point to known declarations or modules).
|
||||
, typeClassRoots :: Bool
|
||||
-- ^ If True, consider all declarations in a type class as part of the root
|
||||
-- set. Weeder is currently unable to identify whether or not a type class
|
||||
-- instance is used - enabling this option can prevent false positives.
|
||||
}
|
||||
|
||||
|
||||
config :: Dhall.Type Config
|
||||
config :: Dhall.Decoder Config
|
||||
config =
|
||||
Dhall.record do
|
||||
ignore <- Set.fromList <$> Dhall.field "ignore" ( Dhall.list declaration )
|
||||
roots <- Set.fromList <$> Dhall.field "roots" ( Dhall.list root )
|
||||
strict <- Dhall.field "strict" Dhall.bool
|
||||
|
||||
typeClassRoots <- Dhall.field "type-class-roots" Dhall.bool
|
||||
|
||||
return Config{..}
|
||||
|
||||
|
||||
declaration :: Dhall.Type Declaration
|
||||
declaration :: Dhall.Decoder Declaration
|
||||
declaration =
|
||||
Dhall.record do
|
||||
unitId <- Dhall.field "unit-id" Dhall.string
|
||||
@ -58,7 +72,7 @@ declaration =
|
||||
}
|
||||
|
||||
|
||||
root :: Dhall.Type Root
|
||||
root :: Dhall.Decoder Root
|
||||
root =
|
||||
Dhall.union $ mconcat
|
||||
[ ModuleRoot <$> Dhall.constructor "Module" moduleDecoder
|
||||
@ -66,7 +80,7 @@ root =
|
||||
]
|
||||
|
||||
|
||||
moduleDecoder :: Dhall.Type Module
|
||||
moduleDecoder :: Dhall.Decoder Module
|
||||
moduleDecoder =
|
||||
Dhall.record do
|
||||
unitId <- Dhall.field "unit-id" Dhall.string
|
||||
|
@ -1,70 +1,38 @@
|
||||
{-# language ApplicativeDo #-}
|
||||
{-# language LambdaCase #-}
|
||||
{-# language BlockArguments #-}
|
||||
{-# language FlexibleContexts #-}
|
||||
{-# language LambdaCase #-}
|
||||
{-# language NamedFieldPuns #-}
|
||||
{-# language OverloadedStrings #-}
|
||||
{-# language PackageImports #-}
|
||||
|
||||
module Weeder.Main ( mainWith ) where
|
||||
module Weeder.Main ( main, mainWithConfig ) where
|
||||
|
||||
import "base" Control.Applicative ( (<**>), liftA2, many, some )
|
||||
import "base" Control.Monad ( guard )
|
||||
import "base" Control.Monad.IO.Class ( liftIO )
|
||||
import "base" Data.Foldable ( fold, for_ )
|
||||
import Control.Monad ( guard )
|
||||
import Control.Monad.IO.Class ( liftIO )
|
||||
|
||||
import qualified "containers" Data.Map.Strict as Map
|
||||
import "containers" Data.Set ( Set )
|
||||
import qualified "containers" Data.Set as Set
|
||||
import qualified Data.Map.Strict as Map
|
||||
import qualified Data.Set as Set
|
||||
|
||||
import "directory" System.Directory ( doesPathExist, withCurrentDirectory, canonicalizePath, listDirectory, doesFileExist, doesDirectoryExist )
|
||||
import System.Directory ( doesPathExist, withCurrentDirectory, canonicalizePath, listDirectory, doesFileExist, doesDirectoryExist )
|
||||
|
||||
import "filepath" System.FilePath ( isExtensionOf )
|
||||
import System.FilePath ( isExtensionOf )
|
||||
|
||||
import "ghc" FastString ( unpackFS )
|
||||
import "ghc" HieBin ( HieFileResult( HieFileResult, hie_file_result ) )
|
||||
import "ghc" HieBin ( readHieFile )
|
||||
import "ghc" Module
|
||||
( DefUnitId( DefUnitId )
|
||||
, Module( Module )
|
||||
, UnitId( DefiniteUnitId )
|
||||
, mkModuleName
|
||||
, moduleUnitId
|
||||
, stringToInstalledUnitId
|
||||
, unitIdFS
|
||||
)
|
||||
import "ghc" NameCache ( initNameCache )
|
||||
import "ghc" OccName
|
||||
( mkOccName
|
||||
, occNameString
|
||||
, varName
|
||||
)
|
||||
import "ghc" SrcLoc ( srcLocLine, srcLocCol, realSrcSpanStart )
|
||||
import "ghc" UniqSupply ( mkSplitUniqSupply )
|
||||
import HieBin ( HieFileResult( HieFileResult, hie_file_result ) )
|
||||
import HieBin ( readHieFile )
|
||||
import NameCache ( initNameCache )
|
||||
import OccName ( occNameString )
|
||||
import SrcLoc ( srcLocLine, srcLocCol, realSrcSpanStart )
|
||||
import UniqSupply ( mkSplitUniqSupply )
|
||||
|
||||
import "optparse-applicative" Options.Applicative
|
||||
( Parser
|
||||
, execParser
|
||||
, fullDesc
|
||||
, header
|
||||
, help
|
||||
, helper
|
||||
, info
|
||||
, long
|
||||
, maybeReader
|
||||
, metavar
|
||||
, option
|
||||
, strArgument
|
||||
, strOption
|
||||
)
|
||||
|
||||
import "transformers" Control.Monad.Trans.State.Strict ( execStateT )
|
||||
import Control.Monad.Trans.State.Strict ( execStateT )
|
||||
|
||||
import Weeder
|
||||
import Weeder.Config
|
||||
|
||||
|
||||
data Mode = Run Config
|
||||
import qualified Dhall
|
||||
import Options.Applicative
|
||||
import Data.Foldable
|
||||
import Data.Bool
|
||||
|
||||
|
||||
main :: IO ()
|
||||
@ -72,113 +40,101 @@ main = do
|
||||
configExpr <-
|
||||
execParser $
|
||||
info
|
||||
( asum
|
||||
[ Run <$>
|
||||
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."
|
||||
)
|
||||
, GenerateConfig
|
||||
]
|
||||
( 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"
|
||||
)
|
||||
)
|
||||
mempty
|
||||
|
||||
Dhall.input config configExpr >>= mainWithConfig
|
||||
|
||||
|
||||
|
||||
|
||||
mainWithConfig :: Config -> [ FilePath ] -> IO ()
|
||||
mainWithConfig Config{} hiePaths = do
|
||||
mainWithConfig :: Config -> IO ()
|
||||
mainWithConfig Config{ roots, typeClassRoots, ignore } = do
|
||||
hieFilePaths <-
|
||||
foldMap getHieFilesIn hiePaths
|
||||
getHieFilesIn "./."
|
||||
|
||||
nameCache <- do
|
||||
uniqSupply <- mkSplitUniqSupply 'z'
|
||||
return ( initNameCache uniqSupply [] )
|
||||
|
||||
-- analysis <-
|
||||
-- flip execStateT emptyAnalysis do
|
||||
-- for_ hieFilePaths \hieFilePath -> do
|
||||
-- ( HieFileResult{ hie_file_result }, _ ) <-
|
||||
-- liftIO ( readHieFile nameCache hieFilePath )
|
||||
analysis <-
|
||||
flip execStateT emptyAnalysis do
|
||||
for_ hieFilePaths \hieFilePath -> do
|
||||
( HieFileResult{ hie_file_result }, _ ) <-
|
||||
liftIO ( readHieFile nameCache hieFilePath )
|
||||
|
||||
-- analyseHieFile hie_file_result
|
||||
analyseHieFile hie_file_result
|
||||
|
||||
-- let
|
||||
-- reachableSet =
|
||||
-- reachable
|
||||
-- analysis
|
||||
-- ( roots <> Set.map DeclarationRoot ( implicitRoots analysis ) )
|
||||
let
|
||||
reachableSet =
|
||||
reachable
|
||||
analysis
|
||||
( roots <> bool mempty ( Set.map DeclarationRoot ( implicitRoots analysis ) ) typeClassRoots )
|
||||
|
||||
-- dead =
|
||||
-- Set.filter
|
||||
-- ( \d ->
|
||||
-- if Set.null units then
|
||||
-- True
|
||||
dead =
|
||||
Set.filter
|
||||
( not . ( `Set.member` ignore ) )
|
||||
( allDeclarations analysis Set.\\ reachableSet )
|
||||
|
||||
-- else
|
||||
-- Set.member
|
||||
-- ( unpackFS ( unitIdFS ( moduleUnitId ( declModule d ) ) ) )
|
||||
-- units
|
||||
-- )
|
||||
-- ( allDeclarations analysis Set.\\ reachableSet )
|
||||
warnings =
|
||||
Map.unionsWith (++) $
|
||||
foldMap
|
||||
( \d ->
|
||||
fold $ do
|
||||
moduleFilePath <- Map.lookup ( declModule d ) ( modulePaths analysis )
|
||||
|
||||
-- warnings =
|
||||
-- Map.unionsWith (++) $
|
||||
-- foldMap
|
||||
-- ( \d ->
|
||||
-- fold $ do
|
||||
-- moduleFilePath <- Map.lookup ( declModule d ) ( modulePaths analysis )
|
||||
spans <- Map.lookup d ( declarationSites analysis )
|
||||
guard $ not $ null spans
|
||||
|
||||
-- spans <- Map.lookup d ( declarationSites analysis )
|
||||
-- guard $ not $ null spans
|
||||
return [ Map.singleton moduleFilePath ( liftA2 (,) (Set.toList spans) (pure d) ) ]
|
||||
)
|
||||
dead
|
||||
|
||||
-- return [ Map.singleton moduleFilePath ( liftA2 (,) (Set.toList spans) (pure d) ) ]
|
||||
-- )
|
||||
-- dead
|
||||
for_ ( Map.toList warnings ) \( path, declarations ) ->
|
||||
for_ declarations \( srcSpan, d ) -> do
|
||||
let start = realSrcSpanStart srcSpan
|
||||
|
||||
-- for_ ( Map.toList warnings ) \( path, declarations ) ->
|
||||
-- for_ declarations \( srcSpan, d ) -> do
|
||||
-- let start = realSrcSpanStart srcSpan
|
||||
|
||||
-- putStrLn $
|
||||
-- unwords
|
||||
-- [ foldMap ( <> ":" ) [ path, show ( srcLocLine start ), show ( srcLocCol start ) ]
|
||||
-- , occNameString ( declOccName d )
|
||||
-- ]
|
||||
putStrLn $
|
||||
unwords
|
||||
[ foldMap ( <> ":" ) [ path, show ( srcLocLine start ), show ( srcLocCol start ) ]
|
||||
, occNameString ( declOccName d )
|
||||
]
|
||||
|
||||
|
||||
-- -- | Recursively search for .hie files in given directory
|
||||
-- getHieFilesIn :: FilePath -> IO [FilePath]
|
||||
-- getHieFilesIn path = do
|
||||
-- exists <-
|
||||
-- doesPathExist path
|
||||
-- | Recursively search for .hie files in given directory
|
||||
getHieFilesIn :: FilePath -> IO [FilePath]
|
||||
getHieFilesIn path = do
|
||||
exists <-
|
||||
doesPathExist path
|
||||
|
||||
-- if exists
|
||||
-- then do
|
||||
-- isFile <-
|
||||
-- doesFileExist path
|
||||
if exists
|
||||
then do
|
||||
isFile <-
|
||||
doesFileExist path
|
||||
|
||||
-- if isFile && "hie" `isExtensionOf` path
|
||||
-- then do
|
||||
-- path' <-
|
||||
-- canonicalizePath path
|
||||
if isFile && "hie" `isExtensionOf` path
|
||||
then do
|
||||
path' <-
|
||||
canonicalizePath path
|
||||
|
||||
-- return [ path' ]
|
||||
return [ path' ]
|
||||
|
||||
-- else do
|
||||
-- isDir <-
|
||||
-- doesDirectoryExist path
|
||||
else do
|
||||
isDir <-
|
||||
doesDirectoryExist path
|
||||
|
||||
-- if isDir
|
||||
-- then do
|
||||
-- cnts <-
|
||||
-- listDirectory path
|
||||
if isDir
|
||||
then do
|
||||
cnts <-
|
||||
listDirectory path
|
||||
|
||||
-- withCurrentDirectory path ( foldMap getHieFilesIn cnts )
|
||||
withCurrentDirectory path ( foldMap getHieFilesIn cnts )
|
||||
|
||||
-- else
|
||||
-- return []
|
||||
else
|
||||
return []
|
||||
|
||||
-- else
|
||||
-- return []
|
||||
else
|
||||
return []
|
||||
|
25
weeder.cabal
25
weeder.cabal
@ -5,17 +5,18 @@ version: 1.0
|
||||
|
||||
library
|
||||
build-depends:
|
||||
algebraic-graphs ^>= 0.5 ,
|
||||
base ^>= 4.13.0.0 ,
|
||||
containers ^>= 0.6.2.1 ,
|
||||
dhall ^>= 1.26.1 ,
|
||||
directory ^>= 1.3.3.2 ,
|
||||
filepath ^>= 1.4.2.1 ,
|
||||
generic-lens ^>= 1.1.0.0 || ^>= 2.0.0.0 ,
|
||||
ghc ^>= 8.8.1 ,
|
||||
lens ^>= 4.18.1 ,
|
||||
mtl ^>= 2.2.2 ,
|
||||
transformers ^>= 0.5.6.2
|
||||
algebraic-graphs ^>= 0.4 ,
|
||||
base ^>= 4.13.0.0 ,
|
||||
containers ^>= 0.6.2.1 ,
|
||||
dhall ^>= 1.30.0 ,
|
||||
directory ^>= 1.3.3.2 ,
|
||||
filepath ^>= 1.4.2.1 ,
|
||||
generic-lens ^>= 1.1.0.0 || ^>= 2.0.0.0 ,
|
||||
ghc ^>= 8.8.1 ,
|
||||
lens ^>= 4.18.1 ,
|
||||
mtl ^>= 2.2.2 ,
|
||||
optparse-applicative ^>= 0.14.3.0 ,
|
||||
transformers ^>= 0.5.6.2
|
||||
hs-source-dirs: src
|
||||
exposed-modules:
|
||||
Weeder
|
||||
@ -31,7 +32,7 @@ executable weeder
|
||||
directory ^>= 1.3.3.2 ,
|
||||
filepath ^>= 1.4.2.1 ,
|
||||
ghc ^>= 8.8.1 ,
|
||||
optparse-applicative ^>= 0.15.0.0 ,
|
||||
optparse-applicative ^>= 0.14.3.0 ,
|
||||
transformers ^>= 0.5.6.2 ,
|
||||
weeder
|
||||
main-is: Main.hs
|
||||
|
23
weeder.dhall
Normal file
23
weeder.dhall
Normal file
@ -0,0 +1,23 @@
|
||||
let Module
|
||||
: Type
|
||||
= { unit-id : Text, module : Text }
|
||||
|
||||
let Declaration
|
||||
: Type
|
||||
= Module ⩓ { symbol : Text }
|
||||
|
||||
let Root
|
||||
: Type
|
||||
= < Declaration : Declaration | Module : Module >
|
||||
|
||||
in { ignore = [] : List Declaration
|
||||
, roots =
|
||||
[ Root.Declaration
|
||||
{ unit-id = "weeder-1.0-inplace"
|
||||
, module = "Weeder.Main"
|
||||
, symbol = "main"
|
||||
}
|
||||
]
|
||||
, strict = True
|
||||
, type-class-roots = True
|
||||
}
|
Loading…
Reference in New Issue
Block a user