Skip to content

Commit 1dadf55

Browse files
committed
[new-hs-indexer] Index type signatures
From a ValBind we can go to the SigDecl, so we can support "jump to type definition" type operations.
1 parent 6fc34de commit 1dadf55

4 files changed

Lines changed: 182 additions & 33 deletions

File tree

glean/lang/haskell/HieIndexer/Index.hs

Lines changed: 61 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -138,13 +138,15 @@ isRecFieldRef _ = False
138138

139139
produceDecl
140140
:: Glean.NewFact m
141-
=> Hs.Name
141+
=> Map (Glean.IdOf Hs.Name) Hs.SigDecl
142+
-> Hs.Name
142143
-> Maybe Hs.Type
143144
-> ContextInfo
144145
-> m ()
145-
produceDecl name maybeTy ctx = case ctx of
146+
produceDecl sigMap name maybeTy ctx = case ctx of
146147
ValBind RegularBind _ _ ->
147-
Glean.makeFact_ @Hs.ValBind $ Hs.ValBind_key name maybeTy
148+
Glean.makeFact_ @Hs.ValBind $
149+
Hs.ValBind_key name maybeTy (Map.lookup (Glean.getId name) sigMap)
148150
Decl FamDec _ ->
149151
Glean.makeFact_ @Hs.TypeFamilyDecl $ Hs.TypeFamilyDecl_key name
150152
Decl SynDec _ ->
@@ -167,30 +169,40 @@ produceDecl name maybeTy ctx = case ctx of
167169

168170
produceDeclInfo
169171
:: Glean.NewFact m
170-
=> Map GHC.Name Hs.Name
172+
=> Src.File
173+
-> (GHC.RealSrcSpan -> Src.ByteSpan)
174+
-> Map GHC.Name Hs.Name
171175
-> Map GHC.Name DeclInfo
172-
-> m ()
173-
produceDeclInfo nameMap declInfoMap =
174-
forM_ (Map.toList declInfoMap) $ \(name, info) ->
175-
whenJust (Map.lookup name nameMap) $ \hsName ->
176-
case info of
177-
DataDecl { dataDeclConstrs = cs } -> do
178-
cons <- forM cs $ \(ConstrInfo cName fields) -> do
179-
forM (Map.lookup cName nameMap) $ \hsCName -> do
180-
fNames <- forM (mapMaybe (`Map.lookup` nameMap) fields) $ \fName ->
181-
Glean.makeFact @Hs.RecordFieldDecl $
182-
Hs.RecordFieldDecl_key fName hsCName
183-
Glean.makeFact @Hs.ConstrDecl $
184-
Hs.ConstrDecl_key hsCName hsName fNames
185-
Glean.makeFact_ @Hs.DataDecl $
186-
Hs.DataDecl_key hsName (catMaybes cons)
187-
188-
ClassDecl { classDeclMethods = ms } -> do
189-
mNames <- forM (mapMaybe (`Map.lookup` nameMap) ms) $ \mName ->
190-
Glean.makeFact @Hs.MethDecl $
191-
Hs.MethDecl_key mName hsName
192-
Glean.makeFact_ @Hs.ClassDecl $
193-
Hs.ClassDecl_key hsName mNames
176+
-> m (Map (Glean.IdOf Hs.Name) Hs.SigDecl)
177+
produceDeclInfo fileFact toByteSpan nameMap declInfoMap =
178+
fmap (Map.fromList . catMaybes) $
179+
forM (Map.toList declInfoMap) $ \(name, info) ->
180+
flip (maybe (return Nothing)) (Map.lookup name nameMap) $ \hsName ->
181+
case info of
182+
SigDecl { sigDeclSpan = span } -> do
183+
decl <- Glean.makeFact @Hs.SigDecl $
184+
Hs.SigDecl_key hsName (
185+
Src.FileLocation fileFact (toByteSpan span))
186+
return (Just (Glean.getId hsName, decl))
187+
DataDecl { dataDeclConstrs = cs } -> do
188+
cons <- forM cs $ \(ConstrInfo cName fields) -> do
189+
forM (Map.lookup cName nameMap) $ \hsCName -> do
190+
fNames <- forM (mapMaybe (`Map.lookup` nameMap) fields) $
191+
\fName ->
192+
Glean.makeFact @Hs.RecordFieldDecl $
193+
Hs.RecordFieldDecl_key fName hsCName
194+
Glean.makeFact @Hs.ConstrDecl $
195+
Hs.ConstrDecl_key hsCName hsName fNames
196+
Glean.makeFact_ @Hs.DataDecl $
197+
Hs.DataDecl_key hsName (catMaybes cons)
198+
return Nothing
199+
ClassDecl { classDeclMethods = ms } -> do
200+
mNames <- forM (mapMaybe (`Map.lookup` nameMap) ms) $ \mName ->
201+
Glean.makeFact @Hs.MethDecl $
202+
Hs.MethDecl_key mName hsName
203+
Glean.makeFact_ @Hs.ClassDecl $
204+
Hs.ClassDecl_key hsName mNames
205+
return Nothing
194206

