Skip to content

Commit 29d7741

Browse files
authored
Add .hie files support for home modules (#440)
* Add .hie files support for home modules This is required for goto definition when using interface files. .hie files are never stored in the Shake graph, as they are - expensive in space - quick to load - only used for go to definition While there, we remove package module .hie files from the Shake graph too * Review feedbacks
1 parent 1e68cb0 commit 29d7741

File tree

5 files changed

+84
-44
lines changed

5 files changed

+84
-44
lines changed

src/Development/IDE/Core/Compile.hs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ module Development.IDE.Core.Compile
1616
, addRelativeImport
1717
, mkTcModuleResult
1818
, generateByteCode
19+
, loadHieFile
1920
) where
2021

2122
import Development.IDE.Core.RuleTypes
@@ -43,12 +44,13 @@ import ErrUtils
4344
#endif
4445

4546
import Finder
46-
import qualified GHC
47+
import qualified Development.IDE.GHC.Compat as GHC
4748
import GhcMonad
4849
import GhcPlugins as GHC hiding (fst3, (<>))
4950
import qualified HeaderInfo as Hdr
5051
import HscMain (hscInteractive, hscSimplify)
5152
import MkIface
53+
import NameCache
5254
import StringBuffer as SB
5355
import TcRnMonad (tcg_th_coreplugins)
5456
import TidyPgm
@@ -406,3 +408,9 @@ parseFileContents customPreprocessor dflags filename contents = do
406408
}
407409
warnings = diagFromErrMsgs "parser" dflags warns
408410
pure (warnings ++ preproc_warnings, pm)
411+
412+
loadHieFile :: FilePath -> IO GHC.HieFile
413+
loadHieFile f = do
414+
u <- mkSplitUniqSupply 'a'
415+
let nameCache = initNameCache u []
416+
fmap (GHC.hie_file_result . fst) $ GHC.readHieFile nameCache f

src/Development/IDE/Core/RuleTypes.hs

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@ import GHC.Generics (Generic)
2424
import GHC
2525
import Module (InstalledUnitId)
2626
import HscTypes (CgGuts, Linkable, HomeModInfo, ModDetails)
27-
import Development.IDE.GHC.Compat
2827

