Skip to content

Commit b316b47

Browse files
authored
Merge pull request #3928 from haskell/wip/multi-reexport
Fix multi unit session when some packages have reexported modules.
2 parents 2156ac2 + 9a7b0d2 commit b316b47

File tree

12 files changed

+151
-24
lines changed

12 files changed

+151
-24
lines changed

ghcide/session-loader/Development/IDE/Session.hs

+1-2
Original file line numberDiff line numberDiff line change
@@ -121,8 +121,7 @@ import Development.IDE.GHC.Compat.CmdLine
121121
import qualified Data.Set as OS
122122

123123
import GHC.Data.Bag
124-
import GHC.Driver.Env (hscSetActiveUnitId,
125-
hsc_all_home_unit_ids)
124+
import GHC.Driver.Env (hsc_all_home_unit_ids)
126125
import GHC.Driver.Errors.Types
127126
import GHC.Driver.Make (checkHomeUnitsClosed)
128127
import GHC.Types.Error (errMsgDiagnostic)

ghcide/src/Development/IDE/GHC/Compat/Env.hs

+13-1
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,8 @@ module Development.IDE.GHC.Compat.Env (
5757
Development.IDE.GHC.Compat.Env.platformDefaultBackend,
5858
workingDirectory,
5959
setWorkingDirectory,
60+
hscSetActiveUnitId,
61+
reexportedModules,
6062
) where
6163

6264
import GHC (setInteractiveDynFlags)
@@ -78,10 +80,20 @@ import GHC.Utils.TmpFs
7880

7981
#if !MIN_VERSION_ghc(9,3,0)
8082
import GHC.Driver.Env (HscEnv, hsc_EPS)
83+
import qualified Data.Set as S
8184
#endif
8285

8386
#if MIN_VERSION_ghc(9,3,0)
84-
import GHC.Driver.Env (HscEnv)
87+
import GHC.Driver.Env (HscEnv, hscSetActiveUnitId)
88+
#endif
89+
90+
91+
#if !MIN_VERSION_ghc(9,3,0)
92+
hscSetActiveUnitId :: UnitId -> HscEnv -> HscEnv
93+
hscSetActiveUnitId _ env = env
94+
95+
reexportedModules :: HscEnv -> S.Set a
96+
reexportedModules _ = S.empty
8597
#endif
8698

8799
#if MIN_VERSION_ghc(9,3,0)

ghcide/src/Development/IDE/Import/FindImports.hs

+48-21
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,8 @@ import Development.IDE.Types.Location
2323
-- standard imports
2424
import Control.Monad.Extra
2525
import Control.Monad.IO.Class
26-
import Data.List (isSuffixOf)
26+
import Data.List (isSuffixOf, find)
27+
import qualified Data.Set as S
2728
import Data.Maybe
2829
import System.FilePath
2930

