on master help sign in
Detect cycles in imports

Also improve import chasing, by only following imports in the current source tree (so not in dependencies we happen to have the source for, which will be part of other packages)

commit8bd99edf3ecb28c7c6e7d48eec29dd2403602d17 authorEdwin Brady <ecb10@st-andrews.ac.uk> dateWed 14 Oct 2015, 15:37 (11 years ago) parentfe38aac
8 files changed+52−18
idris.cabal+3 src/Idris/Chaser.hs+29−16 src/Idris/REPL.hs+3−2 test/basic016/CycleA.idr+4 test/basic016/CycleB.idr+5 test/basic016/CycleC.idr+4 test/basic016/expected+1 test/basic016/run+3
idris.cabal+3
@@ -356,6 +356,9 @@ Extra-source-files:
356356 test/basic015/run
357357 test/basic015/*.idr
358358 test/basic015/expected
359+ test/basic016/run
360+ test/basic016/*.idr
361+ test/basic016/expected
359362
360363 test/bignum001/run
361364 test/bignum001/*.idr
src/Idris/Chaser.hs+29−16
@@ -1,5 +1,6 @@
11module Idris.Chaser(buildTree, getImports, getModuleFiles, ModuleTree(..)) where
22
3+import Idris.Core.TT
34import Idris.Parser
45import Idris.AbsSyntax
56import Idris.Imports
@@ -79,22 +80,31 @@ getIModTime (IBC i _) = getModificationTime i
7980getIModTime (IDR i) = getModificationTime i
8081getIModTime (LIDR i) = getModificationTime i
8182
82−getImports :: FilePath -> Idris (FilePath, [ImportInfo])
83−getImports f = do
84− i <- getIState
85− let file = extractFileName f
86− ibcsd <- valIBCSubDir i
87− ids <- allImportDirs
88− fp <- findImport ids ibcsd file
83+getImports :: [(FilePath, [ImportInfo])] -> [FilePath] ->
84+ Idris [(FilePath, [ImportInfo])]
85+getImports acc [] = return acc
86+getImports acc (f : fs) = do
87+ i <- getIState
88+ let file = extractFileName f
89+ ibcsd <- valIBCSubDir i
90+ ids <- allImportDirs
91+ idrisCatch (do
92+ fp <- findImport ["."] ibcsd file
8993 let parsef = fname fp
90− exist <- runIO $ doesFileExist parsef
91− if exist then do
92− src_in <- runIO $ readSource parsef
93− src <- if lit fp then tclift $ unlit parsef src_in else return src_in
94− (_, _, modules, _) <- parseImports parsef src
95− clearParserWarnings
96− return (parsef, modules)
97− else return (parsef, [])
94+ case lookup parsef acc of
95+ Just _ -> getImports acc fs
96+ _ -> do
97+ exist <- runIO $ doesFileExist parsef
98+ if exist then do
99+ src_in <- runIO $ readSource parsef
100+ src <- if lit fp then tclift $ unlit parsef src_in
101+ else return src_in
102+ (_, _, modules, _) <- parseImports parsef src
103+ clearParserWarnings
104+ getImports ((parsef, modules) : acc)
105+ (fs ++ map import_path modules)
106+ else getImports ((parsef, []) : acc) fs)
107+ (\_ -> getImports acc fs) -- not in current soure tree, ignore
98108 where
99109 lit (LIDR _) = True
100110 lit _ = False
@@ -127,7 +137,10 @@ buildTree built importlists fp = evalStateT (btree [] fp) []
127137 mt <- lift $ runIO $ getIModTime fp
128138 if (file `elem` built)
129139 then return [MTree fp False mt []]
130− else if file `elem` stk then return []
140+ else if file `elem` stk then
141+ do lift $ tclift $ tfail
142+ (Msg $ "Cycle detected in imports: "
143+ ++ showSep " -> " (reverse (file : stk)))
131144 else do donetree <- get
132145 case lookup file donetree of
133146 Just t -> return t
src/Idris/REPL.hs+3−2
@@ -1513,8 +1513,9 @@ loadInputs inputs toline -- furthest line to read in input source files
15131513 -- For each ifile list, check it and build ibcs in the same clean IState
15141514 -- so that they don't interfere with each other when checking
15151515
1516− importlists <- mapM getImports inputs
1517− logLvl 5 (show (map (\(i,m) -> (i, map import_path m)) importlists))
1516+ importlists <- getImports [] inputs
1517+
1518+ logLvl 1 (show (map (\(i,m) -> (i, map import_path m)) importlists))
15181519
15191520 let ninputs = zip [1..] inputs
15201521 ifiles <- mapWhileOK (\(num, input) ->
test/basic016/CycleA.idrnew file+4
@@ -0,0 +1,4 @@
1+module CycleA
2+
3+import CycleB
4+
test/basic016/CycleB.idrnew file+5
@@ -0,0 +1,5 @@
1+module CycleB
2+
3+import CycleC
4+
5+
test/basic016/CycleC.idrnew file+4
@@ -0,0 +1,4 @@
1+module CycleC
2+
3+import CycleA
4+
test/basic016/expectednew file+1
@@ -0,0 +1 @@
1+Cycle detected in imports: CycleA.idr -> CycleB -> CycleC -> CycleA -> CycleB
test/basic016/runnew file+3
@@ -0,0 +1,3 @@
1+#!/usr/bin/env bash
2+idris $@ CycleA.idr --check
3+rm -f *.ibc