Skip to content
This repository was archived by the owner on Oct 7, 2020. It is now read-only.

Commit f22b2dd

Browse files
authored
Merge pull request #1186 from fendor/typemap-reimplementation
Typemap reimplementation
2 parents 69878c9 + a5be92e commit f22b2dd

File tree

7 files changed

+715
-17
lines changed

7 files changed

+715
-17
lines changed

hie-plugin-api/Haskell/Ide/Engine/ArtifactMap.hs

-10
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ import GHC (TypecheckedModule)
1010
import qualified SrcLoc as GHC
1111
import qualified Var
1212
import qualified GhcMod.Gap as GM
13-
import GhcMod.SrcUtils
1413

1514
import Language.Haskell.LSP.Types
1615

@@ -33,15 +32,6 @@ genIntervalMap ts = foldr go IM.empty ts
3332

3433
-- ---------------------------------------------------------------------
3534

36-
genTypeMap :: GHC.GhcMonad m => TypecheckedModule -> m TypeMap
37-
genTypeMap tm = do
38-
ts <- collectAllSpansTypes True tm
39-
return $ foldr go IM.empty ts
40-
where
41-
go (GHC.RealSrcSpan spn, typ) im =
42-
IM.insert (rspToInt spn) typ im
43-
go _ im = im
44-
4535
-- | Generates a LocMap from a TypecheckedModule,
4636
-- which allows fast queries for all the symbols
4737
-- located at a particular point in the source

hie-plugin-api/Haskell/Ide/Engine/Compat.hs

