diff --git a/.gitignore b/.gitignore index c3e127b1f..e7fac986e 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,8 @@ elisp/*.elc /.cabal-sandbox/ /.stack-work/ /test/data/**/stack.yaml +/test/data/**/.stack-work +/test/data/**/.cabal-sandbox add-source-timestamps package.cache cabal.sandbox.config @@ -20,3 +22,6 @@ cabal.sandbox.config cabal-dev /TAGS /tags +/.bash_history +/.travis.yml.orig +/cabal.project.az diff --git a/GhcMod.hs b/GhcMod.hs index 893475901..7793d772d 100644 --- a/GhcMod.hs +++ b/GhcMod.hs @@ -48,7 +48,7 @@ module GhcMod ( , splits , sig , refine - , auto + -- , auto , modules , languages , flags @@ -67,6 +67,9 @@ module GhcMod ( , loadMappedFile , loadMappedFileSource , unloadMappedFile + -- * API moving around stuff, temporary for now + , pretty + , collectAllSpansTypes ) where import GhcMod.Exe.Boot @@ -90,3 +93,6 @@ import GhcMod.Monad import GhcMod.Output import GhcMod.Target import GhcMod.Types + + +import GhcMod.SrcUtils (pretty,collectAllSpansTypes) diff --git a/GhcMod/Exe/CaseSplit.hs b/GhcMod/Exe/CaseSplit.hs index d866fb6f2..4753f1db7 100644 --- a/GhcMod/Exe/CaseSplit.hs +++ b/GhcMod/Exe/CaseSplit.hs @@ -2,6 +2,8 @@ module GhcMod.Exe.CaseSplit ( splits + , splits' + , SplitResult(..) ) where import Data.List (find, intercalate) @@ -12,7 +14,6 @@ import System.FilePath import Prelude import qualified DataCon as Ty -import GHC (GhcMonad, LPat, Id, ParsedModule(..), TypecheckedModule(..), DynFlags, SrcSpan, Type, GenLocated(L)) import qualified GHC as G import Outputable (PprStyle) import qualified TyCon as Ty @@ -35,15 +36,22 @@ import Control.DeepSeq -- CASE SPLITTING ---------------------------------------------------------------- -data SplitInfo = SplitInfo G.Name SrcSpan (SrcSpan, Type) [SrcSpan] - | TySplitInfo G.Name SrcSpan (SrcSpan, Ty.Kind) +data SplitInfo = SplitInfo G.Name G.SrcSpan (G.SrcSpan, G.Type) [G.SrcSpan] + | TySplitInfo G.Name G.SrcSpan (G.SrcSpan, Ty.Kind) data SplitToTextInfo = SplitToTextInfo { sVarName :: String - , sBindingSpan :: SrcSpan - , sVarSpan :: SrcSpan + , sBindingSpan :: G.SrcSpan + , sVarSpan :: G.SrcSpan , sTycons :: [String] } +data SplitResult = SplitResult { sStartLine :: Int + , sStartCol :: Int + , sEndLine :: Int + , sEndCol :: Int + , sNewText :: T.Text } -- | Splitting a variable in a equation. +-- Unlike splits', this performs parsing an type checking on every invocation. +-- This is meant for consumption by tools that call ghc-mod as a binary. splits :: IOish m => FilePath -- ^ A target file. -> Int -- ^ Line number. @@ -53,84 +61,122 @@ splits file lineNo colNo = ghandle handler $ runGmlT' [Left file] deferErrors $ do oopts <- outputOpts crdl <- cradle - style <- getStyle - dflag <- G.getSessionDynFlags modSum <- fileModSummaryWithMapping (cradleCurrentDir crdl file) - whenFound' oopts (getSrcSpanTypeForSplit modSum lineNo colNo) $ \x -> do - let (varName, bndLoc, (varLoc,varT)) - | (SplitInfo vn bl vlvt _matches) <- x - = (vn, bl, vlvt) - | (TySplitInfo vn bl vlvt) <- x - = (vn, bl, vlvt) - varName' = showName dflag style varName -- Convert name to string - t <- withMappedFile file $ \file' -> - genCaseSplitTextFile file' (SplitToTextInfo varName' bndLoc varLoc $ - getTyCons dflag style varName varT) - return $!! (fourInts bndLoc, t) + p <- G.parseModule modSum + tcm <- G.typecheckModule p + whenFound' oopts (performSplit file tcm lineNo colNo) $ + \(SplitResult sLine sCol eLine eCol newText) -> + return $!! ((sLine, sCol, eLine, eCol), T.unpack newText) where handler (SomeException ex) = do gmLog GmException "splits" $ text "" $$ nest 4 (showToDoc ex) emptyResult =<< outputOpts +-- | Split an identifier in a function definition. +-- Meant for library-usage. +splits' :: IOish m => FilePath -> G.TypecheckedModule -> Int -> Int -> GhcModT m (Maybe SplitResult) +splits' file tcm lineNo colNo = + ghandle handler $ runGmlT' [Left file] deferErrors $ performSplit file tcm lineNo colNo + where + handler (SomeException ex) = do + gmLog GmException "splits'" $ + text "" $$ nest 4 (showToDoc ex) + return Nothing + +performSplit :: IOish m => FilePath -> G.TypecheckedModule -> Int -> Int -> GmlT m (Maybe SplitResult) +performSplit file tcm lineNo colNo = do + style <- getStyle + dflag <- G.getSessionDynFlags + maybeSplitInfo <- getSrcSpanTypeForSplit tcm lineNo colNo + sequenceA $ constructSplitResult file maybeSplitInfo dflag style + +constructSplitResult :: (IOish m) => FilePath -> Maybe SplitInfo -> G.DynFlags -> PprStyle -> Maybe (GmlT m SplitResult) +constructSplitResult file maybeSplitInfo dflag style = do + splitInfo <- maybeSplitInfo + let splitToTextInfo = constructSplitToTextInfo splitInfo dflag style + startLoc <- maybeSrcSpanStart $ sBindingSpan splitToTextInfo + endLoc <- maybeSrcSpanEnd $ sBindingSpan splitToTextInfo + let startLine = G.srcLocLine startLoc + startCol = G.srcLocCol startLoc + endLine = G.srcLocLine endLoc + endCol = G.srcLocCol endLoc + newText = genCaseSplitTextFile file splitToTextInfo + return $ SplitResult startLine startCol endLine endCol . T.pack <$> newText + +constructSplitToTextInfo :: SplitInfo -> G.DynFlags -> PprStyle -> SplitToTextInfo +constructSplitToTextInfo splitInfo dflag style = + SplitToTextInfo varName' bndLoc varLoc typeCons + where + typeCons = getTyCons dflag style varName varT + varName' = showName dflag style varName -- Convert name to string + (varName, bndLoc, varLoc, varT) = case splitInfo of + (SplitInfo vn bl (vl, vt) _matches) -> (vn, bl, vl, vt) + (TySplitInfo vn bl (vl, vt)) -> (vn, bl, vl, vt) + +maybeSrcSpanStart :: G.SrcSpan -> Maybe G.RealSrcLoc +maybeSrcSpanStart s = case G.srcSpanStart s of + (G.RealSrcLoc startLoc) -> Just startLoc + _ -> Nothing + +maybeSrcSpanEnd :: G.SrcSpan -> Maybe G.RealSrcLoc +maybeSrcSpanEnd s = case G.srcSpanEnd s of + (G.RealSrcLoc endLoc) -> Just endLoc + _ -> Nothing ---------------------------------------------------------------- -- a. Code for getting the information of the variable -getSrcSpanTypeForSplit :: GhcMonad m => G.ModSummary -> Int -> Int -> m (Maybe SplitInfo) -getSrcSpanTypeForSplit modSum lineNo colNo = do - fn <- getSrcSpanTypeForFnSplit modSum lineNo colNo +getSrcSpanTypeForSplit :: G.GhcMonad m => G.TypecheckedModule -> Int -> Int -> m (Maybe SplitInfo) +getSrcSpanTypeForSplit tcm lineNo colNo = do + fn <- getSrcSpanTypeForFnSplit tcm lineNo colNo if isJust fn then return fn - else getSrcSpanTypeForTypeSplit modSum lineNo colNo + else getSrcSpanTypeForTypeSplit tcm lineNo colNo -- Information for a function case split -getSrcSpanTypeForFnSplit :: GhcMonad m => G.ModSummary -> Int -> Int -> m (Maybe SplitInfo) -getSrcSpanTypeForFnSplit modSum lineNo colNo = do - p@ParsedModule{pm_parsed_source = _pms} <- G.parseModule modSum - tcm@TypecheckedModule{tm_typechecked_source = tcs} <- G.typecheckModule p - let varPat = find isPatternVar $ listifySpans tcs (lineNo, colNo) :: Maybe (LPat Id) - match = last $ listifySpans tcs (lineNo, colNo) :: Gap.GLMatchI +getSrcSpanTypeForFnSplit :: G.GhcMonad m => G.TypecheckedModule -> Int -> Int -> m (Maybe SplitInfo) +getSrcSpanTypeForFnSplit tcm@G.TypecheckedModule{G.tm_typechecked_source = tcs} lineNo colNo = do + let varPat = find isPatternVar $ listifySpans tcs (lineNo, colNo) :: Maybe (G.LPat Gap.GhcTc) + match = last $ listifySpans tcs (lineNo, colNo) :: G.LMatch Gap.GhcTc (G.LHsExpr Gap.GhcTc) case varPat of Nothing -> return Nothing Just varPat' -> do varT <- Gap.getType tcm varPat' -- Finally we get the type of the var case varT of Just varT' -> -#if __GLASGOW_HASKELL__ >= 710 - let (L matchL (G.Match _ _ _ (G.GRHSs rhsLs _))) = match -#else - let (L matchL (G.Match _ _ (G.GRHSs rhsLs _))) = match -#endif + let (G.L matchL (G.Match { G.m_grhss = G.GRHSs { G.grhssGRHSs = rhsLs }})) = match in return $ Just (SplitInfo (getPatternVarName varPat') matchL varT' (map G.getLoc rhsLs) ) _ -> return Nothing -isPatternVar :: LPat Id -> Bool -isPatternVar (L _ (G.VarPat _)) = True -isPatternVar _ = False +isPatternVar :: G.LPat Gap.GhcTc -> Bool +isPatternVar (G.L _ (G.VarPat {})) = True +isPatternVar _ = False -getPatternVarName :: LPat Id -> G.Name -#if __GLASGOW_HASKELL__ >= 800 -getPatternVarName (L _ (G.VarPat (L _ vName))) = G.getName vName +getPatternVarName :: G.LPat Gap.GhcTc -> G.Name +#if __GLASGOW_HASKELL__ >= 806 +getPatternVarName (G.L _ (G.VarPat _ (G.L _ vName))) = G.getName vName +#elif __GLASGOW_HASKELL__ >= 800 +getPatternVarName (G.L _ (G.VarPat (G.L _ vName))) = G.getName vName #else -getPatternVarName (L _ (G.VarPat vName)) = G.getName vName +getPatternVarName (G.L _ (G.VarPat vName)) = G.getName vName #endif getPatternVarName _ = error "This should never happened" -- TODO: Information for a type family case split -getSrcSpanTypeForTypeSplit :: GhcMonad m => G.ModSummary -> Int -> Int -> m (Maybe SplitInfo) -getSrcSpanTypeForTypeSplit _modSum _lineNo _colNo = return Nothing +getSrcSpanTypeForTypeSplit :: G.GhcMonad m => G.TypecheckedModule -> Int -> Int -> m (Maybe SplitInfo) +getSrcSpanTypeForTypeSplit _tcm _lineNo _colNo = return Nothing ---------------------------------------------------------------- -- b. Code for getting the possible constructors -getTyCons :: DynFlags -> PprStyle -> G.Name -> G.Type -> [String] +getTyCons :: G.DynFlags -> PprStyle -> G.Name -> G.Type -> [String] getTyCons dflag style name ty | Just (tyCon, _) <- Ty.splitTyConApp_maybe ty = let name' = showName dflag style name -- Convert name to string in getTyCon dflag style name' tyCon getTyCons dflag style name _ = [showName dflag style name] -- Write cases for one type -getTyCon :: DynFlags -> PprStyle -> String -> Ty.TyCon -> [String] +getTyCon :: G.DynFlags -> PprStyle -> String -> Ty.TyCon -> [String] -- 1. Non-matcheable type constructors getTyCon _ _ name tyCon | isNotMatcheableTyCon tyCon = [name] -- 2. Special cases @@ -152,7 +198,7 @@ isNotMatcheableTyCon ty = Ty.isPrimTyCon ty -- Primitive types, such as Int# || Ty.isFunTyCon ty -- Function types -- Write case for one constructor -getDataCon :: DynFlags -> PprStyle -> String -> Ty.DataCon -> String +getDataCon :: G.DynFlags -> PprStyle -> String -> Ty.DataCon -> String -- 1. Infix constructors getDataCon dflag style vName dcon | Ty.dataConIsInfix dcon = let dName = showName dflag style $ Ty.dataConName dcon @@ -196,7 +242,7 @@ newVarsSpecialSingleton :: String -> Int -> Int -> String newVarsSpecialSingleton v _ 1 = v newVarsSpecialSingleton v start n = newVars v start n -showFieldNames :: DynFlags -> PprStyle -> String -> [G.Name] -> String +showFieldNames :: G.DynFlags -> PprStyle -> String -> [G.Name] -> String showFieldNames _ _ _ [] = "" -- This should never happen showFieldNames dflag style v (x:xs) = let fName = showName dflag style x fAcc = fName ++ " = " ++ v ++ "_" ++ fName @@ -207,11 +253,13 @@ showFieldNames dflag style v (x:xs) = let fName = showName dflag style x ---------------------------------------------------------------- -- c. Code for performing the case splitting -genCaseSplitTextFile :: (MonadIO m, GhcMonad m) => - FilePath -> SplitToTextInfo -> m String -genCaseSplitTextFile file info = liftIO $ do - t <- T.readFile file - return $ getCaseSplitText (T.lines t) info + +genCaseSplitTextFile :: IOish m => + FilePath -> SplitToTextInfo -> GmlT m String +genCaseSplitTextFile file info = + withMappedFile file $ \file' -> liftIO $ do + t <- T.readFile file' + return $ getCaseSplitText (T.lines t) info getCaseSplitText :: [T.Text] -> SplitToTextInfo -> String getCaseSplitText t SplitToTextInfo{ sVarName = sVN, sBindingSpan = sBS @@ -224,7 +272,7 @@ getCaseSplitText t SplitToTextInfo{ sVarName = sVN, sBindingSpan = sBS replaced' = head replaced : map (indentBindingTo sBS) (tail replaced) in T.unpack $ T.intercalate (T.pack "\n") (concat replaced') -getBindingText :: [T.Text] -> SrcSpan -> [T.Text] +getBindingText :: [T.Text] -> G.SrcSpan -> [T.Text] getBindingText t srcSpan = let Just (sl,sc,el,ec) = Gap.getSrcSpan srcSpan lines_ = drop (sl - 1) $ take el t @@ -235,7 +283,7 @@ getBindingText t srcSpan = let (first,rest,last_) = (head lines_, tail $ init lines_, last lines_) in T.drop (sc - 1) first : rest ++ [T.take ec last_] -srcSpanDifference :: SrcSpan -> SrcSpan -> (Int,Int,Int,Int) +srcSpanDifference :: G.SrcSpan -> G.SrcSpan -> (Int,Int,Int,Int) srcSpanDifference b v = let Just (bsl,bsc,_ ,_) = Gap.getSrcSpan b Just (vsl,vsc,vel,vec) = Gap.getSrcSpan v @@ -254,7 +302,7 @@ replaceVarWithTyCon t (vsl,vsc,_,vec) varname tycon = else T.replicate spacesToAdd (T.pack " ") `T.append` line) [0 ..] t -indentBindingTo :: SrcSpan -> [T.Text] -> [T.Text] +indentBindingTo :: G.SrcSpan -> [T.Text] -> [T.Text] indentBindingTo bndLoc binds = let Just (_,sl,_,_) = Gap.getSrcSpan bndLoc indent = (T.replicate (sl - 1) (T.pack " ") `T.append`) diff --git a/GhcMod/Exe/Debug.hs b/GhcMod/Exe/Debug.hs index 2ba7ac556..81db2cc8b 100644 --- a/GhcMod/Exe/Debug.hs +++ b/GhcMod/Exe/Debug.hs @@ -34,12 +34,15 @@ debugInfo = do Options {..} <- options Cradle {..} <- cradle - [ghcPath, ghcPkgPath] <- liftIO $ + mpaths <- liftIO $ case cradleProject of StackProject se -> catMaybes <$> sequence [getStackGhcPath se, getStackGhcPkgPath se] _ -> return ["ghc", "ghc-pkg"] + (ghcPath, ghcPkgPath) <- case mpaths of + [ghc,ghcp] -> return (ghc,ghcp) + _ -> error "pattern match fail" cabal <- case cradleProject of @@ -67,13 +70,16 @@ debugInfo = do stackPaths :: IOish m => GhcModT m [String] stackPaths = do - Cradle { cradleProject = StackProject senv } <- cradle - ghc <- getStackGhcPath senv - ghcPkg <- getStackGhcPkgPath senv - return $ - [ "Stack ghc executable: " ++ show ghc - , "Stack ghc-pkg executable:" ++ show ghcPkg - ] + Cradle { cradleProject = menv } <- cradle + case menv of + StackProject senv -> do + ghc <- getStackGhcPath senv + ghcPkg <- getStackGhcPkgPath senv + return $ + [ "Stack ghc executable: " ++ show ghc + , "Stack ghc-pkg executable:" ++ show ghcPkg + ] + _ -> error "stackPaths:expected a stack project" cabalDebug :: IOish m => FilePath -> GhcModT m [String] cabalDebug ghcPkgPath = do @@ -123,7 +129,7 @@ componentInfo ts = do mdlcs = moduleComponents mcs `zipMap` Set.toList sefnmn candidates = findCandidates $ map snd mdlcs cn = pickComponent candidates - opts <- targetGhcOptions crdl sefnmn + opts <- fst <$> targetGhcOptions crdl sefnmn return $ unlines $ [ "Matching Components:\n" ++ renderGm (nest 4 $ diff --git a/GhcMod/Exe/FillSig.hs b/GhcMod/Exe/FillSig.hs index c8bb9255a..9c7f8cdc0 100644 --- a/GhcMod/Exe/FillSig.hs +++ b/GhcMod/Exe/FillSig.hs @@ -4,15 +4,14 @@ module GhcMod.Exe.FillSig ( sig , refine - , auto + -- , auto ) where import Data.Char (isSymbol) import Data.Function (on) import Data.Functor -import Data.List (find, nub, sortBy) +import Data.List (find, sortBy) import qualified Data.Map as M -import Data.Maybe (catMaybes) import Prelude import Exception (ghandle, SomeException(..)) @@ -20,7 +19,6 @@ import GHC (GhcMonad, Id, ParsedModule(..), TypecheckedModule(..), DynFlags, SrcSpan, Type, GenLocated(L)) import Pretty (($$), text, nest) import qualified GHC as G -import qualified Name as G import Outputable (PprStyle) import qualified Type as Ty import qualified HsBinds as Ty @@ -28,7 +26,7 @@ import qualified Class as Ty import qualified Var as Ty import qualified HsPat as Ty import qualified Language.Haskell.Exts as HE -import Djinn.GHC +-- import Djinn.GHC import qualified GhcMod.Gap as Gap import GhcMod.Convert @@ -51,7 +49,7 @@ import GHC (unLoc) -- Possible signatures we can find: function or instance data SigInfo - = Signature SrcSpan [G.RdrName] (G.HsType G.RdrName) + = Signature SrcSpan [G.RdrName] (G.HsType Gap.GhcPs) | InstanceDecl SrcSpan G.Class | TyFamDecl SrcSpan G.RdrName TyFamType {- True if closed -} [G.RdrName] @@ -115,8 +113,10 @@ getSignature :: GhcMonad m => G.ModSummary -> Int -> Int -> m (Maybe SigInfo) getSignature modSum lineNo colNo = do p@ParsedModule{pm_parsed_source = ps} <- G.parseModule modSum -- Inspect the parse tree to find the signature - case listifyParsedSpans ps (lineNo, colNo) :: [G.LHsDecl G.RdrName] of -#if __GLASGOW_HASKELL__ >= 802 + case listifyParsedSpans ps (lineNo, colNo) :: [G.LHsDecl Gap.GhcPs] of +#if __GLASGOW_HASKELL__ >= 806 + [L loc (G.SigD _ (Ty.TypeSig _ names (G.HsWC _ (G.HsIB _ (L _ ty)))))] -> +#elif __GLASGOW_HASKELL__ >= 802 [L loc (G.SigD (Ty.TypeSig names (G.HsWC _ (G.HsIB _ (L _ ty) _))))] -> #elif __GLASGOW_HASKELL__ >= 800 [L loc (G.SigD (Ty.TypeSig names (G.HsIB _ (G.HsWC _ _ (L _ ty)))))] -> @@ -127,15 +127,19 @@ getSignature modSum lineNo colNo = do #endif -- We found a type signature return $ Just $ Signature loc (map G.unLoc names) ty - [L _ (G.InstD _)] -> do + [L _ (G.InstD {})] -> do -- We found an instance declaration - TypecheckedModule{tm_renamed_source = Just tcs + TypecheckedModule{tm_renamed_source = mtcs ,tm_checked_module_info = minfo} <- G.typecheckModule p - let lst = listifyRenamedSpans tcs (lineNo, colNo) + let lst = case mtcs of + Just tcs -> listifyRenamedSpans tcs (lineNo, colNo) + _ -> error "pattern match fail" case Gap.getClass lst of Just (clsName,loc) -> obtainClassInfo minfo clsName loc _ -> return Nothing -#if __GLASGOW_HASKELL__ >= 802 +#if __GLASGOW_HASKELL__ >= 806 + [L loc (G.TyClD _ (G.FamDecl _ (G.FamilyDecl _ info (L _ name) (G.HsQTvs _ vars) _ _ _)))] -> do +#elif __GLASGOW_HASKELL__ >= 802 [L loc (G.TyClD (G.FamDecl (G.FamilyDecl info (L _ name) (G.HsQTvs _ vars _) _ _ _)))] -> do #elif __GLASGOW_HASKELL__ >= 800 [L loc (G.TyClD (G.FamDecl (G.FamilyDecl info (L _ name) (G.HsQTvs _ vars _) _ _)))] -> do @@ -157,7 +161,11 @@ getSignature modSum lineNo colNo = do G.DataFamily -> Data #endif -#if __GLASGOW_HASKELL__ >= 800 +#if __GLASGOW_HASKELL__ >= 806 + getTyFamVarName x = case x of + L _ (G.UserTyVar _ (G.L _ n)) -> n + L _ (G.KindedTyVar _ (G.L _ n) _) -> n +#elif __GLASGOW_HASKELL__ >= 800 getTyFamVarName x = case x of L _ (G.UserTyVar (G.L _ n)) -> n L _ (G.KindedTyVar (G.L _ n) _) -> n @@ -279,9 +287,11 @@ class FnArgsInfo ty name | ty -> name, name -> ty where getFnName :: DynFlags -> PprStyle -> name -> String getFnArgs :: ty -> [FnArg] -instance FnArgsInfo (G.HsType G.RdrName) (G.RdrName) where +instance FnArgsInfo (G.HsType Gap.GhcPs) (G.RdrName) where getFnName dflag style name = showOccName dflag style $ Gap.occName name -#if __GLASGOW_HASKELL__ >= 800 +#if __GLASGOW_HASKELL__ >= 806 + getFnArgs (G.HsForAllTy _ _ (L _ iTy)) +#elif __GLASGOW_HASKELL__ >= 800 getFnArgs (G.HsForAllTy _ (L _ iTy)) #elif __GLASGOW_HASKELL__ >= 710 getFnArgs (G.HsForAllTy _ _ _ _ (L _ iTy)) @@ -290,11 +300,18 @@ instance FnArgsInfo (G.HsType G.RdrName) (G.RdrName) where #endif = getFnArgs iTy +#if __GLASGOW_HASKELL__ >= 806 + getFnArgs (G.HsParTy _ (L _ iTy)) = getFnArgs iTy + getFnArgs (G.HsFunTy _ (L _ lTy) (L _ rTy)) = +#else getFnArgs (G.HsParTy (L _ iTy)) = getFnArgs iTy getFnArgs (G.HsFunTy (L _ lTy) (L _ rTy)) = +#endif (if fnarg lTy then FnArgFunction else FnArgNormal):getFnArgs rTy where fnarg ty = case ty of -#if __GLASGOW_HASKELL__ >= 800 +#if __GLASGOW_HASKELL__ >= 806 + (G.HsForAllTy _ _ (L _ iTy)) -> +#elif __GLASGOW_HASKELL__ >= 800 (G.HsForAllTy _ (L _ iTy)) -> #elif __GLASGOW_HASKELL__ >= 710 (G.HsForAllTy _ _ _ _ (L _ iTy)) -> @@ -303,8 +320,12 @@ instance FnArgsInfo (G.HsType G.RdrName) (G.RdrName) where #endif fnarg iTy +#if __GLASGOW_HASKELL__ >= 806 + (G.HsParTy _ (L _ iTy)) -> fnarg iTy +#else (G.HsParTy (L _ iTy)) -> fnarg iTy - (G.HsFunTy _ _) -> True +#endif + (G.HsFunTy {} ) -> True _ -> False getFnArgs _ = [] @@ -401,7 +422,9 @@ findVar -> m (Maybe (SrcSpan, String, Type, Bool)) findVar dflag style tcm tcs lineNo colNo = case lst of -#if __GLASGOW_HASKELL__ >= 800 +#if __GLASGOW_HASKELL__ >= 806 + e@(L _ (G.HsVar _ (L _ i))):others -> do +#elif __GLASGOW_HASKELL__ >= 800 e@(L _ (G.HsVar (L _ i))):others -> do #else e@(L _ (G.HsVar i)):others -> do @@ -415,13 +438,17 @@ findVar dflag style tcm tcs lineNo colNo = name = getFnName dflag style i -- If inside an App, we need parenthesis b = case others of +#if __GLASGOW_HASKELL__ >= 806 + L _ (G.HsApp _ (L _ a1) (L _ a2)):_ -> +#else L _ (G.HsApp (L _ a1) (L _ a2)):_ -> +#endif isSearchedVar i a1 || isSearchedVar i a2 _ -> False _ -> return Nothing _ -> return Nothing where - lst :: [G.LHsExpr Id] + lst :: [G.LHsExpr Gap.GhcTc] lst = sortBy (cmp `on` G.getLoc) $ listifySpans tcs (lineNo, colNo) infinitePrefixSupply :: String -> [String] @@ -432,8 +459,10 @@ doParen :: Bool -> String -> String doParen False s = s doParen True s = if ' ' `elem` s then '(':s ++ ")" else s -isSearchedVar :: Id -> G.HsExpr Id -> Bool -#if __GLASGOW_HASKELL__ >= 800 +isSearchedVar :: Id -> G.HsExpr Gap.GhcTc -> Bool +#if __GLASGOW_HASKELL__ >= 806 +isSearchedVar i (G.HsVar _ (L _ i2)) = i == i2 +#elif __GLASGOW_HASKELL__ >= 800 isSearchedVar i (G.HsVar (L _ i2)) = i == i2 #else isSearchedVar i (G.HsVar i2) = i == i2 @@ -444,7 +473,8 @@ isSearchedVar _ _ = False ---------------------------------------------------------------- -- REFINE AUTOMATICALLY ---------------------------------------------------------------- - +{- +This function needs djinn, which does not seem to be supported any more auto :: IOish m => FilePath -- ^ A target file. -> Int -- ^ Line number. @@ -516,10 +546,10 @@ tyThingsToInfo (G.AnId i : xs) = tyThingsToInfo (_:xs) = tyThingsToInfo xs -- Find the Id of the function and the pattern where the hole is located -getPatsForVariable :: G.TypecheckedSource -> (Int,Int) -> (Id, [Ty.LPat Id]) +getPatsForVariable :: G.TypecheckedSource -> (Int,Int) -> (Id, [Ty.LPat Gap.GhcTc]) getPatsForVariable tcs (lineNo, colNo) = let (L _ bnd:_) = sortBy (cmp `on` G.getLoc) $ - listifySpans tcs (lineNo, colNo) :: [G.LHsBind Id] + listifySpans tcs (lineNo, colNo) :: [G.LHsBind Gap.GhcTc] in case bnd of G.PatBind { Ty.pat_lhs = L ploc pat } -> case pat of Ty.ConPatIn (L _ i) _ -> (i, [L ploc pat]) @@ -527,48 +557,74 @@ getPatsForVariable tcs (lineNo, colNo) = G.FunBind { Ty.fun_id = L _ funId } -> let m = sortBy (cmp `on` G.getLoc) $ listifySpans tcs (lineNo, colNo) #if __GLASGOW_HASKELL__ >= 708 - :: [G.LMatch Id (G.LHsExpr Id)] + :: [G.LMatch Gap.GhcTc (G.LHsExpr Gap.GhcTc)] #else - :: [G.LMatch Id] + :: [G.LMatch Gap.GhcTc] #endif -#if __GLASGOW_HASKELL__ >= 710 +#if __GLASGOW_HASKELL__ >= 804 + (L _ (G.Match _ pats _):_) = m +#elif __GLASGOW_HASKELL__ >= 710 (L _ (G.Match _ pats _ _):_) = m #else (L _ (G.Match pats _ _):_) = m #endif in (funId, pats) _ -> (error "This should never happen", []) +-} -getBindingsForPat :: Ty.Pat Id -> M.Map G.Name Type -#if __GLASGOW_HASKELL__ >= 800 +getBindingsForPat :: Ty.Pat Gap.GhcTc -> M.Map G.Name Type +#if __GLASGOW_HASKELL__ >= 806 +getBindingsForPat (Ty.VarPat _ (L _ i)) = M.singleton (G.getName i) (Ty.varType i) +#elif __GLASGOW_HASKELL__ >= 800 getBindingsForPat (Ty.VarPat (L _ i)) = M.singleton (G.getName i) (Ty.varType i) #else getBindingsForPat (Ty.VarPat i) = M.singleton (G.getName i) (Ty.varType i) #endif +#if __GLASGOW_HASKELL__ >= 806 +getBindingsForPat (Ty.LazyPat _ (L _ l)) = getBindingsForPat l +getBindingsForPat (Ty.BangPat _ (L _ b)) = getBindingsForPat b +getBindingsForPat (Ty.AsPat _ (L _ a) (L _ i)) = +#else getBindingsForPat (Ty.LazyPat (L _ l)) = getBindingsForPat l getBindingsForPat (Ty.BangPat (L _ b)) = getBindingsForPat b getBindingsForPat (Ty.AsPat (L _ a) (L _ i)) = +#endif M.insert (G.getName a) (Ty.varType a) (getBindingsForPat i) -#if __GLASGOW_HASKELL__ >= 708 +#if __GLASGOW_HASKELL__ >= 806 +getBindingsForPat (Ty.ListPat _ l) = + M.unions $ map (\(L _ i) -> getBindingsForPat i) l +#elif __GLASGOW_HASKELL__ >= 708 getBindingsForPat (Ty.ListPat l _ _) = M.unions $ map (\(L _ i) -> getBindingsForPat i) l #else getBindingsForPat (Ty.ListPat l _) = M.unions $ map (\(L _ i) -> getBindingsForPat i) l #endif +#if __GLASGOW_HASKELL__ >= 806 +getBindingsForPat (Ty.TuplePat _ l _) = + M.unions $ map (\(L _ i) -> getBindingsForPat i) l +#else getBindingsForPat (Ty.TuplePat l _ _) = M.unions $ map (\(L _ i) -> getBindingsForPat i) l +#endif +#if __GLASGOW_HASKELL__ < 806 getBindingsForPat (Ty.PArrPat l _) = M.unions $ map (\(L _ i) -> getBindingsForPat i) l +#endif +#if __GLASGOW_HASKELL__ >= 806 +getBindingsForPat (Ty.ViewPat _ _ (L _ i)) = getBindingsForPat i +getBindingsForPat (Ty.SigPat _ (L _ i)) = getBindingsForPat i +#else getBindingsForPat (Ty.ViewPat _ (L _ i) _) = getBindingsForPat i getBindingsForPat (Ty.SigPatIn (L _ i) _) = getBindingsForPat i getBindingsForPat (Ty.SigPatOut (L _ i) _) = getBindingsForPat i +#endif getBindingsForPat (Ty.ConPatIn (L _ i) d) = M.insert (G.getName i) (Ty.varType i) (getBindingsForRecPat d) getBindingsForPat (Ty.ConPatOut { Ty.pat_args = d }) = getBindingsForRecPat d getBindingsForPat _ = M.empty -getBindingsForRecPat :: Ty.HsConPatDetails Id -> M.Map G.Name Type +getBindingsForRecPat :: Ty.HsConPatDetails Gap.GhcTc -> M.Map G.Name Type #if __GLASGOW_HASKELL__ >= 800 getBindingsForRecPat (G.PrefixCon args) = #else diff --git a/GhcMod/Exe/Test.hs b/GhcMod/Exe/Test.hs index d3901270f..e3fd7df5d 100644 --- a/GhcMod/Exe/Test.hs +++ b/GhcMod/Exe/Test.hs @@ -22,11 +22,18 @@ test f = runGmlT' [Left f] (fmap setHscInterpreted . deferErrors) $ do mg <- getModuleGraph root <- cradleRootDir <$> cradle f' <- makeRelative root <$> liftIO (canonicalizePath f) +#if __GLASGOW_HASKELL__ >= 804 + let Just ms = find ((==Just f') . ml_hs_file . ms_location) (mgModSummaries mg) +#else let Just ms = find ((==Just f') . ml_hs_file . ms_location) mg +#endif mdl = ms_mod ms mn = moduleName mdl - Just mi <- getModuleInfo mdl + mmi <- getModuleInfo mdl + mi <- case mmi of + Just mi -> return mi + _ -> error "pattern match fail" let exs = map (occNameString . getOccName) $ modInfoExports mi cqs = filter ("prop_" `isPrefixOf`) exs diff --git a/core/GhcMod/SrcUtils.hs b/GhcMod/SrcUtils.hs similarity index 69% rename from core/GhcMod/SrcUtils.hs rename to GhcMod/SrcUtils.hs index 30999f3f1..4a5f5be4b 100644 --- a/core/GhcMod/SrcUtils.hs +++ b/GhcMod/SrcUtils.hs @@ -1,5 +1,7 @@ -- TODO: remove CPP once Gap(ed) {-# LANGUAGE CPP, TupleSections, FlexibleInstances, Rank2Types #-} +{-# LANGUAGE TypeFamilies #-} +{-# LANGUAGE ScopedTypeVariables #-} {-# OPTIONS_GHC -fno-warn-orphans #-} module GhcMod.SrcUtils where @@ -9,12 +11,13 @@ import CoreUtils (exprType) import Data.Generics import Data.Maybe import Data.Ord as O -import GHC (LHsExpr, LPat, Id, DynFlags, SrcSpan, Type, Located, ParsedSource, RenamedSource, TypecheckedSource, GenLocated(L)) -import Var (Var) +import GHC (LHsExpr, LPat, DynFlags, SrcSpan, Type, Located, ParsedSource, RenamedSource, TypecheckedSource, GenLocated(L)) import qualified GHC as G import qualified Var as G import qualified Type as G +#if __GLASGOW_HASKELL__ < 804 import GHC.SYB.Utils +#endif import GhcMonad import qualified Language.Haskell.Exts as HE import GhcMod.Doc @@ -31,42 +34,63 @@ import qualified Data.Map as M ---------------------------------------------------------------- -instance HasType (LHsExpr Id) where +instance HasType (LHsExpr GhcTc) where getType tcm e = do hs_env <- G.getSession mbe <- liftIO $ Gap.deSugar tcm e hs_env return $ (G.getLoc e, ) <$> CoreUtils.exprType <$> mbe -instance HasType (LPat Id) where +instance HasType (LPat GhcTc) where getType _ (G.L spn pat) = return $ Just (spn, hsPatType pat) ---------------------------------------------------------------- +#if __GLASGOW_HASKELL__ >= 804 +-- | Stores mapping from monomorphic to polymorphic types +type CstGenQS = M.Map (G.IdP GhcTc) Type +-- | Generic type to simplify SYB definition +type CstGenQT m a = a GhcTc -> CstGenQS -> (m [(SrcSpan, Type)], CstGenQS) +#else -- | Stores mapping from monomorphic to polymorphic types -type CstGenQS = M.Map Var Type +type CstGenQS = M.Map G.Var Type -- | Generic type to simplify SYB definition -type CstGenQT a = forall m. GhcMonad m => a Id -> CstGenQS -> (m [(SrcSpan, Type)], CstGenQS) +type CstGenQT a = forall m. GhcMonad m => a G.Id -> CstGenQS -> (m [(SrcSpan, Type)], CstGenQS) +#endif + +collectSpansTypes :: (GhcMonad m) => Bool -> G.TypecheckedModule -> (Int,Int) -> m [(SrcSpan, Type)] +collectSpansTypes withConstraints tcs lc = collectSpansTypes' withConstraints tcs (`G.spans` lc) + +collectAllSpansTypes :: (GhcMonad m) => Bool -> G.TypecheckedModule -> m [(SrcSpan, Type)] +collectAllSpansTypes withConstraints tcs = collectSpansTypes' withConstraints tcs (const True) -collectSpansTypes :: (GhcMonad m) => Bool -> G.TypecheckedModule -> (Int, Int) -> m [(SrcSpan, Type)] -collectSpansTypes withConstraints tcs lc = +collectSpansTypes' :: forall m. (GhcMonad m) => Bool -> G.TypecheckedModule -> (SrcSpan -> Bool) -> m [(SrcSpan, Type)] +-- collectSpansTypes' :: forall m.(GhcMonad m) => Bool -> G.TypecheckedModule -> (Int, Int) -> m [(SrcSpan, Type)] +collectSpansTypes' withConstraints tcs f = -- This walks AST top-down, left-to-right, while carrying CstGenQS down the tree -- (but not left-to-right) +#if __GLASGOW_HASKELL__ >= 804 + everythingWithContext M.empty (liftM2 (++)) +#else everythingStagedWithContext TypeChecker M.empty (liftM2 (++)) (return []) +#endif ((return [],) - `mkQ` (hsBind :: CstGenQT G.LHsBind) -- matches on binds - `extQ` (genericCT :: CstGenQT G.LHsExpr) -- matches on expressions - `extQ` (genericCT :: CstGenQT G.LPat) -- matches on patterns + `mkQ` (hsBind :: G.LHsBind GhcTc -> CstGenQS -> (m [(SrcSpan, Type)], CstGenQS)) -- matches on binds + `extQ` (genericCT :: G.LHsExpr GhcTc -> CstGenQS -> (m [(SrcSpan, Type)], CstGenQS)) -- matches on expressions + `extQ` (genericCT :: G.LPat GhcTc -> CstGenQS -> (m [(SrcSpan, Type)], CstGenQS)) -- matches on patterns + ) (G.tm_typechecked_source tcs) where -- Helper function to insert mapping into CstGenQS insExp x = M.insert (G.abe_mono x) (G.varType $ G.abe_poly x) -- If there is AbsBinds here, insert mapping into CstGenQS if needed + hsBind (L _ G.AbsBinds{abs_exports = es'}) s | withConstraints = (return [], foldr insExp s es') | otherwise = (return [], s) -#if __GLASGOW_HASKELL__ >= 800 +#if __GLASGOW_HASKELL__ >= 804 +#elif __GLASGOW_HASKELL__ >= 800 -- TODO: move to Gap -- Note: this deals with bindings with explicit type signature, e.g. -- double :: Num a => a -> a @@ -83,20 +107,33 @@ collectSpansTypes withConstraints tcs lc = -- Otherwise, it's the same as other cases hsBind x s = genericCT x s -- Generic SYB function to get type + genericCT :: forall b . (Data (b GhcTc), HasType (Located (b GhcTc))) + => Located (b GhcTc) -> CstGenQS -> (m [(SrcSpan, Type)], CstGenQS) genericCT x s | withConstraints = (maybe [] (uncurry $ constrainedType (collectBinders x) s) <$> getType' x, s) | otherwise = (maybeToList <$> getType' x, s) +#if __GLASGOW_HASKELL__ >= 804 + -- Collects everything with Id from LHsBind, LHsExpr, or LPat + collectBinders :: Data a => a -> [G.IdP GhcTc] + collectBinders = listify (const True) +#else -- Collects everything with Id from LHsBind, LHsExpr, or LPat - collectBinders :: Data a => a -> [Id] + collectBinders :: Data a => a -> [G.Id] collectBinders = listifyStaged TypeChecker (const True) +#endif -- Gets monomorphic type with location + getType' :: forall t . (HasType (Located t)) => Located t -> m (Maybe (SrcSpan, Type)) getType' x@(L spn _) - | G.isGoodSrcSpan spn && spn `G.spans` lc + | G.isGoodSrcSpan spn && f spn = getType tcs x | otherwise = return Nothing -- Gets constrained type - constrainedType :: [Var] -- ^ Binders in expression, i.e. anything with Id +#if __GLASGOW_HASKELL__ >= 804 + constrainedType :: [G.IdP GhcTc] -- ^ Binders in expression, i.e. anything with Id +#else + constrainedType :: [G.Var] -- ^ Binders in expression, i.e. anything with Id +#endif -> CstGenQS -- ^ Map from Id to polymorphic type -> SrcSpan -- ^ extent of expression, copied to result -> Type -- ^ monomorphic type @@ -112,10 +149,17 @@ collectSpansTypes withConstraints tcs lc = build x | Just cti <- x `M.lookup` s = let (preds', ctt) = getPreds cti +#if __GLASGOW_HASKELL__ >= 804 + -- list of type variables in monomorphic type + vts = listify G.isTyVar $ G.varType x + -- list of type variables in polymorphic type + tvm = listify G.isTyVarTy ctt +#else -- list of type variables in monomorphic type vts = listifyStaged TypeChecker G.isTyVar $ G.varType x -- list of type variables in polymorphic type tvm = listifyStaged TypeChecker G.isTyVarTy ctt +#endif in Just (preds', zip vts tvm) | otherwise = Nothing -- list of constraints @@ -138,22 +182,36 @@ collectSpansTypes withConstraints tcs lc = | otherwise = ([], x) listifySpans :: Typeable a => TypecheckedSource -> (Int, Int) -> [Located a] +#if __GLASGOW_HASKELL__ >= 804 +listifySpans tcs lc = listify p tcs +#else listifySpans tcs lc = listifyStaged TypeChecker p tcs +#endif where p (L spn _) = G.isGoodSrcSpan spn && spn `G.spans` lc listifyParsedSpans :: Typeable a => ParsedSource -> (Int, Int) -> [Located a] +#if __GLASGOW_HASKELL__ >= 804 +listifyParsedSpans pcs lc = listify p pcs +#else listifyParsedSpans pcs lc = listifyStaged Parser p pcs +#endif where p (L spn _) = G.isGoodSrcSpan spn && spn `G.spans` lc listifyRenamedSpans :: Typeable a => RenamedSource -> (Int, Int) -> [Located a] +#if __GLASGOW_HASKELL__ >= 804 +listifyRenamedSpans pcs lc = listify p pcs +#else listifyRenamedSpans pcs lc = listifyStaged Renamer p pcs +#endif where p (L spn _) = G.isGoodSrcSpan spn && spn `G.spans` lc +#if __GLASGOW_HASKELL__ < 804 listifyStaged :: Typeable r => Stage -> (r -> Bool) -> GenericQ [r] listifyStaged s p = everythingStaged s (++) [] ([] `mkQ` (\x -> [x | p x])) +#endif cmp :: SrcSpan -> SrcSpan -> Ordering cmp a b diff --git a/cabal.project b/cabal.project index 0d3711349..834156e56 100644 --- a/cabal.project +++ b/cabal.project @@ -1,2 +1,10 @@ packages: . ./core + ../cabal-helper + + +-- source-repository-package +-- type: git +-- location: https://github.com/alanz/cabal-helper +-- tag: 3d13936af2f8fa565e0a64431c6ed309b91b2695 + diff --git a/core/GhcMod/CabalHelper.hs b/core/GhcMod/CabalHelper.hs index 1e82c39e9..c15663a6b 100644 --- a/core/GhcMod/CabalHelper.hs +++ b/core/GhcMod/CabalHelper.hs @@ -15,6 +15,9 @@ -- along with this program. If not, see . {-# LANGUAGE CPP #-} +{-# LANGUAGE RankNTypes #-} +{-# LANGUAGE ScopedTypeVariables #-} +{-# LANGUAGE TypeFamilies #-} module GhcMod.CabalHelper ( getComponents , getGhcMergedPkgOptions @@ -22,18 +25,27 @@ module GhcMod.CabalHelper , prepareCabalHelper , withAutogen , withCabal + , withProjSetup + + , runCHQuery + -- , packageId ) where import Control.Applicative import Control.Monad import Control.Category ((.)) +import Data.Dynamic (toDyn, fromDynamic, Dynamic) +import Data.List.NonEmpty ( NonEmpty(..), toList) +import qualified Data.Map as Map import Data.Maybe import Data.Monoid +import Data.Typeable (Typeable) import Data.Version import Data.Binary (Binary) import Data.Traversable import Distribution.Helper hiding (Programs(..)) import qualified Distribution.Helper as CH +import GhcMod.Cradle import qualified GhcMod.Types as T import GhcMod.Types import GhcMod.Monad.Types @@ -54,24 +66,26 @@ import Paths_ghc_mod_core as GhcMod -- access home modules getGhcMergedPkgOptions :: (Applicative m, IOish m, Gm m) => m [GHCOption] -getGhcMergedPkgOptions = chCached $ \distdir -> Cached { - cacheLens = Just (lGmcMergedPkgOptions . lGmCaches), - cacheFile = mergedPkgOptsCacheFile distdir, - cachedAction = \_tcf (_progs, _projdir, _ver) _ma -> do - opts <- runCHQuery ghcMergedPkgOptions - return ([setupConfigPath distdir], opts) - } +getGhcMergedPkgOptions = undefined +-- getGhcMergedPkgOptions = chCached $ \distdir -> Cached { +-- cacheLens = Just (lGmcMergedPkgOptions . lGmCaches), +-- cacheFile = mergedPkgOptsCacheFile distdir, +-- cachedAction = \_tcf (_progs, _projdir, _ver) _ma -> do +-- opts <- runCHQuery ghcMergedPkgOptions +-- return ([setupConfigPath distdir], opts) +-- } getCabalPackageDbStack :: (IOish m, Gm m) => m [GhcPkgDb] -getCabalPackageDbStack = chCached $ \distdir -> Cached { - cacheLens = Just (lGmcPackageDbStack . lGmCaches), - cacheFile = pkgDbStackCacheFile distdir, - cachedAction = \_tcf (_progs, _projdir, _ver) _ma -> do - crdl <- cradle - dbs <- map chPkgToGhcPkg <$> - runCHQuery packageDbStack - return ([setupConfigFile crdl, sandboxConfigFile crdl], dbs) - } +getCabalPackageDbStack = undefined +-- getCabalPackageDbStack = chCached $ \distdir -> Cached { +-- cacheLens = Just (lGmcPackageDbStack . lGmCaches), +-- cacheFile = pkgDbStackCacheFile distdir, +-- cachedAction = \_tcf (_progs, _projdir, _ver) _ma -> do +-- crdl <- cradle +-- dbs <- map chPkgToGhcPkg <$> +-- runCHQuery packageDbStack +-- return ([setupConfigFile crdl, sandboxConfigFile crdl], dbs) +-- } chPkgToGhcPkg :: ChPkgDb -> GhcPkgDb chPkgToGhcPkg ChPkgGlobal = GlobalDb @@ -83,56 +97,183 @@ chPkgToGhcPkg (ChPkgSpecific f) = PackageDb f -- -- The Component\'s 'gmcHomeModuleGraph' will be empty and has to be resolved by -- 'resolveGmComponents'. -getComponents :: (Applicative m, IOish m, Gm m) - => m [GmComponent 'GMCRaw ChEntrypoint] -getComponents = chCached $ \distdir -> Cached { - cacheLens = Just (lGmcComponents . lGmCaches), - cacheFile = cabalHelperCacheFile distdir, - cachedAction = \ _tcf (_progs, _projdir, _ver) _ma -> do - cs <- runCHQuery $ components $ - GmComponent mempty - CH.<$> ghcOptions - CH.<.> ghcPkgOptions - CH.<.> ghcSrcOptions - CH.<.> ghcLangOptions - CH.<.> entrypoints - CH.<.> entrypoints - CH.<.> sourceDirs - return ([setupConfigPath distdir], cs) - } - -getQueryEnv :: (IOish m, GmOut m, GmEnv m) => m QueryEnv +getComponents :: (Applicative m, IOish m, Gm m, Typeable pt) + => ProjSetup pt -> m [GmComponent 'GMCRaw ChEntrypoint] +getComponents ps = do + let + doComp :: (ChComponentName, ChComponentInfo) -> GmComponent 'GMCRaw ChEntrypoint + doComp (cn,ci) = + GmComponent + { gmcHomeModuleGraph = mempty + , gmcGhcOpts = ciGhcOptions ci + , gmcGhcPkgOpts = ciGhcPkgOptions ci + , gmcGhcSrcOpts = ciGhcSrcOptions ci + , gmcGhcLangOpts = ciGhcLangOptions ci + , gmcRawEntrypoints = ciEntrypoints ci + , gmcEntrypoints = ciEntrypoints ci + , gmcSourceDirs = ciSourceDirs ci + , gmcName = ciComponentName ci + , gmcNeedsBuildOutput = ciNeedsBuildOutput ci + } + foo :: UnitInfo -> [GmComponent 'GMCRaw ChEntrypoint] + foo ui = map doComp $ Map.toList $ uiComponents ui + + ff <- runCHQuery ps $ allUnits foo + let cs = concat $ toList ff + return cs + +-- getComponents = chCached $ \distdir -> Cached { +-- cacheLens = Just (lGmcComponents . lGmCaches), +-- cacheFile = cabalHelperCacheFile distdir, +-- cachedAction = \ _tcf (_progs, _projdir, _ver) _ma -> do +-- cs <- runCHQuery $ components $ +-- GmComponent mempty +-- CH.<$> ghcOptions +-- CH.<.> ghcPkgOptions +-- CH.<.> ghcSrcOptions +-- CH.<.> ghcLangOptions +-- CH.<.> entrypoints +-- CH.<.> entrypoints +-- CH.<.> sourceDirs +-- return ([setupConfigPath distdir], cs) +-- } + +-- getQueryEnv :: forall m pt. (IOish m, GmOut m, GmEnv m) => m (Maybe ( QueryEnv pt)) +getQueryEnv :: (IOish m, GmOut m, GmEnv m) => m (QueryEnv pt) +getQueryEnv = undefined + {- getQueryEnv = do crdl <- cradle progs <- patchStackPrograms crdl =<< (optPrograms <$> options) readProc <- gmReadProcess - let projdir = cradleRootDir crdl - distdir = projdir cradleDistDir crdl - return (mkQueryEnv projdir distdir) { - qeReadProcess = readProc - , qePrograms = helperProgs progs - } - -runCHQuery :: (IOish m, GmOut m, GmEnv m) => Query m b -> m b -runCHQuery a = do - qe <- getQueryEnv - runQuery qe a + case cradleCabalFile crdl of + Nothing -> return Nothing + Just cabalFile -> do + let + distdirCradle = cradleDistDir crdl + (projdir,distdir) = case cradleProject crdl of + CabalProject -> (ProjLocCabalFile cabalFile,DistDirV1 distdirCradle) + CabalNewProject -> (ProjLocCabalFile cabalFile,DistDirV2 distdirCradle) + SandboxProject -> (ProjLocCabalFile cabalFile,DistDirV1 distdirCradle) + PlainProject -> (ProjLocCabalFile cabalFile,DistDirV1 distdirCradle) + StackProject env -> (ProjLocCabalFile cabalFile,DistDirStack distdirCradle) + qe <- liftIO $ mkQueryEnv projdir distdir + return (Just qe) + -- let + -- (projdir,distdir) = case cradleProject crdl of + -- CabalProject -> (ProjLocCabalFile cabalFile,DisDirV1 distdir) + -- CabalNewProject -> (ProjLocCabalFile cabalFile,DisDirV1 distdir) + -- SandboxProject -> (ProjLocCabalFile cabalFile,DisDirV1 distdir) + -- PlainProject -> (ProjLocCabalFile cabalFile,DisDirV1 distdir) + -- StackProject env -> (ProjLocCabalFile cabalFile,DisDirV1 distdir) + -- return (mkQueryEnv projdir distdir) { + -- qeReadProcess = readProc + -- , qePrograms = helperProgs progs + -- } + {- + let projdir = takeDirectory cabal_file + qe <- mkQueryEnv + (psProjDir cabal_file) + (psDistDir projdir) + +-} +-- getQueryEnv = do +-- crdl <- cradle +-- progs <- patchStackPrograms crdl =<< (optPrograms <$> options) +-- readProc <- gmReadProcess +-- let projdir = cradleRootDir crdl +-- distdir = projdir cradleDistDir crdl +-- return (mkQueryEnv projdir distdir) { +-- qeReadProcess = readProc +-- , qePrograms = helperProgs progs +-- } +-} + +runCHQuery :: (IOish m, GmOut m, GmEnv m, Typeable pt) + => ProjSetup (pt :: ProjType) -> Query (pt :: ProjType) b -> m b +runCHQuery ps a = do + crdl <- cradle + progs <- patchStackPrograms crdl =<< (optPrograms <$> options) + readProc <- gmReadProcess + case cradleCabalFile crdl of + Nothing -> error "runCHQuery 1" + Just cabalFile -> runProjSetup ps cabalFile (\qe -> runQuery a qe) +-- --------------------------------------------------------------------- -prepareCabalHelper :: (IOish m, GmEnv m, GmOut m, GmLog m) => m () -prepareCabalHelper = do +runProjSetup :: (IOish m, GmOut m, GmEnv m, Typeable pt) + => ProjSetup (pt :: ProjType) -> FilePath -> (QueryEnv (pt :: ProjType) -> IO a) -> m a +runProjSetup ps cabalFile f = do + let projdir = takeDirectory cabalFile + crdl <- cradle + -- progs <- patchStackPrograms crdl =<< (optPrograms <$> options) + -- readProc <- gmReadProcess + -- qeBare <- liftIO $ mkQueryEnv + -- (psProjDir ps $ cabalFile) + -- (psDistDir ps $ projdir) + -- let qe = qeBare + -- -- { qeReadProcess = \_ -> readProc + -- -- , qePrograms = helperProgs progs + -- -- } + -- r <- liftIO $ f qe + -- return r + r <- case cradleCabalFile crdl of + Nothing -> error "runProjSetup:Nothing" + Just cabalFile -> do + runF ps (cradleQueryEnv crdl) f + -- case cradleProject crdl of + -- CabalProject -> runF ps (cradleQueryEnv crdl) f + -- CabalNewProject -> liftIO $ f (fromJust $ cradleQueryEnvV2 crdl) + -- SandboxProject -> liftIO $ f (fromJust $ cradleQueryEnvV1 crdl) + -- StackProject _env -> liftIO $ f (fromJust $ cradleQueryEnvSt crdl) + -- PlainProject -> error $ "runProjSetup:PlainProject" + return r + +runF :: (MonadIO m, Typeable pt) => ProjSetup (pt :: ProjType) -> (Maybe Dynamic) -> (QueryEnv (pt :: ProjType) -> IO a) -> m a +runF _ Nothing _ = error "CabalHelper.runF" +runF _ (Just dqe) f = liftIO $ f qe + where + qe = case fromDynamic dqe of + Nothing -> error "CabalHelper.runF 2" + Just qe -> qe + +-- --------------------------------------------------------------------- + +withProjSetup :: (IOish m, GmOut m, GmEnv m) => (forall pt . Typeable pt => ProjSetup pt -> m a) -> m (Maybe a) +withProjSetup f = do + crdl <- cradle + progs <- patchStackPrograms crdl =<< (optPrograms <$> options) + readProc <- gmReadProcess + case cradleCabalFile crdl of + Nothing -> return Nothing + Just cabalFile -> do + -- liftIO $ putStrLn $ "withProjSetup:project=" ++ show (cradleProject crdl) + case cradleProject crdl of + CabalProject -> Just <$> f oldBuild + CabalNewProject -> Just <$> f newBuild + SandboxProject -> Just <$> f oldBuild + StackProject _env -> Just <$> f stackBuild + PlainProject -> return Nothing + +-- --------------------------------------------------------------------- + +prepareCabalHelper :: (IOish m, GmEnv m, GmOut m, GmLog m, Typeable pt) => ProjSetup (pt :: ProjType) -> m () +prepareCabalHelper ps = do crdl <- cradle - when (isCabalHelperProject $ cradleProject crdl) $ - withCabal $ prepare =<< getQueryEnv + when (isCabalHelperProject $ cradleProject crdl) $ do + qe <- getQueryEnv + withCabal ps $ liftIO (prepare qe) -withAutogen :: (IOish m, GmEnv m, GmOut m, GmLog m) => m a -> m a -withAutogen action = do - gmLog GmDebug "" $ strDoc $ "making sure autogen files exist" +withAutogen :: (IOish m, GmEnv m, GmOut m, GmLog m, Typeable pt) => ProjSetup pt -> m a -> m a +withAutogen ps action = do + gmLog GmDebug "" $ strDoc "making sure autogen files exist" crdl <- cradle let projdir = cradleRootDir crdl distdir = projdir cradleDistDir crdl - (pkgName', _) <- runCHQuery packageId + unit :| _ <- runCHQuery ps projectUnits + unitInfo <- runCHQuery ps $ unitInfo unit + let (pkgName',_) = uiPackageId unitInfo mCabalFile <- liftIO $ timeFile `traverse` cradleCabalFile crdl mCabalMacroHeader <- liftIO $ timeMaybe (distdir macrosHeaderPath) @@ -140,18 +281,19 @@ withAutogen action = do when (mCabalMacroHeader < mCabalFile || mCabalPathsModule < mCabalFile) $ do gmLog GmDebug "" $ strDoc $ "autogen files out of sync" - writeAutogen + writeAutogen unit action where - writeAutogen = do - gmLog GmDebug "" $ strDoc $ "writing Cabal autogen files" - writeAutogenFiles =<< getQueryEnv + writeAutogen unit = do + gmLog GmDebug "" $ strDoc "writing Cabal autogen files" + runCHQuery ps $ writeAutogenFiles unit +-- --------------------------------------------------------------------- -withCabal :: (IOish m, GmEnv m, GmOut m, GmLog m) => m a -> m a -withCabal action = do +withCabal :: (IOish m, GmEnv m, GmOut m, GmLog m, Typeable pt) => ProjSetup (pt :: ProjType) -> m a -> m a +withCabal ps action = do crdl <- cradle mCabalFile <- liftIO $ timeFile `traverse` cradleCabalFile crdl mCabalConfig <- liftIO $ timeMaybe (setupConfigFile crdl) @@ -162,9 +304,13 @@ withCabal action = do cusPkgDb <- getCustomPkgDbStack (flgs, pkgDbStackOutOfSync) <- do if haveSetupConfig - then runCHQuery $ do - flgs <- nonDefaultConfigFlags - pkgDb <- map chPkgToGhcPkg <$> packageDbStack + then runCHQuery ps $ do + unit :| _ <- projectUnits + ui <- unitInfo unit + -- flgs <- nonDefaultConfigFlags + let flgs = uiNonDefaultConfigFlags ui + -- pkgDb <- map chPkgToGhcPkg <$> packageDbStack + let pkgDb = map chPkgToGhcPkg (uiPackageDbStack ui) return (flgs, fromMaybe False $ (pkgDb /=) <$> cusPkgDb) else return ([], False) @@ -185,7 +331,10 @@ withCabal action = do case proj of CabalProject -> do gmLog GmDebug "" $ strDoc "reconfiguring Cabal project" - cabalReconfigure (optPrograms opts) crdl flgs + cabalReconfigure "configure" (optPrograms opts) crdl flgs + CabalNewProject -> do + gmLog GmDebug "" $ strDoc "reconfiguring Cabal new-build project" + cabalReconfigure "new-configure" (optPrograms opts) crdl flgs StackProject {} -> do gmLog GmDebug "" $ strDoc "reconfiguring Stack project" -- TODO: we could support flags for stack too, but it seems @@ -202,7 +351,7 @@ withCabal action = do action where - cabalReconfigure progs crdl flgs = do + cabalReconfigure cmd progs crdl flgs = do readProc <- gmReadProcess withDirectory_ (cradleRootDir crdl) $ do cusPkgStack <- maybe [] ((PackageDb "clear"):) <$> getCustomPkgDbStack @@ -220,7 +369,7 @@ withCabal action = do toFlag (f, False) = '-':f flagOpt = ["--flags", unwords $ map toFlag flgs] - liftIO $ void $ readProc (T.cabalProgram progs) ("configure":progOpts) "" + liftIO $ void $ readProc (T.cabalProgram progs) (cmd:progOpts) "" stackReconfigure deps crdl progs = do withDirectory_ (cradleRootDir crdl) $ do supported <- haveStackSupport @@ -269,19 +418,20 @@ isSetupConfigOutOfDate worldCabalFile worldCabalConfig = do worldCabalConfig < worldCabalFile helperProgs :: Programs -> CH.Programs -helperProgs progs = CH.Programs { - cabalProgram = T.cabalProgram progs, - ghcProgram = T.ghcProgram progs, - ghcPkgProgram = T.ghcPkgProgram progs - } - -chCached :: (Applicative m, IOish m, Gm m, Binary a) - => (FilePath -> Cached m GhcModState ChCacheData a) -> m a -chCached c = do +helperProgs = undefined +-- helperProgs progs = CH.Programs { +-- cabalProgram = T.cabalProgram progs, +-- ghcProgram = T.ghcProgram progs, +-- ghcPkgProgram = T.ghcPkgProgram progs +-- } + +chCached :: (Applicative m, IOish m, Gm m, Binary a, Typeable pt) + => ProjSetup pt -> (FilePath -> Cached m GhcModState ChCacheData a) -> m a +chCached ps c = do projdir <- cradleRootDir <$> cradle distdir <- (projdir ) . cradleDistDir <$> cradle d <- cacheInputData projdir - withCabal $ cached projdir (c distdir) d + withCabal ps $ cached projdir (c distdir) d where -- we don't need to include the distdir in the cache input because when it -- changes the cache files will be gone anyways ;) diff --git a/core/GhcMod/Cradle.hs b/core/GhcMod/Cradle.hs index 9226fd580..b8815ab58 100644 --- a/core/GhcMod/Cradle.hs +++ b/core/GhcMod/Cradle.hs @@ -1,11 +1,23 @@ {-# LANGUAGE CPP #-} +{-# LANGUAGE TupleSections #-} +{-# LANGUAGE ScopedTypeVariables #-} +{-# LANGUAGE RecordWildCards #-} +{-# LANGUAGE RankNTypes #-} +{-# LANGUAGE DataKinds #-} +{-# LANGUAGE ImplicitParams #-} + + module GhcMod.Cradle ( findCradle , findCradle' , findCradleNoLog , findSpecCradle , cleanupCradle + , shouldLoadGhcEnvironment + , oldBuild + , newBuild + , stackBuild -- * for @spec@ , plainCradle ) where @@ -21,12 +33,18 @@ import GhcMod.Error import Safe import Control.Applicative import Control.Monad.Trans.Maybe +import Data.Dynamic (toDyn, fromDynamic, Dynamic) import Data.Maybe import System.Directory import System.FilePath import System.Environment import Prelude import Control.Monad.Trans.Journal (runJournalT) +-- import Distribution.Helper (runQuery, mkQueryEnv, compilerVersion, distDir) +import Distribution.Helper (runQuery, mkQueryEnv, compilerVersion, DistDir(..), ProjType(..), ProjLoc(..), QueryEnv ) +-- import Distribution.System (buildPlatform) +import Data.List (intercalate) +import Data.Version (Version(..)) ---------------------------------------------------------------- @@ -37,7 +55,7 @@ import Control.Monad.Trans.Journal (runJournalT) findCradle :: (GmLog m, IOish m, GmOut m) => Programs -> m Cradle findCradle progs = findCradle' progs =<< liftIO getCurrentDirectory -findCradleNoLog :: forall m. (IOish m, GmOut m) => Programs -> m Cradle +findCradleNoLog :: forall m pt. (IOish m, GmOut m) => Programs -> m Cradle findCradleNoLog progs = fst <$> (runJournalT (findCradle progs) :: m (Cradle, GhcModLog)) @@ -74,6 +92,16 @@ fillTempDir crdl = do tmpDir <- liftIO $ newTempDir (cradleRootDir crdl) return crdl { cradleTempDir = tmpDir } +-- run :: Monad m => QueryEnv -> Maybe SomeLocalBuildInfo -> Query m a -> m a +-- run e s action = flip runReaderT e (flip evalStateT s (unQuery action)) + +-- -- | @runQuery env query@. Run a 'Query' under a given 'QueryEnv'. +-- runQuery :: Monad m +-- => QueryEnv +-- -> Query m a +-- -> m a +-- runQuery qe action = run qe Nothing action + cabalCradle :: (IOish m, GmLog m, GmOut m) => FilePath -> FilePath -> MaybeT m Cradle cabalCradle cabalProg wdir = do @@ -87,15 +115,40 @@ cabalCradle cabalProg wdir = do gmLog GmInfo "" $ text "'cabal' executable wasn't found, trying next project type" mzero - gmLog GmInfo "" $ text "Using Cabal project at" <+>: text cabalDir - return Cradle { - cradleProject = CabalProject - , cradleCurrentDir = wdir - , cradleRootDir = cabalDir - , cradleTempDir = error "tmpDir" - , cradleCabalFile = Just cabalFile - , cradleDistDir = "dist" - } + isDistNewstyle <- liftIO $ doesDirectoryExist $ cabalDir "dist-newstyle" + -- TODO: consider a flag to choose new-build if neither "dist" nor "dist-newstyle" exist + -- Or default to is for cabal >= 2.0 ?, unless flag saying old style + if isDistNewstyle + then do + -- dd <- liftIO $ runQuery (mkQueryEnv cabalDir "dist-newstyle") distDir + + -- runQuery :: Query pt a -> QueryEnv pt -> IO a + -- dd <- liftIO $ runQuery (mkQueryEnv cabalDir "dist-newstyle") distDir + let dd = "dist-newstyle" + qe <- MaybeT $ Just <$> makeQueryEnv newBuild cabalFile + + gmLog GmInfo "" $ text "Using Cabal new-build project at" <+>: text cabalDir + return Cradle { + cradleProject = CabalNewProject + , cradleCurrentDir = wdir + , cradleRootDir = cabalDir + , cradleTempDir = error "tmpDir" + , cradleCabalFile = Just cabalFile + , cradleDistDir = dd + , cradleQueryEnv = Just $ toDyn qe + } + else do + qe <- MaybeT $ Just <$> makeQueryEnv oldBuild cabalFile + gmLog GmInfo "" $ text "Using Cabal project at" <+>: text cabalDir + return Cradle { + cradleProject = CabalProject + , cradleCurrentDir = wdir + , cradleRootDir = cabalDir + , cradleTempDir = error "tmpDir" + , cradleCabalFile = Just cabalFile + , cradleDistDir = "dist" + , cradleQueryEnv = Just $ toDyn qe + } stackCradle :: (IOish m, GmLog m, GmOut m) => FilePath -> FilePath -> MaybeT m Cradle @@ -136,6 +189,8 @@ stackCradle stackProg wdir = do senv <- MaybeT $ getStackEnv cabalDir stackProg gmLog GmInfo "" $ text "Using Stack project at" <+>: text cabalDir + gmLog GmInfo "" $ text "Using Stack dist dir at" <+>: text (seDistDir senv) -- AZ + qe <- MaybeT $ Just <$> makeQueryEnv stackBuild cabalFile return Cradle { cradleProject = StackProject senv , cradleCurrentDir = wdir @@ -143,6 +198,7 @@ stackCradle stackProg wdir = do , cradleTempDir = error "tmpDir" , cradleCabalFile = Just cabalFile , cradleDistDir = seDistDir senv + , cradleQueryEnv = Just $ toDyn qe } stackCradleSpec :: @@ -170,6 +226,7 @@ sandboxCradle wdir = do , cradleTempDir = error "tmpDir" , cradleCabalFile = Nothing , cradleDistDir = "dist" + , cradleQueryEnv = Nothing } plainCradle :: (IOish m, GmLog m, GmOut m) => FilePath -> MaybeT m Cradle @@ -182,4 +239,57 @@ plainCradle wdir = do , cradleTempDir = error "tmpDir" , cradleCabalFile = Nothing , cradleDistDir = "dist" + , cradleQueryEnv = Nothing } + +-- | Cabal produces .ghc.environment files which are loaded by GHC if +-- they exist. For all bar a plain style project this is incorrect +-- behaviour for ghc-mod, as ghc-mod works out which packages should +-- be loaded. +-- Identify whether this should be inhibited or not +shouldLoadGhcEnvironment :: Cradle -> LoadGhcEnvironment +shouldLoadGhcEnvironment crdl = + if cradleProject crdl == PlainProject + then LoadGhcEnvironment + else DontLoadGhcEnvironment + +-- --------------------------------------------------------------------- +-- The following is moved here from cabal-helper test/GhcSession.hs + +oldBuild :: ProjSetup 'V1 +oldBuild = ProjSetup + { psDistDir = \dir -> DistDirV1 (dir "dist") + , psProjDir = \cabal_file -> ProjLocCabalFile cabal_file + } + +newBuild :: ProjSetup 'V2 +newBuild = ProjSetup + { psDistDir = \dir -> DistDirV2 (dir "dist-newstyle") + , psProjDir = \cabal_file -> ProjLocV2Dir (takeDirectory cabal_file) + } + +stackBuild :: ProjSetup 'Stack +stackBuild = ProjSetup + { psDistDir = \_dir -> DistDirStack Nothing + , psProjDir = \cabal_file -> ProjLocStackYaml ((takeDirectory cabal_file) "stack.yaml") + } + +-- --------------------------------------------------------------------- + +makeQueryEnv :: (IOish m, GmOut m) + => forall pt. ProjSetup (pt :: ProjType) -> FilePath -> m (QueryEnv (pt :: ProjType)) +makeQueryEnv ps cabalFile = do + let projdir = takeDirectory cabalFile + -- crdl <- cradle + -- progs <- patchStackPrograms crdl =<< (optPrograms <$> options) + -- readProc <- gmReadProcess + qeBare <- liftIO $ mkQueryEnv + (psProjDir ps $ cabalFile) + (psDistDir ps $ projdir) + let qe = qeBare + -- { qeReadProcess = \_ -> readProc + -- , qePrograms = helperProgs progs + -- } + return qe + +-- --------------------------------------------------------------------- diff --git a/core/GhcMod/DebugLogger.hs b/core/GhcMod/DebugLogger.hs index 6e9dfa821..b40a3f724 100644 --- a/core/GhcMod/DebugLogger.hs +++ b/core/GhcMod/DebugLogger.hs @@ -133,6 +133,11 @@ gmPrintDoc_ mode pprCols putS doc #if __GLASGOW_HASKELL__ >= 708 put (ZStr s) next = putS (zString s) >> next #endif +#if __GLASGOW_HASKELL__ >= 806 + put (LStr s) next = putS (unpackLitString s) >> next + put (RStr n c) next = putS (replicate n c) >> next +#else put (LStr s _l) next = putS (unpackLitString s) >> next +#endif done = return () -- hPutChar hdl '\n' diff --git a/core/GhcMod/DynFlags.hs b/core/GhcMod/DynFlags.hs index 2021dcf0e..ba4fadb51 100644 --- a/core/GhcMod/DynFlags.hs +++ b/core/GhcMod/DynFlags.hs @@ -55,9 +55,19 @@ setHscInterpreted df = df { } -- | Parse command line ghc options and add them to the 'DynFlags' passed -addCmdOpts :: GhcMonad m => [GHCOption] -> DynFlags -> m DynFlags -addCmdOpts cmdOpts df = - fst3 <$> G.parseDynamicFlags df (map G.noLoc cmdOpts) +addCmdOpts :: GhcMonad m => LoadGhcEnvironment -> [GHCOption] -> DynFlags -> m DynFlags +addCmdOpts loadGhcEnv cmdOpts df = + if loadGhcEnv == LoadGhcEnvironment + then fst3 <$> G.parseDynamicFlags df (map G.noLoc cmdOpts) + else + -- + -- Passes "-hide-all-packages" to the GHC API to prevent parsing of + -- package environment files. However this only works if there is no + -- invocation of `setSessionDynFlags` before calling `initDynFlagsPure`. + -- See ghc tickets #15513, #15541. + -- Thanks @lspitzner + fst3 <$> G.parseDynamicFlags df (map G.noLoc ("-hide-all-packages":cmdOpts)) + -- fst3 <$> G.parseDynamicFlags df (map G.noLoc cmdOpts) where fst3 (a,_,_) = a @@ -75,12 +85,12 @@ withDynFlags setFlags body = G.gbracket setup teardown (\_ -> body) return dflags teardown = void . G.setSessionDynFlags -withCmdFlags :: GhcMonad m => [GHCOption] -> m a -> m a -withCmdFlags flags body = G.gbracket setup teardown (\_ -> body) +withCmdFlags :: GhcMonad m => LoadGhcEnvironment -> [GHCOption] -> m a -> m a +withCmdFlags loadGhcEnv flags body = G.gbracket setup teardown (\_ -> body) where setup = do dflags <- G.getSessionDynFlags - void $ G.setSessionDynFlags =<< addCmdOpts flags dflags + void $ G.setSessionDynFlags =<< addCmdOpts loadGhcEnv flags dflags return dflags teardown = void . G.setSessionDynFlags @@ -98,7 +108,7 @@ allWarningFlags :: Gap.WarnFlags allWarningFlags = unsafePerformIO $ G.runGhc (Just libdir) $ do df <- G.getSessionDynFlags - df' <- addCmdOpts ["-Wall"] df + df' <- addCmdOpts LoadGhcEnvironment ["-Wall"] df return $ G.warningFlags df' ---------------------------------------------------------------- diff --git a/core/GhcMod/DynFlagsTH.hs b/core/GhcMod/DynFlagsTH.hs index 776c588f9..ffd81439b 100644 --- a/core/GhcMod/DynFlagsTH.hs +++ b/core/GhcMod/DynFlagsTH.hs @@ -22,7 +22,13 @@ module GhcMod.DynFlagsTH where import Language.Haskell.TH import Language.Haskell.TH.Syntax import Control.Applicative +#if __GLASGOW_HASKELL__ >= 804 +import GHC.LanguageExtensions +import qualified EnumSet as E +import qualified Data.Set as IS +#else import qualified Data.IntSet as IS +#endif import Data.Maybe import Data.Generics.Aliases import Data.Generics.Schemes @@ -83,6 +89,11 @@ deriveEqDynFlags qds = do , "FlushOut" , "FlushErr" , "Settings" -- I think these can't cange at runtime + , "LogFinaliser" -- added for ghc-8.2 + , "LogOutput" -- added for ghc-8.2 + , "OverridingBool" -- added for ghc-8.2 + , "Scheme" -- added for ghc-8.2 + , "LoadedPlugin" -- added for ghc-8.6 ] ignoredTypeOccNames = [ "OnOff" ] @@ -164,13 +175,26 @@ deriveEqDynFlags qds = do "generalFlags" -> checkIntSet "generalFlags" "warningFlags" -> checkIntSet "warningFlags" +#if __GLASGOW_HASKELL__ >= 804 + "dumpFlags" -> checkIntSet "dumpFlags" + "fatalWarningFlags" -> checkIntSet "fatalWarningFlags" + "extensionFlags" -> checkIntSet "extensionFlags" +#endif _ -> [e| [($(return fa) == $(return fb), if $(return fa) == $(return fb) then "" else ("default changed:" ++ fon) )] |] checkIntSet fieldName = do - let eqfn = [| let fn aa bb = r + let eqfn = [| let fn aa' bb' = r where +#if __GLASGOW_HASKELL__ >= 804 + aa = toSet aa' + bb = toSet bb' +#else + aa = aa' + bb = bb' +#endif + uni = IS.union aa bb dif = IS.intersection aa bb delta = IS.difference uni dif @@ -180,3 +204,18 @@ deriveEqDynFlags qds = do in fn |] [e| $(eqfn) $(return fa) $(return fb) |] + + +#if __GLASGOW_HASKELL__ >= 806 +deriving instance Eq IncludeSpecs +#endif +#if __GLASGOW_HASKELL__ >= 804 +toSet es = IS.fromList $ E.toList es + +deriving instance Ord GeneralFlag +deriving instance Ord DynFlags.WarningFlag +deriving instance Ord DynFlags.DumpFlag +deriving instance Ord DynFlags.LlvmTarget +deriving instance Ord Extension +deriving instance Eq LlvmTarget +#endif diff --git a/core/GhcMod/Error.hs b/core/GhcMod/Error.hs index 968c357b0..e04e1713a 100644 --- a/core/GhcMod/Error.hs +++ b/core/GhcMod/Error.hs @@ -49,6 +49,7 @@ import Paths_ghc_mod_core (version) import GhcMod.Types import GhcMod.Pretty +import Prelude hiding ( (<>) ) type GmError m = MonadError GhcModError m diff --git a/core/GhcMod/FileMapping.hs b/core/GhcMod/FileMapping.hs index e05a54066..76f34414c 100644 --- a/core/GhcMod/FileMapping.hs +++ b/core/GhcMod/FileMapping.hs @@ -48,7 +48,8 @@ loadMappedFileSource from src = do tmpdir <- cradleTempDir `fmap` cradle enc <- liftIO . mkTextEncoding . optEncoding =<< options to <- liftIO $ do - (fn, h) <- openTempFile tmpdir (takeFileName from) + (fn', h) <- openTempFile tmpdir (takeFileName from) + fn <- getCanonicalFileNameSafe fn' hSetEncoding h enc hPutStr h src hClose h @@ -57,11 +58,12 @@ loadMappedFileSource from src = do loadMappedFile' :: IOish m => FilePath -> FilePath -> Bool -> GhcModT m () loadMappedFile' from to isTemp = do - cfn <- getCanonicalFileNameSafe from + cfn <- liftIO $ canonicalizePath from unloadMappedFile' cfn crdl <- cradle - let to' = makeRelative (cradleRootDir crdl) to - addMMappedFile cfn (FileMapping to' isTemp) + to' <- liftIO $ canonicalizePath to + let to'' = makeRelative (cradleRootDir crdl) to' + addMMappedFile cfn (FileMapping to'' isTemp) mapFile :: (IOish m, GmState m) => HscEnv -> Target -> m Target mapFile _ (Target tid@(TargetFile filePath _) taoc _) = do diff --git a/core/GhcMod/Gap.hs b/core/GhcMod/Gap.hs index dff6b87f3..f04e6b420 100644 --- a/core/GhcMod/Gap.hs +++ b/core/GhcMod/Gap.hs @@ -37,8 +37,6 @@ module GhcMod.Gap ( , fileModSummary , WarnFlags , emptyWarnFlags - , GLMatch - , GLMatchI , getClass , occName , listVisibleModuleNames @@ -47,8 +45,19 @@ module GhcMod.Gap ( , GhcMod.Gap.isSynTyCon , parseModuleHeader , mkErrStyle' +#if __GLASGOW_HASKELL__ < 804 , everythingStagedWithContext +#endif , withCleanupSession +#if __GLASGOW_HASKELL__ >= 804 + , GHC.GhcPs + , GHC.GhcRn + , GHC.GhcTc +#else + , GhcPs + , GhcRn + , GhcTc +#endif ) where import Control.Applicative hiding (empty) @@ -70,6 +79,7 @@ import NameSet import OccName import Outputable import PprTyThing +import IfaceSyn import StringBuffer import TcType import Var (varType) @@ -108,7 +118,10 @@ import TcRnTypes #endif #endif -#if MIN_VERSION_GLASGOW_HASKELL(8,0,1,20161117) +#if MIN_VERSION_GLASGOW_HASKELL(8,2,0,0) +import GHC hiding (ClsInst, withCleanupSession, setLogAction) +import qualified GHC (withCleanupSession) +#elif MIN_VERSION_GLASGOW_HASKELL(8,0,1,20161117) import GHC hiding (ClsInst, withCleanupSession) import qualified GHC (withCleanupSession) #elif __GLASGOW_HASKELL__ >= 706 @@ -125,7 +138,9 @@ import UniqFM (eltsUFM) import Module #endif -#if __GLASGOW_HASKELL__ >= 704 +#if __GLASGOW_HASKELL__ >= 804 +import qualified EnumSet as E (EnumSet, empty) +#elif __GLASGOW_HASKELL__ >= 704 import qualified Data.IntSet as I (IntSet, empty) #endif @@ -140,10 +155,12 @@ import Parser import SrcLoc import Packages import Data.Generics (GenericQ, extQ, gmapQ) +#if __GLASGOW_HASKELL__ < 804 import GHC.SYB.Utils (Stage(..)) +#endif import GhcMod.Types (Expression(..)) -import Prelude +import Prelude hiding ( (<>) ) ---------------------------------------------------------------- ---------------------------------------------------------------- @@ -215,12 +232,18 @@ renderGm = Pretty.fullRender Pretty.PageMode 80 1.2 string_txt "" string_txt (Pretty.Chr c) s = c:s string_txt (Pretty.Str s1) s2 = s1 ++ s2 string_txt (Pretty.PStr s1) s2 = unpackFS s1 ++ s2 +#if __GLASGOW_HASKELL__ >= 806 + string_txt (Pretty.LStr s1) s2 = unpackLitString s1 ++ s2 + -- a '\0'-terminated array of bytes + string_txt (Pretty.RStr n c) s2 = replicate n c ++ s2 + -- a repeated character (e.g., ' ') +#else string_txt (Pretty.LStr s1 _) s2 = unpackLitString s1 ++ s2 +#endif #if __GLASGOW_HASKELL__ >= 708 string_txt (Pretty.ZStr s1) s2 = zString s1 ++ s2 #endif - ---------------------------------------------------------------- ---------------------------------------------------------------- @@ -284,9 +307,15 @@ fileModSummary :: GhcMonad m => FilePath -> m ModSummary fileModSummary file' = do mss <- getModuleGraph file <- liftIO $ canonicalizePath file' - [ms] <- liftIO $ flip filterM mss $ \m -> +#if __GLASGOW_HASKELL__ >= 804 + mss <- liftIO $ flip filterM (mgModSummaries mss) $ \m -> +#else + mss <- liftIO $ flip filterM mss $ \m -> +#endif (Just file==) <$> canonicalizePath `traverse` ml_hs_file (ms_location m) - return ms + case mss of + [ms] -> return ms + _ -> error "pattern match Fail" withInteractiveContext :: GhcMonad m => m a -> m a withInteractiveContext action = gbracket setup teardown body @@ -296,8 +325,14 @@ withInteractiveContext action = gbracket setup teardown body body _ = do topImports >>= setCtx action + topImports :: GhcMonad m => m [InteractiveImport] topImports = do +#if __GLASGOW_HASKELL__ >= 804 + mg <- getModuleGraph + ms <- filterM moduleIsInterpreted =<< map ms_mod <$> (return $ mgModSummaries mg) +#else ms <- filterM moduleIsInterpreted =<< map ms_mod <$> getModuleGraph +#endif let iis = map (IIModule . modName) ms #if __GLASGOW_HASKELL__ >= 704 return iis @@ -393,8 +428,13 @@ class HasType a where getType :: GhcMonad m => TypecheckedModule -> a -> m (Maybe (SrcSpan, Type)) -instance HasType (LHsBind Id) where -#if __GLASGOW_HASKELL__ >= 708 +instance HasType (LHsBind GhcTc) where +#if __GLASGOW_HASKELL__ >= 806 + getType _ (L spn FunBind{fun_matches = m}) = return $ Just (spn, typ) + where in_tys = mg_arg_tys $ mg_ext m + out_typ = mg_res_ty $ mg_ext m + typ = mkFunTys in_tys out_typ +#elif __GLASGOW_HASKELL__ >= 708 getType _ (L spn FunBind{fun_matches = m}) = return $ Just (spn, typ) where in_tys = mg_arg_tys m out_typ = mg_res_ty m @@ -417,7 +457,10 @@ filterOutChildren get_thing xs infoThing :: GhcMonad m => (FilePath -> FilePath) -> Expression -> m SDoc infoThing m (Expression str) = do names <- parseName str -#if __GLASGOW_HASKELL__ >= 708 +#if __GLASGOW_HASKELL__ >= 804 + mb_stuffs <- mapM (getInfo False) names + let filtered = filterOutChildren (\(t,_f,_i,_fam,_doc) -> t) (catMaybes mb_stuffs) +#elif __GLASGOW_HASKELL__ >= 708 mb_stuffs <- mapM (getInfo False) names let filtered = filterOutChildren (\(t,_f,_i,_fam) -> t) (catMaybes mb_stuffs) #else @@ -426,7 +469,14 @@ infoThing m (Expression str) = do #endif return $ vcat (intersperse (text "") $ map (pprInfo m False) filtered) -#if __GLASGOW_HASKELL__ >= 708 +#if __GLASGOW_HASKELL__ >= 804 +pprInfo :: (FilePath -> FilePath) -> Bool -> (TyThing, GHC.Fixity, [ClsInst], [FamInst],SDoc) -> SDoc +pprInfo m _ (thing, fixity, insts, famInsts,_doc) + = pprTyThingInContextLoc' thing + $$ show_fixity fixity + $$ vcat (map pprInstance' insts) + $$ vcat (map pprFamInst' famInsts) +#elif __GLASGOW_HASKELL__ >= 708 pprInfo :: (FilePath -> FilePath) -> Bool -> (TyThing, GHC.Fixity, [ClsInst], [FamInst]) -> SDoc pprInfo m _ (thing, fixity, insts, famInsts) = pprTyThingInContextLoc' thing @@ -459,9 +509,9 @@ pprInfo m pefas (thing, fixity, insts) = showWithLoc (pprDefinedAt' (getName axiom)) $ hang (ptext (sLit "type instance") <+> pprTypeApp (coAxiomTyCon axiom) lhs_tys) 2 (equals <+> ppr rhs) -#else +# else pprFamInst' ispec = showWithLoc (pprDefinedAt' (getName ispec)) (pprFamInstHdr ispec) -#endif +# endif #else pprTyThingInContextLoc' pefas' thing' = showWithLoc (pprDefinedAt' thing') (pprTyThingInContext pefas' thing') #endif @@ -514,7 +564,7 @@ nameForUser = pprOccName . getOccName occNameForUser :: OccName -> SDoc occNameForUser = pprOccName -deSugar :: TypecheckedModule -> LHsExpr Id -> HscEnv +deSugar :: TypecheckedModule -> LHsExpr GhcTc -> HscEnv -> IO (Maybe CoreExpr) #if __GLASGOW_HASKELL__ >= 708 deSugar _ e hs_env = snd <$> deSugarExpr hs_env e @@ -555,7 +605,11 @@ fromTyThing _ = GtN ---------------------------------------------------------------- ---------------------------------------------------------------- -#if __GLASGOW_HASKELL__ >= 704 +#if __GLASGOW_HASKELL__ >= 804 +type WarnFlags = E.EnumSet WarningFlag +emptyWarnFlags :: WarnFlags +emptyWarnFlags = E.empty +#elif __GLASGOW_HASKELL__ >= 704 type WarnFlags = I.IntSet emptyWarnFlags :: WarnFlags emptyWarnFlags = I.empty @@ -568,16 +622,20 @@ emptyWarnFlags = [] ---------------------------------------------------------------- ---------------------------------------------------------------- -#if __GLASGOW_HASKELL__ >= 708 -type GLMatch = LMatch RdrName (LHsExpr RdrName) -type GLMatchI = LMatch Id (LHsExpr Id) -#else -type GLMatch = LMatch RdrName -type GLMatchI = LMatch Id +-- See https://ghc.haskell.org/trac/ghc/wiki/Migration/8.4#GHCAPIchanges +#if __GLASGOW_HASKELL__ <= 802 +type GhcPs = RdrName +type GhcRn = Name +type GhcTc = Id #endif -getClass :: [LInstDecl Name] -> Maybe (Name, SrcSpan) -#if __GLASGOW_HASKELL__ >= 802 +getClass :: [LInstDecl GhcRn] -> Maybe (Name, SrcSpan) +#if __GLASGOW_HASKELL__ >= 806 +-- Instance declarations of sort 'instance F (G a)' +getClass [L loc (ClsInstD _ (ClsInstDecl {cid_poly_ty = HsIB _ (L _ (HsForAllTy _ _ (L _ (HsAppTy _ (L _ (HsTyVar _ _ (L _ className))) _))))}))] = Just (className, loc) +-- Instance declarations of sort 'instance F G' (no variables) +getClass [L loc (ClsInstD _ (ClsInstDecl {cid_poly_ty = HsIB _ (L _ (HsAppTy _ (L _ (HsTyVar _ _ (L _ className))) _))}))] = Just (className, loc) +#elif __GLASGOW_HASKELL__ >= 802 -- Instance declarations of sort 'instance F (G a)' getClass [L loc (ClsInstD (ClsInstDecl {cid_poly_ty = HsIB _ (L _ (HsForAllTy _ (L _ (HsAppTy (L _ (HsTyVar _ (L _ className))) _)))) _}))] = Just (className, loc) -- Instance declarations of sort 'instance F G' (no variables) @@ -664,7 +722,7 @@ parseModuleHeader :: String -- ^ Haskell module source text (full Unicode is supported) -> DynFlags -> FilePath -- ^ the filename (for source locations) - -> Either ErrorMessages (WarningMessages, Located (HsModule RdrName)) + -> Either ErrorMessages (WarningMessages, Located (HsModule GhcPs)) parseModuleHeader str dflags filename = let loc = mkRealSrcLoc (mkFastString filename) 1 1 @@ -672,7 +730,11 @@ parseModuleHeader str dflags filename = in case L.unP Parser.parseHeader (mkPState dflags buf loc) of +#if __GLASGOW_HASKELL__ >= 804 + PFailed _ sp err -> +#else PFailed sp err -> +#endif #if __GLASGOW_HASKELL__ >= 706 Left (unitBag (mkPlainErrMsg dflags sp err)) #else @@ -700,8 +762,10 @@ instance NFData ByteString where rnf (Chunk _ b) = rnf b #endif +#if __GLASGOW_HASKELL__ < 804 -- | Like 'everything', but avoid known potholes, based on the 'Stage' that -- generated the Ast. +-- everythingWithContext :: s -> (r -> r -> r) -> GenericQ (s -> (r, s)) -> GenericQ r everythingStagedWithContext :: Stage -> s -> (r -> r -> r) -> r -> GenericQ (s -> (r, s)) -> GenericQ r everythingStagedWithContext stage s0 f z q x | (const False @@ -716,6 +780,7 @@ everythingStagedWithContext stage s0 f z q x #endif fixity = const (stage Bool (r, s') = q x s0 +#endif withCleanupSession :: GhcMonad m => m a -> m a #if __GLASGOW_HASKELL__ >= 800 diff --git a/core/GhcMod/GhcPkg.hs b/core/GhcMod/GhcPkg.hs index 746b2eac9..5b802f308 100644 --- a/core/GhcMod/GhcPkg.hs +++ b/core/GhcMod/GhcPkg.hs @@ -68,8 +68,10 @@ getGhcPkgProgram = do progs <- optPrograms <$> options case cradleProject crdl of (StackProject senv) -> do - Just ghcPkg <- getStackGhcPkgPath senv - return ghcPkg + mghcPkg <- getStackGhcPkgPath senv + case mghcPkg of + Just ghcPkg -> return ghcPkg + _ -> error "pattern match fail" _ -> return $ ghcPkgProgram progs @@ -81,10 +83,14 @@ getPackageDbStack = do PlainProject -> return [GlobalDb, UserDb] SandboxProject -> do - Just db <- liftIO $ getSandboxDb crdl - return $ [GlobalDb, db] + mdb <- liftIO $ getSandboxDb crdl + case mdb of + Just db -> return $ [GlobalDb, db] + _ -> error "pattern match fail" CabalProject -> getCabalPackageDbStack + CabalNewProject -> + getCabalPackageDbStack (StackProject StackEnv {..}) -> return $ [GlobalDb, PackageDb seSnapshotPkgDb, PackageDb seLocalPkgDb] return $ fromMaybe stack mCusPkgStack diff --git a/core/GhcMod/LightGhc.hs b/core/GhcMod/LightGhc.hs index 146f2c095..b9f758250 100644 --- a/core/GhcMod/LightGhc.hs +++ b/core/GhcMod/LightGhc.hs @@ -31,9 +31,20 @@ initStaticOpts = return () newLightEnv :: IOish m => (DynFlags -> LightGhc DynFlags) -> m HscEnv newLightEnv mdf = do df <- liftIO $ do +#if MIN_VERSION_GLASGOW_HASKELL(8,2,0,0) +#else initStaticOpts +#endif settings <- initSysTools (Just libdir) +#if __GLASGOW_HASKELL__ >= 806 + let llvmTgtList = ([],[]) -- TODO: where should this come from? + initDynFlags $ defaultDynFlags settings llvmTgtList +#elif __GLASGOW_HASKELL__ >= 804 + let llvmTgtList = [] -- TODO: where should this come from? + initDynFlags $ defaultDynFlags settings llvmTgtList +#else initDynFlags $ defaultDynFlags settings +#endif hsc_env <- liftIO $ newHscEnv df df' <- runLightGhc hsc_env $ mdf df @@ -50,13 +61,13 @@ withLightHscEnv' :: IOish m => (DynFlags -> LightGhc DynFlags) -> (HscEnv -> m a) -> m a withLightHscEnv' mdf action = gbracket (newLightEnv mdf) teardownLightEnv action -withLightHscEnv :: IOish m => [GHCOption] -> (HscEnv -> m a) -> m a -withLightHscEnv opts = withLightHscEnv' (f <=< liftIO . newHscEnv) +withLightHscEnv :: IOish m => LoadGhcEnvironment -> [GHCOption] -> (HscEnv -> m a) -> m a +withLightHscEnv loadGhcEnv opts = withLightHscEnv' (f <=< liftIO . newHscEnv) where f env = runLightGhc env $ do -- HomeModuleGraph and probably all other clients get into all sorts of -- trouble if the package state isn't initialized here - _ <- setSessionDynFlags =<< addCmdOpts opts =<< getSessionDynFlags + _ <- setSessionDynFlags =<< addCmdOpts loadGhcEnv opts =<< getSessionDynFlags getSessionDynFlags runLightGhc :: MonadIO m => HscEnv -> LightGhc a -> m a diff --git a/core/GhcMod/Logger.hs b/core/GhcMod/Logger.hs index 3ccf89bdb..6fd99f4f9 100644 --- a/core/GhcMod/Logger.hs +++ b/core/GhcMod/Logger.hs @@ -19,7 +19,7 @@ import Data.IORef (IORef, newIORef, readIORef, writeIORef, modifyIORef) import System.FilePath (normalise) import ErrUtils -import GHC +import GHC hiding ( convert ) import HscTypes import Outputable import qualified GHC as G @@ -27,7 +27,7 @@ import Bag import SrcLoc import FastString -import GhcMod.Convert +import GhcMod.Convert (convert) import GhcMod.Doc (showPage) import GhcMod.DynFlags (withDynFlags) import GhcMod.Monad.Types diff --git a/core/GhcMod/Logging.hs b/core/GhcMod/Logging.hs index 930a56b68..7d5ffc878 100644 --- a/core/GhcMod/Logging.hs +++ b/core/GhcMod/Logging.hs @@ -48,8 +48,10 @@ gmSetLogLevel level = gmGetLogLevel :: forall m. GmLog m => m GmLogLevel gmGetLogLevel = do - GhcModLog { gmLogLevel = Just level } <- gmlHistory - return level + GhcModLog { gmLogLevel = mlevel } <- gmlHistory + case mlevel of + Just level -> return level + _ -> error "mempty value for GhcModLog must use a Just value" gmSetDumpLevel :: GmLog m => Bool -> m () gmSetDumpLevel level = @@ -73,7 +75,10 @@ decreaseLogLevel l = pred l -- False gmLog :: (MonadIO m, GmLog m, GmOut m) => GmLogLevel -> String -> Doc -> m () gmLog level loc' doc = do - GhcModLog { gmLogLevel = Just level' } <- gmlHistory + GhcModLog { gmLogLevel = mlevel' } <- gmlHistory + level' <- case mlevel' of + Nothing -> error "mempty value for GhcModLog must use a Just value" + Just l -> return l let loc | loc' == "" = empty | otherwise = text loc' <+>: empty diff --git a/core/GhcMod/ModuleLoader.hs b/core/GhcMod/ModuleLoader.hs new file mode 100644 index 000000000..b34a5e52d --- /dev/null +++ b/core/GhcMod/ModuleLoader.hs @@ -0,0 +1,156 @@ +{-# LANGUAGE CPP #-} +{-# LANGUAGE FlexibleContexts #-} +{-# LANGUAGE OverloadedStrings #-} +-- | Uses GHC hooks to load a TypecheckedModule + +module GhcMod.ModuleLoader + ( getModulesGhc + , getModulesGhc' + ) where + +import Control.Monad.IO.Class + +import qualified Data.Map as Map +import Data.IORef + +import qualified GhcMod.Monad as GM +import qualified GhcMod.Target as GM +import qualified GhcMod.Types as GM + +import GHC (TypecheckedModule, ParsedModule) +import qualified GHC +import qualified DynFlags as GHC +import qualified GhcMonad as GHC +import qualified Hooks as GHC +import qualified HscMain as GHC +import qualified HscTypes as GHC +import qualified TcRnMonad as GHC + +import System.Directory +import System.FilePath + +-- --------------------------------------------------------------------- + +getMappedFileName :: FilePath -> GM.FileMappingMap -> FilePath +getMappedFileName fname mfs = + case Map.lookup fname mfs of + Just fm -> GM.fmPath fm + Nothing -> fname + +canonicalizeModSummary :: (MonadIO m) => + GHC.ModSummary -> m (Maybe FilePath) +canonicalizeModSummary = + traverse (liftIO . canonicalizePath) . GHC.ml_hs_file . GHC.ms_location + +tweakModSummaryDynFlags :: GHC.ModSummary -> GHC.ModSummary +tweakModSummaryDynFlags ms = + let df = GHC.ms_hspp_opts ms + in ms { GHC.ms_hspp_opts = GHC.gopt_set df GHC.Opt_KeepRawTokenStream } + +-- | Gets a TypecheckedModule and/or ParsedModule from a given file +-- The `wrapper` allows arbitary data to be captured during +-- the compilation process, like errors and warnings +-- Appends the parent directories of all the mapped files +-- to the includePaths for CPP purposes. +-- Use in combination with `runActionInContext` for best results +getModulesGhc' :: GM.IOish m + => (GM.GmlT m () -> GM.GmlT m a) -> FilePath -> GM.GhcModT m (a, Maybe TypecheckedModule, Maybe ParsedModule) +getModulesGhc' wrapper targetFile = do + cfileName <- liftIO $ canonicalizePath targetFile + mfs <- GM.getMMappedFiles + mFileName <- liftIO . canonicalizePath $ getMappedFileName cfileName mfs + refTypechecked <- liftIO $ newIORef Nothing + refParsed <- liftIO $ newIORef Nothing + let keepInfo = pure . (mFileName ==) + saveTypechecked = writeIORef refTypechecked . Just + saveParsed = writeIORef refParsed . Just + res <- getModulesGhc wrapper [cfileName] keepInfo saveTypechecked saveParsed + mtm <- liftIO $ readIORef refTypechecked + mpm <- liftIO $ readIORef refParsed + return (res, mtm, mpm) + +-- | like getModulesGhc' but allows you to keep an arbitary number of Modules +-- `keepInfo` decides which module to keep +-- `saveTypechecked` is the callback that is passed the TypecheckedModule +-- `saveParsed` is the callback that is passed the ParsedModule +getModulesGhc :: GM.IOish m + => (GM.GmlT m () -> GM.GmlT m a) -> [FilePath] -> (FilePath -> IO Bool) -> (TypecheckedModule -> IO ()) -> (ParsedModule -> IO ()) -> GM.GhcModT m a +getModulesGhc wrapper targetFiles keepInfo saveTypechecked saveParsed = do + mfs <- GM.getMMappedFiles + +#if __GLASGOW_HASKELL__ >= 806 + let ips = map takeDirectory $ Map.keys mfs + getPaths ips' df = GHC.IncludeSpecs qpaths (ips' ++ gpaths) + where + -- Note, introduced for include path issues on windows, see + -- https://ghc.haskell.org/trac/ghc/ticket/14312 + GHC.IncludeSpecs qpaths gpaths = GHC.includePaths df + setIncludePaths df = df { GHC.includePaths = getPaths ips df } +#else + let ips = map takeDirectory $ Map.keys mfs + setIncludePaths df = df { GHC.includePaths = ips ++ GHC.includePaths df } +#endif + GM.runGmlTWith' (map Left targetFiles) + (return . setIncludePaths) + (Just $ updateHooks keepInfo saveTypechecked saveParsed) + wrapper + (return ()) + +updateHooks + :: (FilePath -> IO Bool) + -> (TypecheckedModule -> IO ()) + -> (ParsedModule -> IO ()) + -> GHC.Hooks + -> GHC.Hooks +updateHooks fp ref refParsed hooks = hooks { +#if __GLASGOW_HASKELL__ <= 710 + GHC.hscFrontendHook = Just $ hscFrontend fp ref +#else + GHC.hscFrontendHook = Just $ fmap GHC.FrontendTypecheck . hscFrontend fp ref refParsed +#endif + } + + +-- | Warning: discards all changes to Session +runGhcInHsc :: GHC.Ghc a -> GHC.Hsc a +runGhcInHsc action = do + env <- GHC.getHscEnv + session <- liftIO $ newIORef env + liftIO $ GHC.reflectGhc action $ GHC.Session session + + +-- | Frontend hook that keeps the TypecheckedModule for its first argument +-- and stores it in the IORef passed to it +hscFrontend :: (FilePath -> IO Bool) -> (TypecheckedModule -> IO ()) -> (ParsedModule -> IO ()) -> GHC.ModSummary -> GHC.Hsc GHC.TcGblEnv +hscFrontend keepInfoFunc saveTypechecked saveParsed mod_summary = do + mfn <- canonicalizeModSummary mod_summary + -- md = GHC.moduleNameString $ GHC.moduleName $ GHC.ms_mod mod_summary + keepInfo <- case mfn of + Just fileName -> liftIO $ keepInfoFunc fileName + Nothing -> pure False + + if keepInfo + then runGhcInHsc $ do + let modSumWithRaw = tweakModSummaryDynFlags mod_summary + + p' <- GHC.parseModule modSumWithRaw + let p = p' {GHC.pm_mod_summary = mod_summary} + liftIO $ saveParsed p + + tc <- GHC.typecheckModule p + let tc_gbl_env = fst $ GHC.tm_internals_ tc + + liftIO $ saveTypechecked tc + return tc_gbl_env + else do + hpm <- GHC.hscParse' mod_summary + hsc_env <- GHC.getHscEnv +#if __GLASGOW_HASKELL__ >= 804 + -- tcRnModule' :: ModSummary -> Bool -> HsParsedModule -> Hsc TcGblEnv + GHC.tcRnModule' mod_summary False hpm +#else + GHC.tcRnModule' hsc_env mod_summary False hpm +#endif + +-- --------------------------------------------------------------------- + diff --git a/core/GhcMod/Monad/Compat.hs_h b/core/GhcMod/Monad/Compat.hs_h index 7437bfc90..cd90e5f1a 100644 --- a/core/GhcMod/Monad/Compat.hs_h +++ b/core/GhcMod/Monad/Compat.hs_h @@ -22,7 +22,4 @@ -- 'CoreMonad.MonadIO' and 'Control.Monad.IO.Class.MonadIO' are different -- classes before ghc 7.8 #define DIFFERENT_MONADIO 1 - --- RWST doen't have a MonadIO instance before ghc 7.8 -#define MONADIO_INSTANCES 1 #endif diff --git a/core/GhcMod/Options/Help.hs b/core/GhcMod/Options/Help.hs index b23487cc5..5156f588f 100644 --- a/core/GhcMod/Options/Help.hs +++ b/core/GhcMod/Options/Help.hs @@ -14,6 +14,7 @@ -- You should have received a copy of the GNU Affero General Public License -- along with this program. If not, see . {-# LANGUAGE OverloadedStrings, FlexibleInstances, GeneralizedNewtypeDeriving #-} +{-# LANGUAGE CPP #-} module GhcMod.Options.Help where @@ -33,6 +34,11 @@ type MyDoc = MyDocM (Maybe Doc) () instance IsString (MyDocM (Maybe Doc) a) where fromString = append . para +#if __GLASGOW_HASKELL__ >= 804 +instance Semigroup (MyDocM (Maybe Doc) ()) where + (<>) = mappend +#endif + instance Monoid (MyDocM (Maybe Doc) ()) where mappend a b = append $ doc a <> doc b mempty = append PP.empty @@ -47,7 +53,7 @@ append s = modify m >> return undefined m Nothing = Just s m (Just old) = Just $ old PP..$. s -infixr 7 \\ +infixr 7 \\ -- comment to sort out CPP (\\) :: MyDoc -> MyDoc -> MyDoc (\\) a b = append $ doc a PP.<+> doc b diff --git a/core/GhcMod/PathsAndFiles.hs b/core/GhcMod/PathsAndFiles.hs index c55987c24..560dcaaad 100644 --- a/core/GhcMod/PathsAndFiles.hs +++ b/core/GhcMod/PathsAndFiles.hs @@ -14,6 +14,7 @@ -- You should have received a copy of the GNU Affero General Public License -- along with this program. If not, see . +{-# LANGUAGE CPP #-} module GhcMod.PathsAndFiles ( module GhcMod.PathsAndFiles , module GhcMod.Caching @@ -81,7 +82,12 @@ findCustomPackageDbFile dir = getSandboxDb :: Cradle -> IO (Maybe GhcPkgDb) getSandboxDb crdl = do mConf <- traverse readFile =<< mightExist (sandboxConfigFile crdl) + +#if MIN_VERSION_cabal_helper(1,0,0) + bp <- return buildPlatform +#else bp <- buildPlatform readProcess +#endif return $ PackageDb . fixPkgDbVer bp <$> (extractSandboxDbDir =<< mConf) where diff --git a/core/GhcMod/Pretty.hs b/core/GhcMod/Pretty.hs index d2737888c..c83d4356a 100644 --- a/core/GhcMod/Pretty.hs +++ b/core/GhcMod/Pretty.hs @@ -38,8 +38,10 @@ import GHC import Outputable (SDoc, withPprStyleDoc) import GhcMod.Types +import Distribution.Helper import GhcMod.Doc import GhcMod.Gap (renderGm) +import Prelude hiding ( (<>)) renderSDoc :: GhcMonad m => SDoc -> m Doc renderSDoc sdoc = do @@ -49,7 +51,11 @@ renderSDoc sdoc = do gmComponentNameDoc :: ChComponentName -> Doc gmComponentNameDoc ChSetupHsName = text $ "Setup.hs" -#if MIN_VERSION_cabal_helper(0,8,0) +#if MIN_VERSION_cabal_helper(1,0,0) +gmComponentNameDoc (ChLibName ChMainLibName) = text $ "library" +gmComponentNameDoc (ChLibName (ChSubLibName n)) = text $ "library:" ++ n +gmComponentNameDoc (ChFLibName _) = text $ "flibrary" +#elif MIN_VERSION_cabal_helper(0,8,0) gmComponentNameDoc ChLibName = text $ "library" gmComponentNameDoc (ChSubLibName _)= text $ "library" gmComponentNameDoc (ChFLibName _) = text $ "flibrary" diff --git a/core/GhcMod/Stack.hs b/core/GhcMod/Stack.hs index 0c0ada00e..4a1605fb1 100644 --- a/core/GhcMod/Stack.hs +++ b/core/GhcMod/Stack.hs @@ -40,12 +40,15 @@ import Prelude patchStackPrograms :: (IOish m, GmOut m) => Cradle -> Programs -> m Programs patchStackPrograms Cradle { cradleProject = (StackProject senv) } progs = do - Just ghc <- getStackGhcPath senv - Just ghcPkg <- getStackGhcPkgPath senv - return $ progs { - ghcProgram = ghc - , ghcPkgProgram = ghcPkg - } + mghc <- getStackGhcPath senv + mghcPkg <- getStackGhcPkgPath senv + case (mghc,mghcPkg) of + (Just ghc,Just ghcPkg) -> + return $ progs { + ghcProgram = ghc + , ghcPkgProgram = ghcPkg + } + _ -> error "pattern match fail" patchStackPrograms _crdl progs = return progs getStackEnv :: (IOish m, GmOut m, GmLog m) diff --git a/core/GhcMod/Target.hs b/core/GhcMod/Target.hs index 2294cfa8d..859774a1c 100644 --- a/core/GhcMod/Target.hs +++ b/core/GhcMod/Target.hs @@ -21,6 +21,9 @@ import Control.Arrow import Control.Applicative import Control.Category ((.)) import GHC +import qualified Hooks as GHC +import qualified HscTypes as GHC +import qualified GhcMonad as G #if __GLASGOW_HASKELL__ >= 800 import GHC.LanguageExtensions #endif @@ -30,6 +33,7 @@ import DynFlags import HscTypes import Pretty +import GhcMod.Cradle import GhcMod.DynFlags import GhcMod.Monad.Types import GhcMod.CabalHelper @@ -67,8 +71,10 @@ import System.FilePath runGmPkgGhc :: (IOish m, Gm m) => LightGhc a -> m a runGmPkgGhc action = do + crdl <- cradle + let loadGhcEnv = shouldLoadGhcEnvironment crdl pkgOpts <- packageGhcOptions - withLightHscEnv pkgOpts $ \env -> liftIO $ runLightGhc env action + withLightHscEnv loadGhcEnv pkgOpts $ \env -> liftIO $ runLightGhc env action initSession :: IOish m => [GHCOption] @@ -101,8 +107,8 @@ initSession opts mdf = do else gmLog GmDebug "initSession" $ text "Session already initialized" where - initDF Cradle { cradleTempDir } df = - setTmpDir cradleTempDir <$> (mdf =<< addCmdOpts opts df) + initDF c@Cradle { cradleTempDir, cradleProject } df = + setTmpDir cradleTempDir <$> (mdf =<< addCmdOpts (shouldLoadGhcEnvironment c) opts df) teardownSession hsc_env_ref = do hsc_env <- liftIO $ readIORef hsc_env_ref @@ -146,16 +152,42 @@ runGmlT' :: IOish m -> GhcModT m a runGmlT' fns mdf action = runGmlTWith fns mdf id action --- | Run a GmlT action (i.e. a function in the GhcMonad) in the context --- of certain files or modules, with updated GHC flags and a final --- transformation +-- | Run a GmlT action (i.e. a function in the GhcMonad) in the context of +-- certain files or modules, with updated GHC flags, and updated ModuleGraph +runGmlTfm :: IOish m + => [Either FilePath ModuleName] + -> (forall gm. GhcMonad gm => DynFlags -> gm DynFlags) + -> Maybe (GHC.Hooks -> GHC.Hooks) + -> GmlT m a + -> GhcModT m a +runGmlTfm fns mdf mUpdateHooks action + = runGmlTWith' fns mdf mUpdateHooks id action + +-- | Run a GmlT action (i.e. a function in the GhcMonad) in the context of +-- certain files or modules, with updated GHC flags, updated ModuleGraph and a +-- final transformation runGmlTWith :: IOish m => [Either FilePath ModuleName] -> (forall gm. GhcMonad gm => DynFlags -> gm DynFlags) -> (GmlT m a -> GmlT m b) -> GmlT m a -> GhcModT m b -runGmlTWith efnmns' mdf wrapper action = do +runGmlTWith efnmns' mdf wrapper action = + runGmlTWith' efnmns' mdf Nothing wrapper action + +-- | Run a GmlT action (i.e. a function in the GhcMonad) in the context of +-- certain files or modules, with updated GHC flags, updated ModuleGraph and a +-- final transformation +runGmlTWith' :: IOish m + => [Either FilePath ModuleName] + -> (forall gm. GhcMonad gm => DynFlags -> gm DynFlags) + -> Maybe (GHC.Hooks -> GHC.Hooks) + -- ^ If a hook update is provided, force the reloading + -- of the specified targets + -> (GmlT m a -> GmlT m b) + -> GmlT m a + -> GhcModT m b +runGmlTWith' efnmns' mdf mUpdateHooks wrapper action = do crdl <- cradle Options { optGhcUserOptions } <- options @@ -163,15 +195,34 @@ runGmlTWith efnmns' mdf wrapper action = do ccfns = map (cradleCurrentDir crdl ) fns cfns <- mapM getCanonicalFileNameSafe ccfns let serfnmn = Set.fromList $ map Right mns ++ map Left cfns - opts <- targetGhcOptions crdl serfnmn + (opts, mappedStrs) <- targetGhcOptions crdl serfnmn + let opts' = opts ++ ["-O0", "-fno-warn-missing-home-modules"] ++ optGhcUserOptions + gmVomit + "session-ghc-options" + (text "hide all packages(ignore .ghc.environment):") + (show $ shouldLoadGhcEnvironment crdl) + + gmVomit + "session-ghc-options" + (text "Using the following mapped files") + (intercalate " " $ map (("\""++) . (++"\"")) mappedStrs) + + gmVomit + "session-ghc-options" + (text "Using the following mapped files") + (intercalate " " $ map (("\""++) . (++"\"")) mappedStrs) + gmVomit "session-ghc-options" (text "Initializing GHC session with following options") (intercalate " " $ map (("\""++) . (++"\"")) opts') - GhcModLog { gmLogLevel = Just level } <- gmlHistory + GhcModLog { gmLogLevel = mlevel } <- gmlHistory + level <- case mlevel of + Just level -> return level + _ -> error "pattern match fail" putErr <- gmErrStrIO let setLogger | level >= GmDebug = setDebugLogger putErr | otherwise = setEmptyLogger @@ -179,31 +230,39 @@ runGmlTWith efnmns' mdf wrapper action = do initSession opts' $ setHscNothing >>> setLogger >>> mdf - mappedStrs <- getMMappedFilePaths let targetStrs = mappedStrs ++ map moduleNameString mns ++ cfns + gmVomit + "session-ghc-options" + (text "Using the following targets") + (intercalate " " $ map (("\""++) . (++"\"")) targetStrs) + unGmlT $ wrapper $ do - loadTargets opts targetStrs + loadTargets opts targetStrs mUpdateHooks action targetGhcOptions :: forall m. IOish m => Cradle -> Set (Either FilePath ModuleName) - -> GhcModT m [GHCOption] + -> GhcModT m ([GHCOption],[FilePath]) targetGhcOptions crdl sefnmn = do when (Set.null sefnmn) $ error "targetGhcOptions: no targets given" case cradleProject crdl of proj | isCabalHelperProject proj -> cabalOpts crdl - | otherwise -> sandboxOpts crdl + | otherwise -> do + opts <- sandboxOpts crdl + mappedStrs <- getMMappedFilePaths + return (opts, mappedStrs) where zipMap f l = l `zip` (f `map` l) - cabalOpts :: Cradle -> GhcModT m [String] + cabalOpts :: Cradle -> GhcModT m ([GHCOption],[FilePath]) cabalOpts Cradle{..} = do mcs <- cabalResolvedComponents - + mappedStrs <- getMMappedFilePaths + let mappedComps = zipMap (moduleComponents mcs . Left) mappedStrs let mdlcs = moduleComponents mcs `zipMap` Set.toList sefnmn candidates = findCandidates $ map snd mdlcs @@ -214,15 +273,22 @@ targetGhcOptions crdl sefnmn = do then do -- First component should be ChLibName, if no lib will take lexically first exe. let cns = filter (/= ChSetupHsName) $ Map.keys mcs + cn = head cns + mappedStrsInComp = map fst $ filter (Set.member cn . snd) mappedComps gmLog GmDebug "" $ strDoc $ "Could not find a component assignment, falling back to picking library component in cabal file." - return $ gmcGhcOpts $ fromJustNote "targetGhcOptions, no-assignment" $ Map.lookup (head cns) mcs + let opts = gmcGhcOpts (fromJustNote "targetGhcOptions, no-assignment" $ Map.lookup cn mcs) + ++ ["-Wno-missing-home-modules"] + return (opts, mappedStrsInComp) else do when noCandidates $ throwError $ GMECabalCompAssignment mdlcs let cn = pickComponent candidates - return $ gmcGhcOpts $ fromJustNote "targetGhcOptions" $ Map.lookup cn mcs + mappedStrsInComp = map fst $ filter (Set.member cn . snd) mappedComps + opts = gmcGhcOpts (fromJustNote "targetGhcOptions" $ Map.lookup cn mcs) + ++ ["-Wno-missing-home-modules"] + return (opts, mappedStrsInComp) resolvedComponentsCache :: IOish m => FilePath -> Cached (GhcModT m) GhcModState @@ -311,7 +377,7 @@ packageGhcOptions = do | otherwise -> sandboxOpts crdl -- also works for plain projects! -sandboxOpts :: (IOish m, GmEnv m) => Cradle -> m [String] +sandboxOpts :: (IOish m, GmEnv m) => Cradle -> m [GHCOption] sandboxOpts crdl = do mCusPkgDb <- getCustomPkgDbStack pkgDbStack <- liftIO $ getSandboxPackageDbStack @@ -330,8 +396,10 @@ resolveGmComponent :: (IOish m, Gm m) -> m (GmComponent 'GMCResolved (Set ModulePath)) resolveGmComponent mums c@GmComponent {..} = do distDir <- cradleDistDir <$> cradle + crdl <- cradle + let loadGhcEnv = shouldLoadGhcEnvironment crdl gmLog GmDebug "resolveGmComponent" $ text $ show $ ghcOpts distDir - withLightHscEnv (ghcOpts distDir) $ \env -> do + withLightHscEnv loadGhcEnv (ghcOpts distDir) $ \env -> do let srcDirs = if null gmcSourceDirs then [""] else gmcSourceDirs let mg = gmcHomeModuleGraph let simp = gmcEntrypoints @@ -355,9 +423,9 @@ resolveEntrypoint :: (IOish m, Gm m) => Cradle -> GmComponent 'GMCRaw ChEntrypoint -> m (GmComponent 'GMCRaw (Set ModulePath)) -resolveEntrypoint Cradle {..} c@GmComponent {..} = do +resolveEntrypoint crdl@Cradle {..} c@GmComponent {..} = do gmLog GmDebug "resolveEntrypoint" $ text $ show $ gmcGhcSrcOpts - withLightHscEnv gmcGhcSrcOpts $ \env -> do + withLightHscEnv (shouldLoadGhcEnvironment crdl) gmcGhcSrcOpts $ \env -> do let srcDirs = if null gmcSourceDirs then [""] else gmcSourceDirs eps <- liftIO $ resolveChEntrypoints cradleRootDir gmcEntrypoints rms <- resolveModule env srcDirs `mapM` eps @@ -367,8 +435,8 @@ resolveEntrypoint Cradle {..} c@GmComponent {..} = do -- ghc do the warning about it. Right now we run that module through -- resolveModule like any other resolveChEntrypoints :: FilePath -> ChEntrypoint -> IO [CompilationUnit] -resolveChEntrypoints _ (ChLibEntrypoint em om _) = - return $ map (Right . chModToMod) (em ++ om) +resolveChEntrypoints _ (ChLibEntrypoint em om sm) = + return $ map (Right . chModToMod) (em ++ om ++ sm) resolveChEntrypoints _ (ChExeEntrypoint main om) = return $ [Left main] ++ map (Right . chModToMod) om @@ -448,41 +516,62 @@ resolveGmComponents mcache cs = do same f a b = (f a) == (f b) -- | Set the files as targets and load them. -loadTargets :: IOish m => [GHCOption] -> [FilePath] -> GmlT m () -loadTargets opts targetStrs = do +loadTargets :: IOish m => [GHCOption] -> [FilePath] -> Maybe (GHC.Hooks -> GHC.Hooks) -> GmlT m () +loadTargets opts targetStrs mUpdateHooks = do + crdl <- cradle + let loadGhcEnv = shouldLoadGhcEnvironment crdl targets' <- - withLightHscEnv opts $ \env -> + withLightHscEnv loadGhcEnv opts $ \env -> liftM (nubBy ((==) `on` targetId)) (mapM ((`guessTarget` Nothing) >=> mapFile env) targetStrs) >>= mapM relativize let targets = map (\t -> t { targetAllowObjCode = False }) targets' + targetFileNames = concatMap filePathFromTarget targets gmLog GmDebug "loadTargets" $ text "Loading" <+>: fsep (map (text . showTargetId) targets) + + let filterModSums = isJust mUpdateHooks + gmLog GmDebug "loadTargets" $ + text "filterModSums" <+>: text (show filterModSums) + setTargets targets + when filterModSums $ updateModuleGraph setDynFlagsRecompile targetFileNames + mg <- depanal [] False let interp = needsHscInterpreted mg target <- hscTarget <$> getSessionDynFlags when (interp && target /= HscInterpreted) $ do - _ <- setSessionDynFlags . setHscInterpreted =<< getSessionDynFlags + let + setHooks :: DynFlags -> DynFlags + setHooks df = df { GHC.hooks = (fromMaybe id mUpdateHooks) (GHC.hooks df) } + _ <- setSessionDynFlags . setHscInterpreted . setHooks =<< getSessionDynFlags gmLog GmInfo "loadTargets" $ text "Target needs interpeter, switching to LinkInMemory/HscInterpreted. Perfectly normal if anything is using TemplateHaskell, QuasiQuotes or PatternSynonyms." + when filterModSums $ updateModuleGraph setDynFlagsRecompile targetFileNames + target' <- hscTarget <$> getSessionDynFlags case target' of HscNothing -> do void $ load LoadAllTargets +#if __GLASGOW_HASKELL__ >= 804 + forM_ (mgModSummaries mg) $ +#else forM_ mg $ +#endif handleSourceError (gmLog GmDebug "loadTargets" . text . show) . void . (parseModule >=> typecheckModule >=> desugarModule) HscInterpreted -> do void $ load LoadAllTargets _ -> error ("loadTargets: unsupported hscTarget") + when filterModSums $ updateModuleGraph unSetDynFlagsRecompile targetFileNames + gmLog GmDebug "loadTargets" $ text "Loading done" where @@ -496,8 +585,41 @@ loadTargets opts targetStrs = do showTargetId (Target (TargetModule s) _ _) = moduleNameString s showTargetId (Target (TargetFile s _) _ _) = s + filePathFromTarget (Target (TargetModule _) _ _) = [] + filePathFromTarget (Target (TargetFile s _) _ _) = [s] + + updateModuleGraph :: (GhcMonad m, GmState m, GmEnv m, + MonadIO m, GmLog m, GmOut m) + => (DynFlags -> DynFlags) -> [FilePath] -> m () + updateModuleGraph df fps = do + let + fpSet = Set.fromList fps + updateHooks df = df { GHC.hooks = (fromMaybe id mUpdateHooks) (GHC.hooks df)} + mustRecompile ms = case (ml_hs_file . ms_location) ms of + Nothing -> ms + Just f -> if Set.member f fpSet + then ms {ms_hspp_opts = (df . updateHooks) (ms_hspp_opts ms)} + else ms +#if __GLASGOW_HASKELL__ >= 804 + update s = s {hsc_mod_graph = mkModuleGraph $ map mustRecompile (mgModSummaries $ hsc_mod_graph s)} +#else + update s = s {hsc_mod_graph = map mustRecompile (hsc_mod_graph s)} +#endif + G.modifySession update + + setDynFlagsRecompile :: DynFlags -> DynFlags + setDynFlagsRecompile df = gopt_set df Opt_ForceRecomp + + unSetDynFlagsRecompile :: DynFlags -> DynFlags + unSetDynFlagsRecompile df = gopt_unset df Opt_ForceRecomp + needsHscInterpreted :: ModuleGraph -> Bool +#if __GLASGOW_HASKELL__ >= 804 +needsHscInterpreted mg = foo (mgModSummaries mg) + where foo = any $ \ms -> +#else needsHscInterpreted = any $ \ms -> +#endif let df = ms_hspp_opts ms in #if __GLASGOW_HASKELL__ >= 800 TemplateHaskell `xopt` df @@ -515,6 +637,10 @@ cabalResolvedComponents :: (IOish m) => GhcModT m (Map ChComponentName (GmComponent 'GMCResolved (Set ModulePath))) cabalResolvedComponents = do crdl@(Cradle{..}) <- cradle - comps <- mapM (resolveEntrypoint crdl) =<< getComponents - withAutogen $ - cached cradleRootDir (resolvedComponentsCache cradleDistDir) comps + r <- withProjSetup $ \ps -> do + comps <- mapM (resolveEntrypoint crdl) =<< (getComponents ps) + withAutogen ps $ + cached cradleRootDir (resolvedComponentsCache cradleDistDir) comps + case r of + Just foo -> return foo + Nothing -> error "cabalResolvedComponents" diff --git a/core/GhcMod/Types.hs b/core/GhcMod/Types.hs index 06d3a625a..98450dea4 100644 --- a/core/GhcMod/Types.hs +++ b/core/GhcMod/Types.hs @@ -19,6 +19,7 @@ import Control.Monad import Control.DeepSeq import Data.Binary import Data.Binary.Generic +import Data.Dynamic (Dynamic) import Data.Map (Map) import qualified Data.Map as Map import Data.Set (Set) @@ -30,6 +31,7 @@ import Data.IORef import Data.Label.Derive import Distribution.Helper hiding (Programs(..)) import qualified Distribution.Helper as CabalHelper +-- CabalHelper.Shared.InterfaceTypes import Exception (ExceptionMonad) #if __GLASGOW_HASKELL__ < 708 import qualified MonadUtils as GHC (MonadIO(..)) @@ -133,6 +135,7 @@ defaultOptions = Options { ---------------------------------------------------------------- data Project = CabalProject + | CabalNewProject | SandboxProject | PlainProject | StackProject StackEnv @@ -141,6 +144,7 @@ data Project = CabalProject isCabalHelperProject :: Project -> Bool isCabalHelperProject StackProject {} = True isCabalHelperProject CabalProject {} = True +isCabalHelperProject CabalNewProject {} = True isCabalHelperProject _ = False data StackEnv = StackEnv { @@ -163,7 +167,19 @@ data Cradle = Cradle { , cradleCabalFile :: Maybe FilePath -- | The build info directory. , cradleDistDir :: FilePath - } deriving (Eq, Show, Ord) + , cradleQueryEnv :: !(Maybe Dynamic) -- QueryEnv pt + } + -- } deriving (Eq, Ord, Show) + +data ProjSetup (pt :: ProjType) = + ProjSetup + { psDistDir :: FilePath -> DistDir pt + , psProjDir :: FilePath -> ProjLoc pt + } + +data LoadGhcEnvironment = LoadGhcEnvironment + | DontLoadGhcEnvironment + deriving (Show,Eq) data GmStream = GmOutStream | GmErrStream deriving (Show) @@ -184,6 +200,11 @@ data GhcModLog = GhcModLog { gmLogMessages :: [(GmLogLevel, String, Doc)] } deriving (Show) +#if __GLASGOW_HASKELL__ >= 804 +instance Semigroup GhcModLog where + (<>) = mappend +#endif + instance Monoid GhcModLog where mempty = GhcModLog (Just GmPanic) (Last Nothing) mempty GhcModLog ml vd ls `mappend` GhcModLog ml' vd' ls' = @@ -206,6 +227,7 @@ data GhcModState = GhcModState { gmGhcSession :: !(Maybe GmGhcSession) , gmCaches :: !GhcModCaches , gmMMappedFiles :: !FileMappingMap + -- , gmQueryEnvs :: ! } defaultGhcModState :: GhcModState @@ -275,6 +297,11 @@ instance Binary GmModuleGraph where swapMap :: Ord v => Map k v -> Map v k swapMap = Map.fromList . map (\(x, y) -> (y, x)) . Map.toList +#if __GLASGOW_HASKELL__ >= 804 +instance Semigroup GmModuleGraph where + (<>) = mappend +#endif + instance Monoid GmModuleGraph where mempty = GmModuleGraph mempty mappend (GmModuleGraph a) (GmModuleGraph a') = @@ -283,20 +310,21 @@ instance Monoid GmModuleGraph where data GmComponentType = GMCRaw | GMCResolved data GmComponent (t :: GmComponentType) eps = GmComponent { - gmcHomeModuleGraph :: GmModuleGraph - , gmcGhcOpts :: [GHCOption] - , gmcGhcPkgOpts :: [GHCOption] - , gmcGhcSrcOpts :: [GHCOption] - , gmcGhcLangOpts :: [GHCOption] - , gmcRawEntrypoints :: ChEntrypoint - , gmcEntrypoints :: eps - , gmcSourceDirs :: [FilePath] - , gmcName :: ChComponentName + gmcHomeModuleGraph :: GmModuleGraph + , gmcGhcOpts :: [GHCOption] + , gmcGhcPkgOpts :: [GHCOption] + , gmcGhcSrcOpts :: [GHCOption] + , gmcGhcLangOpts :: [GHCOption] + , gmcRawEntrypoints :: ChEntrypoint + , gmcEntrypoints :: eps + , gmcSourceDirs :: [FilePath] + , gmcName :: ChComponentName + , gmcNeedsBuildOutput :: NeedsBuildOutput } deriving (Eq, Ord, Show, Read, Generic, Functor) -instance Binary eps => Binary (GmComponent t eps) where - put = ggput . from - get = to `fmap` ggget +-- instance Binary eps => Binary (GmComponent t eps) where +-- put = ggput . from +-- get = to `fmap` ggget data ModulePath = ModulePath { mpModule :: ModuleName, mpPath :: FilePath } deriving (Eq, Ord, Show, Read, Generic, Typeable) @@ -372,6 +400,18 @@ instance Binary ChComponentName where instance Binary ChEntrypoint where put = ggput . from get = to `fmap` ggget +instance Binary CabalHelper.ChLibraryName where + put = ggput . from + get = to `fmap` ggget +instance Binary NeedsBuildOutput where + put = ggput . from + get = to `fmap` ggget +instance Binary (GmComponent 'GMCResolved (Set ModulePath)) where + put = ggput . from + get = to `fmap` ggget +instance Binary (GmComponent 'GMCRaw (Set ModulePath)) where + put = ggput . from + get = to `fmap` ggget -- | Options for "lintWith" function data LintOpts = LintOpts { diff --git a/core/GhcMod/Utils.hs b/core/GhcMod/Utils.hs index 7c282357b..0d18ce81c 100644 --- a/core/GhcMod/Utils.hs +++ b/core/GhcMod/Utils.hs @@ -113,7 +113,7 @@ withMappedFile file action = getCanonicalFileNameSafe file >>= lookupMMappedFile runWithFile (Just to) = action $ fmPath to runWithFile _ = action file -getCanonicalFileNameSafe :: (IOish m, GmEnv m) => FilePath -> m FilePath +getCanonicalFileNameSafe :: (IOish m) => FilePath -> m FilePath getCanonicalFileNameSafe fn = do let fn' = normalise fn pl <- liftIO $ rights <$> (mapM ((try :: IO FilePath -> IO (Either SomeException FilePath)) . canonicalizePath . joinPath) $ reverse $ inits $ splitPath' fn') diff --git a/core/GhcModCore.hs b/core/GhcModCore.hs index a7ba32d13..d010879c5 100644 --- a/core/GhcModCore.hs +++ b/core/GhcModCore.hs @@ -12,7 +12,7 @@ module GhcModCore ( , FileMapping(..) , defaultOptions -- * Logging - , GmLogLevel + , GmLogLevel(..) , increaseLogLevel , decreaseLogLevel , gmSetLogLevel @@ -27,6 +27,7 @@ module GhcModCore ( -- * Monad Types , GhcModT , IOish + , MonadIO(..) -- * Monad utilities , runGhcModT , withOptions @@ -40,12 +41,64 @@ module GhcModCore ( , loadMappedFile , loadMappedFileSource , unloadMappedFile + -- * HIE integration utilities + , getModulesGhc + , getModulesGhc' + -- * Manage GHC AST index vars for older GHCs + , GhcPs,GhcRn,GhcTc + -- Temporary home, see what there is, before strippint out + , findCradle' + , GmEnv + , gmeLocal + , gmCradle + , mkRevRedirMapFunc + , cradle + , GmOut(..) + , options + , GmLog(..) + , makeAbsolute' + , withMappedFile + , listVisibleModuleNames + , runLightGhc + , GHandler(..) + , gcatches + , GmlT(..) + , defaultLintOpts + , gmsGet + , gmGhcSession + , gmgsSession + , getMMappedFiles + , withDynFlags + , ghcExceptionDoc + , mkErrStyle' + , renderGm + , LightGhc(..) + , OutputOpts(..) + , gmlGetSession + , gmlSetSession + , cabalResolvedComponents + , ModulePath(..) + , GmComponent(..) + , GmComponentType(..) + , GmModuleGraph(..) + + -- * Used in ghc-mod + , convert ) where import GhcMod.Cradle import GhcMod.FileMapping import GhcMod.Logging import GhcMod.Monad +import GhcMod.ModuleLoader import GhcMod.Output import GhcMod.Target import GhcMod.Types + +import GhcMod.Gap (GhcPs,GhcRn,GhcTc,listVisibleModuleNames,mkErrStyle') +import GhcMod.Utils (mkRevRedirMapFunc,makeAbsolute',withMappedFile) +import GhcMod.LightGhc (runLightGhc) +import GhcMod.Error (GHandler(..),gcatches,ghcExceptionDoc) +-- import GhcMod.SrcUtils (pretty,collectAllSpansTypes) +import GhcMod.DynFlags (withDynFlags) +import GhcMod.Convert ( convert ) diff --git a/core/LICENSE b/core/LICENSE new file mode 100644 index 000000000..9f26b637f --- /dev/null +++ b/core/LICENSE @@ -0,0 +1 @@ +Foo \ No newline at end of file diff --git a/core/ghc-mod-core.cabal b/core/ghc-mod-core.cabal index e077f3a61..16ca8a471 100644 --- a/core/ghc-mod-core.cabal +++ b/core/ghc-mod-core.cabal @@ -26,6 +26,7 @@ Description: Category: GHC, Development Cabal-Version: >= 1.24 Build-Type: Simple +Extra-Source-Files: GhcMod/Monad/Compat.hs_h Library Default-Language: Haskell2010 @@ -54,6 +55,7 @@ Library GhcMod.LightGhc GhcMod.Logger GhcMod.Logging + GhcMod.ModuleLoader GhcMod.Monad GhcMod.Monad.Env GhcMod.Monad.Log @@ -69,7 +71,6 @@ Library GhcMod.PathsAndFiles GhcMod.Pretty GhcMod.Read - GhcMod.SrcUtils GhcMod.Stack GhcMod.Target GhcMod.Types @@ -88,40 +89,35 @@ Library , directory , filepath , mtl - , old-time , process , template-haskell , time , transformers - , base < 4.11 && >= 4.6.0.1 - , djinn-ghc < 0.1 && >= 0.0.2.2 + , base < 4.14 && >= 4.6.0.1 , extra < 1.7 && >= 1.4 , fclabels < 2.1 && >= 2.0 - , fingertree < 0.2 && >= 0.1.1.0 , ghc-paths < 0.2 && >= 0.1.0.9 - , ghc-syb-utils < 0.3 && >= 0.2.3 - , haskell-src-exts < 1.20 && >= 1.18 - , hlint < 3.0 && >= 2.0.8 + -- , haskell-src-exts < 1.22 && >= 1.18 , monad-control < 1.1 && >= 1 , monad-journal < 0.9 && >= 0.4 , optparse-applicative < 0.15 && >= 0.13.0.0 , pipes < 4.4 && >= 4.1 , safe < 0.4 && >= 0.3.9 - , semigroups < 0.19 && >= 0.10.0 , split < 0.3 && >= 0.2.2 , syb < 0.8 && >= 0.5.1 , temporary < 1.3 && >= 1.2.0.3 - , text < 1.3 && >= 1.2.1.3 , transformers-base < 0.5 && >= 0.4.4 - , cabal-helper < 0.9 && >= 0.8.0.2 - , ghc < 8.4 && >= 7.6 + , cabal-helper < 1.10 && >= 1.0.0.0 + , ghc < 8.7 && >= 7.6 if impl(ghc >= 8.0) Build-Depends: ghc-boot if impl(ghc < 7.8) Build-Depends: convertible < 1.2 && >= 1.1.0.0 + if impl(ghc < 8.4) + Build-Depends: ghc-syb-utils < 0.3 && >= 0.2.3 Source-Repository head Type: git diff --git a/ghc-mod.cabal b/ghc-mod.cabal index 71ebb09b4..c01b6302d 100644 --- a/ghc-mod.cabal +++ b/ghc-mod.cabal @@ -30,7 +30,7 @@ Data-Files: elisp/Makefile elisp/*.el Extra-Source-Files: ChangeLog README.md - core/GhcMod/Monad/Compat.hs_h + -- core/GhcMod/Monad/Compat.hs_h test/data/annotations/*.hs test/data/broken-cabal/*.cabal test/data/broken-sandbox/cabal.sandbox.config @@ -92,7 +92,7 @@ Extra-Source-Files: ChangeLog Custom-Setup Setup-Depends: base - , Cabal < 2.1 && >= 1.24 + , Cabal < 2.5 && >= 1.24 , cabal-doctest < 1.1 && >= 1 Library @@ -119,6 +119,7 @@ Library GhcMod.Exe.Modules GhcMod.Exe.PkgDoc GhcMod.Exe.Test + GhcMod.SrcUtils Other-Modules: Paths_ghc_mod Build-Depends: -- See Note [GHC Boot libraries] @@ -135,33 +136,33 @@ Library , time , transformers - , base < 4.11 && >= 4.6.0.1 - , djinn-ghc < 0.1 && >= 0.0.2.2 - , extra < 1.6 && >= 1.4 + , base < 4.14 && >= 4.6.0.1 + -- , djinn-ghc < 0.1 && >= 0.0.2.2 + , extra < 1.7 && >= 1.4 , fclabels < 2.1 && >= 2.0 , ghc-paths < 0.2 && >= 0.1.0.9 - , ghc-syb-utils < 0.3 && >= 0.2.3 , ghc-mod-core == 5.9.0.0 - , haskell-src-exts < 1.20 && >= 1.18 - , hlint < 2.1 && >= 2.0.8 + , haskell-src-exts < 1.22 && >= 1.18 + , hlint < 2.2 && >= 2.0.8 , monad-control < 1.1 && >= 1 - , monad-journal < 0.8 && >= 0.4 - , optparse-applicative < 0.14 && >= 0.13.0.0 + , monad-journal < 0.9 && >= 0.4 + , optparse-applicative < 0.15 && >= 0.13.0.0 , pipes < 4.4 && >= 4.1 , safe < 0.4 && >= 0.3.9 , semigroups < 0.19 && >= 0.10.0 , split < 0.3 && >= 0.2.2 , syb < 0.8 && >= 0.5.1 , temporary < 1.3 && >= 1.2.0.3 - , text < 1.3 && >= 1.2.1.3 + , text < 1.4 && >= 1.2.1.3 , transformers-base < 0.5 && >= 0.4.4 - , cabal-helper < 0.9 && >= 0.8.0.0 - , ghc < 8.4 && >= 7.8 - , ghc-mod-core + , cabal-helper < 1.10 && >= 1.0.0.0 + , ghc < 8.7 && >= 7.8 if impl(ghc >= 8.0) Build-Depends: ghc-boot + if impl(ghc < 8.4) + Build-Depends: ghc-syb-utils < 0.3 && >= 0.2.3 Executable ghc-mod Default-Language: Haskell2010 @@ -182,14 +183,14 @@ Executable ghc-mod , mtl , process - , base < 4.11 && >= 4.6.0.1 + , base < 4.14 && >= 4.6.0.1 , fclabels < 2.1 && >= 2.0 , monad-control < 1.1 && >= 1 - , optparse-applicative < 0.14 && >= 0.13.0.0 + , optparse-applicative < 0.15 && >= 0.13.0.0 , semigroups < 0.19 && >= 0.10.0 , split < 0.3 && >= 0.2.2 - , ghc < 8.4 && >= 7.8 + , ghc < 8.7 && >= 7.8 , ghc-mod , ghc-mod-core @@ -213,7 +214,7 @@ Executable ghc-modi , process , time - , base < 4.11 && >= 4.6.0.1 + , base < 4.14 && >= 4.6.0.1 , ghc-mod , ghc-mod-core @@ -226,8 +227,8 @@ Test-Suite doctest Ghc-Options: -Wall Default-Extensions: ConstraintKinds, FlexibleContexts Main-Is: doctests.hs - Build-Depends: base < 4.11 && >= 4.6.0.1 - , doctest < 0.14 && >= 0.11.3 + Build-Depends: base < 4.14 && >= 4.6.0.1 + , doctest < 0.17 && >= 0.11.3 Test-Suite spec Default-Language: Haskell2010 @@ -273,22 +274,21 @@ Test-Suite spec , process , transformers - , base < 4.11 && >= 4.6.0.1 + , base < 4.14 && >= 4.6.0.1 , fclabels < 2.1 && >= 2.0 - , hspec < 2.5 && >= 2.0.0 - , monad-journal < 0.8 && >= 0.4 + , hspec < 2.6 && >= 2.0.0 + , monad-journal < 0.9 && >= 0.4 , split < 0.3 && >= 0.2.2 , temporary < 1.3 && >= 1.2.0.3 - if impl(ghc < 7.8) Build-Depends: convertible < 1.2 && >= 1.1.0.0 if impl(ghc >= 8.0) Build-Depends: ghc-boot Build-Depends: - cabal-helper < 0.9 && >= 0.8.0.0 - , ghc < 8.4 && >= 7.8 + cabal-helper < 1.10 && >= 1.0.0.0 + , ghc < 8.7 && >= 7.8 , ghc-mod , ghc-mod-core @@ -300,7 +300,7 @@ Test-Suite shelltest Type: exitcode-stdio-1.0 if flag(shelltest) Build-Tools: shelltest - Build-Depends: base < 4.11 && >= 4.6.0.1 + Build-Depends: base < 4.14 && >= 4.6.0.1 , process < 1.5 -- , shelltestrunner >= 1.3.5 if !flag(shelltest) @@ -320,7 +320,7 @@ Benchmark criterion directory , filepath - , base < 4.11 && >= 4.6.0.1 + , base < 4.14 && >= 4.6.0.1 , criterion < 1.2 && >= 1.1.1.0 , temporary < 1.3 && >= 1.2.0.3 diff --git a/rundocker.sh b/rundocker.sh new file mode 100755 index 000000000..6a938c125 --- /dev/null +++ b/rundocker.sh @@ -0,0 +1,6 @@ +#!/bin/sh + +# Start up a docker vm using the gitlab CI container + +# docker run --rm -i -t registry.gitlab.com/dxld/ghc-mod:ghc8.2.1-cabal-install2.0.0.0 /bin/bash +docker run --rm -i -t -v `pwd`:/root registry.gitlab.com/dxld/ghc-mod:ghc8.2.1-cabal-install2.0.0.0 /bin/bash diff --git a/src/GhcMod/Exe/Options/Commands.hs b/src/GhcMod/Exe/Options/Commands.hs index cd8ef550a..e4929246b 100644 --- a/src/GhcMod/Exe/Options/Commands.hs +++ b/src/GhcMod/Exe/Options/Commands.hs @@ -13,12 +13,15 @@ -- -- You should have received a copy of the GNU Affero General Public License -- along with this program. If not, see . +{-# LANGUAGE CPP #-} {-# LANGUAGE OverloadedStrings #-} {-# OPTIONS_GHC -fno-warn-unused-do-bind #-} module GhcMod.Exe.Options.Commands where +#if __GLASGOW_HASKELL__ < 804 import Data.Semigroup +#endif import Options.Applicative import Options.Applicative.Types import Options.Applicative.Builder.Internal @@ -55,7 +58,7 @@ data GhcModCommands = | CmdType Bool FilePath Point | CmdSplit FilePath Point | CmdSig FilePath Point - | CmdAuto FilePath Point + -- | CmdAuto FilePath Point | CmdRefine FilePath Point Expr | CmdTest FilePath -- interactive-only commands @@ -167,9 +170,9 @@ commands = "ghc-mod would add the following on the next line:" code "func x y z f = _func_body" "(See: https://github.com/DanielG/ghc-mod/pull/274)" - <> command "auto" - $$ info autoArgSpec - $$ progDesc "Try to automatically fill the contents of a hole" + -- <> command "auto" + -- $$ info autoArgSpec + -- $$ progDesc "Try to automatically fill the contents of a hole" <> command "refine" $$ info refineArgSpec $$ progDesc "Refine the typed hole at (LINE,COL) given EXPR" @@ -229,7 +232,7 @@ locArgSpec x = x modulesArgSpec, docArgSpec, findArgSpec, lintArgSpec, browseArgSpec, checkArgSpec, expandArgSpec, - infoArgSpec, typeArgSpec, autoArgSpec, splitArgSpec, + infoArgSpec, typeArgSpec, splitArgSpec, sigArgSpec, refineArgSpec, debugComponentArgSpec, mapArgSpec, unmapArgSpec, legacyInteractiveArgSpec :: Parser GhcModCommands @@ -277,7 +280,7 @@ typeArgSpec = locArgSpec $ CmdType <$> $$ long "constraints" <=> short 'c' <=> help "Include constraints into type signature" -autoArgSpec = locArgSpec (pure CmdAuto) +-- autoArgSpec = locArgSpec (pure CmdAuto) splitArgSpec = locArgSpec (pure CmdSplit) sigArgSpec = locArgSpec (pure CmdSig) refineArgSpec = locArgSpec (pure CmdRefine) <*> strArg "SYMBOL" diff --git a/src/GhcModMain.hs b/src/GhcModMain.hs index d4c011661..4517de98e 100644 --- a/src/GhcModMain.hs +++ b/src/GhcModMain.hs @@ -48,11 +48,12 @@ progMain (globalOptions, commands) = runGmOutT globalOptions $ -- ghc-modi legacyInteractive :: IOish m => GhcModT m () -legacyInteractive = do - prepareCabalHelper - asyncSymbolDb <- newAsyncSymbolDb - world <- getCurrentWorld - legacyInteractiveLoop asyncSymbolDb world +legacyInteractive = undefined +-- legacyInteractive = do +-- prepareCabalHelper +-- asyncSymbolDb <- newAsyncSymbolDb +-- world <- getCurrentWorld +-- legacyInteractiveLoop asyncSymbolDb world legacyInteractiveLoop :: IOish m => AsyncSymbolDb -> World -> GhcModT m () legacyInteractiveLoop asyncSymbolDb world = do @@ -151,7 +152,7 @@ ghcCommands (CmdInfo file symb) = info file $ Expression symb ghcCommands (CmdType wCon file (line, col)) = types wCon file line col ghcCommands (CmdSplit file (line, col)) = splits file line col ghcCommands (CmdSig file (line, col)) = sig file line col -ghcCommands (CmdAuto file (line, col)) = auto file line col +-- ghcCommands (CmdAuto file (line, col)) = auto file line col ghcCommands (CmdRefine file (line, col) expr) = refine file line col $ Expression expr -- interactive-only commands ghcCommands (CmdMapFile f) = diff --git a/test/CabalHelperSpec.hs b/test/CabalHelperSpec.hs index f2119b024..43cd33506 100644 --- a/test/CabalHelperSpec.hs +++ b/test/CabalHelperSpec.hs @@ -86,7 +86,7 @@ spec = do opts <- map gmcGhcOpts <$> runD' tdir getComponents let ghcOpts = head opts pkgs = pkgOptions ghcOpts - pkgs `shouldBe` ["Cabal","base"] + pkgs `shouldBe` ["base","Cabal"] test diff --git a/test/CheckSpec.hs b/test/CheckSpec.hs index efe024cd2..c14d1603c 100644 --- a/test/CheckSpec.hs +++ b/test/CheckSpec.hs @@ -64,8 +64,10 @@ spec = do it "works with cabal builtin preprocessors" $ do withDirectory_ "test/data/cabal-preprocessors" $ do _ <- system "cabal clean" - _ <- system "cabal build" - res <- runD $ checkSyntax ["Main.hs"] + -- _ <- system "cabal build" + _ <- system "cabal build -v3" + -- res <- runD $ checkSyntax ["Main.hs"] + res <- runV $ checkSyntax ["Main.hs"] res `shouldBe` "Preprocessed.hsc:3:1:Warning: Top-level binding with no type signature: warning :: ()\n" it "Uses the right qualification style" $ do diff --git a/test/CustomPackageDbSpec.hs b/test/CustomPackageDbSpec.hs index f25d59a5c..a1a3207e8 100644 --- a/test/CustomPackageDbSpec.hs +++ b/test/CustomPackageDbSpec.hs @@ -28,7 +28,10 @@ spec = do withDirectory_ "test/data/custom-cradle" $ do _ <- system "cabal configure" (s, s') <- runD $ do - Just stack <- getCustomPkgDbStack + mstack <- getCustomPkgDbStack + stack <- case mstack of + Just stack -> return stack + _ -> error "match failed" withCabal $ do stack' <- getCabalPackageDbStack return (stack, stack') diff --git a/test/GhcPkgSpec.hs b/test/GhcPkgSpec.hs index 19a160491..8ecdb4dbf 100644 --- a/test/GhcPkgSpec.hs +++ b/test/GhcPkgSpec.hs @@ -16,7 +16,10 @@ spec = do withDirectory_ "test/data/custom-cradle" $ do _ <- system "cabal configure" (s, s') <- runD $ do - Just stack <- getCustomPkgDbStack + mstack <- getCustomPkgDbStack + stack <- case mstack of + Just stack -> return stack + _ -> error "match failed" withCabal $ do stack' <- getPackageDbStack return (stack, stack') diff --git a/test/MonadSpec.hs b/test/MonadSpec.hs index 1110e7666..be02b2a1d 100644 --- a/test/MonadSpec.hs +++ b/test/MonadSpec.hs @@ -1,3 +1,4 @@ +{-# LANGUAGe CPP #-} module MonadSpec where import Test.Hspec @@ -13,8 +14,14 @@ spec = do (a, _h) <- runGmOutDef $ runGhcModT defaultOptions $ do +#if __GLASGOW_HASKELL__ >= 806 + mj <- return Nothing + case mj of + Just _ -> return "hello" + Nothing -> fail "oh noes" +#else Just _ <- return Nothing - return "hello" +#endif `catchError` (const $ fail "oh noes") a `shouldBe` (Left $ GMEString "oh noes") diff --git a/test/TestUtils.hs b/test/TestUtils.hs index 3d252f151..9be17968c 100644 --- a/test/TestUtils.hs +++ b/test/TestUtils.hs @@ -3,6 +3,7 @@ module TestUtils ( run , runD , runD' + , runV , runE , runNullLog , runGmOutDef @@ -77,6 +78,11 @@ runD' :: FilePath -> GhcModT IO a -> IO a runD' dir = extract . runGhcModTSpec' dir (setLogLevel testLogLevel defaultOptions) +-- | Run GhcMod with default options, and vomit log level +runV :: GhcModT IO a -> IO a +runV = + extract . runGhcModTSpec (setLogLevel GmVomit defaultOptions) + setLogLevel :: GmLogLevel -> Options -> Options setLogLevel = set (lOoptLogLevel . lOptOutput)