Skip to content

Commit 4df88eb

Browse files
committed
Remove uses of pathtype library.
`pathtype` was not a great success for us: - it did not catch any bugs other than exposing some odd behavior in `readProjectFromPaths`; - it baffled everyone who hadn't spent hours staring into its API (that is to say, me) - it used `error` to check string literals, which is... fine, I guess, but doesn't help much in actuality; - it complicated error reporting and assemblage; - completely switching away from `FilePath` was not an option, as libraries like `directory-tree` and `Glob` require it; - its documentation is very poor and difficult to navigate; Furthermore, `pathtype` doesn't solve the most fundamental problem with the `FilePath` type currently in `base`: its `String` representation. The only valid representation for cross-platform file paths is `ByteString`, because Windows paths can contain unpaired UTF-16 surrogates. Upcoming revisions of the library are switching it to a `ShortByteString` representation, which is the Right Thing. I think the lesson learned here is that "parse, don't validate" is not super practical when the entire world has built around validation of file paths rather than parsing them. Indeed, true validation of file paths would entail IO everywhere, as we'd want to check for the existence and validity of a file path.
1 parent 4cfe7cc commit 4df88eb

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

63 files changed

+213
-301
lines changed

WORKSPACE

-1
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,6 @@ stack_snapshot(
101101
"optparse-applicative",
102102
"optparse-generic",
103103
"parsers",
104-
"pathtype",
105104
"pretty-show",
106105
"pretty-simple",
107106
"prettyprinter",

build/common.bzl

-2
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,6 @@ def semantic_language_library(language, name, srcs, ts_package = "", nodetypes =
129129
"@stackage//:generic-lens",
130130
"@stackage//:hashable",
131131
"@stackage//:lens",
132-
"@stackage//:pathtype",
133132
"@stackage//:semilattices",
134133
"@stackage//:tree-sitter",
135134
"@stackage//:tree-sitter-" + language,
@@ -154,7 +153,6 @@ def semantic_language_parsing_test(language, semantic_package = "", ts_package =
154153
"//semantic-ast",
155154
"@stackage//:bazel-runfiles",
156155
"@stackage//:hedgehog",
157-
"@stackage//:pathtype",
158156
"@stackage//:tasty",
159157
"@stackage//:tasty-hedgehog",
160158
"@stackage//:tasty-hunit",

semantic-analysis/BUILD.bazel

-1
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,6 @@ haskell_library(
3030
"@stackage//:aeson",
3131
"@stackage//:fused-effects",
3232
"@stackage//:hashable",
33-
"@stackage//:pathtype",
3433
"@stackage//:vector",
3534
],
3635
)

semantic-analysis/semantic-analysis.cabal

+1-1
Original file line numberDiff line numberDiff line change
@@ -73,9 +73,9 @@ library
7373
, base >= 4.13 && < 5
7474
, bytestring >= 0.10.8.2 && < 0.13
7575
, containers ^>= 0.6
76+
, filepath
7677
, fused-effects ^>= 1.1
7778
, hashable
78-
, pathtype ^>= 0.8.1
7979
, semantic-source ^>= 0.1.0.1
8080
, text ^>= 1.2.3.1
8181
, transformers ^>= 0.5

semantic-analysis/src/Analysis/Analysis/Concrete.hs

+2-3
Original file line numberDiff line numberDiff line change
@@ -35,10 +35,9 @@ import Data.Semigroup (Last(..))
3535
import Data.Text as Text (Text)
3636
import Prelude hiding (fail)
3737
import Source.Span
38-
import qualified System.Path as Path
3938

4039
data Concrete
41-
= Closure Path.AbsRelFile Span (Named (Concrete -> Concrete))
40+
= Closure FilePath Span (Named (Concrete -> Concrete))
4241
| Unit
4342
| Bool Bool
4443
| Int Int
@@ -93,7 +92,7 @@ vsubst n v = go
9392

9493
data FO
9594
= FOVar Name
96-
| FOClosure Path.AbsRelFile Span (Named FO)
95+
| FOClosure FilePath Span (Named FO)
9796
| FOUnit
9897
| FOBool Bool
9998
| FOInt Int

semantic-analysis/src/Analysis/Blob.hs

+6-8
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,6 @@ import Analysis.Reference as A
1313
import Data.Aeson
1414
import Source.Language as Language
1515
import Source.Source as Source
16-
import qualified System.Path as Path
17-
import qualified System.Path.PartClass as Path.PartClass
1816

1917
-- | The source, path information, and language of a file read from disk.
2018
data Blob = Blob
@@ -25,27 +23,27 @@ data Blob = Blob
2523
instance FromJSON Blob where
2624
parseJSON = withObject "Blob" $ \b -> do
2725
src <- b .: "content"
28-
Right pth <- fmap Path.parse (b .: "path")
26+
pth <- b .: "path"
2927
lang <- b .: "language"
3028
let lang' = if knownLanguage lang then lang else Language.forPath pth
31-
pure (fromSource (pth :: Path.AbsRelFile) lang' src)
29+
pure (fromSource (pth :: FilePath) lang' src)
3230

3331

3432
-- | Create a Blob from a provided path, language, and UTF-8 source.
3533
-- The resulting Blob's span is taken from the 'totalSpan' of the source.
36-
fromSource :: Path.PartClass.AbsRel ar => Path.File ar -> Language -> Source -> Blob
34+
fromSource :: FilePath -> Language -> Source -> Blob
3735
fromSource filepath language source
38-
= Blob source (A.File (A.Reference (Path.toAbsRel filepath) (totalSpan source)) language)
36+
= Blob source (A.File (A.Reference filepath (totalSpan source)) language)
3937

4038
blobLanguage :: Blob -> Language
4139
blobLanguage = A.fileBody . blobFile
4240

43-
blobPath :: Blob -> Path.AbsRelFile
41+
blobPath :: Blob -> FilePath
4442
blobPath = A.refPath . A.fileRef . blobFile
4543

4644
-- | Show FilePath for error or json outputs.
4745
blobFilePath :: Blob -> String
48-
blobFilePath = Path.toString . blobPath
46+
blobFilePath = blobPath
4947

5048
nullBlob :: Blob -> Bool
5149
nullBlob = Source.null . blobSource

semantic-analysis/src/Analysis/File.hs

+2-4
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,6 @@ import Data.Maybe (fromJust, listToMaybe)
1414
import GHC.Stack
1515
import Source.Language as Language
1616
import Source.Span
17-
import qualified System.Path as Path
18-
import qualified System.Path.PartClass as Path.PartClass
1917

2018
-- Files
2119

@@ -29,10 +27,10 @@ data File a = File
2927
-- Constructors
3028

3129
fromBody :: HasCallStack => a -> File a
32-
fromBody body = File (A.Reference (Path.absRel (srcLocFile srcLoc)) (spanFromSrcLoc srcLoc)) body where
30+
fromBody = File (A.Reference (srcLocFile srcLoc) (spanFromSrcLoc srcLoc)) where
3331
srcLoc = snd (fromJust (listToMaybe (getCallStack callStack)))
3432

35-
fromPath :: Path.PartClass.AbsRel ar => Path.File ar -> File Language
33+
fromPath :: FilePath -> File Language
3634
fromPath p = File (A.fromPath p) (Language.forPath p)
3735

3836

semantic-analysis/src/Analysis/Project.hs

+4-4
Original file line numberDiff line numberDiff line change
@@ -12,19 +12,19 @@ import Analysis.File
1212
import Data.Text (Text)
1313
import qualified Data.Text as T
1414
import Source.Language
15-
import qualified System.Path as Path
15+
import System.FilePath
1616

1717
-- | A 'Project' contains all the information that semantic needs
1818
-- to execute an analysis, diffing, or graphing pass.
1919
data Project = Project
20-
{ projectRootDir :: Path.AbsRelDir
20+
{ projectRootDir :: FilePath
2121
, projectBlobs :: [Blob]
2222
, projectLanguage :: Language
23-
, projectExcludeDirs :: [Path.AbsRelDir]
23+
, projectExcludeDirs :: [FilePath]
2424
} deriving (Eq, Show)
2525

2626
projectName :: Project -> Text
27-
projectName = T.pack . maybe "" Path.toString . Path.takeDirName . projectRootDir
27+
projectName = T.pack . takeDirectory . projectRootDir
2828

2929
projectExtensions :: Project -> [String]
3030
projectExtensions = extensionsForLanguage . projectLanguage

semantic-analysis/src/Analysis/Reference.hs

+3-5
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,11 @@ module Analysis.Reference
66
) where
77

88
import Source.Span
9-
import System.Path as Path
10-
import System.Path.PartClass as Path.PartClass
119

1210
-- Reference
1311

1412
data Reference = Reference
15-
{ refPath :: Path.AbsRelFile
13+
{ refPath :: FilePath
1614
, refSpan :: Span
1715
}
1816
deriving (Eq, Ord, Show)
@@ -21,5 +19,5 @@ data Reference = Reference
2119

2220
-- Constructors
2321

24-
fromPath :: Path.PartClass.AbsRel ar => Path.File ar -> Reference
25-
fromPath p = Reference (Path.toAbsRel p) (point (Pos 0 0))
22+
fromPath :: FilePath -> Reference
23+
fromPath p = Reference p (point (Pos 0 0))

semantic-analysis/src/Analysis/Syntax.hs

+2-3
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,6 @@ import Data.Monoid (First (..))
4343
import Data.String (IsString (..))
4444
import Data.Text (Text)
4545
import qualified Data.Vector as V
46-
import qualified System.Path as Path
4746

4847
data Term
4948
= Var Name
@@ -95,12 +94,12 @@ let' n v m = do
9594
parseFile :: (Has (Throw String) sig m, MonadIO m) => FilePath -> m (File Term)
9695
parseFile path = do
9796
contents <- liftIO (B.readFile path)
98-
case (A.eitherDecodeWith A.json' (A.iparse parseGraph) contents) of
97+
case A.eitherDecodeWith A.json' (A.iparse parseGraph) contents of
9998
Left (_, err) -> throwError err
10099
Right (_, Nothing) -> throwError "no root node found"
101100
-- FIXME: this should get the path to the source file, not the path to the JSON.
102101
-- FIXME: this should use the span of the source file, not an empty span.
103-
Right (_, Just root) -> pure (File (Ref.fromPath (Path.absRel path)) root)
102+
Right (_, Just root) -> pure (File (Ref.fromPath path) root)
104103

105104
parseGraph :: A.Value -> A.Parser (IntMap.IntMap Term, Maybe Term)
106105
parseGraph = A.withArray "nodes" $ \ nodes -> do

semantic-ast/BUILD.bazel

-1
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,6 @@ haskell_library(
3737
"@stackage//:fused-effects",
3838
"@stackage//:hedgehog",
3939
"@stackage//:optparse-applicative",
40-
"@stackage//:pathtype",
4140
"@stackage//:pretty-simple",
4241
"@stackage//:tasty",
4342
"@stackage//:tasty-hedgehog",

semantic-ast/semantic-ast.cabal

-1
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,6 @@ library
7070
, filepath ^>= 1.4.1
7171
, fused-effects ^>= 1.1
7272
, Glob ^>= 0.10.0
73-
, pathtype ^>= 0.8.1
7473
, semantic-source ^>= 0.1.0.1
7574
, tasty ^>= 1.2.3
7675
, tasty-hunit ^>= 0.10.0.2

semantic-ast/src/AST/TestHelpers.hs

+15-19
Original file line numberDiff line numberDiff line change
@@ -17,19 +17,18 @@ import Data.Either
1717
import Data.Functor
1818
import Prelude hiding (takeWhile)
1919
import System.Exit (exitFailure)
20-
import System.Path ((</>))
21-
import qualified System.Path as Path
22-
import qualified System.Path.Directory as Path
20+
import System.Directory
21+
import System.FilePath
2322
import System.FilePath.Glob
2423
import Test.Tasty
2524
import Test.Tasty.HUnit
2625

27-
testCorpus :: (ByteString -> IO (Either String (t a))) -> Path.AbsRelFile -> IO TestTree
26+
testCorpus :: (ByteString -> IO (Either String (t a))) -> FilePath -> IO TestTree
2827
testCorpus parse path = do
2928
xs <- parseCorpusFile path
3029
case xs of
31-
Left e -> print ("Failed to parse corpus: " <> show (Path.toString path) <> " " <> "Error: " <> show e) *> exitFailure
32-
Right xs -> testGroup (Path.toString path) <$> traverse corpusTestCase xs
30+
Left e -> print ("Failed to parse corpus: " <> path <> " " <> "Error: " <> show e) *> exitFailure
31+
Right xs -> testGroup path <$> traverse corpusTestCase xs
3332
where
3433
corpusTestCase (CorpusExample name code) = testCase name . either (errMsg code) pass <$> parse code
3534
pass = const (pure ())
@@ -38,31 +37,28 @@ testCorpus parse path = do
3837
-- Depending on whether these tests are invoked via cabal run or cabal test,
3938
-- we might be in a project subdirectory or not, so let's make sure we're
4039
-- in project subdirectories as needed.
41-
findCorpus :: Path.RelDir -> IO Path.RelDir
40+
findCorpus :: FilePath -> IO FilePath
4241
findCorpus p = do
43-
cwd <- Path.getCurrentDirectory
44-
if Path.takeDirName cwd == Just (Path.relDir "haskell-tree-sitter")
42+
cwd <- getCurrentDirectory
43+
if takeDirectory cwd == "haskell-tree-sitter"
4544
then pure p
46-
else pure (Path.relDir ".." </> p)
45+
else pure (".." </> p)
4746

4847
-- The path is expected to be relative to the language project.
49-
readCorpusFiles :: Path.RelDir -> IO [Path.RelFile]
48+
readCorpusFiles :: FilePath -> IO [FilePath]
5049
readCorpusFiles parent = do
5150
dir <- findCorpus parent
52-
files <- globDir1 (compile "**/*.txt") (Path.toString dir)
53-
pure (Path.relPath <$> files)
51+
globDir1 (compile "**/*.txt") dir
5452

55-
readCorpusFiles' :: Path.AbsRelDir -> IO [Path.AbsRelFile]
56-
readCorpusFiles' dir = do
57-
files <- globDir1 (compile "**/*.txt") (Path.toString dir)
58-
pure (Path.file <$> files)
53+
readCorpusFiles' :: FilePath -> IO [FilePath]
54+
readCorpusFiles' = globDir1 (compile "**/*.txt")
5955

6056
data CorpusExample = CorpusExample { name :: String, code :: ByteString }
6157
deriving (Eq, Show)
6258

63-
parseCorpusFile :: Path.AbsRelFile -> IO (Either String [CorpusExample])
59+
parseCorpusFile :: FilePath -> IO (Either String [CorpusExample])
6460
parseCorpusFile path = do
65-
c <- Data.ByteString.readFile (Path.toString path)
61+
c <- Data.ByteString.readFile path
6662
pure $ parseOnly corpusParser c
6763

6864
corpusParser :: Parser [CorpusExample]

semantic-ast/src/System/Path/Fixture.hs

+12-13
Original file line numberDiff line numberDiff line change
@@ -14,29 +14,28 @@ where
1414
import Control.Concurrent
1515
import GHC.Stack
1616
import System.IO
17-
import qualified System.Path as Path
18-
import System.Path ((</>))
17+
import System.FilePath
1918

2019
#if BAZEL_BUILD
2120
import qualified Bazel.Runfiles as Bazel
2221

2322
type HasFixture =
2423
( ?runfiles :: Bazel.Runfiles,
25-
?project :: Path.RelDir,
24+
?project :: FilePath,
2625
HasCallStack
2726
)
2827

2928
create :: IO Bazel.Runfiles
3029
create = Bazel.create
3130

32-
root :: HasFixture => Path.AbsRelDir
33-
root = Path.absRel (Bazel.rlocation ?runfiles ".")
31+
root :: HasFixture => FilePath
32+
root = Bazel.rlocation ?runfiles "."
3433

35-
absRelFile :: (HasFixture) => String -> Path.AbsRelFile
36-
absRelFile x = Path.toAbsRel (root </> Path.relDir "semantic" </> ?project </> Path.relFile x)
34+
absRelFile :: (HasFixture) => String -> FilePath
35+
absRelFile x = root </> "semantic" </> ?project </> x
3736

38-
absRelDir :: HasFixture => String -> Path.AbsRelDir
39-
absRelDir x = Path.toAbsRel (root </> Path.relDir "semantic" </> ?project </> Path.relDir x)
37+
absRelDir :: HasFixture => String -> FilePath
38+
absRelDir x = root </> "semantic" </> ?project </> x
4039

4140
#else
4241

@@ -46,11 +45,11 @@ type HasFixture = HasCallStack
4645
create :: IO ()
4746
create = pure ()
4847

49-
absRelFile :: String -> Path.AbsRelFile
50-
absRelFile x = Path.absRel "semantic" </> Path.relFile x
48+
absRelFile :: String -> FilePath
49+
absRelFile x = "semantic" </> x
5150

52-
absRelDir :: String -> Path.AbsRelDir
53-
absRelDir x = Path.absRel "semantic" </> Path.relDir x
51+
absRelDir :: String -> FilePath
52+
absRelDir x = "semantic" </> x
5453

5554
#endif
5655

semantic-codeql/semantic-codeql.cabal

-1
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,6 @@ test-suite test
6868
main-is: PreciseTest.hs
6969
build-depends:
7070
, base
71-
, pathtype ^>= 0.8.1
7271
, semantic-ast
7372
, semantic-codeql
7473
, tasty

semantic-codeql/test/PreciseTest.hs

+2-3
Original file line numberDiff line numberDiff line change
@@ -6,20 +6,19 @@ import AST.TestHelpers
66
import AST.Unmarshal
77
import qualified Language.CodeQL.AST as CodeQL
88
import Language.CodeQL.Grammar
9-
import qualified System.Path as Path
109
import Test.Tasty
1110
import qualified System.Path.Fixture as Fixture
1211

1312
main :: IO ()
1413
main = do
1514
#if BAZEL_BUILD
1615
rf <- Fixture.create
17-
let ?project = Path.relDir "external/tree-sitter-ql"
16+
let ?project = "external/tree-sitter-ql"
1817
?runfiles = rf
1918

2019
let dirs = Fixture.absRelDir "test/corpus"
2120
#else
22-
dirs <- Path.absRel <$> CodeQL.getTestCorpusDir
21+
dirs <- CodeQL.getTestCorpusDir
2322
#endif
2423
let parse = parseByteString @CodeQL.Ql @() tree_sitter_ql
2524

semantic-go/semantic-go.cabal

-1
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,6 @@ test-suite test
6868
main-is: PreciseTest.hs
6969
build-depends:
7070
, base
71-
, pathtype ^>= 0.8.1
7271
, semantic-ast
7372
, semantic-go
7473
, tasty

semantic-go/test/PreciseTest.hs

+2-3
Original file line numberDiff line numberDiff line change
@@ -7,19 +7,18 @@ import qualified Language.Go.AST as Go
77
import Language.Go.Grammar
88
import AST.TestHelpers
99
import AST.Unmarshal
10-
import qualified System.Path as Path
1110
import Test.Tasty
1211
import qualified System.Path.Fixture as Fixture
1312

1413
main :: IO ()
1514
main = do
1615
#if BAZEL_BUILD
1716
rf <- Fixture.create
18-
let ?project = Path.relDir "external/tree-sitter-go"
17+
let ?project = "external/tree-sitter-go"
1918
?runfiles = rf
2019
let dirs = Fixture.absRelDir "corpus"
2120
#else
22-
dirs <- Path.absRel <$> Go.getTestCorpusDir
21+
dirs <- Go.getTestCorpusDir
2322
#endif
2423

2524
readCorpusFiles' dirs

0 commit comments

Comments
 (0)