content: {-
    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, ConstraintKinds #-}
  {-# OPTIONS_GHC -Wno-star-is-type #-}
  
  -- | The 'Cxx.Declaration' type has so many cases.  Try to capture some
  -- using 'IsDeclaration' so we can reuse them.
  module Glean.Util.Declarations
    ( -- * Visiting branches
      DeclBranch, applyDeclaration
      -- * Ranges
    , declarationSrcRange
      -- * Fact Id
    , getDeclId
    ) where
  
  import Data.Int (Int64)
  import Data.Typeable
  
  import Glean.Schema.Cxx1.Types as Cxx -- gen
  import Glean.Schema.Src.Types as Src -- gen
  import Glean.Util.Range (HasSrcRange(..))
  import Glean
  
  -- -----------------------------------------------------------------------------
  
  -- | 'DeclBranch' is the class constraint for writing the case handler
  -- of 'applyDeclaration'
  type DeclBranch p =
    ( Predicate p
    , Typeable p
    , Show p
    , Typeable (KeyType p), Show (KeyType p)
    , HasSrcRange (KeyType p)
    )
  
  -- -----------------------------------------------------------------------------
  
  {-# INLINE applyDeclaration #-}
  -- | Provide a case handler.  If the 'DeclBranch' does not provide enough
  -- constraints then please extend it, or use 'applyConstainedDeclaration'
  applyDeclaration
    :: (forall a . DeclBranch a => a -> b)
    -> Cxx.Declaration
    -> b
  applyDeclaration f = \case
    Cxx.Declaration_namespace_ d -> f d
    Cxx.Declaration_usingDeclaration d -> f d
    Cxx.Declaration_usingDirective d -> f d
    Cxx.Declaration_record_ d -> f d
    Cxx.Declaration_enum_ d -> f d
    Cxx.Declaration_function_ d -> f d
    Cxx.Declaration_variable d -> f d
    Cxx.Declaration_objcContainer d -> f d
    Cxx.Declaration_objcMethod d -> f d
    Cxx.Declaration_objcProperty d -> f d
    Cxx.Declaration_typeAlias d -> f d
    Cxx.Declaration_namespaceAlias d -> f d
    Cxx.Declaration_EMPTY -> error "unknown declaration branch"
  
  
  -- -----------------------------------------------------------------------------
  
  declarationSrcRange :: Cxx.Declaration -> Maybe Src.Range
  declarationSrcRange = applyDeclaration (fmap srcRange . getFactKey)
  -- -----------------------------------------------------------------------------
  
  {-# INLINE getDeclId #-}
  -- | The fact id from 'getDeclId' can be from the type of any branch, so
  -- it cannot be 'IdOf'
  getDeclId :: Cxx.Declaration -> Int64
  getDeclId = applyDeclaration (fromFid . idOf . getId)
  