@@ -70,19 +71,30 @@ modSummaryToArtifactsLocation nfp ms = ArtifactsLocation nfp (ms_location <$> ms
7071
Just modSum -> isSource (ms_hsc_src modSum)
7172
mbMod = ms_mod <$> ms
7273

74+
data LocateResult
75+
= LocateNotFound
76+
| LocateFoundReexport UnitId
77+
| LocateFoundFile UnitId NormalizedFilePath
78+
7379
-- | locate a module in the file system. Where we go from *daml to Haskell
7480
locateModuleFile :: MonadIO m
75-
=> [(UnitId, [FilePath])]
81+
=> [(UnitId, [FilePath], S.Set ModuleName)]
7682
-> [String]
7783
-> (ModuleName -> NormalizedFilePath -> m (Maybe NormalizedFilePath))
7884
-> Bool
7985
-> ModuleName
80-
-> m (Maybe (UnitId, NormalizedFilePath))
86+
-> m LocateResult
8187
locateModuleFile import_dirss exts targetFor isSource modName = do
8288
let candidates import_dirs =
8389
[ toNormalizedFilePath' (prefix </> moduleNameSlashes modName <.> maybeBoot ext)
8490
| prefix <- import_dirs , ext <- exts]
85-
firstJustM go (concat [map (uid,) (candidates dirs) | (uid, dirs) <- import_dirss])
91+
mf <- firstJustM go (concat [map (uid,) (candidates dirs) | (uid, dirs, _) <- import_dirss])
92+
case mf of
93+
Nothing ->
94+
case find (\(_ , _, reexports) -> S.member modName reexports) import_dirss of
95+
Just (uid,_,_) -> pure $ LocateFoundReexport uid
96+
Nothing -> pure $ LocateNotFound
97+
Just (uid,file) -> pure $ LocateFoundFile uid file
8698
where
8799
go (uid, candidate) = fmap ((uid,) <$>) $ targetFor modName candidate
88100
maybeBoot ext
@@ -94,11 +106,11 @@ locateModuleFile import_dirss exts targetFor isSource modName = do
94106
-- current module. In particular, it will return Nothing for 'main' components
95107
-- as they can never be imported into another package.
96108
#if MIN_VERSION_ghc(9,3,0)
97-
mkImportDirs :: HscEnv -> (UnitId, DynFlags) -> Maybe (UnitId, [FilePath])
98-
mkImportDirs _env (i, flags) = Just (i, importPaths flags)
109+
mkImportDirs :: HscEnv -> (UnitId, DynFlags) -> Maybe (UnitId, ([FilePath], S.Set ModuleName))
110+
mkImportDirs _env (i, flags) = Just (i, (importPaths flags, reexportedModules flags))
99111
#else
100-
mkImportDirs :: HscEnv -> (UnitId, DynFlags) -> Maybe (PackageName, (UnitId, [FilePath]))
101-
mkImportDirs env (i, flags) = (, (i, importPaths flags)) <$> getUnitName env i
112+
mkImportDirs :: HscEnv -> (UnitId, DynFlags) -> Maybe (PackageName, (UnitId, [FilePath], S.Set ModuleName))
113+
mkImportDirs env (i, flags) = (, (i, importPaths flags, S.empty)) <$> getUnitName env i
102114
#endif
103115

104116
-- | locate a module in either the file system or the package database. Where we go from *daml to
@@ -125,16 +137,16 @@ locateModule env comp_info exts targetFor modName mbPkgName isSource = do
125137
#else
126138
Just "this" -> do
127139
#endif
128-
lookupLocal (homeUnitId_ dflags) (importPaths dflags)
140+
lookupLocal (homeUnitId_ dflags) (importPaths dflags) S.empty
129141
-- if a package name is given we only go look for a package
130142
#if MIN_VERSION_ghc(9,3,0)
131143
OtherPkg uid
132-
| Just dirs <- lookup uid import_paths
133-
-> lookupLocal uid dirs
144+
| Just (dirs, reexports) <- lookup uid import_paths
145+
-> lookupLocal uid dirs reexports
134146
#else
135147
Just pkgName
136-
| Just (uid, dirs) <- lookup (PackageName pkgName) import_paths
137-
-> lookupLocal uid dirs
148+
| Just (uid, dirs, reexports) <- lookup (PackageName pkgName) import_paths
149+
-> lookupLocal uid dirs reexports
138150
#endif
139151
| otherwise -> lookupInPackageDB
140152
#if MIN_VERSION_ghc(9,3,0)
@@ -143,10 +155,15 @@ locateModule env comp_info exts targetFor modName mbPkgName isSource = do
143155
Nothing -> do
144156
#endif
145157

146-
mbFile <- locateModuleFile ((homeUnitId_ dflags, importPaths dflags) : other_imports) exts targetFor isSource $ unLoc modName
158+
-- Reexports for current unit have to be empty because they only apply to other units depending on the
159+
-- current unit. If we set the reexports to be the actual reexports then we risk looping forever trying
160+
-- to find the module from the perspective of the current unit.
161+
mbFile <- locateModuleFile ((homeUnitId_ dflags, importPaths dflags, S.empty) : other_imports) exts targetFor isSource $ unLoc modName
147162
case mbFile of
148-
Nothing -> lookupInPackageDB
149-
Just (uid, file) -> toModLocation uid file
163+
LocateNotFound -> lookupInPackageDB
164+
-- Lookup again with the perspective of the unit reexporting the file
165+
LocateFoundReexport uid -> locateModule (hscSetActiveUnitId uid env) comp_info exts targetFor modName noPkgQual isSource
166+
LocateFoundFile uid file -> toModLocation uid file
150167
where
151168
dflags = hsc_dflags env
152169
import_paths = mapMaybe (mkImportDirs env) comp_info
@@ -160,7 +177,7 @@ locateModule env comp_info exts targetFor modName mbPkgName isSource = do
160177
-- about which module unit a imports.
161178
-- Without multi-component support it is hard to recontruct the dependency environment so
162179
-- unit a will have both unit b and unit c in scope.
163-
map (\uid -> (uid, importPaths (homeUnitEnv_dflags (ue_findHomeUnitEnv uid ue)))) hpt_deps
180+
map (\uid -> let this_df = homeUnitEnv_dflags (ue_findHomeUnitEnv uid ue) in (uid, importPaths this_df, reexportedModules this_df)) hpt_deps
164181
ue = hsc_unit_env env
165182
units = homeUnitEnv_units $ ue_findHomeUnitEnv (homeUnitId_ dflags) ue
166183
hpt_deps :: [UnitId]
@@ -186,11 +203,13 @@ locateModule env comp_info exts targetFor modName mbPkgName isSource = do
186203
let genMod = mkModule (RealUnit $ Definite uid) (unLoc modName) -- TODO support backpack holes
187204
return $ Right $ FileImport $ ArtifactsLocation file (Just loc) (not isSource) (Just genMod)
188205

189-
lookupLocal uid dirs = do
190-
mbFile <- locateModuleFile [(uid, dirs)] exts targetFor isSource $ unLoc modName
206+
lookupLocal uid dirs reexports = do
207+
mbFile <- locateModuleFile [(uid, dirs, reexports)] exts targetFor isSource $ unLoc modName
191208
case mbFile of
192-
Nothing -> return $ Left $ notFoundErr env modName $ LookupNotFound []
193-
Just (uid', file) -> toModLocation uid' file
209+
LocateNotFound -> return $ Left $ notFoundErr env modName $ LookupNotFound []
210+
-- Lookup again with the perspective of the unit reexporting the file
211+
LocateFoundReexport uid' -> locateModule (hscSetActiveUnitId uid' env) comp_info exts targetFor modName noPkgQual isSource
212+
LocateFoundFile uid' file -> toModLocation uid' file
194213

195214
lookupInPackageDB = do
196215
case Compat.lookupModuleWithSuggestions env (unLoc modName) mbPkgName of
@@ -239,3 +258,11 @@ notFound = NotFound
239258
, fr_unusables = []
240259
, fr_suggestions = []
241260
}
261+
262+
#if MIN_VERSION_ghc(9,3,0)
263+
noPkgQual :: PkgQual
264+
noPkgQual = NoPkgQual
265+
#else
266+
noPkgQual :: Maybe a
267+
noPkgQual = Nothing
268+
#endif
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
-this-package-name
2+
a
3+
-working-dir
4+
a
5+
-fbuilding-cabal-package
6+
-O0
7+
-i.
8+
-this-unit-id
9+
a-1.0.0-inplace
10+
-hide-all-packages
11+
-Wmissing-home-modules
12+
-no-user-package-db
13+
-package
14+
base
15+
-package
16+
text
17+
-XHaskell98
18+
A
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
module A(foo) where
2+
import Data.Text
3+
foo = ()
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
-this-package-name
2+
b
3+
-working-dir
4+
b
5+
-fbuilding-cabal-package
6+
-O0
7+
-i
8+
-i.
9+
-this-unit-id
10+
b-1.0.0-inplace
11+
-hide-all-packages
12+
-Wmissing-home-modules
13+
-no-user-package-db
14+
-package-id
15+
a-1.0.0-inplace
16+
-reexported-module
17+
A
18+
-package
19+
base
20+
-XHaskell98
21+
B
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
module B(module B) where
2+
import A
3+
qux = foo
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
-this-package-name
2+
c
3+
-working-dir
4+
c
5+
-fbuilding-cabal-package
6+
-O0
7+
-i
8+
-i.
9+
-this-unit-id
10+
c-1.0.0-inplace
11+
-hide-all-packages
12+
-Wmissing-home-modules
13+
-no-user-package-db
14+
-package-id
15+
b-1.0.0-inplace
16+
-package
17+
base
18+
-XHaskell98
19+
C
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
module C(module C) where
2+
import A
3+
import B
4+
cux = foo `seq` qux
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
packages: a b c
2+
multi-repl: True
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
cradle:
2+
direct:
3+
arguments: ["-unit" ,"@a-1.0.0-inplace"
4+
,"-unit" ,"@b-1.0.0-inplace"
5+
,"-unit" ,"@c-1.0.0-inplace"
6+
]

