packages feed

retrie-2.0.0: Retrie/Expr.hs

-- Copyright (c) 2025 Andrew Farmer
-- Copyright (c) 2020-2024 Facebook, Inc. and its affiliates.
--
-- This source code is licensed under the MIT license found in the
-- LICENSE file in the root directory of this source tree.
--
{-# LANGUAGE CPP #-}
{-# LANGUAGE OverloadedLists #-}
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE StandaloneDeriving #-}
{-# LANGUAGE TupleSections #-}
{-# LANGUAGE ViewPatterns #-}
module Retrie.Expr
  ( bitraverseHsConDetails
  , getUnparened
  , grhsToExpr
  , mkApps
  , mkConPatIn
  , mkEpAnn
  , mkHsAppsTy
  , mkLams
  , mkLet
  , mkLoc
  , mkLocA
  , mkLocatedHsVar
  , mkParen
  , mkVarPat
  , mkTyVar
  , parenify
  , parenifyT
  , parenifyP
  , patToExpr
  -- , patToExprA
  -- , setAnnsFor
  , unparen
  , unparenP
  , unparenT
  , wildSupply
  ) where

import Control.Monad
import Control.Monad.State.Lazy

import Retrie.ExactPrint
import Retrie.Fixity
import Retrie.GHC
import Retrie.SYB
import Retrie.Types

-------------------------------------------------------------------------------

mkLocatedHsVar :: Monad m => LocatedN RdrName -> TransformT m (LHsExpr GhcPs)
mkLocatedHsVar (L l n) = do
  mkLocA (SameLine 0)  (HsVar noExtField (L (setMoveAnchor (SameLine 0) l) n))

#if __GLASGOW_HASKELL__ < 912
-- TODO: move to ghc-exactprint
setMoveAnchor :: (Monoid an) => DeltaPos -> SrcAnn an -> SrcAnn an
setMoveAnchor dp (SrcSpanAnn EpAnnNotUsed l)
  = SrcSpanAnn (EpAnn (dpAnchor l dp) mempty emptyComments) l
setMoveAnchor dp (SrcSpanAnn (EpAnn (Anchor a _) an cs) l)
  = SrcSpanAnn (EpAnn (Anchor a (MovedAnchor dp)) an cs) l

-- TODO: move to ghc-exactprint
dpAnchor :: SrcSpan -> DeltaPos -> Anchor
dpAnchor l dp = Anchor (realSrcSpan l) (MovedAnchor dp)
#else
-- TODO: move to ghc-exactprint
setMoveAnchor :: (Monoid an) => DeltaPos -> EpAnn an -> EpAnn an
setMoveAnchor dp (EpAnn (EpaSpan l) an cs)
  = EpAnn (dpAnchor l dp) an cs
setMoveAnchor dp (EpAnn (EpaDelta l _ _) an cs)
  = EpAnn (dpAnchor l dp) an cs

-- TODO: move to ghc-exactprint
dpAnchor :: SrcSpan -> DeltaPos -> EpaLocation
dpAnchor l dp = EpaDelta l dp []
#endif

-------------------------------------------------------------------------------

-- setAnnsFor :: (Data e, Monad m)
--            => Located e -> [(KeywordId, DeltaPos)] -> TransformT m (Located e)
-- setAnnsFor e anns = modifyAnnsT (M.alter f (mkAnnKey e)) >> return e
--   where f Nothing  = Just annNone { annsDP = anns }
--         f (Just a) = Just a { annsDP = M.toList
--                                      $ M.union (M.fromList anns)
--                                                (M.fromList (annsDP a)) }

mkLoc :: (Data e, Monad m) => e -> TransformT m (Located e)
mkLoc e = do
  L <$> uniqueSrcSpanT <*> pure e

#if __GLASGOW_HASKELL__ < 912
-- ++AZ++:TODO: move to ghc-exactprint
mkLocA :: (Data e, Monad m, Monoid an)
  => DeltaPos -> e -> TransformT m (LocatedAn an e)
mkLocA dp e = mkLocAA dp mempty e

-- ++AZ++:TODO: move to ghc-exactprint
mkLocAA :: (Data e, Monad m) => DeltaPos -> an -> e -> TransformT m (LocatedAn an e)
mkLocAA dp an e = do
  l <- uniqueSrcSpanT
  let anc = Anchor (realSrcSpan l) (MovedAnchor dp)
  return (L (SrcSpanAnn (EpAnn anc an emptyComments) l) e)


-- ++AZ++:TODO: move to ghc-exactprint
mkEpAnn :: Monad m => DeltaPos -> an -> TransformT m (EpAnn an)
mkEpAnn dp an = do
  anc <- mkAnchor dp
  return $ EpAnn anc an emptyComments

mkAnchor :: Monad m => DeltaPos -> TransformT m (Anchor)
mkAnchor dp = do
  l <- uniqueSrcSpanT
  return (Anchor (realSrcSpan l) (MovedAnchor dp))
#else
-- ++AZ++:TODO: move to ghc-exactprint
mkLocA :: (Data e, Monad m, Monoid an)
  => DeltaPos -> e -> TransformT m (LocatedAn an e)
mkLocA dp e = mkLocAA dp mempty e

-- ++AZ++:TODO: move to ghc-exactprint
mkLocAA :: (Data e, Monad m) => DeltaPos -> an -> e -> TransformT m (LocatedAn an e)
mkLocAA dp an e = do
  l <- uniqueSrcSpanT
  let anc = EpaDelta l dp []
  return (L (EpAnn anc an emptyComments) e)


-- ++AZ++:TODO: move to ghc-exactprint
mkEpAnn :: Monad m => DeltaPos -> an -> TransformT m (EpAnn an)
mkEpAnn dp an = do
  anc <- mkAnchor dp
  return $ EpAnn anc an emptyComments

mkAnchor :: Monad m => DeltaPos -> TransformT m (EpaLocation)
mkAnchor dp = do
  l <- uniqueSrcSpanT
  return (EpaDelta l dp [])
#endif

-------------------------------------------------------------------------------

#if __GLASGOW_HASKELL__ < 912
mkLams
  :: [LPat GhcPs]
  -> LHsExpr GhcPs
  -> TransformT IO (LHsExpr GhcPs)
mkLams [] e = return e
mkLams vs e = do
  ancg <- mkAnchor (SameLine 0)
  ancm <- mkAnchor (SameLine 0)
  let
    ga = GrhsAnn Nothing (AddEpAnn AnnRarrow (EpaDelta (SameLine 1) []))
    ang = EpAnn ancg ga emptyComments
    anm = EpAnn ancm [(AddEpAnn AnnLam (EpaDelta (SameLine 0) []))] emptyComments
    L l (Match _ ctxt pats (GRHSs cs grhs binds)) = mkMatch LambdaExpr vs e emptyLocalBinds
    grhs' = case grhs of
      [L lg (GRHS _ guards rhs)] -> [L lg (GRHS ang guards rhs)]
      _ -> fail "mkLams: lambda expression can only have a single grhs!"
  matches <- mkLocA (SameLine 0) [L l (Match anm ctxt pats (GRHSs cs grhs' binds))]
  let
#if __GLASGOW_HASKELL__ < 908
    mg = mkMatchGroup Generated matches
#else
    mg = mkMatchGroup (Generated SkipPmc) matches
#endif
  mkLocA (SameLine 1) $ HsLam noExtField mg
#else
mkLams
  :: [LPat GhcPs]
  -> LHsExpr GhcPs
  -> TransformT IO (LHsExpr GhcPs)
mkLams [] e = return e
mkLams (p:ps) e = do
  ancg <- mkAnchor (SameLine 1)
  ancm <- mkAnchor (SameLine 0)
  ancGrhs <- mkAnchor (SameLine 0)
  let
    rarrow = EpUniTok ancg NormalSyntax
    ga = GrhsAnn Nothing (Right rarrow)
    anGrhs = EpAnn ancGrhs ga emptyComments
    
    lamTok = EpTok ancm
    lamAnn = EpAnnLam lamTok Nothing

    -- Ensure space after lambda
    vs' = setEntryDP p (SameLine 1) : ps
    
    L l (Match _ ctxt pats (GRHSs cs grhs binds)) = mkMatch (LamAlt LamSingle) (L (EpaSpan noSrcSpan) vs') e emptyLocalBinds
    grhs' = case grhs of
      [L lg (GRHS _ guards rhs)] -> [L lg (GRHS anGrhs guards rhs)]
      _ -> error "mkLams: lambda expression can only have a single grhs!"
  matches <- mkLocA (SameLine 0) [L l (Match noExtField ctxt pats (GRHSs cs grhs' binds))]
  let
    mg = mkMatchGroup (Generated OtherExpansion SkipPmc) matches
  mkLocA (SameLine 1) $ HsLam lamAnn LamSingle mg
#endif

mkLet :: Monad m => HsLocalBinds GhcPs -> LHsExpr GhcPs -> TransformT m (LHsExpr GhcPs)
mkLet EmptyLocalBinds{} e = return e
mkLet lbs e = do
#if __GLASGOW_HASKELL__ < 912
  an <- mkEpAnn (SameLine 0) NoEpAnns
  let tokLet = L (TokenLoc (EpaDelta (SameLine 0) [])) HsTok
      tokIn = L (TokenLoc (EpaDelta (DifferentLine 1 1) [])) HsTok
  le <- mkLocA (SameLine 1) $ HsLet an tokLet lbs tokIn e
#else
  letTokLoc <- mkAnchor (SameLine 0)
  inTokLoc <- mkAnchor (DifferentLine 1 1)
  let tokLet = EpTok letTokLoc
      tokIn = EpTok inTokLoc
  le <- mkLocA (SameLine 1) $ HsLet (tokLet, tokIn) lbs e
#endif
  transferBinds le

transferBinds :: Monad m => LHsExpr GhcPs -> TransformT m (LHsExpr GhcPs)
#if __GLASGOW_HASKELL__ < 912
transferBinds le = hsDecls le >>= replaceDecls le
#else
transferBinds le = return $ replaceDecls le (hsDecls le)
#endif

mkApps :: MonadIO m => LHsExpr GhcPs -> [LHsExpr GhcPs] -> TransformT m (LHsExpr GhcPs)
mkApps e []     = return e
mkApps f (a:as) = do
  -- lift $ liftIO $ debugPrint Loud "mkApps:f="  [showAst f]
  let a' = setEntryDP a (SameLine 1)
#if __GLASGOW_HASKELL__ < 912
  f' <- mkLocA (SameLine 0) (HsApp noAnn f a')
#else
  f' <- mkLocA (SameLine 0) (HsApp noExtField f a')
#endif
  mkApps f' as

-- GHC never generates HsAppTy in the parser, using HsAppsTy to keep a list
-- of types.
mkHsAppsTy :: Monad m => [LHsType GhcPs] -> TransformT m (LHsType GhcPs)
mkHsAppsTy [] = error "mkHsAppsTy: empty list"
mkHsAppsTy (t:ts) = do
  let t' = setEntryDP t (SameLine 0)
  foldM (\t1 t2 -> mkLocA (SameLine 1) (HsAppTy noExtField t1 t2)) t' ts

mkTyVar :: Monad m => LocatedN RdrName -> TransformT m (LHsType GhcPs)
mkTyVar nm = do
#if __GLASGOW_HASKELL__ < 912
  tv <- mkLocA (SameLine 1) (HsTyVar noAnn NotPromoted nm)
#else
  tv <- mkLocA (SameLine 1) (HsTyVar NoEpTok NotPromoted nm)
#endif
  -- _ <- setAnnsFor nm [(G AnnVal, DP (0,0))]
  (tv', _) <- swapEntryDPT tv nm
  return tv'

mkVarPat :: Monad m => LocatedN RdrName -> TransformT m (LPat GhcPs)
mkVarPat nm = cLPat <$> mkLocA (SameLine 1) (VarPat noExtField nm)

-- type HsConPatDetails p = HsConDetails (HsPatSigType (NoGhcTc p)) (LPat p) (HsRecFields p (LPat p))

mkConPatIn
  :: Monad m
  => LocatedN RdrName
  -> HsConPatDetails GhcPs
  -- -> HsConDetails Void (LocatedN RdrName) [RecordPatSynField GhcPs]
  -> TransformT m (LPat GhcPs)
mkConPatIn patName params = do
#if __GLASGOW_HASKELL__ < 912
  p <- mkLocA (SameLine 0) $ ConPat noAnn patName params
#else
  p <- mkLocA (SameLine 0) $ ConPat (Nothing, Nothing) patName params
#endif
  -- setEntryDPT p (DP (0,0))
  return p

-------------------------------------------------------------------------------

-- Note [Wildcards]
-- We need to invent unique binders for wildcard patterns and feed
-- them in as quantified variables for the matcher (they will match
-- some expression and be discarded). We do this hackily here, by
-- generating a supply of w1, w2, etc variables, and filter out any
-- other binders we know about. However, we should also filter out
-- the free variables of the expression, to avoid capture. Haven't found
-- a free variable computation on HsExpr though. :-(

type PatQ m = StateT ([RdrName], [RdrName]) (TransformT m)

newWildVar :: Monad m => PatQ m RdrName
newWildVar = do
  (s, u) <- get
  case s of
    (r:s') -> do
      put (s', r:u)
      return r
    [] -> error "impossible: empty wild supply"

wildSupply :: [RdrName] -> [RdrName]
wildSupply used = wildSupplyP (`notElem` used)

wildSupplyP :: (RdrName -> Bool) -> [RdrName]
wildSupplyP p =
  [ r | i <- [0..]
      , let r = mkVarUnqual (mkFastString ('w' : show (i :: Int)))
      , p r ]

patToExpr :: MonadIO m => LPat GhcPs -> PatQ m (LHsExpr GhcPs)
patToExpr orig = case dLPat orig of
  Nothing -> error "patToExpr: called on unlocated Pat!"
  Just lp@(L _ p) -> do
    e <- go p
#if __GLASGOW_HASKELL__ < 912
    lift $ transferEntryDP lp e
#else
    return $ transferEntryDP lp e
#endif
  where
    -- go :: Pat GhcPs -> PatQ m (LHsExpr GhcPs)
    go WildPat{} = do
      w <- newWildVar
      v <- lift $ mkLocA (SameLine 1) w
      lift $ mkLocatedHsVar v
    go (ConPat _ con ds) = conPatHelper con ds
    go (LazyPat _ pat) = patToExpr pat
    go (BangPat _ pat) = patToExpr pat
    go (ListPat _ ps) = do
      ps' <- mapM patToExpr ps
      lift $ do
#if __GLASGOW_HASKELL__ < 912
        an <- mkEpAnn (SameLine 1)
                      (AnnList Nothing (Just (AddEpAnn AnnOpenS d0)) (Just (AddEpAnn AnnCloseS d0)) [] [])
#else
        anc1 <- mkAnchor (SameLine 0)
        anc2 <- mkAnchor (SameLine 0)
        let open = EpTok anc1
            close = EpTok anc2
            an = AnnList Nothing (ListSquare open close) [] () []
#endif
        el <- mkLocA (SameLine 1) $ ExplicitList an ps'
        -- setAnnsFor el [(G AnnOpenS, DP (0,0)), (G AnnCloseS, DP (0,0))]
        return el
#if __GLASGOW_HASKELL__ < 912
    go (LitPat _ lit) = lift $ do
      -- lit' <- cloneT lit
      mkLocA (SameLine 1) $ HsLit noAnn lit
    go (NPat _ llit mbNeg _) = lift $ do
      -- L _ lit <- cloneT llit
      e <- mkLocA (SameLine 1) $ HsOverLit noAnn (unLoc llit)
      negE <- maybe (return e) (mkLocA (SameLine 0) . NegApp noAnn e) mbNeg
      -- addAllAnnsT llit negE
      return negE
    go (ParPat an _ p' _) = do
      p <- patToExpr p'
      let tokLP = L (TokenLoc (EpaDelta (SameLine 0) [])) HsTok
          tokRP = L (TokenLoc (EpaDelta (SameLine 0) [])) HsTok
      lift $ mkLocA (SameLine 1) (HsPar an tokLP p tokRP)
#else
    go (LitPat _ lit) = lift $ do
      -- lit' <- cloneT lit
      mkLocA (SameLine 1) $ HsLit noExtField lit
    go (NPat _ llit mbNeg _) = lift $ do
      -- L _ lit <- cloneT llit
      e <- mkLocA (SameLine 1) $ HsOverLit noExtField (unLoc llit)
      case mbNeg of
        Nothing -> return e
        Just _ -> do
          anc <- mkAnchor (SameLine 0)
          let minusTok = EpTok anc
          mkLocA (SameLine 0) (NegApp minusTok e noSyntaxExpr)
      -- addAllAnnsT llit negE
    go (ParPat _ p') = do
      p <- patToExpr p'
      anc1 <- lift $ mkAnchor (SameLine 0)
      anc2 <- lift $ mkAnchor (SameLine 0)
      let tokLP = EpTok anc1
          tokRP = EpTok anc2
      lift $ mkLocA (SameLine 1) (HsPar (tokLP, tokRP) p)
#endif
    go SigPat{} = error "patToExpr SigPat"
    go (TuplePat an ps boxity) = do
      es <- forM ps $ \pat -> do
        e <- patToExpr pat
#if __GLASGOW_HASKELL__ < 912
        return $ Present noAnn e
#else
        return $ Present noExtField e
#endif
      lift $ mkLocA (SameLine 1) $ ExplicitTuple an es boxity
    go (VarPat _ i) = lift $ mkLocatedHsVar i
    go AsPat{} = error "patToExpr AsPat"
    go NPlusKPat{} = error "patToExpr NPlusKPat"
    go SplicePat{} = error "patToExpr SplicePat"
    go SumPat{} = error "patToExpr SumPat"
    go ViewPat{} = error "patToExpr ViewPat"
#if __GLASGOW_HASKELL__ < 912
#else
    go OrPat{} = error "patToExpr OrPat"
    go EmbTyPat{} = error "patToExpr EmbTyPat"
    go InvisPat{} = error "patToExpr InvisPat"
#endif

conPatHelper :: MonadIO m
             => LocatedN RdrName
             -> HsConPatDetails GhcPs
             -> PatQ m (LHsExpr GhcPs)
conPatHelper con (InfixCon x y) =
  lift . mkLocA (SameLine 1)
#if __GLASGOW_HASKELL__ < 912
               =<< OpApp <$> pure noAnn
#else
               =<< OpApp <$> pure noExtField
#endif
                         <*> patToExpr x
                         <*> lift (mkLocatedHsVar con)
                         <*> patToExpr y
#if __GLASGOW_HASKELL__ < 914
-- TODO(xich): Properly handle tyargs here!
conPatHelper con (PrefixCon _tyargs xs) = do
#else
conPatHelper con (PrefixCon xs) = do
#endif
  f <- lift $ mkLocatedHsVar con
  as <- mapM patToExpr $ dropInvisPats xs
  -- lift $ lift $ liftIO $ debugPrint Loud "conPatHelper:f="  [showAst f]
  lift $ mkApps f as
conPatHelper _ _ = error "conPatHelper RecCon"

-------------------------------------------------------------------------------

grhsToExpr :: LGRHS GhcPs (LHsExpr GhcPs) -> LHsExpr GhcPs
grhsToExpr (L _ (GRHS _ [] e)) = e
grhsToExpr (L _ (GRHS _ (_:_) e)) = e -- not sure about this

-------------------------------------------------------------------------------

precedence :: FixityEnv -> HsExpr GhcPs -> Maybe Fixity
#if __GLASGOW_HASKELL__ < 912
precedence _        (HsApp {})       = Just $ Fixity NoSourceText 10 InfixL
#else
precedence _        (HsApp {})       = Just $ Fixity 10 InfixL
#endif
precedence fixities (OpApp _ _ op _) = Just $ lookupOp op fixities
precedence _        _                = Nothing

parenify
  :: Monad m => Context -> LHsExpr GhcPs -> TransformT m (LHsExpr GhcPs)
parenify Context{..} le@(L _ e)
  | needed ctxtParentPrec (precedence ctxtFixityEnv e) && needsParens e =
    mkParen le
  | otherwise = return le
  where
           {- parent -}               {- child -}
#if __GLASGOW_HASKELL__ < 912
    needed (HasPrec (Fixity _ p1 d1)) (Just (Fixity _ p2 d2)) =
#else
    needed (HasPrec (Fixity p1 d1)) (Just (Fixity p2 d2)) =
#endif
      p1 > p2 || (p1 == p2 && (d1 /= d2 || d2 == InfixN))
    needed NeverParen _ = False
    needed _ Nothing = True
    needed _ _ = False

-- | Wrap in parentheses.
mkParen :: Monad m => LHsExpr GhcPs -> TransformT m (LHsExpr GhcPs)
mkParen le = do
  let inner = setTrailingAnns [] (setEntryDP le (SameLine 0))
#if __GLASGOW_HASKELL__ < 912
  let tokLP = L (TokenLoc (EpaDelta (SameLine 0) [])) HsTok
      tokRP = L (TokenLoc (EpaDelta (SameLine 0) [])) HsTok
  p <- mkLocA (getEntryDP le) (HsPar noAnn tokLP inner tokRP)
#else
  anc1 <- mkAnchor (SameLine 0)
  anc2 <- mkAnchor (SameLine 0)
  let tokLP = EpTok anc1
      tokRP = EpTok anc2
  p <- mkParen' (getEntryDP le) (\_ -> HsPar (tokLP, tokRP) inner)
#endif
  transferAnnsT (const True) le p

getUnparened :: Data k => k -> k
getUnparened = mkT unparen `extT` unparenT `extT` unparenP

-- TODO: what about comments?
unparen :: LHsExpr GhcPs -> LHsExpr GhcPs
unparen expr = case expr of
#if __GLASGOW_HASKELL__ < 912
  L _ (HsPar _ _ e _)
#else
  L _ (HsPar _ e)
#endif
    -- see Note [Sections in HsSyn] in GHC.Hs.Expr
    | L _ SectionL{} <- e -> expr
    | L _ SectionR{} <- e -> expr
    | otherwise -> e
  _ -> expr

-- | hsExprNeedsParens is not always up-to-date, so this allows us to override
needsParens :: HsExpr GhcPs -> Bool
needsParens = hsExprNeedsParens (PprPrec 10)

#if __GLASGOW_HASKELL__ < 912
#else
mkParen' :: (Data x, Monad m, Monoid an)
         => DeltaPos -> (EpAnn AnnListItem -> x) -> TransformT m (LocatedAn an x)
mkParen' dp k = do
  let an = AnnListItem []
  l <- uniqueSrcSpanT
  let anc = EpaDelta l dp []
  pe <- mkLocA dp (k (EpAnn anc an emptyComments))
  return pe
#endif

#if __GLASGOW_HASKELL__ < 912
mkParenTy :: (Data x, Monad m, Monoid an)
         => DeltaPos -> (EpAnn AnnParen -> x) -> TransformT m (LocatedAn an x)
mkParenTy dp k = do
  let an = AnnParen AnnParens (EpaDelta (SameLine 0) []) (EpaDelta (SameLine 0) [])
  l <- uniqueSrcSpanT
  let anc = Anchor (realSrcSpan l) (MovedAnchor (SameLine 0))
  pe <- mkLocA dp (k (EpAnn anc an emptyComments))
  return pe
#else
mkParenTy :: (Data x, Monad m, Monoid an)
         => DeltaPos -> (EpAnn AnnListItem -> x) -> TransformT m (LocatedAn an x)
mkParenTy dp k = do
  let an = AnnListItem []
  l <- uniqueSrcSpanT
  let anc = EpaDelta l dp []
  pe <- mkLocA dp (k (EpAnn anc an emptyComments))
  return pe
#endif

-- This explicitly operates on 'Located (Pat GhcPs)' instead of 'LPat GhcPs'
-- because it is applied at that type by SYB.
parenifyP
  :: Monad m
  => Context
  -> LPat GhcPs
  -> TransformT m (LPat GhcPs)
#if __GLASGOW_HASKELL__ < 912
parenifyP Context{..} p@(L _ pat)
  | IsLhs <- ctxtParentPrec
  , needed pat = do
    let tokLP = L (TokenLoc (EpaDelta (SameLine 0) [])) HsTok
        tokRP = L (TokenLoc (EpaDelta (SameLine 0) [])) HsTok
    pe <- mkLocA (getEntryDP p) (ParPat noAnn tokLP inner tokRP)
    transferAnnsT (const True) p pe
  | otherwise = return p
#else
parenifyP Context{..} p@(L _ pat)
  | IsLhs <- ctxtParentPrec
  , needed pat = do
    anc1 <- mkAnchor (SameLine 0)
    anc2 <- mkAnchor (SameLine 0)
    let tokLP = EpTok anc1
        tokRP = EpTok anc2
    pe <- mkParen' (getEntryDP p) (\_ -> ParPat (tokLP, tokRP) inner)
    transferAnnsT (const True) p pe
  | otherwise = return p
#endif
  where
    needed BangPat{}                          = False
    needed LazyPat{}                          = False
    needed ListPat{}                          = False
    needed LitPat{}                           = False
    needed ParPat{}                           = False
    needed SumPat{}                           = False
    needed TuplePat{}                         = False
    needed VarPat{}                           = False
    needed WildPat{}                          = False
#if __GLASGOW_HASKELL__ < 914
    needed (ConPat _ _ (PrefixCon _ []))      = False
#else
    needed (ConPat _ _ (PrefixCon []))        = False
#endif
    needed _                                  = True
    inner = setTrailingAnns [] (setEntryDP p (SameLine 0))

parenifyT
  :: Monad m => Context -> LHsType GhcPs -> TransformT m (LHsType GhcPs)
#if __GLASGOW_HASKELL__ < 912
parenifyT Context{..} lty@(L _ ty)
  | needed ty = do
      p <- mkParenTy (getEntryDP lty) (\an -> HsParTy an inner)
      transferAnnsT (const True) lty p
  | otherwise = return lty
  where
    needed t = case ctxtParentPrec of
      HasPrec (Fixity _ prec InfixN) -> hsTypeNeedsParens (PprPrec prec) t
      HasPrec (Fixity _ prec _) -> hsTypeNeedsParens (PprPrec $ prec - 1) t
      IsLhs -> False
      NeverParen -> False
#else
parenifyT Context{..} lty@(L _ ty)
  | needed ty = do
      anc1 <- mkAnchor (SameLine 0)
      anc2 <- mkAnchor (SameLine 0)
      let tokLP = EpTok anc1
          tokRP = EpTok anc2
      p <- mkParenTy (getEntryDP lty) (\_ -> HsParTy (tokLP, tokRP) inner)
      transferAnnsT (const True) lty p
  | otherwise = return lty
  where
    needed t = case ctxtParentPrec of
      HasPrec (Fixity prec InfixN) -> hsTypeNeedsParens (PprPrec prec) t
      HasPrec (Fixity prec _) -> hsTypeNeedsParens (PprPrec $ prec - 1) t
      IsLhs -> False
      NeverParen -> False
#endif
    inner = setTrailingAnns [] (setEntryDP lty (SameLine 0))

unparenT :: LHsType GhcPs -> LHsType GhcPs
unparenT (L _ (HsParTy _ ty)) = ty
unparenT ty = ty

unparenP :: LPat GhcPs -> LPat GhcPs
#if __GLASGOW_HASKELL__ < 912
unparenP (L _ (ParPat _ _ p _)) = p
#else
unparenP (L _ (ParPat _ p)) = p
#endif
unparenP p = p

--------------------------------------------------------------------

#if __GLASGOW_HASKELL__ < 914
bitraverseHsConDetails
  :: Applicative m
  => ([tyarg] -> m [tyarg'])
  -> (arg -> m arg')
  -> (rec -> m rec')
  -> HsConDetails tyarg arg rec
  -> m (HsConDetails tyarg' arg' rec')
bitraverseHsConDetails argt argf _ (PrefixCon tyargs args) =
  PrefixCon <$> (argt tyargs) <*> (argf `traverse` args)
bitraverseHsConDetails _ _ recf (RecCon r) =
  RecCon <$> recf r
bitraverseHsConDetails _ argf _ (InfixCon a1 a2) =
  InfixCon <$> argf a1 <*> argf a2
#else
bitraverseHsConDetails
  :: Applicative m
  => (arg -> m arg')
  -> (rec -> m rec')
  -> HsConDetails arg rec
  -> m (HsConDetails arg' rec')
bitraverseHsConDetails argf _ (PrefixCon args) =
  PrefixCon <$> (argf `traverse` args)
bitraverseHsConDetails _ recf (RecCon r) =
  RecCon <$> recf r
bitraverseHsConDetails argf _ (InfixCon a1 a2) =
  InfixCon <$> argf a1 <*> argf a2
#endif