Skip to content

Commit

Permalink
Warn on missing ignored files and detect ignored files better
Browse files Browse the repository at this point in the history
  • Loading branch information
soenkehahn committed Jun 3, 2016
1 parent 4302f85 commit db6e4b1
Show file tree
Hide file tree
Showing 4 changed files with 74 additions and 15 deletions.
5 changes: 3 additions & 2 deletions dead-code-detection.cabal
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
-- This file has been generated from package.yaml by hpack version 0.13.0.
-- This file has been generated from package.yaml by hpack version 0.14.0.
--
-- see: https://github.com/sol/hpack

Expand Down Expand Up @@ -40,6 +40,7 @@ executable dead-code-detection
, uniplate
, ghc-paths
, gitrev
, directory
other-modules:
Ast
Ast.UsedNames
Expand Down Expand Up @@ -69,11 +70,11 @@ test-suite spec
, uniplate
, ghc-paths
, gitrev
, directory
, hspec
, mockery
, interpolate
, filepath
, directory
other-modules:
Ast.UsedNamesSpec
AstSpec
Expand Down
2 changes: 1 addition & 1 deletion package.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ dependencies:
- uniplate
- ghc-paths
- gitrev
- directory

executables:
dead-code-detection:
Expand All @@ -43,4 +44,3 @@ tests:
- mockery
- interpolate
- filepath
- directory
29 changes: 27 additions & 2 deletions src/Run.hs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import Development.GitRev
import FastString
import GHC
import OccName
import System.Directory
import System.Exit
import WithCli

Expand Down Expand Up @@ -43,8 +44,8 @@ run = do
throwIO ExitSuccess
when (null $ root options) $
die "missing option: --root=STRING"
files <- filter (`notElem` ignore options) <$>
findHaskellFiles (sourceDirs options)
files <- findHaskellFiles (sourceDirs options)
>>= filterNotIgnored (ignore options)
deadNames <- deadNamesFromFiles
files
(map mkModuleName (root options))
Expand All @@ -66,6 +67,30 @@ versionOutput =
"branch: " ++ $(gitBranch)
else "version: " ++ showVersion Paths.version

filterNotIgnored :: [FilePath] -> [FilePath] -> IO [FilePath]
filterNotIgnored ignored files = do
ignoredCanonicalized <- mapM safeCanonicalize ignored
forFilterM files $ \ file -> do
canonicalized <- safeCanonicalize file
return $ not (canonicalized `elem` ignoredCanonicalized)

safeCanonicalize :: FilePath -> IO FilePath
safeCanonicalize file = do
exists <- doesFileExist file
when (not exists) $ do
die ("file not found: " ++ file)
canonicalizePath file

forFilterM :: Monad m => [a] -> (a -> m Bool) -> m [a]
forFilterM list pred = case list of
(a : r) -> do
cond <- pred a
rest <- forFilterM r pred
if cond
then return (a : rest)
else return rest
[] -> return []

deadNamesFromFiles :: [FilePath] -> [ModuleName] -> Bool -> IO [String]
deadNamesFromFiles files roots includeUnderscoreNames = do
ast <- parse files
Expand Down
53 changes: 43 additions & 10 deletions test/RunSpec.hs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
{-# LANGUAGE QuasiQuotes #-}
{-# LANGUAGE ScopedTypeVariables #-}

module RunSpec where

Expand Down Expand Up @@ -32,7 +33,7 @@ spec = do

it "exits with a non-zero exit-code" $ do
withModules [main] $ do
run' `shouldThrow` (== ExitFailure 1)
run' `shouldDie` ""

it "allows to set multiple roots" $ do
let a = ("A", [i|
Expand All @@ -57,16 +58,37 @@ spec = do
withArgs ["--version"] run
output `shouldContain` "version: "

it "ignores files if told to do so" $ do
let main = ("Main", [i|
module Main where
main = return ()
|])
b = ("B", [i|
This is some arbitrary text that is not Haskell.
context "--ignore" $ do

it "ignores files if told to do so" $ do
let main = ("Main", [i|
module Main where
main = return ()
|])
run' = withArgs (words "-i. -e./B.hs --root Main") run
withModules [main, b] $ run' `shouldReturn` ()
b = ("B", [i|
This is some arbitrary text that is not Haskell.
|])
run' = withArgs (words "-i. -e./B.hs --root Main") run
withModules [main, b] $ run' `shouldReturn` ()

it "errors out on missing ignored files" $ do
let main = ("Main", [i|
module Main where
main = return ()
|])
run' = withArgs (words "-i. -e./B.hs --root Main") run
withModules [main] $ run' `shouldDie` "file not found: ./B.hs\n"

it "ignores files if referenced differently" $ do
let main = ("Main", [i|
module Main where
main = return ()
|])
b = ("B", [i|
This is some arbitrary text that is not Haskell.
|])
run' = withArgs (words "-i. -e B.hs --root Main") run
withModules [main, b] $ run' `shouldReturn` ()

describe "deadNamesFromFiles" $ do
it "should clearly mark ghc's output as such" $ do
Expand Down Expand Up @@ -137,3 +159,14 @@ spec = do
withModules [a, b] $ do
dead <- deadNamesFromFiles ["A.hs", "B.hs"] [mkModuleName "A"] False
dead `shouldMatchList` ["A.hs:4:1: bar", "B.hs:2:1: baz"]

shouldDie :: IO a -> String -> IO ()
shouldDie action err = do
(output, exception) <- hCapture [stderr] $ catch
(action >> return Nothing)
(\ (e :: ExitCode) -> return $ Just e)
case exception of
Nothing -> throwIO $ ErrorCall "shouldDie: didn't receive ExitCode exception"
Just ExitSuccess -> throwIO $ ErrorCall "shouldDie: received ExitSuccess exception"
Just (ExitFailure _) ->
output `shouldBe` err

0 comments on commit db6e4b1

Please sign in to comment.