ghcide/test/exe/CradleTests.hs

+13
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,8 @@ tests = testGroup "cradle"
4444
,ignoreFor (BrokenForGHC [GHC92]) "multiple units not supported on 9.2"
4545
$ testGroup "multi-unit" (multiTests "multi-unit")
4646
,testGroup "sub-directory" [simpleSubDirectoryTest]
47+
,ignoreFor (BrokenForGHC [GHC92]) "multiple units not supported on 9.2"
48+
$ testGroup "multi-unit-rexport" [multiRexportTest]
4749
]
4850

4951
loadCradleOnlyonce :: TestTree
@@ -187,6 +189,17 @@ simpleMultiDefTest variant = testCase (multiTestName variant "def-test") $ runWi
187189
checkDefs locs (pure [fooL])
188190
expectNoMoreDiagnostics 0.5
189191

192+
multiRexportTest :: TestTree
193+
multiRexportTest =
194+
testCase "multi-unit-reexport-test" $ runWithExtraFiles "multi-unit-reexport" $ \dir -> do
195+
let cPath = dir </> "c/C.hs"
196+
cdoc <- openDoc cPath "haskell"
197+
WaitForIdeRuleResult {} <- waitForAction "TypeCheck" cdoc
198+
locs <- getDefinitions cdoc (Position 3 7)
199+
let aPath = dir </> "a/A.hs"
200+
let fooL = mkL (filePathToUri aPath) 2 0 2 3
201+
checkDefs locs (pure [fooL])
202+
expectNoMoreDiagnostics 0.5
190203

191204
sessionDepsArePickedUp :: TestTree
192205
sessionDepsArePickedUp = testSession'

0 commit comments

Comments
 (0)