canontra-0.1.0.0: src/Canontra/Analysis/Scope.hs
{- |
Module : Canontra.Analysis.Scope
Description : Lexical scope analysis and symbol definition-use resolver.
This module constructs the language-independent lexical scope tree for a program.
It resolves variable bindings, parameters, imports, global/nonlocal boundaries,
and references across nested function, class, struct, trait, lambda, and comprehension scopes.
-}
module Canontra.Analysis.Scope
( ScopeId
, ScopeKind (..)
, LocalOrGlobal (..)
, SymbolKind (..)
, SymbolBinding (..)
, ScopeTree (..)
, analyzeProgramScope
, analyzeModuleScope
, findBinding
, findBindingInHierarchy
, allBindings
) where
import Control.DeepSeq (NFData)
import Data.Aeson (FromJSON, ToJSON)
import Data.Map.Strict (Map)
import qualified Data.Map.Strict as Map
import Data.Set (Set)
import qualified Data.Set as Set
import Data.Text (Text)
import qualified Data.Text as T
import GHC.Generics (Generic)
import Canontra.IR.Declaration
import Canontra.IR.Dependency
import Canontra.IR.Expression
import Canontra.IR.Program
type ScopeId = Int
data ScopeKind
= ScopeModule
| ScopeClass Text
| ScopeStruct Text
| ScopeTrait Text
| ScopeImpl Text
| ScopeFunction Text
| ScopeLambda
| ScopeComprehension
| ScopeBlock
deriving stock (Eq, Ord, Show, Generic)
deriving anyclass (ToJSON, FromJSON, NFData)
data LocalOrGlobal
= BindingLocal
| BindingGlobal
| BindingNonLocal
deriving stock (Eq, Ord, Show, Generic)
deriving anyclass (ToJSON, FromJSON, NFData)
data SymbolKind
= SymFunction
| SymClass
| SymStruct
| SymTrait
| SymParameter ParamKind
| SymVariable LocalOrGlobal
| SymImported Text (Maybe Text) -- e.g. (Original name, Source module)
| SymTypeAlias
deriving stock (Eq, Ord, Show, Generic)
deriving anyclass (ToJSON, FromJSON, NFData)
data SymbolBinding = SymbolBinding
{ symName :: Text
, symKind :: SymbolKind
, symDefinedAt :: ScopeId
, symReferences :: [ScopeId]
, symIsExported :: Bool
} deriving stock (Eq, Show, Generic)
deriving anyclass (ToJSON, FromJSON, NFData)
data ScopeTree = ScopeTree
{ scopeId :: ScopeId
, scopeKind :: ScopeKind
, scopeSymbols :: Map Text SymbolBinding
, scopeParent :: Maybe ScopeId
, scopeChildren :: [ScopeTree]
} deriving stock (Eq, Show, Generic)
deriving anyclass (ToJSON, FromJSON, NFData)
-- | Analyze a whole Program into a list of ScopeTrees (one per module).
analyzeProgramScope :: Program -> [ScopeTree]
analyzeProgramScope (Program modules _) =
map (analyzeModuleScope 0) modules
-- | Analyze a single Module starting from a given ScopeId.
analyzeModuleScope :: ScopeId -> Module -> ScopeTree
analyzeModuleScope rootId (Module _ imps decls stmts) =
let initialSymbols = collectImports rootId imps
(globals, nonlocals) = collectExplicitDirectives stmts
topLevelVars = collectStmtBindings rootId globals nonlocals stmts
(declBindings, childTrees, _) = processDeclarations (rootId + 1) rootId decls
combinedSymbols = Map.unions [declBindings, topLevelVars, initialSymbols]
refs = collectStmtRefs stmts
updatedSymbols = recordReferences rootId refs combinedSymbols
in ScopeTree
{ scopeId = rootId
, scopeKind = ScopeModule
, scopeSymbols = updatedSymbols
, scopeParent = Nothing
, scopeChildren = childTrees
}
collectImports :: ScopeId -> [ImportDecl] -> Map Text SymbolBinding
collectImports sid imps = Map.fromList $ concatMap (importToBindings sid) imps
where
importToBindings sId (ImportModule modName maybeAlias) =
let boundName = maybe (lastModulePart modName) id maybeAlias
in [(boundName, SymbolBinding boundName (SymImported modName Nothing) sId [] (isPublic boundName))]
importToBindings sId (ImportFrom modName target) = case target of
ImportAll -> []
ImportSymbols syms ->
[ (boundName, SymbolBinding boundName (SymImported symName (Just modName)) sId [] (isPublic boundName))
| (symName, maybeAlias) <- syms
, let boundName = maybe symName id maybeAlias
]
lastModulePart m = case T.splitOn "." m of
[] -> m
xs -> last xs
processDeclarations :: ScopeId -> ScopeId -> [Declaration] -> (Map Text SymbolBinding, [ScopeTree], ScopeId)
processDeclarations startId parentId decls =
foldl step (Map.empty, [], startId) decls
where
step (symAcc, treesAcc, curId) decl = case decl of
DeclFunction fn ->
let (fnTree, nextId) = analyzeFunction curId (Just parentId) fn
binding = SymbolBinding (fnName fn) SymFunction parentId [] (isPublic (fnName fn))
in (Map.insert (fnName fn) binding symAcc, treesAcc ++ [fnTree], nextId)
DeclClass cls ->
let (clsTree, nextId) = analyzeClass curId (Just parentId) cls
binding = SymbolBinding (clsName cls) SymClass parentId [] (isPublic (clsName cls))
in (Map.insert (clsName cls) binding symAcc, treesAcc ++ [clsTree], nextId)
DeclStruct st ->
let (stTree, nextId) = analyzeStruct curId (Just parentId) st
binding = SymbolBinding (stName st) SymStruct parentId [] (isPublic (stName st))
in (Map.insert (stName st) binding symAcc, treesAcc ++ [stTree], nextId)
DeclTrait tr ->
let (trTree, nextId) = analyzeTrait curId (Just parentId) tr
binding = SymbolBinding (trName tr) SymTrait parentId [] (isPublic (trName tr))
in (Map.insert (trName tr) binding symAcc, treesAcc ++ [trTree], nextId)
DeclImpl imp ->
let (impTree, nextId) = analyzeImpl curId (Just parentId) imp
in (symAcc, treesAcc ++ [impTree], nextId)
DeclReceiver _ fn ->
let (fnTree, nextId) = analyzeFunction curId (Just parentId) fn
in (symAcc, treesAcc ++ [fnTree], nextId)
DeclVariable varName _ ->
let binding = SymbolBinding varName (SymVariable BindingLocal) parentId [] (isPublic varName)
in (Map.insert varName binding symAcc, treesAcc, curId)
DeclInterface iface ->
let binding = SymbolBinding (ifName iface) SymTrait parentId [] (isPublic (ifName iface))
in (Map.insert (ifName iface) binding symAcc, treesAcc, curId)
DeclTypeAlias aliasName _ ->
let binding = SymbolBinding aliasName SymTypeAlias parentId [] (isPublic aliasName)
in (Map.insert aliasName binding symAcc, treesAcc, curId)
analyzeFunction :: ScopeId -> Maybe ScopeId -> Function -> (ScopeTree, ScopeId)
analyzeFunction curId mParent fn =
let pBindings = Map.fromList
[ (paramName p, SymbolBinding (paramName p) (SymParameter (paramKind p)) curId [] False)
| p <- fnParams fn
]
(globals, nonlocals) = collectExplicitDirectives (fnBody fn)
bodyVars = collectStmtBindings curId globals nonlocals (fnBody fn)
(exprTrees, nextId) = extractExprScopeTrees (curId + 1) curId (fnBody fn)
combinedSyms = Map.unions [bodyVars, pBindings]
refs = collectStmtRefs (fnBody fn)
updatedSyms = recordReferences curId refs combinedSyms
fnTree = ScopeTree
{ scopeId = curId
, scopeKind = ScopeFunction (fnName fn)
, scopeSymbols = updatedSyms
, scopeParent = mParent
, scopeChildren = exprTrees
}
in (fnTree, nextId)
analyzeClass :: ScopeId -> Maybe ScopeId -> Class -> (ScopeTree, ScopeId)
analyzeClass curId mParent cls =
let (methodTrees, nextId) = foldl step ([], curId + 1) (clsMethods cls)
methodBindings = Map.fromList
[ (fnName m, SymbolBinding (fnName m) SymFunction curId [] (isPublic (fnName m)))
| m <- clsMethods cls
]
clsTree = ScopeTree
{ scopeId = curId
, scopeKind = ScopeClass (clsName cls)
, scopeSymbols = methodBindings
, scopeParent = mParent
, scopeChildren = methodTrees
}
in (clsTree, nextId)
where
step (acc, cId) m =
let (mTree, nId) = analyzeFunction cId (Just curId) m
in (acc ++ [mTree], nId)
analyzeStruct :: ScopeId -> Maybe ScopeId -> Struct -> (ScopeTree, ScopeId)
analyzeStruct curId mParent st =
let (methodTrees, nextId) = foldl step ([], curId + 1) (stMethods st)
fieldBindings = Map.fromList
[ (fName, SymbolBinding fName (SymVariable BindingLocal) curId [] (isPublic fName))
| (fName, _) <- stFields st
]
stTree = ScopeTree
{ scopeId = curId
, scopeKind = ScopeStruct (stName st)
, scopeSymbols = fieldBindings
, scopeParent = mParent
, scopeChildren = methodTrees
}
in (stTree, nextId)
where
step (acc, cId) m =
let (mTree, nId) = analyzeFunction cId (Just curId) m
in (acc ++ [mTree], nId)
analyzeTrait :: ScopeId -> Maybe ScopeId -> Trait -> (ScopeTree, ScopeId)
analyzeTrait curId mParent tr =
let (methodTrees, nextId) = foldl step ([], curId + 1) (trMethods tr)
methodBindings = Map.fromList
[ (fnName m, SymbolBinding (fnName m) SymFunction curId [] (isPublic (fnName m)))
| m <- trMethods tr
]
trTree = ScopeTree
{ scopeId = curId
, scopeKind = ScopeTrait (trName tr)
, scopeSymbols = methodBindings
, scopeParent = mParent
, scopeChildren = methodTrees
}
in (trTree, nextId)
where
step (acc, cId) m =
let (mTree, nId) = analyzeFunction cId (Just curId) m
in (acc ++ [mTree], nId)
analyzeImpl :: ScopeId -> Maybe ScopeId -> Impl -> (ScopeTree, ScopeId)
analyzeImpl curId mParent imp =
let (methodTrees, nextId) = foldl step ([], curId + 1) (impMethods imp)
impTree = ScopeTree
{ scopeId = curId
, scopeKind = ScopeImpl (impTarget imp)
, scopeSymbols = Map.empty
, scopeParent = mParent
, scopeChildren = methodTrees
}
in (impTree, nextId)
where
step (acc, cId) m =
let (mTree, nId) = analyzeFunction cId (Just curId) m
in (acc ++ [mTree], nId)
extractExprScopeTrees :: ScopeId -> ScopeId -> [Stmt] -> ([ScopeTree], ScopeId)
extractExprScopeTrees startId parentId stmts =
let allExprs = concatMap getStmtExprs stmts
in foldl processExpr ([], startId) allExprs
where
processExpr (trees, curId) expr = case expr of
ExprLambda params body ->
let pBinds = Map.fromList
[ (paramName p, SymbolBinding (paramName p) (SymParameter (paramKind p)) curId [] False)
| p <- params
]
refs = collectExprRefs body
upBinds = recordReferences curId refs pBinds
tree = ScopeTree curId ScopeLambda upBinds (Just parentId) []
in (trees ++ [tree], curId + 1)
ExprListComp body comps ->
let (compTree, nextId) = buildCompTree curId parentId body comps
in (trees ++ [compTree], nextId)
ExprDictComp k v comps ->
let (compTree, nextId) = buildCompTree curId parentId (ExprTuple [k, v]) comps
in (trees ++ [compTree], nextId)
ExprSetComp body comps ->
let (compTree, nextId) = buildCompTree curId parentId body comps
in (trees ++ [compTree], nextId)
ExprGenerator body comps ->
let (compTree, nextId) = buildCompTree curId parentId body comps
in (trees ++ [compTree], nextId)
_ -> (trees, curId)
buildCompTree cId pId body comps =
let targets = concatMap (getExprIds . compTarget) comps
binds = Map.fromList
[ (t, SymbolBinding t (SymVariable BindingLocal) cId [] False)
| t <- targets
]
refs = collectExprRefs body
upBinds = recordReferences cId refs binds
tree = ScopeTree cId ScopeComprehension upBinds (Just pId) []
in (tree, cId + 1)
collectExplicitDirectives :: [Stmt] -> (Set Text, Set Text)
collectExplicitDirectives stmts =
foldl checkDirective (Set.empty, Set.empty) stmts
where
checkDirective (g, nl) s = case s of
StmtGlobal vars -> (Set.union g (Set.fromList vars), nl)
StmtNonlocal vars -> (g, Set.union nl (Set.fromList vars))
StmtIf _ b e ->
let (g1, n1) = collectExplicitDirectives b
(g2, n2) = collectExplicitDirectives e
in (Set.unions [g, g1, g2], Set.unions [nl, n1, n2])
StmtWhile _ b e ->
let (g1, n1) = collectExplicitDirectives b
(g2, n2) = collectExplicitDirectives e
in (Set.unions [g, g1, g2], Set.unions [nl, n1, n2])
StmtFor _ _ b e ->
let (g1, n1) = collectExplicitDirectives b
(g2, n2) = collectExplicitDirectives e
in (Set.unions [g, g1, g2], Set.unions [nl, n1, n2])
StmtAsyncFor _ _ b e ->
let (g1, n1) = collectExplicitDirectives b
(g2, n2) = collectExplicitDirectives e
in (Set.unions [g, g1, g2], Set.unions [nl, n1, n2])
StmtTry b h e f ->
let (g1, n1) = collectExplicitDirectives b
(g2, n2) = foldl (\(ga, na) (_, _, hb) ->
let (gb, nb) = collectExplicitDirectives hb
in (Set.union ga gb, Set.union na nb)) (Set.empty, Set.empty) h
(g3, n3) = collectExplicitDirectives e
(g4, n4) = collectExplicitDirectives f
in (Set.unions [g, g1, g2, g3, g4], Set.unions [nl, n1, n2, n3, n4])
StmtWith _ b ->
let (g1, n1) = collectExplicitDirectives b in (Set.union g g1, Set.union nl n1)
StmtAsyncWith _ b ->
let (g1, n1) = collectExplicitDirectives b in (Set.union g g1, Set.union nl n1)
StmtLoop b ->
let (g1, n1) = collectExplicitDirectives b in (Set.union g g1, Set.union nl n1)
StmtSwitch _ cases defStmts ->
let casePairs = concatMap snd cases
(g1, n1) = collectExplicitDirectives (casePairs ++ defStmts)
in (Set.union g g1, Set.union nl n1)
_ -> (g, nl)
collectStmtBindings :: ScopeId -> Set Text -> Set Text -> [Stmt] -> Map Text SymbolBinding
collectStmtBindings sid globals nonlocals stmts =
Map.fromList $ map createBinding (Set.toList (foldMap getStmtTargets stmts))
where
createBinding varName
| Set.member varName globals = (varName, SymbolBinding varName (SymVariable BindingGlobal) sid [] (isPublic varName))
| Set.member varName nonlocals = (varName, SymbolBinding varName (SymVariable BindingNonLocal) sid [] (isPublic varName))
| otherwise = (varName, SymbolBinding varName (SymVariable BindingLocal) sid [] (isPublic varName))
getStmtTargets :: Stmt -> Set Text
getStmtTargets stmt =
let direct = case stmt of
StmtAssign targets _ -> Set.fromList (concatMap getExprIds targets)
StmtAnnAssign target _ _ -> Set.fromList (getExprIds target)
StmtAugAssign target _ _ -> Set.fromList (getExprIds target)
StmtFor target _ body els -> Set.unions [Set.fromList (getExprIds target), foldMap getStmtTargets body, foldMap getStmtTargets els]
StmtAsyncFor target _ body els -> Set.unions [Set.fromList (getExprIds target), foldMap getStmtTargets body, foldMap getStmtTargets els]
StmtIf _ body els -> Set.union (foldMap getStmtTargets body) (foldMap getStmtTargets els)
StmtWhile _ body els -> Set.union (foldMap getStmtTargets body) (foldMap getStmtTargets els)
StmtLoop body -> foldMap getStmtTargets body
StmtTry b h els fin -> Set.unions [foldMap getStmtTargets b, foldMap (\(_, _, hb) -> foldMap getStmtTargets hb) h, foldMap getStmtTargets els, foldMap getStmtTargets fin]
StmtWith items body ->
let itemTargets = [name | (_, Just alias) <- items, name <- getExprIds alias]
in Set.union (Set.fromList itemTargets) (foldMap getStmtTargets body)
StmtAsyncWith items body ->
let itemTargets = [name | (_, Just alias) <- items, name <- getExprIds alias]
in Set.union (Set.fromList itemTargets) (foldMap getStmtTargets body)
StmtMatch _ cases -> foldMap (\mc -> Set.union (Set.fromList (getExprIds (mcPattern mc))) (foldMap getStmtTargets (mcBody mc))) cases
StmtSwitch _ cases defS -> Set.union (foldMap (\(_, ss) -> foldMap getStmtTargets ss) cases) (foldMap getStmtTargets defS)
_ -> Set.empty
-- PEP 572: Walrus operator (:=) targets are hoisted to the enclosing function/module scope
walrusHoisted = foldMap collectWalrusTargets (getStmtExprs stmt)
in Set.union direct walrusHoisted
-- | Recursively collect targets of walrus expressions (:=) inside any sub-expressions (PEP 572).
collectWalrusTargets :: Expr -> Set Text
collectWalrusTargets expr = case expr of
ExprWalrus name val -> Set.insert name (collectWalrusTargets val)
ExprBinary _ e1 e2 -> Set.union (collectWalrusTargets e1) (collectWalrusTargets e2)
ExprUnary _ e -> collectWalrusTargets e
ExprCall t args kwargs -> Set.unions (collectWalrusTargets t : map collectWalrusTargets args ++ map (collectWalrusTargets . snd) kwargs)
ExprAttr t _ -> collectWalrusTargets t
ExprSubscript t idx -> Set.union (collectWalrusTargets t) (collectWalrusTargets idx)
ExprSlice ms me mst -> Set.unions [maybe Set.empty collectWalrusTargets ms, maybe Set.empty collectWalrusTargets me, maybe Set.empty collectWalrusTargets mst]
ExprList es -> foldMap collectWalrusTargets es
ExprTuple es -> foldMap collectWalrusTargets es
ExprDict pairs -> foldMap (\(k, v) -> Set.union (collectWalrusTargets k) (collectWalrusTargets v)) pairs
ExprSet es -> foldMap collectWalrusTargets es
ExprLambda _ body -> collectWalrusTargets body
ExprTernary c t f -> Set.unions [collectWalrusTargets c, collectWalrusTargets t, collectWalrusTargets f]
ExprListComp item comps -> Set.union (collectWalrusTargets item) (foldMap compWalrus comps)
ExprDictComp k v comps -> Set.unions [collectWalrusTargets k, collectWalrusTargets v, foldMap compWalrus comps]
ExprSetComp item comps -> Set.union (collectWalrusTargets item) (foldMap compWalrus comps)
ExprGenerator item comps-> Set.union (collectWalrusTargets item) (foldMap compWalrus comps)
ExprAwait e -> collectWalrusTargets e
ExprYield me -> maybe Set.empty collectWalrusTargets me
ExprYieldFrom e -> collectWalrusTargets e
ExprFormattedString ps -> foldMap fpartWalrus ps
ExprStarred e -> collectWalrusTargets e
ExprKwStarred e -> collectWalrusTargets e
ExprOptChain e _ -> collectWalrusTargets e
ExprNullish e1 e2 -> Set.union (collectWalrusTargets e1) (collectWalrusTargets e2)
ExprChanRecv ch -> collectWalrusTargets ch
ExprTryOp e -> collectWalrusTargets e
ExprMacroCall _ args -> foldMap collectWalrusTargets args
ExprJSX _ attrs children-> Set.unions (map (collectWalrusTargets . snd) attrs ++ map collectWalrusTargets children)
_ -> Set.empty
where
compWalrus (CompFor _ iter ifs) = Set.union (collectWalrusTargets iter) (foldMap collectWalrusTargets ifs)
fpartWalrus (FStringExpr e _ _) = collectWalrusTargets e
fpartWalrus _ = Set.empty
getExprIds :: Expr -> [Text]
getExprIds expr = case expr of
ExprId name -> [name]
ExprTuple es -> concatMap getExprIds es
ExprList es -> concatMap getExprIds es
ExprStarred e -> getExprIds e
ExprWalrus name _ -> [name]
ExprCall _ args _ -> concatMap getExprIds args
_ -> []
getStmtExprs :: Stmt -> [Expr]
getStmtExprs stmt = case stmt of
StmtAssign targets val -> val : targets
StmtAnnAssign target ty v -> target : ty : maybe [] pure v
StmtAugAssign t _ v -> [t, v]
StmtExpr e -> [e]
StmtReturn me -> maybe [] pure me
StmtIf c b e -> c : concatMap getStmtExprs b ++ concatMap getStmtExprs e
StmtWhile c b e -> c : concatMap getStmtExprs b ++ concatMap getStmtExprs e
StmtFor t i b e -> t : i : concatMap getStmtExprs b ++ concatMap getStmtExprs e
StmtAsyncFor t i b e -> t : i : concatMap getStmtExprs b ++ concatMap getStmtExprs e
StmtTry b h e f -> concatMap getStmtExprs b ++ concatMap (\(me, _, hb) -> maybe [] pure me ++ concatMap getStmtExprs hb) h ++ concatMap getStmtExprs e ++ concatMap getStmtExprs f
StmtWith items b -> concatMap (\(e, ma) -> e : maybe [] pure ma) items ++ concatMap getStmtExprs b
StmtAsyncWith items b -> concatMap (\(e, ma) -> e : maybe [] pure ma) items ++ concatMap getStmtExprs b
StmtAssert e me -> e : maybe [] pure me
StmtRaise me mc -> maybe [] pure me ++ maybe [] pure mc
StmtDelete es -> es
StmtMatch s cs -> s : concatMap (\mc -> mcPattern mc : maybe [] pure (mcGuard mc) ++ concatMap getStmtExprs (mcBody mc)) cs
StmtGo e -> [e]
StmtDefer e -> [e]
StmtChanSend ch val -> [ch, val]
StmtLoop b -> concatMap getStmtExprs b
StmtSwitch s cases defS -> s : concatMap (getStmtExprs . StmtExpr . fst) cases ++ concatMap (concatMap getStmtExprs . snd) cases ++ concatMap getStmtExprs defS
_ -> []
collectStmtRefs :: [Stmt] -> Set Text
collectStmtRefs stmts = foldMap (collectExprRefs . fst) [(e, ()) | e <- concatMap getStmtExprs stmts]
collectExprRefs :: Expr -> Set Text
collectExprRefs expr = case expr of
ExprId name -> Set.singleton name
ExprLit _ -> Set.empty
ExprBinary _ e1 e2 -> Set.union (collectExprRefs e1) (collectExprRefs e2)
ExprUnary _ e -> collectExprRefs e
ExprCall t args kwargs -> Set.unions (collectExprRefs t : map collectExprRefs args ++ map (collectExprRefs . snd) kwargs)
ExprAttr t _ -> collectExprRefs t
ExprSubscript t idx -> Set.union (collectExprRefs t) (collectExprRefs idx)
ExprSlice ms me mst -> Set.unions [maybe Set.empty collectExprRefs ms, maybe Set.empty collectExprRefs me, maybe Set.empty collectExprRefs mst]
ExprList es -> foldMap collectExprRefs es
ExprTuple es -> foldMap collectExprRefs es
ExprDict pairs -> foldMap (\(k, v) -> Set.union (collectExprRefs k) (collectExprRefs v)) pairs
ExprSet es -> foldMap collectExprRefs es
ExprLambda _ body -> collectExprRefs body
ExprTernary c t f -> Set.unions [collectExprRefs c, collectExprRefs t, collectExprRefs f]
ExprListComp item comps -> Set.union (collectExprRefs item) (foldMap compRefs comps)
ExprDictComp k v comps -> Set.unions [collectExprRefs k, collectExprRefs v, foldMap compRefs comps]
ExprSetComp item comps -> Set.union (collectExprRefs item) (foldMap compRefs comps)
ExprGenerator item comps-> Set.union (collectExprRefs item) (foldMap compRefs comps)
ExprWalrus name val -> Set.insert name (collectExprRefs val)
ExprAwait e -> collectExprRefs e
ExprYield me -> maybe Set.empty collectExprRefs me
ExprYieldFrom e -> collectExprRefs e
ExprFormattedString ps -> foldMap fpartRefs ps
ExprStarred e -> collectExprRefs e
ExprKwStarred e -> collectExprRefs e
ExprOptChain e _ -> collectExprRefs e
ExprNullish e1 e2 -> Set.union (collectExprRefs e1) (collectExprRefs e2)
ExprChanRecv ch -> collectExprRefs ch
ExprTryOp e -> collectExprRefs e
ExprMacroCall _ args -> foldMap collectExprRefs args
ExprJSX _ attrs children-> Set.unions (map (collectExprRefs . snd) attrs ++ map collectExprRefs children)
where
compRefs (CompFor _ iter ifs) = Set.union (collectExprRefs iter) (foldMap collectExprRefs ifs)
fpartRefs (FStringText _) = Set.empty
fpartRefs (FStringExpr e _ _) = collectExprRefs e
recordReferences :: ScopeId -> Set Text -> Map Text SymbolBinding -> Map Text SymbolBinding
recordReferences sid refs syms =
Map.mapWithKey updateBinding syms
where
updateBinding name b
| Set.member name refs = b { symReferences = sid : symReferences b }
| otherwise = b
isPublic :: Text -> Bool
isPublic name = not (T.isPrefixOf "_" name)
findBinding :: Text -> ScopeTree -> Maybe SymbolBinding
findBinding name tree = Map.lookup name (scopeSymbols tree)
-- | Resolve a binding by name walking up the hierarchy of scope trees.
findBindingInHierarchy :: Text -> [ScopeTree] -> Maybe SymbolBinding
findBindingInHierarchy _ [] = Nothing
findBindingInHierarchy name (t:ts) = case Map.lookup name (scopeSymbols t) of
Just b -> Just b
Nothing -> findBindingInHierarchy name ts
allBindings :: ScopeTree -> [SymbolBinding]
allBindings tree =
Map.elems (scopeSymbols tree) ++ concatMap allBindings (scopeChildren tree)