+113
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,14 @@
11
{-# LANGUAGE CPP #-}
2+
{-# LANGUAGE PatternSynonyms #-}
3+
{-# LANGUAGE ViewPatterns #-}
24
module Haskell.Ide.Engine.Compat where
35

6+
import qualified GHC
7+
import qualified Type
8+
import qualified TcHsSyn
9+
import qualified TysWiredIn
10+
import qualified Var
11+
412
#if MIN_VERSION_filepath(1,4,2)
513
#else
614
import Data.List
@@ -27,3 +35,108 @@ isExtensionOf :: String -> FilePath -> Bool
2735
isExtensionOf ext@('.':_) = isSuffixOf ext . takeExtensions
2836
isExtensionOf ext = isSuffixOf ('.':ext) . takeExtensions
2937
#endif
38+
39+
40+
#if MIN_VERSION_ghc(8, 4, 0)
41+
type GhcTc = GHC.GhcTc
42+
#else
43+
type GhcTc = GHC.Id
44+
#endif
45+
46+
pattern HsOverLitType :: Type.Type -> GHC.HsExpr GhcTc
47+
pattern HsOverLitType t <-
48+
#if MIN_VERSION_ghc(8, 6, 0)
49+
GHC.HsOverLit _ (GHC.overLitType -> t)
50+
#elif MIN_VERSION_ghc(8, 4, 0)
51+
GHC.HsOverLit (GHC.overLitType -> t)
52+
#else
53+
GHC.HsOverLit (GHC.overLitType -> t)
54+
#endif
55+
56+
pattern HsLitType :: Type.Type -> GHC.HsExpr GhcTc
57+
pattern HsLitType t <-
58+
#if MIN_VERSION_ghc(8, 6, 0)
59+
GHC.HsLit _ (TcHsSyn.hsLitType -> t)
60+
#elif MIN_VERSION_ghc(8, 4, 0)
61+
GHC.HsLit (TcHsSyn.hsLitType -> t)
62+
#else
63+
GHC.HsLit (TcHsSyn.hsLitType -> t)
64+
#endif
65+
66+
pattern HsLamType :: Type.Type -> GHC.HsExpr GhcTc
67+
pattern HsLamType t <-
68+
#if MIN_VERSION_ghc(8, 6, 0)
69+
GHC.HsLam _ ((\(GHC.MG { GHC.mg_ext = groupTy }) -> matchGroupType groupTy) -> t)
70+
#elif MIN_VERSION_ghc(8, 4, 0)
71+
GHC.HsLam (\GHC.MG { GHC.mg_res_ty = res, GHC.mg_arg_tys = args } -> Type.mkFunTys args res -> t)
72+
#else
73+
GHC.HsLam (\GHC.MG { GHC.mg_res_ty = res, GHC.mg_arg_tys = args } -> Type.mkFunTys args res -> t)
74+
#endif
75+
76+
pattern HsLamCaseType :: Type.Type -> GHC.HsExpr GhcTc
77+
pattern HsLamCaseType t <-
78+
#if MIN_VERSION_ghc(8, 6, 0)
79+
GHC.HsLamCase _ ((\(GHC.MG { GHC.mg_ext = groupTy }) -> matchGroupType groupTy) -> t)
80+
#elif MIN_VERSION_ghc(8, 4, 0)
81+
GHC.HsLamCase (\GHC.MG { GHC.mg_res_ty = res, GHC.mg_arg_tys = args } -> Type.mkFunTys args res -> t)
82+
#else
83+
GHC.HsLamCase (\GHC.MG { GHC.mg_res_ty = res, GHC.mg_arg_tys = args } -> Type.mkFunTys args res -> t)
84+
#endif
85+
86+
pattern HsCaseType :: Type.Type -> GHC.HsExpr GhcTc
87+
pattern HsCaseType t <-
88+
#if MIN_VERSION_ghc(8, 6, 0)
89+
GHC.HsCase _ _ ((\(GHC.MG { GHC.mg_ext = groupTy }) -> matchGroupType groupTy) -> t)
90+
#elif MIN_VERSION_ghc(8, 4, 0)
91+
GHC.HsCase _ (\GHC.MG { GHC.mg_res_ty = res, GHC.mg_arg_tys = args } -> Type.mkFunTys args res -> t)
92+
#else
93+
GHC.HsCase _ (\GHC.MG { GHC.mg_res_ty = res, GHC.mg_arg_tys = args } -> Type.mkFunTys args res -> t)
94+
#endif
95+
96+
pattern ExplicitListType :: Type.Type -> GHC.HsExpr GhcTc
97+
pattern ExplicitListType t <-
98+
#if MIN_VERSION_ghc(8, 6, 0)
99+
GHC.ExplicitList (TysWiredIn.mkListTy -> t) _ _
100+
#elif MIN_VERSION_ghc(8, 4, 0)
101+
GHC.ExplicitList (TysWiredIn.mkListTy -> t) _ _
102+
#else
103+
GHC.ExplicitList (TysWiredIn.mkListTy -> t) _ _
104+
#endif
105+
106+
pattern ExplicitSumType :: Type.Type -> GHC.HsExpr GhcTc
107+
pattern ExplicitSumType t <-
108+
#if MIN_VERSION_ghc(8, 6, 0)
109+
GHC.ExplicitSum (TysWiredIn.mkSumTy -> t) _ _ _
110+
#elif MIN_VERSION_ghc(8, 4, 0)
111+
GHC.ExplicitSum _ _ _ (TysWiredIn.mkSumTy -> t)
112+
#else
113+
GHC.ExplicitSum _ _ _ (TysWiredIn.mkSumTy -> t)
114+
#endif
115+
116+
117+
pattern HsMultiIfType :: Type.Type -> GHC.HsExpr GhcTc
118+
pattern HsMultiIfType t <-
119+
#if MIN_VERSION_ghc(8, 6, 0)
120+
GHC.HsMultiIf t _
121+
#elif MIN_VERSION_ghc(8, 4, 0)
122+
GHC.HsMultiIf t _
123+
#else
124+
GHC.HsMultiIf t _
125+
#endif
126+
127+
pattern FunBindType :: Type.Type -> GHC.HsBindLR GhcTc GhcTc
128+
pattern FunBindType t <-
129+
#if MIN_VERSION_ghc(8, 6, 0)
130+
GHC.FunBind _ (GHC.L _ (Var.varType -> t)) _ _ _
131+
#elif MIN_VERSION_ghc(8, 4, 0)
132+
GHC.FunBind (GHC.L _ (Var.varType -> t)) _ _ _ _
133+
#else
134+
GHC.FunBind (GHC.L _ (Var.varType -> t)) _ _ _ _
135+
#endif
136+
137+
138+
#if MIN_VERSION_ghc(8, 6, 0)
139+
matchGroupType :: GHC.MatchGroupTc -> GHC.Type
140+
matchGroupType (GHC.MatchGroupTc args res) = Type.mkFunTys args res
141+
#endif
142+

hie-plugin-api/Haskell/Ide/Engine/ModuleCache.hs

+1
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ import qualified GhcMod.Utils as GM
4242
import qualified GHC as GHC
4343

4444
import Haskell.Ide.Engine.ArtifactMap
45+
import Haskell.Ide.Engine.TypeMap
4546
import Haskell.Ide.Engine.GhcModuleCache
4647
import Haskell.Ide.Engine.MultiThreadState
4748
import Haskell.Ide.Engine.PluginsIdeMonads
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
1+
{-# LANGUAGE TupleSections #-}
2+
{-# LANGUAGE AllowAmbiguousTypes #-}
3+
{-# LANGUAGE RankNTypes #-}
4+
{-# LANGUAGE ScopedTypeVariables #-}
5+
{-# LANGUAGE TypeApplications #-}
6+
{-# LANGUAGE LambdaCase #-}
7+
module Haskell.Ide.Engine.TypeMap where
8+
9+
import qualified Data.IntervalMap.FingerTree as IM
10+
11+
import qualified GHC
12+
import GHC ( TypecheckedModule )
13+
14+
import Data.Data as Data
15+
import Control.Monad.IO.Class
16+
import Data.Maybe
17+
import qualified TcHsSyn
18+
import qualified CoreUtils
19+
import qualified Type
20+
import qualified Desugar
21+
import Haskell.Ide.Engine.Compat
22+
23+
import Haskell.Ide.Engine.ArtifactMap
24+
25+
-- | Generate a mapping from an Interval to types.
26+
-- Intervals may overlap and return more specific results.
27+
genTypeMap :: GHC.GhcMonad m => TypecheckedModule -> m TypeMap
28+
genTypeMap tm = do
29+
let typecheckedSource = GHC.tm_typechecked_source tm
30+
hs_env <- GHC.getSession
31+
liftIO $ types hs_env typecheckedSource
32+
33+
34+
everythingInTypecheckedSourceM
35+
:: Data x => (forall a . Data a => a -> IO TypeMap) -> x -> IO TypeMap
36+
everythingInTypecheckedSourceM = everythingButTypeM @GHC.Id
37+
38+
39+
-- | Obtain details map for types.
40+
types :: GHC.HscEnv -> GHC.TypecheckedSource -> IO TypeMap
41+
types hs_env = everythingInTypecheckedSourceM (ty `combineM` fun `combineM` funBind)
42+
where
43+
ty :: forall a . Data a => a -> IO TypeMap
44+
ty term = case cast term of
45+
(Just lhsExprGhc@(GHC.L (GHC.RealSrcSpan spn) _)) ->
46+
getType hs_env lhsExprGhc >>= \case
47+
Nothing -> return IM.empty
48+
Just (_, typ) -> return (IM.singleton (rspToInt spn) typ)
49+
_ -> return IM.empty
50+
51+
fun :: forall a . Data a => a -> IO TypeMap
52+
fun term = case cast term of
53+
(Just (GHC.L (GHC.RealSrcSpan spn) hsPatType)) ->
54+
return (IM.singleton (rspToInt spn) (TcHsSyn.hsPatType hsPatType))
55+
_ -> return IM.empty
56+
57+
funBind :: forall a . Data a => a -> IO TypeMap
58+
funBind term = case cast term of
59+
(Just (GHC.L (GHC.RealSrcSpan spn) (FunBindType t))) ->
60+
return (IM.singleton (rspToInt spn) t)
61+
_ -> return IM.empty
62+
63+
-- | Combine two queries into one using alternative combinator.
64+
combineM
65+
:: (forall a . Data a => a -> IO TypeMap)
66+
-> (forall a . Data a => a -> IO TypeMap)
67+
-> (forall a . Data a => a -> IO TypeMap)
68+
combineM f g x = do
69+
a <- f x
70+
b <- g x
71+
return (a `IM.union` b)
72+
73+
-- | Variation of "everything" that does not recurse into children of type t
74+
-- requires AllowAmbiguousTypes
75+
everythingButTypeM
76+
:: forall t
77+
. (Typeable t)
78+
=> (forall a . Data a => a -> IO TypeMap)
79+
-> (forall a . Data a => a -> IO TypeMap)
80+
everythingButTypeM f = everythingButM $ (,) <$> f <*> isType @t
81+
82+
-- | Returns true if a == t.
83+
-- requires AllowAmbiguousTypes
84+
isType :: forall a b . (Typeable a, Typeable b) => b -> Bool
85+
isType _ = isJust $ eqT @a @b
86+
87+
-- | Variation of "everything" with an added stop condition
88+
-- Just like 'everything', this is stolen from SYB package.
89+
everythingButM
90+
:: (forall a . Data a => a -> (IO TypeMap, Bool))
91+
-> (forall a . Data a => a -> IO TypeMap)
92+
everythingButM f x = do
93+
let (v, stop) = f x
94+
if stop
95+
then v
96+
else Data.gmapQr
97+
(\e acc -> do
98+
e' <- e
99+
a <- acc
100+
return (e' `IM.union` a)
101+
)
102+
v
103+
(everythingButM f)
104+
x
105+
106+
-- | Attempts to get the type for expressions in a lazy and cost saving way.
107+
-- Avoids costly desugaring of Expressions and only obtains the type at the leaf of an expression.
108+
--
109+
-- Implementation is taken from: HieAst.hs<https://gitlab.haskell.org/ghc/ghc/blob/1f5cc9dc8aeeafa439d6d12c3c4565ada524b926/compiler/hieFile/HieAst.hs>
110+
-- Slightly adapted to work for the supported GHC versions 8.2.1 - 8.6.4
111+
--
112+
-- See #16233<https://gitlab.haskell.org/ghc/ghc/issues/16233>
113+
getType
114+
:: GHC.HscEnv -> GHC.LHsExpr GhcTc -> IO (Maybe (GHC.SrcSpan, Type.Type))
115+
getType hs_env e@(GHC.L spn e') =
116+
-- Some expression forms have their type immediately available
117+
let
118+
tyOpt = case e' of
119+
HsOverLitType t -> Just t
120+
HsLitType t -> Just t
121+
HsLamType t -> Just t
122+
HsLamCaseType t -> Just t
123+
HsCaseType t -> Just t
124+
ExplicitListType t -> Just t
125+
ExplicitSumType t -> Just t
126+
HsMultiIfType t -> Just t
127+
128+
_ -> Nothing
129+
in case tyOpt of
130+
Just t -> return $ Just (spn ,t)
131+
Nothing
132+
| skipDesugaring e' -> pure Nothing
133+
| otherwise -> do
134+
(_, mbe) <- Desugar.deSugarExpr hs_env e
135+
let res = (spn, ) . CoreUtils.exprType <$> mbe
136+
pure res
137+
where
138+
-- | Skip desugaring of these expressions for performance reasons.
139+
--
140+
-- See impact on Haddock output (esp. missing type annotations or links)
141+
-- before marking more things here as 'False'. See impact on Haddock
142+
-- performance before marking more things as 'True'.
143+
skipDesugaring :: GHC.HsExpr a -> Bool
144+
skipDesugaring expression = case expression of
145+
GHC.HsVar{} -> False
146+
GHC.HsUnboundVar{} -> False
147+
GHC.HsConLikeOut{} -> False
148+
GHC.HsRecFld{} -> False
149+
GHC.HsOverLabel{} -> False
150+
GHC.HsIPVar{} -> False
151+
GHC.HsWrap{} -> False
152+
_ -> True

hie-plugin-api/hie-plugin-api.cabal

+1
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ library
2929
Haskell.Ide.Engine.MultiThreadState
3030
Haskell.Ide.Engine.PluginsIdeMonads
3131
Haskell.Ide.Engine.PluginUtils
32+
Haskell.Ide.Engine.TypeMap
3233
build-depends: base >= 4.9 && < 5
3334
, Diff
3435
, aeson

test/testdata/Types.hs

+33
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
module Types where
2+
3+
import Control.Applicative
4+
5+
foo :: Maybe Int -> Int
6+
foo (Just x) = x
7+
foo Nothing = 0
8+
9+
bar :: Maybe Int -> Int
10+
bar x = case x of
11+
Just y -> y + 1
12+
Nothing -> 0
13+
14+
maybeMonad :: Maybe Int -> Maybe Int
15+
maybeMonad x = do
16+
y <- x
17+
let z = return (y + 10)
18+
b <- z
19+
return (b + y)
20+
21+
funcTest :: (a -> a) -> a -> a
22+
funcTest f a = f a
23+
24+
compTest :: (b -> c) -> (a -> b) -> a -> c
25+
compTest f g = let h = f . g in h
26+
27+
monadStuff :: (a -> b) -> IO a -> IO b
28+
monadStuff f action = f <$> action
29+
30+
data Test
31+
= TestC Int
32+
| TestM String
33+
deriving (Show, Eq, Ord)

0 commit comments

Comments
 (0)