Skip to content
This repository was archived by the owner on Jan 2, 2021. It is now read-only.

Commit 7dacc23

Browse files
authored
Import paths are relative to cradle (#781)
* Import paths are relative to cradle I noticed ghcide HEAD was broken on the ghcide submodule of the hls repo. * remove unused * Fix comment placement * Special case the implicit cradle The implicit cradle comes without import paths, so we need to preserve the old logic that synthetised them from the current module * Hlint * Fix timing issue: update known files before restarting the session Also, DO NOT filter out missing targets * Use --verbose when running tests * Log test outputs on 3rd attempt * Fall back to filtering known files * hlint * Upgrade KnownFiles to KnownTargets * Use KnownTargets to filter modules, not module paths * Fix test cradle * Increase pauses in flaky test * remove no longer needed check * Disable ansi color codes in CI * Disable flaky test
1 parent 15ab2ff commit 7dacc23

File tree

10 files changed

+167
-92
lines changed

10 files changed

+167
-92
lines changed

.azure/linux-stack.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ jobs:
4545
displayName: 'stack build --only-dependencies'
4646
- bash: |
4747
export PATH=/opt/cabal/bin:$PATH
48-
stack test --ghc-options=-Werror --stack-yaml=$STACK_YAML || stack test --ghc-options=-Werror --stack-yaml=$STACK_YAML|| stack test --ghc-options=-Werror --stack-yaml=$STACK_YAML
48+
stack test --ghc-options=-Werror --stack-yaml=$STACK_YAML || stack test --ghc-options=-Werror --stack-yaml=$STACK_YAML|| LSP_TEST_LOG_COLOR=0 LSP_TEST_LOG_MESSAGES=true LSP_TEST_LOG_STDERR=true stack test --ghc-options=-Werror --stack-yaml=$STACK_YAML
4949
# ghcide stack tests are flaky, see https://github.com/digital-asset/daml/issues/2606.
5050
displayName: 'stack test --ghc-options=-Werror'
5151
- bash: |

session-loader/Development/IDE/Session.hs

Lines changed: 78 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@ import Data.Bifunctor
2525
import qualified Data.ByteString.Base16 as B16
2626
import Data.Either.Extra
2727
import Data.Function
28-
import qualified Data.HashSet as HashSet
2928
import Data.Hashable
3029
import Data.List
3130
import Data.IORef
@@ -65,6 +64,7 @@ import Module
6564
import NameCache
6665
import Packages
6766
import Control.Exception (evaluate)
67+
import Data.Char
6868

6969
-- | Given a root directory, return a Shake 'Action' which setups an
7070
-- 'IdeGhcSession' given a file.
@@ -104,14 +104,28 @@ loadSession dir = do
104104

105105
return $ do
106106
extras@ShakeExtras{logger, eventer, restartShakeSession,
107-
withIndefiniteProgress, ideNc, knownFilesVar
107+
withIndefiniteProgress, ideNc, knownTargetsVar
108108
} <- getShakeExtras
109109

110110
IdeOptions{ optTesting = IdeTesting optTesting
111111
, optCheckProject = CheckProject checkProject
112112
, optCustomDynFlags
113113
} <- getIdeOptions
114114

115+
-- populate the knownTargetsVar with all the
116+
-- files in the project so that `knownFiles` can learn about them and
117+
-- we can generate a complete module graph
118+
let extendKnownTargets newTargets = do
119+
knownTargets <- forM newTargets $ \TargetDetails{..} -> do
120+
found <- filterM (IO.doesFileExist . fromNormalizedFilePath) targetLocations
121+
return (targetModule, found)
122+
modifyVar_ knownTargetsVar $ traverseHashed $ \known -> do
123+
let known' = HM.unionWith (<>) known $ HM.fromList knownTargets
124+
when (known /= known') $
125+
logDebug logger $ "Known files updated: " <>
126+
T.pack(show $ (HM.map . map) fromNormalizedFilePath known')
127+
evaluate known'
128+
115129
-- Create a new HscEnv from a hieYaml root and a set of options
116130
-- If the hieYaml file already has an HscEnv, the new component is
117131
-- combined with the components in the old HscEnv into a new HscEnv
@@ -212,20 +226,26 @@ loadSession dir = do
212226

213227
-- New HscEnv for the component in question, returns the new HscEnvEq and
214228
-- a mapping from FilePath to the newly created HscEnvEq.
215-
let new_cache = newComponentCache logger isImplicit hscEnv uids
216-
isImplicit = isNothing hieYaml
229+
let new_cache = newComponentCache logger hieYaml hscEnv uids
217230
(cs, res) <- new_cache new
218231
-- Modified cache targets for everything else in the hie.yaml file
219232
-- which now uses the same EPS and so on
220233
cached_targets <- concatMapM (fmap fst . new_cache) old_deps
234+
235+
let all_targets = cs ++ cached_targets
236+
221237
modifyVar_ fileToFlags $ \var -> do
222-
pure $ Map.insert hieYaml (HM.fromList (cs ++ cached_targets)) var
238+
pure $ Map.insert hieYaml (HM.fromList (concatMap toFlagsMap all_targets)) var
239+
240+
extendKnownTargets all_targets
223241

224242
-- Invalidate all the existing GhcSession build nodes by restarting the Shake session
225243
invalidateShakeCache
226244
restartShakeSession [kick]
227245

228-
return (map fst cs ++ map fst cached_targets, second Map.keys res)
246+
let resultCachedTargets = concatMap targetLocations all_targets
247+
248+
return (resultCachedTargets, second Map.keys res)
229249

230250
let consultCradle :: Maybe FilePath -> FilePath -> IO ([NormalizedFilePath], (IdeResult HscEnvEq, [FilePath]))
231251
consultCradle hieYaml cfp = do
@@ -299,14 +319,10 @@ loadSession dir = do
299319
void $ wait as
300320
as <- async $ getOptions file
301321
return (fmap snd as, wait as)
302-
unless (null cs) $
322+
unless (null cs) $ do
323+
cfps' <- liftIO $ filterM (IO.doesFileExist . fromNormalizedFilePath) cs
303324
-- Typecheck all files in the project on startup
304325
void $ shakeEnqueue extras $ mkDelayedAction "InitialLoad" Debug $ void $ do
305-
cfps' <- liftIO $ filterM (IO.doesFileExist . fromNormalizedFilePath) cs
306-
-- populate the knownFilesVar with all the
307-
-- files in the project so that `knownFiles` can learn about them and
308-
-- we can generate a complete module graph
309-
liftIO $ modifyVar_ knownFilesVar $ traverseHashed $ pure . HashSet.union (HashSet.fromList cfps')
310326
when checkProject $ do
311327
mmt <- uses GetModificationTime cfps'
312328
let cs_exist = catMaybes (zipWith (<$) cfps' mmt)
@@ -320,6 +336,7 @@ loadSession dir = do
320336
-- | Run the specific cradle on a specific FilePath via hie-bios.
321337
-- This then builds dependencies or whatever based on the cradle, gets the
322338
-- GHC options/dynflags needed for the session and the GHC library directory
339+
323340
cradleToOptsAndLibDir :: Show a => Cradle a -> FilePath
324341
-> IO (Either [CradleError] (ComponentOptions, FilePath))
325342
cradleToOptsAndLibDir cradle file = do
@@ -349,52 +366,79 @@ emptyHscEnv nc libDir = do
349366
initDynLinker env
350367
pure $ setNameCache nc env
351368

352-
-- | Convert a target to a list of potential absolute paths.
353-
-- A TargetModule can be anywhere listed by the supplied include
354-
-- directories
355-
-- A target file is a relative path but with a specific prefix so just need
356-
-- to canonicalise it.
357-
targetToFile :: [FilePath] -> TargetId -> IO [NormalizedFilePath]
358-
targetToFile is (TargetModule mod) = do
369+
data TargetDetails = TargetDetails
370+
{
371+
targetModule :: !ModuleName,
372+
targetEnv :: !(IdeResult HscEnvEq),
373+
targetDepends :: !DependencyInfo,
374+
targetLocations :: ![NormalizedFilePath]
375+
}
376+
377+
fromTargetId :: [FilePath] -- ^ import paths
378+
-> TargetId
379+
-> IdeResult HscEnvEq
380+
-> DependencyInfo
381+
-> IO [TargetDetails]
382+
-- For a target module we consider all the import paths
383+
fromTargetId is (TargetModule mod) env dep = do
359384
let fps = [i </> moduleNameSlashes mod -<.> ext | ext <- exts, i <- is ]
360385
exts = ["hs", "hs-boot", "lhs"]
361-
mapM (fmap toNormalizedFilePath' . canonicalizePath) fps
362-
targetToFile _ (TargetFile f _) = do
363-
f' <- canonicalizePath f
364-
return [toNormalizedFilePath' f']
386+
locs <- mapM (fmap toNormalizedFilePath' . canonicalizePath) fps
387+
return [TargetDetails mod env dep locs]
388+
-- For a 'TargetFile' we consider all the possible module names
389+
fromTargetId _ (TargetFile f _) env deps = do
390+
nf <- toNormalizedFilePath' <$> canonicalizePath f
391+
return [TargetDetails m env deps [nf] | m <- moduleNames f]
392+
393+
-- >>> moduleNames "src/A/B.hs"
394+
-- [A.B,B]
395+
moduleNames :: FilePath -> [ModuleName]
396+
moduleNames f = map (mkModuleName .intercalate ".") $ init $ tails nameSegments
397+
where
398+
nameSegments = reverse
399+
$ takeWhile (isUpper . head)
400+
$ reverse
401+
$ splitDirectories
402+
$ dropExtension f
403+
404+
toFlagsMap :: TargetDetails -> [(NormalizedFilePath, (IdeResult HscEnvEq, DependencyInfo))]
405+
toFlagsMap TargetDetails{..} =
406+
[ (l, (targetEnv, targetDepends)) | l <- targetLocations]
407+
365408

366409
setNameCache :: IORef NameCache -> HscEnv -> HscEnv
367410
setNameCache nc hsc = hsc { hsc_NC = nc }
368411

369-
370412
-- | Create a mapping from FilePaths to HscEnvEqs
371413
newComponentCache
372414
:: Logger
373-
-> Bool -- ^ Is this for an implicit/crappy cradle
415+
-> Maybe FilePath -- Path to cradle
374416
-> HscEnv
375417
-> [(InstalledUnitId, DynFlags)]
376418
-> ComponentInfo
377-
-> IO ([(NormalizedFilePath, (IdeResult HscEnvEq, DependencyInfo))], (IdeResult HscEnvEq, DependencyInfo))
378-
newComponentCache logger isImplicit hsc_env uids ci = do
419+
-> IO ( [TargetDetails], (IdeResult HscEnvEq, DependencyInfo))
420+
newComponentCache logger cradlePath hsc_env uids ci = do
379421
let df = componentDynFlags ci
380422
let hscEnv' = hsc_env { hsc_dflags = df
381423
, hsc_IC = (hsc_IC hsc_env) { ic_dflags = df } }
382424

383-
let newFunc = if isImplicit then newHscEnvEqPreserveImportPaths else newHscEnvEq
425+
let newFunc = maybe newHscEnvEqPreserveImportPaths newHscEnvEq cradlePath
384426
henv <- newFunc hscEnv' uids
385-
let res = (([], Just henv), componentDependencyInfo ci)
427+
let targetEnv = ([], Just henv)
428+
targetDepends = componentDependencyInfo ci
429+
res = (targetEnv, targetDepends)
386430
logDebug logger ("New Component Cache HscEnvEq: " <> T.pack (show res))
387431

388-
let is = importPaths df
389-
ctargets <- concatMapM (targetToFile is . targetId) (componentTargets ci)
432+
let mk t = fromTargetId (importPaths df) (targetId t) targetEnv targetDepends
433+
ctargets <- concatMapM mk (componentTargets ci)
434+
390435
-- A special target for the file which caused this wonderful
391436
-- component to be created. In case the cradle doesn't list all the targets for
392437
-- the component, in which case things will be horribly broken anyway.
393438
-- Otherwise, we will immediately attempt to reload this module which
394439
-- causes an infinite loop and high CPU usage.
395-
let special_target = (componentFP ci, res)
396-
let xs = map (,res) ctargets
397-
return (special_target:xs, res)
440+
let special_target = TargetDetails (mkModuleName "special") targetEnv targetDepends [componentFP ci]
441+
return (special_target:ctargets, res)
398442

399443
{- Note [Avoiding bad interface files]
400444
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

src/Development/IDE/Core/FileStore.hs

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -252,15 +252,13 @@ typecheckParents state nfp = void $ shakeEnqueue (shakeExtras state) parents
252252

253253
typecheckParentsAction :: NormalizedFilePath -> Action ()
254254
typecheckParentsAction nfp = do
255-
fs <- useNoFile_ GetKnownFiles
256-
unless (null fs) $ do
257-
revs <- reverseDependencies nfp <$> useNoFile_ GetModuleGraph
258-
logger <- logger <$> getShakeExtras
259-
let log = L.logInfo logger . T.pack
260-
liftIO $ do
261-
(log $ "Typechecking reverse dependencies for" ++ show nfp ++ ": " ++ show revs)
262-
`catch` \(e :: SomeException) -> log (show e)
263-
() <$ uses GetModIface revs
255+
revs <- reverseDependencies nfp <$> useNoFile_ GetModuleGraph
256+
logger <- logger <$> getShakeExtras
257+
let log = L.logInfo logger . T.pack
258+
liftIO $ do
259+
(log $ "Typechecking reverse dependencies for" ++ show nfp ++ ": " ++ show revs)
260+
`catch` \(e :: SomeException) -> log (show e)
261+
() <$ uses GetModIface revs
264262

265263
-- | Note that some buffer somewhere has been modified, but don't say what.
266264
-- Only valid if the virtual file system was initialised by LSP, as that

src/Development/IDE/Core/RuleTypes.hs

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,10 @@ import Data.Binary
1616
import Development.IDE.Import.DependencyInformation
1717
import Development.IDE.GHC.Compat
1818
import Development.IDE.GHC.Util
19+
import Development.IDE.Core.Shake (KnownTargets)
1920
import Data.Hashable
2021
import Data.Typeable
2122
import qualified Data.Set as S
22-
import qualified Data.HashSet as HS
2323
import Development.Shake
2424
import GHC.Generics (Generic)
2525

@@ -29,7 +29,6 @@ import HscTypes (hm_iface, CgGuts, Linkable, HomeModInfo, ModDetails)
2929
import Development.IDE.Spans.Type
3030
import Development.IDE.Import.FindImports (ArtifactsLocation)
3131
import Data.ByteString (ByteString)
32-
import Language.Haskell.LSP.Types (NormalizedFilePath)
3332

3433

3534
-- NOTATION
@@ -50,12 +49,12 @@ type instance RuleResult GetDependencies = TransitiveDependencies
5049

5150
type instance RuleResult GetModuleGraph = DependencyInformation
5251

53-
data GetKnownFiles = GetKnownFiles
52+
data GetKnownTargets = GetKnownTargets
5453
deriving (Show, Generic, Eq, Ord)
55-
instance Hashable GetKnownFiles
56-
instance NFData GetKnownFiles
57-
instance Binary GetKnownFiles
58-
type instance RuleResult GetKnownFiles = HS.HashSet NormalizedFilePath
54+
instance Hashable GetKnownTargets
55+
instance NFData GetKnownTargets
56+
instance Binary GetKnownTargets
57+
type instance RuleResult GetKnownTargets = KnownTargets
5958

6059
-- | Contains the typechecked module and the OrigNameCache entry for
6160
-- that module.

src/Development/IDE/Core/Rules.hs

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@ import qualified HeaderInfo as Hdr
9090
import Data.Time (UTCTime(..))
9191
import Data.Hashable
9292
import qualified Data.HashSet as HashSet
93+
import qualified Data.HashMap.Strict as HM
9394

9495
-- | This is useful for rules to convert rules that can only produce errors or
9596
-- a result into the more general IdeResult type that supports producing
@@ -322,15 +323,20 @@ getLocatedImportsRule :: Rules ()
322323
getLocatedImportsRule =
323324
define $ \GetLocatedImports file -> do
324325
ms <- use_ GetModSummaryWithoutTimestamps file
325-
targets <- useNoFile_ GetKnownFiles
326+
targets <- useNoFile_ GetKnownTargets
326327
let imports = [(False, imp) | imp <- ms_textual_imps ms] ++ [(True, imp) | imp <- ms_srcimps ms]
327328
env_eq <- use_ GhcSession file
328329
let env = hscEnvWithImportPaths env_eq
329330
let import_dirs = deps env_eq
330-
let dflags = addRelativeImport file (moduleName $ ms_mod ms) $ hsc_dflags env
331+
let dflags = hsc_dflags env
332+
isImplicitCradle = isNothing $ envImportPaths env_eq
333+
dflags <- return $ if isImplicitCradle
334+
then addRelativeImport file (moduleName $ ms_mod ms) dflags
335+
else dflags
331336
opt <- getIdeOptions
332-
let getTargetExists nfp
333-
| HashSet.null targets || nfp `HashSet.member` targets = getFileExists nfp
337+
let getTargetExists modName nfp
338+
| isImplicitCradle = getFileExists nfp
339+
| HM.member modName targets = getFileExists nfp
334340
| otherwise = return False
335341
(diags, imports') <- fmap unzip $ forM imports $ \(isSource, (mbPkgName, modName)) -> do
336342
diagOrImp <- locateModule dflags import_dirs (optExtensions opt) getTargetExists modName mbPkgName isSource
@@ -532,14 +538,14 @@ typeCheckRule = define $ \TypeCheck file -> do
532538
typeCheckRuleDefinition hsc pm isFoi (Just source)
533539

534540
knownFilesRule :: Rules ()
535-
knownFilesRule = defineEarlyCutOffNoFile $ \GetKnownFiles -> do
541+
knownFilesRule = defineEarlyCutOffNoFile $ \GetKnownTargets -> do
536542
alwaysRerun
537-
fs <- knownFiles
543+
fs <- knownTargets
538544
pure (BS.pack (show $ hash fs), unhashed fs)
539545

540546
getModuleGraphRule :: Rules ()
541547
getModuleGraphRule = defineNoFile $ \GetModuleGraph -> do
542-
fs <- useNoFile_ GetKnownFiles
548+
fs <- toKnownFiles <$> useNoFile_ GetKnownTargets
543549
rawDepInfo <- rawDependencyInformation (HashSet.toList fs)
544550
pure $ processDependencyInformation rawDepInfo
545551

@@ -683,7 +689,7 @@ ghcSessionDepsDefinition file = do
683689
setupFinderCache (map hirModSummary ifaces)
684690
mapM_ (uncurry loadDepModule) inLoadOrder
685691

686-
res <- liftIO $ newHscEnvEq session' []
692+
res <- liftIO $ newHscEnvEq "" session' []
687693
return ([], Just res)
688694
where
689695
unpack HiFileResult{..} bc = (hirModIface, bc)

0 commit comments

Comments
 (0)