195207
nat :: Integral a => a -> Glean.Nat
196208
nat = Glean.toNat . fromIntegral
@@ -338,8 +350,6 @@ indexHieFile writer srcPaths path hie = do
338350
GHC.occNameString (nameOccName name) <> ": " <>
339351
show (ppr sp)) $ return ()
340352
-}
341-
let ty = identType dets >>= \ix -> IntMap.lookup ix typeMap
342-
mapM_ (produceDecl namefact ty) (Set.toList (identInfo dets))
343353
return $ Just (name, namefact)
344354
| otherwise -> return Nothing
345355

@@ -348,6 +358,15 @@ indexHieFile writer srcPaths path hie = do
348358
nameMap :: Map GHC.Name Hs.Name
349359
nameMap = Map.fromList names
350360

361+
sigMap <- produceDeclInfo filefact toByteSpan nameMap declInfo
362+
363+
forM_ allIds $ \(ident, (_, dets)) -> if
364+
| Right name <- ident
365+
, Just namefact <- Map.lookup name nameMap -> do
366+
let ty = identType dets >>= \ix -> IntMap.lookup ix typeMap
367+
mapM_ (produceDecl sigMap namefact ty) (Set.toList (identInfo dets))
368+
| otherwise -> return ()
369+
351370
eNames <- forM (concatMap GHC.availNames (hie_exports hie)) $ \name ->
352371
case Map.lookup name nameMap of
353372
Just hsName -> return $ Just hsName
@@ -357,8 +376,6 @@ indexHieFile writer srcPaths path hie = do
357376
mod <- if m == smod then return modfact else mkModule m
358377
Just <$> mkName name mod (Hs.NameSort_external def)
359378

360-
produceDeclInfo nameMap declInfo
361-
362379
Glean.makeFact_ @Hs.ModuleDeclarations $ Hs.ModuleDeclarations_key
363380
modfact (map snd names) (catMaybes eNames)
364381

@@ -508,6 +525,9 @@ data DeclInfo
508525
| ClassDecl {
509526
classDeclMethods :: [GHC.Name]
510527
}
528+
| SigDecl {
529+
sigDeclSpan :: GHC.RealSrcSpan
530+
}
511531

