{- 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. -} {-# LANGUAGE TypeApplications #-} {-# OPTIONS_GHC -Wno-orphans #-} module Glean.Glass.SymbolId.Flow ( {- instances -} ) where import Data.Text ( stripSuffix, intercalate, Text ) import Data.Hashable import qualified Glean import Glean.Angle as Angle import qualified Glean.Haxl.Repos as Glean import Glean.Glass.SymbolId.Class import qualified Glean.Schema.Flow.Types as Flow import qualified Glean.Schema.Src.Types as Src import Glean.Glass.Utils import Glean.Glass.Base ( GleanPath(..)) import Glean.Glass.Types ( Name(..) ) import qualified Haxl.Core.Memo as Haxl import Glean.Schema.CodeFlow.Types as Flow ( Entity(..) ) instance Symbol Flow.Entity where toSymbol e = case e of Flow.Entity_decl decl -> toSymbol decl Flow.Entity_module_ module_ -> do symId <- toSymbolPredicate module_ case symId of [_] -> return symId -- keep Haste or string_ module symIds neat _ -> return ("m" : symId) Flow.Entity_EMPTY -> return [] instance Symbol Flow.SomeDeclaration where toSymbol e = case e of Flow.SomeDeclaration_localDecl decl -> toSymbolPredicate decl Flow.SomeDeclaration_memberDecl member -> toSymbolPredicate member Flow.SomeDeclaration_typeDecl type_ -> toSymbolPredicate type_ Flow.SomeDeclaration_EMPTY -> return [] -- flow declarations are a bare name and a `Range`, which can be a module or -- other location instance Symbol Flow.Declaration_key where toSymbol (Flow.Declaration_key name loc_range) = loc_range <:> name instance Symbol Flow.MemberDeclaration_key where toSymbol (Flow.MemberDeclaration_key name container) = container <:> name instance Symbol Flow.TypeDeclaration_key where toSymbol (Flow.TypeDeclaration_key name container) = container <:> name instance Symbol Flow.Range where toSymbol = toSymbolPredicate instance Symbol Flow.Range_key where toSymbol (Flow.Range_key module_ _span) = toSymbolPredicate module_ instance Symbol Flow.Module_key where toSymbol m = case m of Flow.Module_key_file file -> memoModuleNameQuery file Flow.Module_key_builtin _ -> return [] Flow.Module_key_lib text -> return [text] Flow.Module_key_noSource _ -> return [] Flow.Module_key_string_ text -> return [text] Flow.Module_key_EMPTY -> return [] instance Symbol Flow.Name where toSymbol k = do v <- Glean.keyOf k return [v] newtype Glass_SymbolId_Flow_ModuleName_Key = Glass_SymbolId_Flow_ModuleName_Key (Glean.Repo, Text) deriving (Eq, Hashable, Ord, Show) -- | Memoize the result of figuring out the best short name for a file -- since this is common to all symbols in a file it can be a good win. memoModuleNameQuery :: Src.File -> Glean.RepoHaxl u w [Text] memoModuleNameQuery file = do repo <- Glean.haxlRepo key <- Glean.keyOf file Haxl.memo (Glass_SymbolId_Flow_ModuleName_Key (repo, key)) $ runModuleNameQuery file -- Need to encode the module as a "container". For most (?) cases -- there is a nice short string associated with the file, so we get -- ww/js/Module/identifier and unique globally (?) -- -- But in case we don't , use the file name. -- -- T90301808 - Haste short names are not always present. In the case they -- are missing we should use the fully qualified filepath. Note: this will leak -- the non-relative paths used in the www indexers. TBD -- runModuleNameQuery :: Src.File -> Glean.RepoHaxl u w [Text] runModuleNameQuery file = do mfile <- toJSFile file names <- case mfile of -- its not a .js.flow file so we already have a valid fact id Nothing -> fetchData (moduleStringName $ Glean.getId file) -- try for a .js equivalent Just path -> fetchData (moduleStringNameByFile path) case names of Nothing -> pathFragments <$> Glean.keyOf file Just n -> return [n] -- Normalize any .flow suffix of a file to its base .js file -- -- A smarter way would be to reverse the SourceOfTypeExport for .flow files -- toJSFile :: Src.File -> Glean.RepoHaxl u w (Maybe GleanPath) toJSFile file = do path <- Glean.keyOf file return $ GleanPath <$> stripSuffix flowSuffix path where flowSuffix :: Text flowSuffix = ".flow" -- Flow module name of file id of module moduleStringName :: Glean.IdOf Src.File -> Angle Text moduleStringName fileid = vars $ \(str :: Angle Text) (file :: Angle Src.File) -> str `where_` [ file .= factId fileid, wild .= predicate @Flow.FileOfStringModule ( rec $ field @"file" (asPredicate file) $ field @"string_" str end) ] -- Flow module name of file path of module moduleStringNameByFile :: GleanPath -> Angle Text moduleStringNameByFile (GleanPath path) = vars $ \(str :: Angle Text) (file :: Angle Src.File) -> str `where_` [ file .= predicate @Src.File (string path), wild .= predicate @Flow.FileOfStringModule ( rec $ field @"file" (asPredicate file) $ field @"string_" str end) ] instance ToQName Flow.Entity where toQName e = case e of Flow.Entity_decl x -> toQName x Flow.Entity_module_ x -> Glean.keyOf x >>= toQName Flow.Entity_EMPTY -> return $ Left "unknown Entity" instance ToQName Flow.SomeDeclaration where toQName e = case e of Flow.SomeDeclaration_localDecl x -> Glean.keyOf x >>= toQName Flow.SomeDeclaration_memberDecl x -> Glean.keyOf x >>= toQName Flow.SomeDeclaration_typeDecl x -> Glean.keyOf x >>= toQName Flow.SomeDeclaration_EMPTY -> return $ Left "unknown SomeDeclaration" instance ToQName Flow.Declaration_key where toQName (Flow.Declaration_key name container) = pairToQName name container instance ToQName Flow.MemberDeclaration_key where toQName (Flow.MemberDeclaration_key name container) = pairToQName name container instance ToQName Flow.TypeDeclaration_key where toQName (Flow.TypeDeclaration_key name container) = pairToQName name container instance ToQName Flow.Module_key where toQName m = do sym <- toSymbol m return $ case reverse sym of [] -> Left "QName not supported for this symbol" (h:t) -> Right (Name h, Name (intercalate "." (reverse t))) pairToQName :: (Symbol name, Symbol container) => name -> container -> Glean.RepoHaxl u w (Either a (Name, Name)) pairToQName a b = Right <$> symbolPairToQName "." a b