@@ -1,10 +1,12 @@
11-- Copyright (c) 2019 The DAML Authors. All rights reserved.
22-- SPDX-License-Identifier: Apache-2.0
33
4−{-# LANGUAGE TypeFamilies #-}
5−{-# LANGUAGE FlexibleInstances #-}
4+{-# LANGUAGE CPP #-}
5+{-# LANGUAGE TypeFamilies #-}
6+{-# LANGUAGE FlexibleInstances #-}
67{-# LANGUAGE DuplicateRecordFields #-}
78{-# LANGUAGE PatternSynonyms #-}
9+#include "ghc-api-version.h"
810
911-- | A Shake implementation of the compiler service, built
1012-- using the "Shaker" abstraction layer for in-memory use.
@@ -44,6 +46,7 @@ import Development.IDE.Types.Diagnostics
4446import Development.IDE.Types.Location
4547import Development.IDE.GHC.Compat hiding (parseModule, typecheckModule)
4648import Development.IDE.GHC.Util
49+import Development.IDE.GHC.WithDynFlags
4750import Data.Coerce
4851import Data.Either.Extra
4952import Data.Maybe
@@ -62,13 +65,16 @@ import Development.IDE.Spans.Type
6265
6366import qualified GHC.LanguageExtensions as LangExt
6467import HscTypes
65−import DynFlags (xopt)
68+import DynFlags (gopt_set, xopt)
6669import GHC.Generics(Generic)
6770
6871import qualified Development.IDE.Spans.AtPoint as AtPoint
6972import Development.IDE.Core.Service
7073import Development.IDE.Core.Shake
7174import Development.Shake.Classes
75+import Control.Monad.Trans.Except (runExceptT)
76+import Data.ByteString (ByteString)
77+import Control.Concurrent.Async (concurrently)
7278
7379-- | This is useful for rules to convert rules that can only produce errors or
7480-- a result into the more general IdeResult type that supports producing
@@ -131,9 +137,9 @@ getHieFile file mod = do
131137
132138getHomeHieFile :: NormalizedFilePath -> Action ([a], Maybe HieFile)
133139getHomeHieFile f = do
134− pm <- use_ GetParsedModule f
140+ ms <- use_ GetModSummary f
135141 let normal_hie_f = toNormalizedFilePath' hie_f
136− hie_f = ml_hie_file $ ms_location $ pm_mod_summary pm
142+ hie_f = ml_hie_file $ ms_location ms
137143 mbHieTimestamp <- use GetModificationTime normal_hie_f
138144 srcTimestamp <- use_ GetModificationTime f
139145
@@ -185,28 +191,51 @@ priorityFilesOfInterest :: Priority
185191priorityFilesOfInterest = Priority (-2)
186192
187193getParsedModuleRule :: Rules ()
188−getParsedModuleRule =
189− defineEarlyCutoff $ \GetParsedModule file -> do
190− (_, contents) <- getFileContents file
191− packageState <- hscEnv <$> use_ GhcSession file
192− opt <- getIdeOptions
193− (diag, res) <- liftIO $ parseModule opt packageState (fromNormalizedFilePath file) (fmap textToStringBuffer contents)
194− case res of
195− Nothing -> pure (Nothing, (diag, Nothing))
196− Just (contents, modu) -> do
197− mbFingerprint <- if isNothing $ optShakeFiles opt
198− then pure Nothing
199− else liftIO $ Just . fingerprintToBS <$> fingerprintFromStringBuffer contents
200− pure (mbFingerprint, (diag, Just modu))
194+getParsedModuleRule = defineEarlyCutoff $ \GetParsedModule file -> do
195+ hsc <- hscEnv <$> use_ GhcSession file
196+ opt <- getIdeOptions
197+ (_, contents) <- getFileContents file
198+
199+ let dflags = hsc_dflags hsc
200+ mainParse = getParsedModuleDefinition hsc opt file contents
201+
202+ -- Parse again (if necessary) to capture Haddock parse errors
203+ if gopt Opt_Haddock dflags
204+ then
205+ liftIO mainParse
206+ else do
207+ let hscHaddock = hsc{hsc_dflags = gopt_set dflags Opt_Haddock}
208+ haddockParse = do
209+ (_, (!diagsHaddock, _)) <-
210+ getParsedModuleDefinition hscHaddock opt file contents
211+ return diagsHaddock
212+
213+ ((fingerPrint, (diags, res)), diagsHaddock) <-
214+ -- parse twice, with and without Haddocks, concurrently
215+ -- we cannot ignore Haddock parse errors because files of
216+ -- non-interest are always parsed with Haddocks
217+ liftIO $ concurrently mainParse haddockParse
218+
219+ return (fingerPrint, (mergeDiagnostics diags diagsHaddock, res))
220+
221+getParsedModuleDefinition :: HscEnv -> IdeOptions -> NormalizedFilePath -> Maybe T.Text -> IO (Maybe ByteString, ([FileDiagnostic], Maybe ParsedModule))
222+getParsedModuleDefinition packageState opt file contents = do
223+ (diag, res) <- parseModule opt packageState (fromNormalizedFilePath file) (fmap textToStringBuffer contents)
224+ case res of
225+ Nothing -> pure (Nothing, (diag, Nothing))
226+ Just (contents, modu) -> do
227+ mbFingerprint <- if isNothing $ optShakeFiles opt
228+ then pure Nothing
229+ else Just . fingerprintToBS <$> fingerprintFromStringBuffer contents
230+ pure (mbFingerprint, (diag, Just modu))
201231
202232getLocatedImportsRule :: Rules ()
203233getLocatedImportsRule =
204234 define $ \GetLocatedImports file -> do
205− pm <- use_ GetParsedModule file
206− let ms = pm_mod_summary pm
235+ ms <- use_ GetModSummary file
207236 let imports = [(False, imp) | imp <- ms_textual_imps ms] ++ [(True, imp) | imp <- ms_srcimps ms]
208237 env <- hscEnv <$> use_ GhcSession file
209− let dflags = addRelativeImport file pm $ hsc_dflags env
238+ let dflags = addRelativeImport file (moduleName $ ms_mod ms) $ hsc_dflags env
210239 opt <- getIdeOptions
211240 (diags, imports') <- fmap unzip $ forM imports $ \(isSource, (mbPkgName, modName)) -> do
212241 diagOrImp <- locateModule dflags (optExtensions opt) getFileExists modName mbPkgName isSource
@@ -223,7 +252,6 @@ getLocatedImportsRule =
223252 Nothing -> pure (concat diags, Nothing)
224253 Just pkgImports -> pure (concat diags, Just (moduleImports, Set.fromList $ concat pkgImports))
225254
226−
227255-- | Given a target file path, construct the raw dependency results by following
228256-- imports recursively.
229257rawDependencyInformation :: NormalizedFilePath -> Action RawDependencyInformation
@@ -321,8 +349,8 @@ reportImportCyclesRule =
321349 where loc = srcSpanToLocation (getLoc imp)
322350 fp = toNormalizedFilePath' $ srcSpanToFilename (getLoc imp)
323351 getModuleName file = do
324− pm <- use_ GetParsedModule file
325− pure (moduleNameString . moduleName . ms_mod $ pm_mod_summary pm)
352+ ms <- use_ GetModSummary file
353+ pure (moduleNameString . moduleName . ms_mod $ ms)
326354 showCycle mods = T.intercalate ", " (map T.pack mods)
327355
328356-- returns all transitive dependencies in topological order.
@@ -342,22 +370,31 @@ getSpanInfoRule :: Rules ()
342370getSpanInfoRule =
343371 define $ \GetSpanInfo file -> do
344372 tc <- use_ TypeCheck file
373+ packageState <- hscEnv <$> use_ GhcSession file
345374 deps <- maybe (TransitiveDependencies [] [] []) fst <$> useWithStale GetDependencies file
346375 let tdeps = transitiveModuleDeps deps
376+
377+-- When possible, rely on the haddocks embedded in our interface files
378+-- This creates problems on ghc-lib, see comment on 'getDocumentationTryGhc'
379+#if MIN_GHC_API_VERSION(8,6,0) && !defined(GHC_LIB)
380+ let parsedDeps = []
381+#else
347382 parsedDeps <- uses_ GetParsedModule tdeps
383+#endif
384+
348385 ifaces <- uses_ GetModIface tdeps
349386 (fileImports, _) <- use_ GetLocatedImports file
350− packageState <- hscEnv <$> use_ GhcSession file
351387 let imports = second (fmap artifactFilePath) <$> fileImports
352− x <- liftIO $ getSrcSpanInfos packageState imports tc (zip parsedDeps $ map hirModIface ifaces)
388+ x <- liftIO $ getSrcSpanInfos packageState imports tc parsedDeps (map hirModIface ifaces)
353389 return ([], Just x)
354390
355391-- Typechecks a module.
356392typeCheckRule :: Rules ()
357−typeCheckRule = define $ \TypeCheck file ->
393+typeCheckRule = define $ \TypeCheck file -> do
394+ pm <- use_ GetParsedModule file
358395 -- do not generate interface files as this rule is called
359396 -- for files of interest on every keystroke
360− typeCheckRuleDefinition file SkipGenerationOfInterfaceFiles
397+ typeCheckRuleDefinition file pm SkipGenerationOfInterfaceFiles
361398
362399data GenerateInterfaceFiles
363400 = DoGenerateInterfaceFiles
@@ -370,10 +407,10 @@ data GenerateInterfaceFiles
370407-- retain the information forever in the shake graph.
371408typeCheckRuleDefinition
372409 :: NormalizedFilePath -- ^ Path to source file
410+ -> ParsedModule
373411 -> GenerateInterfaceFiles -- ^ Should generate .hi and .hie files ?
374412 -> Action (IdeResult TcModuleResult)
375−typeCheckRuleDefinition file generateArtifacts = do
376− pm <- use_ GetParsedModule file
413+typeCheckRuleDefinition file pm generateArtifacts = do
377414 deps <- use_ GetDependencies file
378415 hsc <- hscEnv <$> use_ GhcSession file
379416 -- Figure out whether we need TemplateHaskell or QuasiQuotes support
@@ -454,20 +491,15 @@ loadGhcSession = do
454491
455492getHiFileRule :: Rules ()
456493getHiFileRule = defineEarlyCutoff $ \GetHiFile f -> do
457− session <- hscEnv <$> use_ GhcSession f
458494 -- get all dependencies interface files, to check for freshness
459495 (deps,_) <- use_ GetLocatedImports f
460496 depHis <- traverse (use GetHiFile) (mapMaybe (fmap artifactFilePath . snd) deps)
461497
462− -- TODO find the hi file without relying on the parsed module
463− -- it should be possible to construct a ModSummary parsing just the imports
464− -- (see HeaderInfo in the GHC package)
465− pm <- use_ GetParsedModule f
466− let hiFile = toNormalizedFilePath' $
467− case ms_hsc_src ms of
498+ ms <- use_ GetModSummary f
499+ let hiFile = toNormalizedFilePath'
500+ $ case ms_hsc_src ms of
468501 HsBootFile -> addBootSuffix (ml_hi_file $ ms_location ms)
469502 _ -> ml_hi_file $ ms_location ms
470− ms = pm_mod_summary pm
471503
472504 IdeOptions{optInterfaceLoadingDiagnostics} <- getIdeOptions
473505
@@ -500,6 +532,7 @@ getHiFileRule = defineEarlyCutoff $ \GetHiFile f -> do
500532 let d = mkInterfaceFilesGenerationDiag f "Stale interface file"
501533 pure (Nothing, (d, Nothing))
502534 else do
535+ session <- hscEnv <$> use_ GhcSession f
503536 r <- liftIO $ loadInterface session ms deps
504537 case r of
505538 Right iface -> do
@@ -509,6 +542,13 @@ getHiFileRule = defineEarlyCutoff $ \GetHiFile f -> do
509542 let diag = ideErrorWithSource (Just "interface file loading") (Just DsError) f . T.pack $ err
510543 return (Nothing, (pure diag, Nothing))
511544
545+getModSummaryRule :: Rules ()
546+getModSummaryRule = define $ \GetModSummary f -> do
547+ dflags <- hsc_dflags . hscEnv <$> use_ GhcSession f
548+ (_, mFileContent) <- getFileContents f
549+ modS <- liftIO $ evalWithDynFlags dflags $ runExceptT $
550+ getModSummaryFromImports (fromNormalizedFilePath f) (textToStringBuffer <$> mFileContent)
551+ return $ either (,Nothing) (([], ) . Just) modS
512552
513553getModIfaceRule :: Rules ()
514554getModIfaceRule = define $ \GetModIface f -> do
@@ -526,11 +566,22 @@ getModIfaceRule = define $ \GetModIface f -> do
526566 tmr <- use TypeCheck f
527567 return ([], extract tmr)
528568 | otherwise -> do
529− -- Otherwise the interface file does not exist or is out of date. Invoke typechecking directly to update it without incurring a dependency on the typecheck rule.
530− (diags, tmr) <- typeCheckRuleDefinition f DoGenerateInterfaceFiles
531− -- Bang pattern is important to avoid leaking 'tmr'
532− let !res = extract tmr
533− return (diags, res)
569+ -- the interface file does not exist or is out of date.
570+ -- Invoke typechecking directly to update it without incurring a dependency
571+ -- on the parsed module and the typecheck rules
572+ hsc <- hscEnv <$> use_ GhcSession f
573+ opt <- getIdeOptions
574+ (_, contents) <- getFileContents f
575+ -- Embed --haddocks in the interface file
576+ hsc <- pure hsc{hsc_dflags = gopt_set (hsc_dflags hsc) Opt_Haddock}
577+ (_, (diags, mb_pm)) <- liftIO $ getParsedModuleDefinition hsc opt f contents
578+ case mb_pm of
579+ Nothing -> return (diags, Nothing)
580+ Just pm -> do
581+ (diags', tmr) <- typeCheckRuleDefinition f pm DoGenerateInterfaceFiles
582+ -- Bang pattern is important to avoid leaking 'tmr'
583+ let !res = extract tmr
584+ return (diags <> diags', res)
534585 where
535586 extract Nothing = Nothing
536587 extract (Just tmr) =
@@ -559,3 +610,4 @@ mainRule = do
559610 getHiFileRule
560611 getModIfaceRule
561612 isFileOfInterestRule
613+ getModSummaryRule