512532
data ConstrInfo =
513533
ConstrInfo {
@@ -539,6 +559,9 @@ getDeclInfos node = snd $ State.runState (go node) Map.empty
539559
| isClassDecl nodeInfo,
540560
[name] <- findIdent classDeclCtx nodeInfo node =
541561
Just (name, ClassDecl { classDeclMethods = findMethods node })
562+
| isTypeSig nodeInfo,
563+
[name] <- findIdent tyDeclCtx nodeInfo node =
564+
Just (name, SigDecl { sigDeclSpan = nodeSpan node })
542565
| otherwise =
543566
Nothing
544567

@@ -547,6 +570,7 @@ getDeclInfos node = snd $ State.runState (go node) Map.empty
547570
concatMap (namesWithContext ctx) $
548571
map nodeIdentifiers (ni : map getNodeInfo nodeChildren)
549572

573+
isTypeSig = any (== typeSigAnnot) . nodeAnnotations
550574
isDataDecl = any (== dataDeclAnnot) . nodeAnnotations
551575
isConstrDecl = any (`elem` [constrAnnot, constrGadtAnnot]) . nodeAnnotations
552576
isClassDecl = any (== classDeclAnnot) . nodeAnnotations
@@ -604,6 +628,10 @@ recFieldDeclCtx :: ContextInfo -> Bool
604628
recFieldDeclCtx (RecField r _) = not (isRecFieldRef r)
605629
recFieldDeclCtx _ = False
606630

631+
tyDeclCtx :: ContextInfo -> Bool
632+
tyDeclCtx TyDecl = True
633+
tyDeclCtx _ = False
634+
607635
classDeclCtx :: ContextInfo -> Bool
608636
classDeclCtx (Decl ClassDec _) = True
609637
classDeclCtx _ = False
@@ -612,6 +640,9 @@ classTyDeclCtx :: ContextInfo -> Bool
612640
classTyDeclCtx (ClassTyDecl _) = True
613641
classTyDeclCtx _ = False
614642

643+
typeSigAnnot :: NodeAnnotation
644+
typeSigAnnot = NodeAnnotation "TypeSig" "Sig"
645+
615646
dataDeclAnnot :: NodeAnnotation
616647
dataDeclAnnot = NodeAnnotation "DataDecl" "TyClDecl"
617648

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
[
2+
"@generated",
3+
{
4+
"key": {
5+
"loc": {
6+
"file": { "key": "glean/lang/haskell/tests/code/A.hs" },
7+
"span": { "length": 9, "start": 571 }
8+
},
9+
"name": {
10+
"key": {
11+
"mod": {
12+
"key": { "name": { "key": "A" }, "unit": { "key": "main" } }
13+
},
14+
"occ": { "key": { "name": "zero", "namespace_": 0 } },
15+
"sort": { "external": { } }
16+
}
17+
}
18+
}
19+
},
20+
{
21+
"key": {
22+
"loc": {
23+
"file": { "key": "glean/lang/haskell/tests/code/A.hs" },
24+
"span": { "length": 11, "start": 370 }
25+
},
26+
"name": {
27+
"key": {
28+
"mod": {
29+
"key": { "name": { "key": "A" }, "unit": { "key": "main" } }
30+
},
31+
"occ": { "key": { "name": "a", "namespace_": 0 } },
32+
"sort": { "external": { } }
33+
}
34+
}
35+
}
36+
},
37+
{
38+
"key": {
39+
"loc": {
40+
"file": { "key": "glean/lang/haskell/tests/code/A.hs" },
41+
"span": { "length": 15, "start": 591 }
42+
},
43+
"name": {
44+
"key": {
45+
"mod": {
46+
"key": { "name": { "key": "A" }, "unit": { "key": "main" } }
47+
},
48+
"occ": { "key": { "name": "f", "namespace_": 0 } },
49+
"sort": { "external": { } }
50+
}
51+
}
52+
}
53+
},
54+
{
55+
"key": {
56+
"loc": {
57+
"file": { "key": "glean/lang/haskell/tests/code/B.hs" },
58+
"span": { "length": 10, "start": 279 }
59+
},
60+
"name": {
61+
"key": {
62+
"mod": {
63+
"key": { "name": { "key": "B" }, "unit": { "key": "main" } }
64+
},
65+
"occ": { "key": { "name": "b", "namespace_": 0 } },
66+
"sort": { "external": { } }
67+
}
68+
}
69+
}
70+
},
71+
{
72+
"key": {
73+
"loc": {
74+
"file": { "key": "glean/lang/haskell/tests/code/B.hs" },
75+
"span": { "length": 11, "start": 315 }
76+
},
77+
"name": {
78+
"key": {
79+
"mod": {
80+
"key": { "name": { "key": "B" }, "unit": { "key": "main" } }
81+
},
82+
"occ": { "key": { "name": "r", "namespace_": 0 } },
83+
"sort": { "external": { } }
84+
}
85+
}
86+
}
87+
}
88+
]
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
# ValBinds that have a signature
2+
query: |
3+
Val.sig.just? where Val : hs.ValBind
4+
transform: [normord, []]

glean/schema/source/hs.angle

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,7 @@ predicate Type:
121121

122122
# Declarations / definitions
123123

124+
# A declaration, which may or may not define a Name.
124125
type Declaration =
125126
{
126127
val: ValBind |
@@ -135,14 +136,15 @@ type Declaration =
135136
patBind: PatBind |
136137
tyVarBind: TyVarBind |
137138
field: RecordFieldDecl |
139+
sig: SigDecl |
138140
}
139141

142+
# Go from a Name to its definition, or vice versa.
140143
predicate DeclarationOfName:
141144
{
142145
name: Name,
143146
decl: Declaration,
144147
}
145-
# maybe store this? Turns 12 lookups into 1 lookup.
146148
{ N, { val = { name = N }}} |
147149
{ N, { typeFamily = { name = N }}} |
148150
{ N, { type_ = { name = N }}} |
@@ -161,7 +163,13 @@ predicate ValBind:
161163
name: Name,
162164
ty: maybe Type,
163165
# fixity
164-
# location of signature
166+
sig: maybe SigDecl
167+
}
168+
169+
predicate SigDecl:
170+
{
171+
name: Name,
172+
loc: src.FileLocation,
165173
}
166174

167175
predicate TypeFamilyDecl:
@@ -233,17 +241,35 @@ predicate RecordFieldDecl:
233241
# type
234242
}
235243

244+
# From a Name to the location of its defining declaration
236245
predicate DeclarationLocation:
237246
{
238247
name: Name,
239248
file: src.File,
240249
span: src.ByteSpan
241250
}
242251

252+
# From a Declaration to its location
253+
predicate DeclarationSpan:
254+
{
255+
decl: Declaration,
256+
loc: src.FileLocation,
257+
}
258+
{ Decl, Loc } where
259+
# it's either a definition, or it has a location
260+
(
261+
DeclarationOfName { Name, Decl };
262+
DeclarationLocation { Name, File, Span };
263+
Loc = { File, Span };
264+
) | (
265+
Decl.sig?.loc = Loc;
266+
)
267+
243268
predicate ModuleDeclarations:
244269
{
245270
module: Module,
246271
names: set Name,
272+
# TODO: decls: [Declaration],
247273
exports: set Name,
248274
}
249275

@@ -257,7 +283,7 @@ type RefSpan =
257283

258284
# It's useful to be able to distinguish import/export refs from code refs,
259285
# because e.g. dead code can still have import/export refs.
260-
type RefKind = enum { importref | exportref | coderef | tydecl }
286+
type RefKind = enum { importref | exportref | coderef | tydecl | instbind }
261287

262288
type RefTarget =
263289
{

0 commit comments

Comments
 (0)