2928
import Development.IDE.Spans.Type
3029
import Development.IDE.Import.FindImports (ArtifactsLocation)
@@ -82,10 +81,6 @@ type instance RuleResult GetLocatedImports = ([(Located ModuleName, Maybe Artifa
8281
-- we can only report diagnostics for the current file.
8382
type instance RuleResult ReportImportCycles = ()
8483

85-
-- | Read the given HIE file.
86-
type instance RuleResult GetHieFile = HieFile
87-
88-
8984
data GetParsedModule = GetParsedModule
9085
deriving (Eq, Show, Typeable, Generic)
9186
instance Hashable GetParsedModule
@@ -145,11 +140,3 @@ data GhcSession = GhcSession
145140
instance Hashable GhcSession
146141
instance NFData GhcSession
147142
instance Binary GhcSession
148-
149-
-- Note that we embed the filepath here instead of using the filepath associated with Shake keys.
150-
-- Otherwise we will garbage collect the result since files in package dependencies will not be declared reachable.
151-
data GetHieFile = GetHieFile FilePath
152-
deriving (Eq, Show, Typeable, Generic)
153-
instance Hashable GetHieFile
154-
instance NFData GetHieFile
155-
instance Binary GetHieFile

src/Development/IDE/Core/Rules.hs

Lines changed: 56 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ import Fingerprint
2929

3030
import Data.Binary
3131
import Data.Bifunctor (second)
32-
import Control.Monad
32+
import Control.Monad.Extra
3333
import Control.Monad.Trans.Class
3434
import Control.Monad.Trans.Maybe
3535
import Development.IDE.Core.Compile
@@ -50,6 +50,7 @@ import Data.Foldable
5050
import qualified Data.IntMap.Strict as IntMap
5151
import qualified Data.IntSet as IntSet
5252
import Data.List
53+
import Data.Ord
5354
import qualified Data.Set as Set
5455
import qualified Data.Text as T
5556
import Development.IDE.GHC.Error
@@ -58,8 +59,6 @@ import Development.IDE.Core.RuleTypes
5859
import Development.IDE.Spans.Type
5960

6061
import qualified GHC.LanguageExtensions as LangExt
61-
import UniqSupply
62-
import NameCache
6362
import HscTypes
6463
import DynFlags (xopt)
6564
import GHC.Generics(Generic)
@@ -112,9 +111,60 @@ getDefinition :: NormalizedFilePath -> Position -> Action (Maybe Location)
112111
getDefinition file pos = fmap join $ runMaybeT $ do
113112
opts <- lift getIdeOptions
114113
spans <- useE GetSpanInfo file
115-
pkgState <- hscEnv <$> useE GhcSession file
116-
let getHieFile x = useNoFile (GetHieFile x)
117-
lift $ AtPoint.gotoDefinition getHieFile opts pkgState (spansExprs spans) pos
114+
lift $ AtPoint.gotoDefinition (getHieFile file) opts (spansExprs spans) pos
115+
116+
getHieFile
117+
:: NormalizedFilePath -- ^ file we're editing
118+
-> Module -- ^ module dep we want info for
119+
-> Action (Maybe (HieFile, FilePath)) -- ^ hie stuff for the module
120+
getHieFile file mod = do
121+
TransitiveDependencies {transitiveNamedModuleDeps} <- use_ GetDependencies file
122+
case find (\x -> nmdModuleName x == moduleName mod) transitiveNamedModuleDeps of
123+
Just NamedModuleDep{nmdFilePath=nfp} -> do
124+
let modPath = fromNormalizedFilePath nfp
125+
(_diags, hieFile) <- getHomeHieFile nfp
126+
return $ (, modPath) <$> hieFile
127+
_ -> getPackageHieFile mod file
128+
129+
130+
getHomeHieFile :: NormalizedFilePath -> Action ([a], Maybe HieFile)
131+
getHomeHieFile f = do
132+
pm <- use_ GetParsedModule f
133+
let normal_hie_f = toNormalizedFilePath hie_f
134+
hie_f = ml_hie_file $ ms_location $ pm_mod_summary pm
135+
mbHieTimestamp <- use GetModificationTime normal_hie_f
136+
srcTimestamp <- use_ GetModificationTime f
137+
138+
let isUpToDate
139+
| Just d <- mbHieTimestamp = comparing modificationTime d srcTimestamp == GT
140+
| otherwise = False
141+
142+
-- In the future, TypeCheck will emit .hie files as a side effect
143+
-- unless isUpToDate $
144+
-- void $ use_ TypeCheck f
145+
146+
hf <- liftIO $ if isUpToDate then Just <$> loadHieFile hie_f else pure Nothing
147+
return ([], hf)
148+
149+
getPackageHieFile :: Module -- ^ Package Module to load .hie file for
150+
-> NormalizedFilePath -- ^ Path of home module importing the package module
151+
-> Action (Maybe (HieFile, FilePath))
152+
getPackageHieFile mod file = do
153+
pkgState <- hscEnv <$> use_ GhcSession file
154+
IdeOptions {..} <- getIdeOptions
155+
let unitId = moduleUnitId mod
156+
case lookupPackageConfig unitId pkgState of
157+
Just pkgConfig -> do
158+
-- 'optLocateHieFile' returns Nothing if the file does not exist
159+
hieFile <- liftIO $ optLocateHieFile optPkgLocationOpts pkgConfig mod
160+
path <- liftIO $ optLocateSrcFile optPkgLocationOpts pkgConfig mod
161+
case (hieFile, path) of
162+
(Just hiePath, Just modPath) ->
163+
-- deliberately loaded outside the Shake graph
164+
-- to avoid dependencies on non-workspace files
165+
liftIO $ Just . (, modPath) <$> loadHieFile hiePath
166+
_ -> return Nothing
167+
_ -> return Nothing
118168

119169
-- | Parse the contents of a daml file.
120170
getParsedModule :: NormalizedFilePath -> Action (Maybe ParsedModule)
@@ -348,14 +398,6 @@ loadGhcSession = do
348398
opts <- getIdeOptions
349399
return ("" <$ optShakeFiles opts, ([], Just val))
350400

351-
352-
getHieFileRule :: Rules ()
353-
getHieFileRule =
354-
defineNoFile $ \(GetHieFile f) -> do
355-
u <- liftIO $ mkSplitUniqSupply 'a'
356-
let nameCache = initNameCache u []
357-
liftIO $ fmap (hie_file_result . fst) $ readHieFile nameCache f
358-
359401
-- | A rule that wires per-file rules together
360402
mainRule :: Rules ()
361403
mainRule = do
@@ -369,4 +411,3 @@ mainRule = do
369411
generateCoreRule
370412
generateByteCodeRule
371413
loadGhcSession
372-
getHieFileRule

src/Development/IDE/GHC/Compat.hs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@ module Development.IDE.GHC.Compat(
1414
readHieFile,
1515
setDefaultHieDir,
1616
dontWriteHieFiles,
17+
#if !MIN_GHC_API_VERSION(8,8,0)
18+
ml_hie_file,
19+
#endif
1720
hPutStringBuffer,
1821
includePathsGlobal,
1922
includePathsQuote,
@@ -52,12 +55,10 @@ import System.IO
5255
import Foreign.ForeignPtr
5356

5457

55-
#if !MIN_GHC_API_VERSION(8,8,0)
5658
hPutStringBuffer :: Handle -> StringBuffer -> IO ()
5759
hPutStringBuffer hdl (StringBuffer buf len cur)
5860
= withForeignPtr (plusForeignPtr buf cur) $ \ptr ->
5961
hPutBuf hdl ptr len
60-
#endif
6162

6263
mkHieFile :: ModSummary -> TcGblEnv -> RenamedSource -> Hsc HieFile
6364
mkHieFile _ _ _ = return (HieFile () [])
@@ -68,6 +69,9 @@ writeHieFile _ _ = return ()
6869
readHieFile :: NameCache -> FilePath -> IO (HieFileResult, ())
6970
readHieFile _ _ = return (HieFileResult (HieFile () []), ())
7071

72+
ml_hie_file :: GHC.ModLocation -> FilePath
73+
ml_hie_file _ = ""
74+
7175
data HieFile = HieFile {hie_module :: (), hie_exports :: [AvailInfo]}
7276
data HieFileResult = HieFileResult { hie_file_result :: HieFile }
7377
#endif

src/Development/IDE/Spans/AtPoint.hs

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,6 @@ import Development.IDE.GHC.Orphans()
1313
import Development.IDE.Types.Location
1414

1515
-- DAML compiler and infrastructure
16-
import Development.Shake
17-
import Development.IDE.GHC.Util
1816
import Development.IDE.GHC.Compat
1917
import Development.IDE.Types.Options
2018
import Development.IDE.Spans.Type as SpanInfo
@@ -40,14 +38,13 @@ import qualified Data.Text as T
4038
-- | Locate the definition of the name at a given position.
4139
gotoDefinition
4240
:: MonadIO m
43-
=> (FilePath -> m (Maybe HieFile))
41+
=> (Module -> m (Maybe (HieFile, FilePath)))
4442
-> IdeOptions
45-
-> HscEnv
4643
-> [SpanInfo]
4744
-> Position
4845
-> m (Maybe Location)
49-
gotoDefinition getHieFile ideOpts pkgState srcSpans pos =
50-
listToMaybe <$> locationsAtPoint getHieFile ideOpts pkgState pos srcSpans
46+
gotoDefinition getHieFile ideOpts srcSpans pos =
47+
listToMaybe <$> locationsAtPoint getHieFile ideOpts pos srcSpans
5148

5249
-- | Synopsis for the name at a given position.
5350
atPoint
@@ -119,8 +116,15 @@ atPoint IdeOptions{..} (SpansInfo srcSpans cntsSpans) pos = do
119116
Just name -> any (`isInfixOf` getOccString name) ["==", "showsPrec"]
120117
Nothing -> False
121118

122-
locationsAtPoint :: forall m . MonadIO m => (FilePath -> m (Maybe HieFile)) -> IdeOptions -> HscEnv -> Position -> [SpanInfo] -> m [Location]
123-
locationsAtPoint getHieFile IdeOptions{..} pkgState pos =
119+
locationsAtPoint
120+
:: forall m
121+
. MonadIO m
122+
=> (Module -> m (Maybe (HieFile, FilePath)))
123+
-> IdeOptions
124+
-> Position
125+
-> [SpanInfo]
126+
-> m [Location]
127+
locationsAtPoint getHieFile IdeOptions{..} pos =
124128
fmap (map srcSpanToLocation) . mapMaybeM (getSpan . spaninfoSource) . spansAtPoint pos
125129
where getSpan :: SpanSource -> m (Maybe SrcSpan)
126130
getSpan NoSource = pure Nothing
@@ -134,12 +138,8 @@ locationsAtPoint getHieFile IdeOptions{..} pkgState pos =
134138
-- In this case the interface files contain garbage source spans
135139
-- so we instead read the .hie files to get useful source spans.
136140
mod <- MaybeT $ return $ nameModule_maybe name
137-
let unitId = moduleUnitId mod
138-
pkgConfig <- MaybeT $ pure $ lookupPackageConfig unitId pkgState
139-
hiePath <- MaybeT $ liftIO $ optLocateHieFile optPkgLocationOpts pkgConfig mod
140-
hieFile <- MaybeT $ getHieFile hiePath
141+
(hieFile, srcPath) <- MaybeT $ getHieFile mod
141142
avail <- MaybeT $ pure $ listToMaybe (filterAvails (eqName name) $ hie_exports hieFile)
142-
srcPath <- MaybeT $ liftIO $ optLocateSrcFile optPkgLocationOpts pkgConfig mod
143143
-- The location will point to the source file used during compilation.
144144
-- This file might no longer exists and even if it does the path will be relative
145145
-- to the compilation directory which we don’t know.

0 commit comments

Comments
 (0)