
module Agda.Syntax.Abstract.Views where

import Prelude hiding (null)

import Control.Applicative ( Const(Const), getConst )
import Control.Arrow (first)
import Control.Monad.Identity

import Data.Foldable (foldMap)
import Data.Semigroup ((<>))
import Data.Void

import Agda.Syntax.Common
import Agda.Syntax.Abstract as A
import Agda.Syntax.Concrete (FieldAssignment', exprFieldA)
import Agda.Syntax.Info
import Agda.Syntax.Scope.Base (KindOfName(..), conKindOfName, WithKind(..))

import Agda.Utils.Either
import Agda.Utils.List1 (List1)
import Agda.Utils.Null
import Agda.Utils.Singleton

import Agda.Utils.Impossible


data AppView' arg = Application Expr [NamedArg arg]
  deriving (Functor)

type AppView = AppView' Expr

-- | Gather applications to expose head and spine.
--
--   Note: everything is an application, possibly of itself to 0 arguments
appView :: Expr -> AppView
appView = fmap snd . appView'

appView' :: Expr -> AppView' (AppInfo, Expr)
appView' e =
  case e of
    App i e1 e2
      | Dot _ e2' <- unScope $ namedArg e2
      , Just f <- maybeProjTurnPostfix e2'
      , getHiding e2 == NotHidden -- Jesper, 2018-12-13: postfix projections shouldn't be hidden
                   -> Application f [defaultNamedArg (i, e1)]
    App i e1 arg
      | Application hd es <- appView' e1
                   -> Application hd $ es ++ [(fmap . fmap) (i,) arg]
    ScopedExpr _ e -> appView' e
    _              -> Application e []

maybeProjTurnPostfix :: Expr -> Maybe Expr
maybeProjTurnPostfix e =
  case e of
    ScopedExpr i e' -> ScopedExpr i <$> maybeProjTurnPostfix e'
    Proj _ x        -> return $ Proj ProjPostfix x
    _               -> Nothing

unAppView :: AppView -> Expr
unAppView (Application h es) =
  foldl (App defaultAppInfo_) h es

-- | Collects plain lambdas.
data LamView = LamView [LamBinding] Expr

lamView :: Expr -> LamView
lamView (Lam i b e) = cons b $ lamView e
  where cons b (LamView bs e) = LamView (b : bs) e
lamView (ScopedExpr _ e) = lamView e
lamView e = LamView [] e

-- | Gather top-level 'AsP'atterns and 'AnnP'atterns to expose underlying pattern.
asView :: A.Pattern -> ([Name], [A.Expr], A.Pattern)
asView (A.AsP _ x p)  = (\(asb, ann, p) -> (unBind x : asb, ann, p)) $ asView p
asView (A.AnnP _ a p) = (\(asb, ann, p) -> (asb, a : ann, p))        $ asView p
asView p              = ([], [], p)

-- | Remove top 'ScopedExpr' wrappers.
unScope :: Expr -> Expr
unScope (ScopedExpr scope e) = unScope e
unScope (QuestionMark i ii)  = QuestionMark (i {metaScope = empty}) ii
unScope (Underscore i)       = Underscore (i {metaScope = empty})
unScope e                    = e

-- | Remove 'ScopedExpr' wrappers everywhere.
--
--   NB: Unless the implementation of 'ExprLike' for clauses
--   has been finished, this does not work for clauses yet.
deepUnscope :: ExprLike a => a -> a
deepUnscope = mapExpr unScope

deepUnscopeDecls :: [A.Declaration] -> [A.Declaration]
deepUnscopeDecls = concatMap deepUnscopeDecl

deepUnscopeDecl :: A.Declaration -> [A.Declaration]
deepUnscopeDecl (A.ScopedDecl _ ds)              = deepUnscopeDecls ds
deepUnscopeDecl (A.Mutual i ds)                  = [A.Mutual i (deepUnscopeDecls ds)]
deepUnscopeDecl (A.Section i m tel ds)           = [A.Section i m (deepUnscope tel) (deepUnscopeDecls ds)]
deepUnscopeDecl (A.RecDef i x uc dir bs e ds) =
  [ A.RecDef i x uc dir (deepUnscope bs) (deepUnscope e) (deepUnscopeDecls ds) ]
deepUnscopeDecl d                                = [deepUnscope d]

-- * Traversal
---------------------------------------------------------------------------

-- Type aliases to abbreviate the quantified foralls which we use to avoid
-- giving in to NoMonoLocalBinds.
type RecurseExprFn m a = Applicative m => (Expr -> m Expr -> m Expr) -> a -> m a
type RecurseExprRecFn m = forall a. ExprLike a => a -> m a

type FoldExprFn m a = Monoid m => (Expr -> m) -> a -> m
type FoldExprRecFn m = forall a. ExprLike a => a -> m

type TraverseExprFn m a = (Applicative m, Monad m) => (Expr -> m Expr) -> a -> m a
type TraverseExprRecFn m = forall a. ExprLike a => a -> m a

-- | Apply an expression rewriting to every subexpression, inside-out.
--   See "Agda.Syntax.Internal.Generic".
class ExprLike a where
  -- | The first expression is pre-traversal, the second one post-traversal.
  recurseExpr :: RecurseExprFn m a
  default recurseExpr :: (Traversable f, ExprLike a', a ~ f a', Applicative m)
                      => (Expr -> m Expr -> m Expr) -> a -> m a
  recurseExpr = traverse . recurseExpr

  foldExpr :: FoldExprFn m a
  foldExpr f = getConst . recurseExpr (\ pre post -> Const (f pre) <* post)

  traverseExpr :: TraverseExprFn m a
  traverseExpr f = recurseExpr (\ pre post -> f =<< post)

  mapExpr :: (Expr -> Expr) -> (a -> a)
  mapExpr f = runIdentity . traverseExpr (Identity . f)

instance ExprLike Expr where
  recurseExpr :: forall m. RecurseExprFn m Expr
  recurseExpr f e0 = f e0 $ do
    let
      recurse :: RecurseExprRecFn m
      recurse e = recurseExpr f e
    case e0 of
      Var{}                      -> pure e0
      Def'{}                     -> pure e0
      Proj{}                     -> pure e0
      Con{}                      -> pure e0
      Lit{}                      -> pure e0
      QuestionMark{}             -> pure e0
      Underscore{}               -> pure e0
      Dot ei e                   -> Dot ei <$> recurse e
      App ei e arg               -> App ei <$> recurse e <*> recurse arg
      WithApp ei e es            -> WithApp ei <$> recurse e <*> recurse es
      Lam ei b e                 -> Lam ei <$> recurse b <*> recurse e
      AbsurdLam{}                -> pure e0
      ExtendedLam ei di er x cls -> ExtendedLam ei di er x <$> recurse cls
      Pi ei tel e                -> Pi ei <$> recurse tel <*> recurse e
      Generalized  s e           -> Generalized s <$> recurse e
      Fun ei arg e               -> Fun ei <$> recurse arg <*> recurse e
      Let ei bs e                -> Let ei <$> recurse bs <*> recurse e
      ETel tel                   -> ETel <$> recurse tel
      Rec ei bs                  -> Rec ei <$> recurse bs
      RecUpdate ei e bs          -> RecUpdate ei <$> recurse e <*> recurse bs
      ScopedExpr sc e            -> ScopedExpr sc <$> recurse e
      Quote{}                    -> pure e0
      QuoteTerm{}                -> pure e0
      Unquote{}                  -> pure e0
      DontCare e                 -> DontCare <$> recurse e
      PatternSyn{}               -> pure e0
      Macro{}                    -> pure e0

  foldExpr :: forall m. FoldExprFn m Expr
  foldExpr f e =
    case e of
      Var{}                  -> m
      Def'{}                 -> m
      Proj{}                 -> m
      Con{}                  -> m
      PatternSyn{}           -> m
      Macro{}                -> m
      Lit{}                  -> m
      QuestionMark{}         -> m
      Underscore{}           -> m
      Dot _ e                -> m `mappend` fold e
      App _ e e'             -> m `mappend` fold e `mappend` fold e'
      WithApp _ e es         -> m `mappend` fold e `mappend` fold es
      Lam _ b e              -> m `mappend` fold b `mappend` fold e
      AbsurdLam{}            -> m
      ExtendedLam _ _ _ _ cs -> m `mappend` fold cs
      Pi _ tel e             -> m `mappend` fold tel `mappend` fold e
      Generalized _ e        -> m `mappend` fold e
      Fun _ e e'             -> m `mappend` fold e `mappend` fold e'
      Let _ bs e             -> m `mappend` fold bs `mappend` fold e
      ETel tel               -> m `mappend` fold tel
      Rec _ as               -> m `mappend` fold as
      RecUpdate _ e as       -> m `mappend` fold e `mappend` fold as
      ScopedExpr _ e         -> m `mappend` fold e
      Quote{}                -> m
      QuoteTerm{}            -> m
      Unquote{}              -> m
      DontCare e             -> m `mappend` fold e
   where
     m = f e
     fold :: FoldExprRecFn m
     fold = foldExpr f

  traverseExpr :: forall m. TraverseExprFn m Expr
  traverseExpr f e = do
    let
      trav :: TraverseExprRecFn m
      trav e = traverseExpr f e
    case e of
      Var{}                      -> f e
      Def'{}                     -> f e
      Proj{}                     -> f e
      Con{}                      -> f e
      Lit{}                      -> f e
      QuestionMark{}             -> f e
      Underscore{}               -> f e
      Dot ei e                   -> f =<< Dot ei <$> trav e
      App ei e arg               -> f =<< App ei <$> trav e <*> trav arg
      WithApp ei e es            -> f =<< WithApp ei <$> trav e <*> trav es
      Lam ei b e                 -> f =<< Lam ei <$> trav b <*> trav e
      AbsurdLam{}                -> f e
      ExtendedLam ei di re x cls -> f =<< ExtendedLam ei di re x <$> trav cls
      Pi ei tel e                -> f =<< Pi ei <$> trav tel <*> trav e
      Generalized s e            -> f =<< Generalized s <$> trav e
      Fun ei arg e               -> f =<< Fun ei <$> trav arg <*> trav e
      Let ei bs e                -> f =<< Let ei <$> trav bs <*> trav e
      ETel tel                   -> f =<< ETel <$> trav tel
      Rec ei bs                  -> f =<< Rec ei <$> trav bs
      RecUpdate ei e bs          -> f =<< RecUpdate ei <$> trav e <*> trav bs
      ScopedExpr sc e            -> f =<< ScopedExpr sc <$> trav e
      Quote{}                    -> f e
      QuoteTerm{}                -> f e
      Unquote{}                  -> f e
      DontCare e                 -> f =<< DontCare <$> trav e
      PatternSyn{}               -> f e
      Macro{}                    -> f e

instance ExprLike a => ExprLike (Arg a)
instance ExprLike a => ExprLike (Maybe a)
instance ExprLike a => ExprLike (Named x a)
instance ExprLike a => ExprLike [a]
instance ExprLike a => ExprLike (List1 a)

instance (ExprLike a, ExprLike b) => ExprLike (a, b) where
  recurseExpr f (x, y) = (,) <$> recurseExpr f x <*> recurseExpr f y

instance ExprLike Void where
  recurseExpr f = absurd

instance ExprLike a => ExprLike (FieldAssignment' a) where
  recurseExpr = exprFieldA . recurseExpr

instance (ExprLike a, ExprLike b) => ExprLike (Either a b) where
  recurseExpr f = traverseEither (recurseExpr f)
                                 (recurseExpr f)

instance ExprLike BindName where
  recurseExpr f = pure

instance ExprLike ModuleName where
  recurseExpr f = pure

instance ExprLike QName where
  recurseExpr _ = pure

instance ExprLike LamBinding where
  recurseExpr f e =
    case e of
      DomainFree t x -> DomainFree <$> recurseExpr f t <*> pure x
      DomainFull bs  -> DomainFull <$> recurseExpr f bs
  foldExpr f e =
    case e of
      DomainFree t _ -> foldExpr f t
      DomainFull bs -> foldExpr f bs
  traverseExpr f e =
    case e of
      DomainFree t x -> DomainFree <$> traverseExpr f t <*> pure x
      DomainFull bs  -> DomainFull <$> traverseExpr f bs

instance ExprLike GeneralizeTelescope where
  recurseExpr  f (GeneralizeTel s tel) = GeneralizeTel s <$> recurseExpr f tel
  foldExpr     f (GeneralizeTel s tel) = foldExpr f tel
  traverseExpr f (GeneralizeTel s tel) = GeneralizeTel s <$> traverseExpr f tel

instance ExprLike DataDefParams where
  recurseExpr  f (DataDefParams s tel) = DataDefParams s <$> recurseExpr f tel
  foldExpr     f (DataDefParams s tel) = foldExpr f tel
  traverseExpr f (DataDefParams s tel) = DataDefParams s <$> traverseExpr f tel

instance ExprLike TypedBinding where
  recurseExpr f e =
    case e of
      TBind r t xs e -> TBind r <$> recurseExpr f t <*> pure xs <*> recurseExpr f e
      TLet r ds      -> TLet r <$> recurseExpr f ds
  foldExpr f e =
    case e of
      TBind _ t _ e -> foldExpr f t `mappend` foldExpr f e
      TLet _ ds     -> foldExpr f ds
  traverseExpr f e =
    case e of
      TBind r t xs e -> TBind r <$> traverseExpr f t <*> pure xs <*> traverseExpr f e
      TLet r ds      -> TLet r <$> traverseExpr f ds

instance ExprLike LetBinding where
  recurseExpr :: forall m. RecurseExprFn m LetBinding
  recurseExpr f e = do
    let
      recurse :: RecurseExprRecFn m
      recurse e = recurseExpr f e
    case e of
      LetBind li ai x e e'  -> LetBind li ai x <$> recurse e <*> recurse e'
      LetPatBind li p e     -> LetPatBind li <$> recurse p <*> recurse e
      LetApply{}            -> pure e
      LetOpen{}             -> pure e
      LetDeclaredVariable _ -> pure e

  foldExpr :: forall m. FoldExprFn m LetBinding
  foldExpr f e =
    case e of
      LetBind _ _ _ e e'    -> fold e `mappend` fold e'
      LetPatBind _ p e      -> fold p `mappend` fold e
      LetApply{}            -> mempty
      LetOpen{}             -> mempty
      LetDeclaredVariable _ -> mempty
    where
      fold :: FoldExprRecFn m
      fold e = foldExpr f e

  traverseExpr :: forall m. TraverseExprFn m LetBinding
  traverseExpr f e = do
    let
      trav :: TraverseExprRecFn m
      trav e = traverseExpr f e
    case e of
      LetBind li ai x e e'  -> LetBind li ai x <$> trav e <*> trav e'
      LetPatBind li p e     -> LetPatBind li <$> trav p <*> trav e
      LetApply{}            -> pure e
      LetOpen{}             -> pure e
      LetDeclaredVariable _ -> pure e

instance ExprLike a => ExprLike (Pattern' a) where

instance ExprLike a => ExprLike (Clause' a) where
  recurseExpr :: forall m. RecurseExprFn m (Clause' a)
  recurseExpr f (Clause lhs spats rhs ds ca) = Clause <$> rec lhs <*> pure spats <*> rec rhs <*> rec ds <*> pure ca
    where
      rec :: RecurseExprRecFn m
      rec = recurseExpr f

instance ExprLike RHS where
  recurseExpr :: forall m. RecurseExprFn m RHS
  recurseExpr f rhs =
    case rhs of
      RHS e c                 -> RHS <$> rec e <*> pure c
      AbsurdRHS{}             -> pure rhs
      WithRHS x es cs         -> WithRHS x <$> rec es <*> rec cs
      RewriteRHS xes spats rhs ds -> RewriteRHS <$> rec xes <*> pure spats <*> rec rhs <*> rec ds
    where
      rec :: RecurseExprRecFn m
      rec e = recurseExpr f e

instance (ExprLike qn, ExprLike nm, ExprLike p, ExprLike e) => ExprLike (RewriteEqn' qn nm p e) where
  recurseExpr f = \case
    Rewrite es    -> Rewrite <$> recurseExpr f es
    Invert qn pes -> Invert <$> recurseExpr f qn <*> recurseExpr f pes

instance ExprLike WhereDeclarations where
  recurseExpr f (WhereDecls a b) = WhereDecls a <$> recurseExpr f b

instance ExprLike ModuleApplication where
  recurseExpr :: forall m. RecurseExprFn m ModuleApplication
  recurseExpr f a =
    case a of
      SectionApp tel m es -> SectionApp <$> rec tel <*> rec m <*> rec es
      RecordModuleInstance{} -> pure a
    where
      rec :: RecurseExprRecFn m
      rec e = recurseExpr f e

instance ExprLike Pragma where
  recurseExpr :: forall m. RecurseExprFn m Pragma
  recurseExpr f p =
    case p of
      BuiltinPragma s x           -> pure p
      OptionsPragma{}             -> pure p
      BuiltinNoDefPragma{}        -> pure p
      RewritePragma{}             -> pure p
      CompilePragma{}             -> pure p
      StaticPragma{}              -> pure p
      InjectivePragma{}           -> pure p
      InlinePragma{}              -> pure p
      EtaPragma{}                 -> pure p
      DisplayPragma f xs e        -> DisplayPragma f <$> rec xs <*> rec e
    where
      rec :: RecurseExprRecFn m
      rec e = recurseExpr f e

instance ExprLike LHS where
  recurseExpr f (LHS i p) = LHS i <$> recurseExpr f p

instance ExprLike a => ExprLike (LHSCore' a)   where
instance ExprLike a => ExprLike (WithHiding a) where

instance ExprLike SpineLHS where
  recurseExpr f (SpineLHS i x ps) = SpineLHS i x <$> recurseExpr f ps

instance ExprLike Declaration where
  recurseExpr :: forall m. RecurseExprFn m Declaration
  recurseExpr f d =
    case d of
      Axiom a d i mp x e        -> Axiom a d i mp x <$> rec e
      Generalize s i j x e      -> Generalize s i j x <$> rec e
      Field i x e               -> Field i x <$> rec e
      Primitive i x e           -> Primitive i x <$> rec e
      Mutual i ds               -> Mutual i <$> rec ds
      Section i m tel ds        -> Section i m <$> rec tel <*> rec ds
      Apply i m a ci d          -> (\ a -> Apply i m a ci d) <$> rec a
      Import{}                  -> pure d
      Pragma i p                -> Pragma i <$> rec p
      Open{}                    -> pure d
      FunDef i f d cs           -> FunDef i f d <$> rec cs
      DataSig i d tel e         -> DataSig i d <$> rec tel <*> rec e
      DataDef i d uc bs cs      -> DataDef i d uc <$> rec bs <*> rec cs
      RecSig i r tel e          -> RecSig i r <$> rec tel <*> rec e
      RecDef i r uc dir bs e ds -> RecDef i r uc dir <$> rec bs <*> rec e <*> rec ds
      PatternSynDef f xs p      -> PatternSynDef f xs <$> rec p
      UnquoteDecl i is xs e     -> UnquoteDecl i is xs <$> rec e
      UnquoteDef i xs e         -> UnquoteDef i xs <$> rec e
      ScopedDecl s ds           -> ScopedDecl s <$> rec ds
    where
      rec :: RecurseExprRecFn m
      rec e = recurseExpr f e


-- * Getting all declared names
---------------------------------------------------------------------------

type KName = WithKind QName

-- | Extracts "all" names which are declared in a 'Declaration'.
--
-- Includes: local modules and @where@ clauses.
-- Excludes: @open public@, @let@, @with@ function names, extended lambdas.

class DeclaredNames a where
  declaredNames :: Collection KName m => a -> m

  default declaredNames
     :: (Foldable t, DeclaredNames b, t b ~ a)
     => Collection KName m => a -> m
  declaredNames = foldMap declaredNames

instance DeclaredNames a => DeclaredNames [a]
instance DeclaredNames a => DeclaredNames (List1 a)
instance DeclaredNames a => DeclaredNames (Maybe a)
instance DeclaredNames a => DeclaredNames (Arg a)
instance DeclaredNames a => DeclaredNames (Named name a)
instance DeclaredNames a => DeclaredNames (FieldAssignment' a)

instance (DeclaredNames a, DeclaredNames b) => DeclaredNames (Either a b) where
  declaredNames = either declaredNames declaredNames

instance (DeclaredNames a, DeclaredNames b) => DeclaredNames (a,b) where
  declaredNames (a,b) = declaredNames a <> declaredNames b

instance DeclaredNames KName where
  declaredNames = singleton

instance DeclaredNames RecordDirectives where
  declaredNames (RecordDirectives i _ _ c) = kc where
    kc = maybe mempty (singleton . WithKind k) c
    k  = maybe ConName (conKindOfName . rangedThing) i

instance DeclaredNames Declaration where
  declaredNames = \case
      Axiom _ di _ _ q _           -> singleton . (`WithKind` q) $
                                      case defMacro di of
                                        MacroDef    -> MacroName
                                        NotMacroDef -> AxiomName
      Generalize _ _ _ q _         -> singleton (WithKind GeneralizeName q)
      Field _ q _                  -> singleton (WithKind FldName q)
      Primitive _ q _              -> singleton (WithKind PrimName q)
      Mutual _ decls               -> declaredNames decls
      DataSig _ q _ _              -> singleton (WithKind DataName q)
      DataDef _ q _ _ decls        -> singleton (WithKind DataName q) <> foldMap con decls
      RecSig _ q _ _               -> singleton (WithKind RecName q)
      RecDef _ q _ dir _ _ decls   -> singleton (WithKind RecName q) <> declaredNames dir <> declaredNames decls
      PatternSynDef q _ _          -> singleton (WithKind PatternSynName q)
      UnquoteDecl _ _ qs _         -> fromList $ map (WithKind OtherDefName) qs  -- could be Fun or Axiom
      UnquoteDef _ qs _            -> fromList $ map (WithKind FunName) qs       -- cannot be Axiom
      FunDef _ q _ cls             -> singleton (WithKind FunName q) <> declaredNames cls
      ScopedDecl _ decls           -> declaredNames decls
      Section _ _ _ decls          -> declaredNames decls
      Pragma _ pragma              -> declaredNames pragma
      Apply{}                      -> mempty
      Import{}                     -> mempty
      Open{}                       -> mempty
    where
    con = \case
      Axiom _ _ _ _ q _ -> singleton $ WithKind ConName q
      _ -> __IMPOSSIBLE__

instance DeclaredNames Pragma where
  declaredNames = \case
    BuiltinNoDefPragma _b kind x -> singleton $ WithKind kind x
    BuiltinPragma{}         -> mempty
    CompilePragma{}         -> mempty
    RewritePragma{}         -> mempty
    StaticPragma{}          -> mempty
    EtaPragma{}             -> mempty
    InjectivePragma{}       -> mempty
    InlinePragma{}          -> mempty
    DisplayPragma{}         -> mempty
    OptionsPragma{}         -> mempty

instance DeclaredNames Clause where
  declaredNames (Clause _ _ rhs decls _) = declaredNames rhs <> declaredNames decls

instance DeclaredNames WhereDeclarations where
  declaredNames (WhereDecls _ ds) = declaredNames ds

instance DeclaredNames RHS where
  declaredNames = \case
    RHS _ _                   -> mempty
    AbsurdRHS                 -> mempty
    WithRHS _q _es cls        -> declaredNames cls
    RewriteRHS _qes _ rhs cls -> declaredNames rhs <> declaredNames cls

-- Andreas, 2020-04-13: Migration from Agda.Syntax.Abstract.AllNames
--
-- Since we are not interested in names of extended lambdas, we do not
-- traverse into expression.
--
-- However, we keep this code (originally Agda.Syntax.Abstract.AllNames) around
-- should arise a need to collect extended lambda names.

-- instance (DeclaredNames a, DeclaredNames b, DeclaredNames c) => DeclaredNames (a,b,c) where
--   declaredNames (a,b,c) = declaredNames a <> declaredNames b <> declaredNames c

-- instance DeclaredNames RHS where
--   declaredNames = \case
--     RHS e _                  -> declaredNames e
--     AbsurdRHS{}              -> mempty
--     WithRHS q _ cls          -> singleton (WithKind FunName q) <> declaredNames cls
--     RewriteRHS qes _ rhs cls -> declaredNames (qes, rhs, cls)

-- instance DeclaredNames ModuleName where
--   declaredNames _ = mempty

-- instance (DeclaredNames qn, DeclaredNames e) => DeclaredNames (RewriteEqn' qn p e) where
--   declaredNames = \case
--     Rewrite es    -> declaredNames es
--     Invert qn pes -> declaredNames qn <> declaredNames pes

-- instance DeclaredNames Expr where
--   declaredNames = \case
--     Var{}                 -> mempty
--     Def{}                 -> mempty
--     Proj{}                -> mempty
--     Con{}                 -> mempty
--     Lit{}                 -> mempty
--     QuestionMark{}        -> mempty
--     Underscore{}          -> mempty
--     Dot _ e               -> declaredNames e
--     App _ e1 e2           -> declaredNames e1 <> declaredNames e2
--     WithApp _ e es        -> declaredNames e <> declaredNames es
--     Lam _ b e             -> declaredNames b <> declaredNames e
--     AbsurdLam{}           -> mempty
--     ExtendedLam _ _ q cls -> singleton (WithKind FunName q) <> declaredNames cls
--     Pi _ tel e            -> declaredNames tel <> declaredNames e
--     Generalized s e       -> declaredNames e  -- NOT: fromList (map (WithKind GeneralizeName) $ Set.toList s) <> declaredNames e
--     Fun _ e1 e2           -> declaredNames e1 <> declaredNames e2
--     Set{}                 -> mempty
--     Prop{}                -> mempty
--     Let _ lbs e           -> declaredNames lbs <> declaredNames e
--     ETel{}                -> __IMPOSSIBLE__
--     Rec _ fields          -> declaredNames fields
--     RecUpdate _ e fs      -> declaredNames e <> declaredNames fs
--     ScopedExpr _ e        -> declaredNames e
--     Quote{}               -> mempty
--     QuoteTerm{}           -> mempty
--     Unquote{}             -> mempty
--     DontCare{}            -> mempty
--     PatternSyn{}          -> mempty
--     Macro{}               -> mempty

-- instance DeclaredNames LamBinding where
--   declaredNames DomainFree{}       = mempty
--   declaredNames (DomainFull binds) = declaredNames binds

-- instance DeclaredNames TypedBinding where
--   declaredNames (TBind _ t _ e) = declaredNames (t, e)
--   declaredNames (TLet _ lbs)    = declaredNames lbs

-- instance DeclaredNames LetBinding where
--   declaredNames (LetBind _ _ _ e1 e2)   = declaredNames e1 <> declaredNames e2
--   declaredNames (LetPatBind _ _ e)      = declaredNames e
--   declaredNames (LetApply _ _ app _ _)  = declaredNames app
--   declaredNames LetOpen{}               = mempty
--   declaredNames (LetDeclaredVariable _) = mempty

-- instance DeclaredNames ModuleApplication where
--   declaredNames (SectionApp bindss _ es) = declaredNames bindss <> declaredNames es
--   declaredNames RecordModuleInstance{}   = mempty
