on master help sign in
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

commit29d774144e419e62b99742fe363d28c86e6487a5 authorPepe Iborra <pepeiborra@gmail.com> dateMon 24 Feb 2020, 13:14 (7 years ago) parent1e68cb0
5 files changed+84−44
src/Development/IDE/Core/Compile.hs+9−1 src/Development/IDE/Core/RuleTypes.hs−13 src/Development/IDE/Core/Rules.hs+56−15 src/Development/IDE/GHC/Compat.hs+6−2 src/Development/IDE/Spans/AtPoint.hs+13−13
src/Development/IDE/Core/Compile.hs+9−1
@@ -16,6 +16,7 @@ module Development.IDE.Core.Compile
1616 , addRelativeImport
1717 , mkTcModuleResult
1818 , generateByteCode
19+ , loadHieFile
1920 ) where
2021
2122import Development.IDE.Core.RuleTypes
@@ -43,12 +44,13 @@ import ErrUtils
4344#endif
4445
4546import Finder
46−import qualified GHC
47+import qualified Development.IDE.GHC.Compat as GHC
4748import GhcMonad
4849import GhcPlugins as GHC hiding (fst3, (<>))
4950import qualified HeaderInfo as Hdr
5051import HscMain (hscInteractive, hscSimplify)
5152import MkIface
53+import NameCache
5254import StringBuffer as SB
5355import TcRnMonad (tcg_th_coreplugins)
5456import 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−13
@@ -24,7 +24,6 @@ import GHC.Generics (Generic)
2424import GHC
2525import Module (InstalledUnitId)
2626import HscTypes (CgGuts, Linkable, HomeModInfo, ModDetails)
27−import Development.IDE.GHC.Compat
2827
2928import Development.IDE.Spans.Type
3029import 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.
8382type instance RuleResult ReportImportCycles = ()
8483
85−-- | Read the given HIE file.
86−type instance RuleResult GetHieFile = HieFile
87−
88−
8984data GetParsedModule = GetParsedModule
9085 deriving (Eq, Show, Typeable, Generic)
9186instance Hashable GetParsedModule
@@ -145,11 +140,3 @@ data GhcSession = GhcSession
145140instance Hashable GhcSession
146141instance NFData GhcSession
147142instance 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+56−15
@@ -29,7 +29,7 @@ import Fingerprint
2929
3030import Data.Binary
3131import Data.Bifunctor (second)
32−import Control.Monad
32+import Control.Monad.Extra
3333import Control.Monad.Trans.Class
3434import Control.Monad.Trans.Maybe
3535import Development.IDE.Core.Compile
@@ -50,6 +50,7 @@ import Data.Foldable
5050import qualified Data.IntMap.Strict as IntMap
5151import qualified Data.IntSet as IntSet
5252import Data.List
53+import Data.Ord
5354import qualified Data.Set as Set
5455import qualified Data.Text as T
5556import Development.IDE.GHC.Error
@@ -58,8 +59,6 @@ import Development.IDE.Core.RuleTypes
5859import Development.IDE.Spans.Type
5960
6061import qualified GHC.LanguageExtensions as LangExt
61−import UniqSupply
62−import NameCache
6362import HscTypes
6463import DynFlags (xopt)
6564import GHC.Generics(Generic)
@@ -112,9 +111,60 @@ getDefinition :: NormalizedFilePath -> Position -> Action (Maybe Location)
112111getDefinition 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.
120170getParsedModule :: 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
360402mainRule :: Rules ()
361403mainRule = do
@@ -369,4 +411,3 @@ mainRule = do
369411 generateCoreRule
370412 generateByteCodeRule
371413 loadGhcSession
372− getHieFileRule
src/Development/IDE/GHC/Compat.hs+6−2
@@ -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
5255import Foreign.ForeignPtr
5356
5457
55−#if !MIN_GHC_API_VERSION(8,8,0)
5658hPutStringBuffer :: Handle -> StringBuffer -> IO ()
5759hPutStringBuffer hdl (StringBuffer buf len cur)
5860 = withForeignPtr (plusForeignPtr buf cur) $ \ptr ->
5961 hPutBuf hdl ptr len
60−#endif
6162
6263mkHieFile :: ModSummary -> TcGblEnv -> RenamedSource -> Hsc HieFile
6364mkHieFile _ _ _ = return (HieFile () [])
@@ -68,6 +69,9 @@ writeHieFile _ _ = return ()
6869readHieFile :: NameCache -> FilePath -> IO (HieFileResult, ())
6970readHieFile _ _ = return (HieFileResult (HieFile () []), ())
7071
72+ml_hie_file :: GHC.ModLocation -> FilePath
73+ml_hie_file _ = ""
74+
7175data HieFile = HieFile {hie_module :: (), hie_exports :: [AvailInfo]}
7276data HieFileResult = HieFileResult { hie_file_result :: HieFile }
7377#endif
src/Development/IDE/Spans/AtPoint.hs+13−13
@@ -13,8 +13,6 @@ import Development.IDE.GHC.Orphans()
1313import Development.IDE.Types.Location
1414
1515-- DAML compiler and infrastructure
16−import Development.Shake
17−import Development.IDE.GHC.Util
1816import Development.IDE.GHC.Compat
1917import Development.IDE.Types.Options
2018import 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.
4139gotoDefinition
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.
5350atPoint
@@ -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.