Skip to content

Instantly share code, notes, and snippets.

@guibou
Last active June 9, 2021 18:02
Show Gist options
  • Select an option

  • Save guibou/711b3123b5cdb3c1745f12c85af97129 to your computer and use it in GitHub Desktop.

Select an option

Save guibou/711b3123b5cdb3c1745f12c85af97129 to your computer and use it in GitHub Desktop.
Check that module name inside an haskell file (e.g. "module Foo.Bar where") match the filename (e.g. `prefix/Foo/Bar.hs`)
import subprocess
import re
import string
# Locate all haskell files in the repo
all_hs = subprocess.run(["git", "ls-files", "**/*.hs"], capture_output=True, encoding='ascii').stdout.split()
for path in all_hs:
module_name = []
# Extract module name
# foo/bar/Baz/Biz/Buz.hs leads to module name
# Baz.Biz.Buz
# -3 removes the trailing ".hs"
# then split on the '/' and works from backward
for path_component in path[:-3].split('/')[::-1]:
# The name must start by a capitalized letter
if not path_component[0] in string.ascii_uppercase:
# rebuild module name
module_name = '.'.join(module_name[::-1])
break
else:
module_name.append(path_component)
# Now we open the module and check if the inner module name match the
# filename.
with open(path) as f:
content = f.read()
# match the module name
m = re.search(r"^module +([^ (\n]+)", content, re.M)
if m:
true_module_name = m.group(1)
# Ignore "Main", they are standalone module, never imported
if true_module_name != "Main":
# Print message in case of discrepancy
if module_name != true_module_name:
print(path)
# Justify the string so module name is aligned with the filename
print(f"{true_module_name}".rjust(len(path) - 3))
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE ViewPatterns #-}
import System.Process
import qualified Data.Text as Text
import qualified Data.Text.IO as Text
import Data.Foldable (for_)
import Data.Char (isLower)
import Control.Monad (when)
import Control.Applicative ((<|>))
getTrueModuleName' (Text.split (\t -> t `elem` (" \n(" :: String)) -> l) =
case l of
"module":(dropWhile Text.null -> module_name:_) -> Just module_name
_ -> Nothing
getTrueModuleName xs
= foldr ((<|>) . getTrueModuleName') Nothing xs
main = do
all_hs <- Text.lines . Text.pack <$> readProcess "git" ["ls-files", "**/*.hs"] ""
for_ all_hs $ \path -> do
let
module_name = Text.intercalate "." $ dropWhile (isLower . Text.head) $ Text.splitOn "/" (Text.dropEnd 3 path)
content <- Text.readFile (Text.unpack path)
case getTrueModuleName (Text.lines content) of
Nothing -> pure ()
Just true_module_name ->
when (true_module_name /= "Main" && module_name /= true_module_name) $ do
Text.putStrLn path
Text.putStrLn (Text.justifyRight (Text.length path - 3) ' ' true_module_name)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment