glean-0.1.0.0: glean/glass/Glean/Glass/Neighborhood.hs
{-
Copyright (c) Meta Platforms, Inc. and affiliates.
All rights reserved.
This source code is licensed under the BSD-style license found in the
LICENSE file in the root directory of this source tree.
-}
module Glean.Glass.Neighborhood ( searchNeighborhood) where
import Data.Ord ( comparing )
import Data.Text ( Text )
import qualified Data.Map.Strict as Map
import Util.List ( uniq, uniqBy )
import Glean.Haxl.Repos as Glean ( withRepo, RepoHaxl, ReposHaxl )
import qualified Glean.Schema.Code.Types as Code
import Glean.Glass.Utils ( fst4 )
import Glean.Glass.Describe
( mkSymbolDescription, mkBriefSymbolDescription )
import Glean.Glass.Repos ( Language(Language_Hack), ScmRevisions )
import Glean.Glass.Types
( SymbolId(SymbolId),
RelatedNeighborhoodRequest(..),
SymbolDescription,
RelatedNeighborhoodResult(..),
InheritedSymbols(..),
RelatedSymbols(RelatedSymbols),
RelationDirection(RelationDirection_Parent,
RelationDirection_Child),
RelationType(RelationType_Extends, RelationType_Contains,
RelationType_RequireExtends, RelationType_RequireImplements,
RelationType_RequireClass),
RepoName,
SymbolBasicDescription,
SymbolDescription(..) )
import Glean.Glass.Search as Search
( CodeEntityLocation(CodeEntityLocation, entityName, entityRange,
entityFile, entity),
SearchEntity(..) )
import Glean.Glass.SearchRelated (InheritedContainer, LocatedEntity)
import qualified Glean.Glass.SearchRelated as Search
import Glean.Glass.Search.Class ( ResultLocation )
type SearchRelatedQuery u w = Code.Entity -> RepoName
-> RepoHaxl u w [Search.RelatedLocatedEntities]
type SearchRelatedList u w = Code.Entity -> RepoName
-> RepoHaxl u w [Search.LocatedEntity]
-- being careful to keep things with clear data dependencies
searchNeighborhood
:: Int
-> RelatedNeighborhoodRequest
-> SymbolId
-> RepoName
-> ScmRevisions -> Language -> SearchEntity (ResultLocation Code.Entity)
-> ReposHaxl u w RelatedNeighborhoodResult
searchNeighborhood limit
RelatedNeighborhoodRequest{..} sym repo scmRevs lang baseEntity =
withRepo (entityRepo baseEntity) $ do
let entity = fst4 $ decl baseEntity
a <- childrenContains1Level
(fromIntegral relatedNeighborhoodRequest_children_limit) entity repo
b <- childrenExtends1Level
(fromIntegral relatedNeighborhoodRequest_inherited_limit) entity repo
c <- parentContainsNLevel
(fromIntegral relatedNeighborhoodRequest_parent_depth) entity repo
d <- parentExtends1Level limit entity repo
e <- parentRequire RelationType_RequireExtends limit entity repo
f <- parentRequire RelationType_RequireImplements limit entity repo
g <- parentRequire RelationType_RequireClass limit entity repo
(parents, members, inheritedSyms) <- inheritedSymbols lang limit
(fromIntegral relatedNeighborhoodRequest_inherited_limit)
(if relatedNeighborhoodRequest_hide_uninteresting
then Search.HideUninteresting
else Search.ShowAll)
entity repo
-- syms visible to the client, we need their full details
let !syms = uniqBy (comparing snd) $ fromSearchEntity sym baseEntity :
a ++ flattenEdges c ++ members
-- full descriptions of final methods
descriptions <- Map.fromAscList <$> mapM (mkDescribe repo scmRevs) syms
-- brief descriptions for inherited things
basics <- Map.fromAscList <$> mapM (mkBriefDescribe repo scmRevs)
(uniqBy (comparing snd) $ (b ++ d ++ e ++ f ++ g) ++ parents)
return $ RelatedNeighborhoodResult {
relatedNeighborhoodResult_childrenContained = map snd a,
relatedNeighborhoodResult_childrenExtended = map snd b,
relatedNeighborhoodResult_containsParents = symbolIdPairs c,
relatedNeighborhoodResult_parentsExtended = map snd d,
relatedNeighborhoodResult_inheritedSymbols = inheritedSyms,
relatedNeighborhoodResult_symbolDetails = descriptions,
relatedNeighborhoodResult_symbolBasicDetails = basics,
relatedNeighborhoodResult_requireExtends = map snd e,
relatedNeighborhoodResult_requireImplements = map snd f,
relatedNeighborhoodResult_requireClass = map snd g
}
-- building map of sym id -> descriptions, by first occurence
mkDescribe
:: RepoName
-> ScmRevisions
-> Search.LocatedEntity
-> Glean.RepoHaxl u w (Text, SymbolDescription)
mkDescribe repo scmRevs e@(_,SymbolId rawSymId) =
(rawSymId,) <$> describe repo scmRevs e
describe
:: RepoName
-> ScmRevisions
-> Search.LocatedEntity
-> Glean.RepoHaxl u w SymbolDescription
describe repo scmRevs ((entity, entityFile, entityRange, entityName), symId)
= mkSymbolDescription symId scmRevs repo CodeEntityLocation{..} Nothing
-- for children by inheritance, we only need name, kind, parent name, sym id
-- and signature. In particular, we don't e.g. need comments or annotations
-- or type xrefs
mkBriefDescribe
:: RepoName
-> ScmRevisions
-> Search.LocatedEntity
-> Glean.RepoHaxl u w (Text, SymbolBasicDescription)
mkBriefDescribe repo scmRevs e@(_,SymbolId rawSymId) =
(rawSymId,) <$> briefDescribe repo scmRevs e
briefDescribe
:: RepoName
-> ScmRevisions
-> Search.LocatedEntity
-> Glean.RepoHaxl u w SymbolBasicDescription
briefDescribe repo scmRevs
((entity, entityFile, entityRange, entityName), symId)
= mkBriefSymbolDescription symId scmRevs repo CodeEntityLocation{..}
Nothing
childrenContains1Level :: Int -> SearchRelatedList u w
childrenContains1Level limit baseEntity repo = map Search.childRL <$>
Search.searchRelatedEntities
limit
Search.ShowAll
Search.NotRecursive
RelationDirection_Child
RelationType_Contains
baseEntity
repo
-- children by `extends`. typically a list. this includes method overrides
-- note that some methods have a lot of overrides (50k+) so be careful with
-- the limit values
childrenExtends1Level :: Int -> SearchRelatedList u w
childrenExtends1Level limit baseEntity repo = map Search.childRL <$>
Search.searchRelatedEntities
limit
Search.ShowAll
Search.NotRecursive
RelationDirection_Child
RelationType_Extends
baseEntity
repo
-- Direct inheritance parents
parentExtends1Level :: Int -> SearchRelatedList u w
parentExtends1Level limit baseEntity repo = map Search.parentRL <$>
Search.searchRelatedEntities
limit
Search.ShowAll
Search.NotRecursive
RelationDirection_Parent
RelationType_Extends
baseEntity
repo
-- N levels of container hierarchy
parentContainsNLevel :: Int -> SearchRelatedQuery u w
parentContainsNLevel limit baseEntity repo = Search.searchRelatedEntities
limit
Search.ShowAll
Search.Recursive
RelationDirection_Parent
RelationType_Contains
baseEntity
repo
-- Returns (recursively) parent entities, inherited members
-- and inherited symbols of a container entity
--
-- If not directly available in DB computes an over approximation
-- of inherited methods computed by unioning the contained children
-- of inheritedLimit levels of extended parents
inheritedSymbols
:: Language -> Int -> Int -> Search.SearchStyle -> Code.Entity -> RepoName
-> RepoHaxl u w ([LocatedEntity], [LocatedEntity], [InheritedSymbols])
-- Optimized implementation for Hack: all members inherited into scope by parent
inheritedSymbols Language_Hack
memberLimit _inheritedLimit style entity repo = do
inherited <- Search.searchInheritedEntities style memberLimit entity repo
let parents = map fst inherited
members = concatMap snd inherited
inheritedSyms = map inheritedSymbolIdSets inherited
return (parents, members, inheritedSyms)
-- Fall back implementation: enumerate inherited parents, and find their members
inheritedSymbols _ memberLimit inheritedLimit style entity repo = do
topoEdges <- Search.searchRelatedEntities -- "extend relationship"
inheritedLimit
style
Search.Recursive
RelationDirection_Parent
RelationType_Extends
entity
repo
let parents = uniq (map Search.parentRL topoEdges)
inherited <- mapM (childrenOf memberLimit repo) parents
let inheritedSyms = map inheritedSymbolIdSets inherited
let members = concatMap snd inherited
return (parents, members, inheritedSyms)
-- Fetch the "required" parent entities
-- This only exists for Hack entity so we can avoid
-- useless queries for other languages
parentRequire :: RelationType -> Int -> SearchRelatedList u w
parentRequire relationType limit baseEntity repo =
case baseEntity of
Code.Entity_hack _ ->
map Search.parentRL <$>
Search.searchRelatedEntities
limit
Search.ShowAll
Search.NotRecursive
RelationDirection_Parent
relationType
baseEntity
repo
_ -> return []
childrenOf
:: Int -> RepoName -> Search.LocatedEntity
-> Glean.RepoHaxl u w (Search.LocatedEntity, [Search.LocatedEntity])
childrenOf limit repo parent = (parent,) <$>
childrenContains1Level limit (toEntity parent) repo
toEntity :: Search.LocatedEntity -> Code.Entity
toEntity ((entity, _file, _rangespan, _name), _symId) = entity
symbolIdPairs :: [Search.RelatedLocatedEntities] -> [RelatedSymbols]
symbolIdPairs = map (\Search.RelatedLocatedEntities{..} ->
RelatedSymbols (snd parentRL) (snd childRL) Nothing)
inheritedSymbolIdSets :: InheritedContainer -> InheritedSymbols
inheritedSymbolIdSets (parent, children) = InheritedSymbols {
inheritedSymbols_base = snd parent,
inheritedSymbols_provides = map snd children
}
fromSearchEntity
:: SymbolId
-> SearchEntity (ResultLocation Code.Entity)
-> Search.LocatedEntity
fromSearchEntity symId SearchEntity{..} = (decl, symId)
flattenEdges :: [Search.RelatedLocatedEntities] -> [Search.LocatedEntity]
flattenEdges pairs = concat
[ [ e1, e2 ] | Search.RelatedLocatedEntities e1 e2 <- pairs ]