summaryrefslogtreecommitdiff
path: root/utils/check-exact
diff options
context:
space:
mode:
authorAlan Zimmerman <alan.zimm@gmail.com>2021-02-21 21:23:40 +0000
committerMarge Bot <ben+marge-bot@smart-cactus.org>2021-03-20 07:48:38 -0400
commit95275a5f25a2e70b71240d4756109180486af1b1 (patch)
treeeb4801bb0e00098b8b9d513479de4fbbd779ddac /utils/check-exact
parentf940fd466a86c2f8e93237b36835797be3f3c898 (diff)
downloadhaskell-95275a5f25a2e70b71240d4756109180486af1b1.tar.gz
GHC Exactprint main commit
Metric Increase: T10370 parsing001 Updates haddock submodule
Diffstat (limited to 'utils/check-exact')
-rw-r--r--utils/check-exact/.ghci3
-rw-r--r--utils/check-exact/ExactPrint.hs4165
-rw-r--r--utils/check-exact/Lookup.hs137
-rw-r--r--utils/check-exact/Main.hs238
-rw-r--r--utils/check-exact/Parsers.hs332
-rw-r--r--utils/check-exact/Preprocess.hs312
-rw-r--r--utils/check-exact/README24
-rw-r--r--utils/check-exact/Test.hs840
-rw-r--r--utils/check-exact/Transform.hs1513
-rw-r--r--utils/check-exact/Types.hs331
-rw-r--r--utils/check-exact/Utils.hs596
-rw-r--r--utils/check-exact/cases/AddDecl1.expected.hs13
-rw-r--r--utils/check-exact/cases/AddDecl1.hs11
-rw-r--r--utils/check-exact/cases/AddDecl2.expected.hs13
-rw-r--r--utils/check-exact/cases/AddDecl2.hs11
-rw-r--r--utils/check-exact/cases/AddDecl3.expected.hs13
-rw-r--r--utils/check-exact/cases/AddDecl3.hs11
-rw-r--r--utils/check-exact/cases/AddHiding1.expected.hs8
-rw-r--r--utils/check-exact/cases/AddHiding1.hs8
-rw-r--r--utils/check-exact/cases/AddHiding2.expected.hs5
-rw-r--r--utils/check-exact/cases/AddHiding2.hs5
-rw-r--r--utils/check-exact/cases/AddLocalDecl1.expected.hs15
-rw-r--r--utils/check-exact/cases/AddLocalDecl1.hs13
-rw-r--r--utils/check-exact/cases/AddLocalDecl2.expected.hs11
-rw-r--r--utils/check-exact/cases/AddLocalDecl2.hs10
-rw-r--r--utils/check-exact/cases/AddLocalDecl3.expected.hs13
-rw-r--r--utils/check-exact/cases/AddLocalDecl3.hs12
-rw-r--r--utils/check-exact/cases/AddLocalDecl4.expected.hs6
-rw-r--r--utils/check-exact/cases/AddLocalDecl4.hs3
-rw-r--r--utils/check-exact/cases/AddLocalDecl5.expected.hs9
-rw-r--r--utils/check-exact/cases/AddLocalDecl5.hs8
-rw-r--r--utils/check-exact/cases/AddLocalDecl6.expected.hs12
-rw-r--r--utils/check-exact/cases/AddLocalDecl6.hs10
-rw-r--r--utils/check-exact/cases/EmptyWheres.hs9
-rw-r--r--utils/check-exact/cases/LayoutIn1.expected.hs9
-rw-r--r--utils/check-exact/cases/LayoutIn1.hs9
-rw-r--r--utils/check-exact/cases/LayoutIn3.expected.hs13
-rw-r--r--utils/check-exact/cases/LayoutIn3.hs13
-rw-r--r--utils/check-exact/cases/LayoutIn3a.expected.hs13
-rw-r--r--utils/check-exact/cases/LayoutIn3a.hs13
-rw-r--r--utils/check-exact/cases/LayoutIn3b.expected.hs12
-rw-r--r--utils/check-exact/cases/LayoutIn3b.hs12
-rw-r--r--utils/check-exact/cases/LayoutIn4.expected.hs13
-rw-r--r--utils/check-exact/cases/LayoutIn4.hs13
-rw-r--r--utils/check-exact/cases/LayoutLet2.expected.hs8
-rw-r--r--utils/check-exact/cases/LayoutLet2.hs8
-rw-r--r--utils/check-exact/cases/LayoutLet3.expected.hs10
-rw-r--r--utils/check-exact/cases/LayoutLet3.hs10
-rw-r--r--utils/check-exact/cases/LayoutLet4.expected.hs12
-rw-r--r--utils/check-exact/cases/LayoutLet4.hs12
-rw-r--r--utils/check-exact/cases/LetIn1.expected.hs18
-rw-r--r--utils/check-exact/cases/LetIn1.hs19
-rw-r--r--utils/check-exact/cases/LocToName.expected.hs25
-rw-r--r--utils/check-exact/cases/LocToName.hs25
-rw-r--r--utils/check-exact/cases/LocalDecls.expected.hs11
-rw-r--r--utils/check-exact/cases/LocalDecls.hs8
-rw-r--r--utils/check-exact/cases/LocalDecls2.expected.hs8
-rw-r--r--utils/check-exact/cases/LocalDecls2.hs3
-rw-r--r--utils/check-exact/cases/Rename1.expected.hs6
-rw-r--r--utils/check-exact/cases/Rename1.hs6
-rw-r--r--utils/check-exact/cases/Rename2.expected.hs4
-rw-r--r--utils/check-exact/cases/Rename2.hs4
-rw-r--r--utils/check-exact/cases/RenameCase1.expected.hs5
-rw-r--r--utils/check-exact/cases/RenameCase1.hs5
-rw-r--r--utils/check-exact/cases/RmDecl1.expected.hs9
-rw-r--r--utils/check-exact/cases/RmDecl1.hs13
-rw-r--r--utils/check-exact/cases/RmDecl2.expected.hs9
-rw-r--r--utils/check-exact/cases/RmDecl2.hs10
-rw-r--r--utils/check-exact/cases/RmDecl3.expected.hs9
-rw-r--r--utils/check-exact/cases/RmDecl3.hs9
-rw-r--r--utils/check-exact/cases/RmDecl4.expected.hs10
-rw-r--r--utils/check-exact/cases/RmDecl4.hs9
-rw-r--r--utils/check-exact/cases/RmDecl5.expected.hs5
-rw-r--r--utils/check-exact/cases/RmDecl5.hs7
-rw-r--r--utils/check-exact/cases/RmDecl6.expected.hs9
-rw-r--r--utils/check-exact/cases/RmDecl6.hs12
-rw-r--r--utils/check-exact/cases/RmDecl7.expected.hs7
-rw-r--r--utils/check-exact/cases/RmDecl7.hs9
-rw-r--r--utils/check-exact/cases/RmTypeSig1.expected.hs8
-rw-r--r--utils/check-exact/cases/RmTypeSig1.hs8
-rw-r--r--utils/check-exact/cases/RmTypeSig2.expected.hs7
-rw-r--r--utils/check-exact/cases/RmTypeSig2.hs8
-rw-r--r--utils/check-exact/cases/WhereIn3a.expected.hs20
-rw-r--r--utils/check-exact/cases/WhereIn3a.hs20
-rw-r--r--utils/check-exact/cases/WhereIn3b.expected.hs27
-rw-r--r--utils/check-exact/cases/WhereIn3b.hs20
-rw-r--r--utils/check-exact/cases/WhereIn4.expected.hs19
-rw-r--r--utils/check-exact/cases/WhereIn4.hs19
-rw-r--r--utils/check-exact/cases/Windows.hs10
-rw-r--r--utils/check-exact/check-exact.cabal38
-rw-r--r--utils/check-exact/ghc.mk18
-rwxr-xr-xutils/check-exact/run.sh3
92 files changed, 9399 insertions, 0 deletions
diff --git a/utils/check-exact/.ghci b/utils/check-exact/.ghci
new file mode 100644
index 0000000000..43ff67a50e
--- /dev/null
+++ b/utils/check-exact/.ghci
@@ -0,0 +1,3 @@
+:set -package ghc
+:set -i./src
+:set -Wall
diff --git a/utils/check-exact/ExactPrint.hs b/utils/check-exact/ExactPrint.hs
new file mode 100644
index 0000000000..8f4f89e265
--- /dev/null
+++ b/utils/check-exact/ExactPrint.hs
@@ -0,0 +1,4165 @@
+{-# LANGUAGE DeriveDataTypeable #-}
+{-# LANGUAGE FlexibleContexts #-}
+{-# LANGUAGE FlexibleInstances #-}
+{-# LANGUAGE GeneralizedNewtypeDeriving #-}
+{-# LANGUAGE MultiWayIf #-}
+{-# LANGUAGE NamedFieldPuns #-}
+{-# LANGUAGE RankNTypes #-}
+{-# LANGUAGE StandaloneDeriving #-}
+{-# LANGUAGE TypeFamilies #-}
+{-# LANGUAGE TypeSynonymInstances #-}
+{-# LANGUAGE ViewPatterns #-}
+
+module ExactPrint
+ (
+ ExactPrint(..)
+ , exactPrint
+ -- , exactPrintWithOptions
+ ) where
+
+import GHC
+import GHC.Core.Coercion.Axiom (Role(..))
+import GHC.Data.Bag
+import qualified GHC.Data.BooleanFormula as BF
+import GHC.Data.FastString
+import GHC.Types.Basic hiding (EP)
+import GHC.Types.Fixity
+import GHC.Types.ForeignCall
+import GHC.Types.SourceText
+import GHC.Utils.Outputable hiding ( (<>) )
+import GHC.Driver.Ppr
+import GHC.Unit.Module.Warnings
+import GHC.Utils.Misc
+import GHC.Utils.Panic
+
+import Control.Monad.Identity
+import Control.Monad.RWS
+import Data.Data ( Data )
+import Data.Foldable
+import Data.Typeable
+import Data.List ( partition, sort, sortBy)
+import Data.Maybe ( isJust )
+
+import Data.Void
+
+import Lookup
+import Utils
+import Types
+
+-- import Debug.Trace
+
+-- ---------------------------------------------------------------------
+
+exactPrint :: ExactPrint ast => Located ast -> ApiAnns -> String
+exactPrint ast anns = runIdentity (runEP anns stringOptions (markAnnotated ast))
+
+type EP w m a = RWST (PrintOptions m w) (EPWriter w) EPState m a
+type EPP a = EP String Identity a
+
+runEP :: ApiAnns -> PrintOptions Identity String
+ -> Annotated () -> Identity String
+runEP anns epReader action =
+ fmap (output . snd) .
+ (\next -> execRWST next epReader (defaultEPState anns))
+ . xx $ action
+
+xx :: Annotated () -> EP String Identity ()
+-- xx :: Annotated() -> RWST (PrintOptions m w) (EPWriter w) EPState m ()
+xx = id
+
+-- ---------------------------------------------------------------------
+
+defaultEPState :: ApiAnns -> EPState
+defaultEPState as = EPState
+ { epPos = (1,1)
+ , epApiAnns = as
+ , dLHS = 1
+ , pMarkLayout = False
+ , pLHS = 1
+ , dMarkLayout = False
+ , dPriorEndPosition = (1,1)
+ , uAnchorSpan = badRealSrcSpan
+ , uExtraDP = Nothing
+ , epComments = rogueComments as
+ }
+
+
+-- ---------------------------------------------------------------------
+-- The EP monad and basic combinators
+
+-- | The R part of RWS. The environment. Updated via 'local' as we
+-- enter a new AST element, having a different anchor point.
+data PrintOptions m a = PrintOptions
+ {
+ epAnn :: !Annotation
+ , epAstPrint :: forall ast . Data ast => GHC.Located ast -> a -> m a
+ , epTokenPrint :: String -> m a
+ , epWhitespacePrint :: String -> m a
+ , epRigidity :: Rigidity
+ , epContext :: !AstContextSet
+ }
+
+-- | Helper to create a 'PrintOptions'
+printOptions ::
+ (forall ast . Data ast => GHC.Located ast -> a -> m a)
+ -> (String -> m a)
+ -> (String -> m a)
+ -> Rigidity
+ -> PrintOptions m a
+printOptions astPrint tokenPrint wsPrint rigidity = PrintOptions
+ {
+ epAnn = annNone
+ , epAstPrint = astPrint
+ , epWhitespacePrint = wsPrint
+ , epTokenPrint = tokenPrint
+ , epRigidity = rigidity
+ , epContext = defaultACS
+ }
+
+-- | Options which can be used to print as a normal String.
+stringOptions :: PrintOptions Identity String
+stringOptions = printOptions (\_ b -> return b) return return NormalLayout
+
+data EPWriter a = EPWriter
+ { output :: !a }
+
+instance Monoid w => Semigroup (EPWriter w) where
+ (EPWriter a) <> (EPWriter b) = EPWriter (a <> b)
+
+instance Monoid w => Monoid (EPWriter w) where
+ mempty = EPWriter mempty
+
+data EPState = EPState
+ { epApiAnns :: !ApiAnns
+
+ , uAnchorSpan :: !RealSrcSpan -- ^ in pre-changed AST
+ -- reference frame, from
+ -- Annotation
+ , uExtraDP :: !(Maybe Anchor) -- ^ Used to anchor a
+ -- list
+
+ -- Print phase
+ , epPos :: !Pos -- ^ Current output position
+ , pMarkLayout :: !Bool
+ , pLHS :: !LayoutStartCol
+
+ -- Delta phase
+ , dPriorEndPosition :: !Pos -- ^ End of Position reached
+ -- when processing the
+ -- preceding element
+ , dMarkLayout :: !Bool
+ , dLHS :: !LayoutStartCol
+
+ -- Shared
+ , epComments :: ![Comment]
+ }
+
+-- ---------------------------------------------------------------------
+
+-- AZ:TODO: this can just be a function :: (ApiAnn' a) -> Entry
+class HasEntry ast where
+ fromAnn :: ast -> Entry
+
+-- ---------------------------------------------------------------------
+
+-- type Annotated = FreeT AnnotationF Identity
+type Annotated a = EP String Identity a
+
+-- ---------------------------------------------------------------------
+
+-- | Key entry point. Switches to an independent AST element with its
+-- own annotation, calculating new offsets, etc
+markAnnotated :: ExactPrint a => a -> Annotated ()
+markAnnotated a = enterAnn (getAnnotationEntry a) a
+
+data Entry = Entry Anchor ApiAnnComments
+ | NoEntryVal
+
+instance (HasEntry (ApiAnn' an)) => HasEntry (SrcSpanAnn' (ApiAnn' an)) where
+ fromAnn (SrcSpanAnn ApiAnnNotUsed ss) = Entry (spanAsAnchor ss) noCom
+ fromAnn (SrcSpanAnn an _) = fromAnn an
+
+instance HasEntry (ApiAnn' a) where
+ fromAnn (ApiAnn anchor _ cs) = Entry anchor cs
+ fromAnn ApiAnnNotUsed = NoEntryVal
+
+-- ---------------------------------------------------------------------
+
+astId :: (Typeable a) => a -> String
+astId a = show (typeOf a)
+
+-- | "Enter" an annotation, by using the associated 'anchor' field as
+-- the new reference point for calculating all DeltaPos positions.
+--
+-- This is combination of the ghc=exactprint Delta.withAST and
+-- Print.exactPC functions and effectively does the delta processing
+-- immediately followed by the print processing. JIT ghc-exactprint.
+enterAnn :: (ExactPrint a) => Entry -> a -> Annotated ()
+enterAnn NoEntryVal a = do
+ p <- getPosP
+ debugM $ "enterAnn:NO ANN:(p,a) =" ++ show (p, astId a) ++ " starting"
+ -- curAnchor <- getAnchorU
+ -- printComments curAnchor
+ exact a
+ debugM $ "enterAnn:NO ANN:p =" ++ show (p, astId a) ++ " done"
+enterAnn (Entry anchor' cs) a = do
+ p <- getPosP
+ debugM $ "enterAnn:(p,a) =" ++ show (p, astId a) ++ " starting"
+ let curAnchor = anchor anchor' -- As a base for the current AST element
+ debugM $ "enterAnn:(curAnchor):=" ++ show (rs2range curAnchor)
+ addCommentsA (priorComments cs)
+ printComments curAnchor
+ -- -------------------------
+ case anchor_op anchor' of
+ MovedAnchor dp -> do
+ debugM $ "enterAnn: MovedAnchor:" ++ show dp
+ -- Set the original anchor as prior end, so the rest of this AST
+ -- fragment has a reference
+ -- BUT: this means the entry DP can be calculated incorrectly too,
+ -- for immediately nested items.
+ setPriorEndNoLayoutD (ss2pos curAnchor)
+ _ -> do
+ return ()
+ -- -------------------------
+ setAnchorU curAnchor
+ -- -------------------------------------------------------------------
+ -- The first part corresponds to the delta phase, so should only use
+ -- delta phase variables
+ -- -----------------------------------
+ -- Calculate offset required to get to the start of the SrcSPan
+ off <- gets dLHS
+ let spanStart = ss2pos curAnchor
+ priorEndAfterComments <- getPriorEndD
+ let edp' = adjustDeltaForOffset 0
+ -- Use the propagated offset if one is set
+ -- Note that we need to use the new offset if it has
+ -- changed.
+ off (ss2delta priorEndAfterComments curAnchor)
+ debugM $ "enterAnn: (edp',off,priorEndAfterComments,curAnchor):" ++ show (edp',off,priorEndAfterComments,rs2range curAnchor)
+ let edp'' = case anchor_op anchor' of
+ MovedAnchor dp -> dp
+ _ -> edp'
+ -- ---------------------------------------------
+ -- let edp = edp''
+ med <- getExtraDP
+ setExtraDP Nothing
+ let edp = case med of
+ Nothing -> edp''
+ -- Just dp -> addDP dp edp''
+ Just (Anchor _ (MovedAnchor dp)) -> dp
+ -- Replace original with desired one. Allows all
+ -- list entry values to be DP (1,0)
+ Just (Anchor r _) -> dp
+ where
+ dp = adjustDeltaForOffset 0
+ off (ss2delta priorEndAfterComments r)
+ when (isJust med) $ debugM $ "enterAnn:(med,edp)=" ++ show (med,edp)
+ -- ---------------------------------------------
+ -- Preparation complete, perform the action
+ when (priorEndAfterComments < spanStart) (do
+ debugM $ "enterAnn.dPriorEndPosition:spanStart=" ++ show spanStart
+ modify (\s -> s { dPriorEndPosition = spanStart } ))
+
+ debugM $ "enterAnn: (anchor_op, curAnchor):" ++ show (anchor_op anchor', rs2range curAnchor)
+ debugM $ "enterAnn: (dLHS,spanStart,pec,edp)=" ++ show (off,spanStart,priorEndAfterComments,edp)
+
+ -- end of delta phase processing
+ -- -------------------------------------------------------------------
+ -- start of print phase processing
+
+ let
+ st = annNone { annEntryDelta = edp }
+ withOffset st (advance edp >> exact a)
+
+ when ((getFollowingComments cs) /= []) $ do
+ debugM $ "starting trailing comments:" ++ showAst (getFollowingComments cs)
+ mapM_ printOneComment (map tokComment $ getFollowingComments cs)
+ debugM $ "ending trailing comments"
+
+-- ---------------------------------------------------------------------
+
+addCommentsA :: [LAnnotationComment] -> EPP ()
+addCommentsA csNew = addComments (map tokComment csNew)
+ -- cs <- getUnallocatedComments
+ -- -- AZ:TODO: sortedlist?
+ -- putUnallocatedComments (sort $ (map tokComment csNew) ++ cs)
+
+addComments :: [Comment] -> EPP ()
+addComments csNew = do
+ debugM $ "addComments:" ++ show csNew
+ cs <- getUnallocatedComments
+ let cmp (Comment _ l1 _) (Comment _ l2 _) = compare (anchor l1) (anchor l2)
+ -- AZ:TODO: sortedlist?
+ putUnallocatedComments (sortBy cmp $ csNew ++ cs)
+
+-- ---------------------------------------------------------------------
+
+-- |In order to interleave annotations into the stream, we turn them into
+-- comments.
+annotationsToComments :: [AddApiAnn] -> [AnnKeywordId] -> EPP ()
+annotationsToComments ans kws = do
+ let
+ getSpans _ [] = []
+ getSpans k1 (AddApiAnn k2 ss:as)
+ | k1 == k2 = ss : getSpans k1 as
+ | otherwise = getSpans k1 as
+ doOne :: AnnKeywordId -> EPP [Comment]
+ doOne kw = do
+ let sps =getSpans kw ans
+ return $ map (mkKWComment kw ) sps
+ -- TODO:AZ make sure these are sorted/merged properly when the invariant for
+ -- allocateComments is re-established.
+ newComments <- mapM doOne kws
+ addComments (concat newComments)
+
+
+-- ---------------------------------------------------------------------
+
+-- Temporary function to simply reproduce the "normal" pretty printer output
+withPpr :: (Outputable a) => a -> Annotated ()
+withPpr a = do
+ ss <- getAnchorU
+ debugM $ "withPpr: ss=" ++ show ss
+ printStringAtKw' ss (showPprUnsafe a)
+
+-- ---------------------------------------------------------------------
+-- Modeled on Outputable
+
+-- | An AST fragment with an annotation must be able to return the
+-- requirements for nesting another one, captured in an 'Entry', and
+-- to be able to use the rest of the exactprint machinery to print the
+-- element. In the analogy to Outputable, 'exact' plays the role of
+-- 'ppr'.
+class (Typeable a) => ExactPrint a where
+ getAnnotationEntry :: a -> Entry
+ exact :: a -> Annotated ()
+
+-- ---------------------------------------------------------------------
+
+-- | Bare Located elements are simply stripped off without further
+-- processing.
+instance (ExactPrint a) => ExactPrint (Located a) where
+ getAnnotationEntry (L l _) = Entry (spanAsAnchor l) noCom
+ exact (L _ a) = markAnnotated a
+
+instance (ExactPrint a) => ExactPrint (LocatedA a) where
+ getAnnotationEntry = entryFromLocatedA
+ exact (L la a) = do
+ debugM $ "LocatedA a:la loc=" ++ show (ss2range $ locA la)
+ markAnnotated a
+ markALocatedA (ann la)
+
+instance (ExactPrint a) => ExactPrint [a] where
+ getAnnotationEntry = const NoEntryVal
+ exact ls = mapM_ markAnnotated ls
+
+instance (ExactPrint a) => ExactPrint (Maybe a) where
+ getAnnotationEntry = const NoEntryVal
+ exact Nothing = return ()
+ exact (Just a) = markAnnotated a
+
+-- ---------------------------------------------------------------------
+
+-- | 'Located (HsModule GhcPs)' corresponds to 'ParsedSource'
+instance ExactPrint HsModule where
+ getAnnotationEntry hsmod = fromAnn (hsmodAnn hsmod)
+
+ exact hsmod@(HsModule ApiAnnNotUsed _ _ _ _ _ _ _) = withPpr hsmod
+ exact (HsModule an _lo mmn mexports imports decls mdeprec mbDoc) = do
+
+ markAnnotated mbDoc
+
+ case mmn of
+ Nothing -> return ()
+ Just (L ln mn) -> do
+ markApiAnn' an am_main AnnModule
+ -- debugM $ "HsModule name: (ss,ln)=" ++ show (ss2pos ss,ss2pos (realSrcSpan ln))
+ -- printStringAtSs ln (moduleNameString mn)
+ markAnnotated (L ln mn)
+
+ -- forM_ mdeprec markLocated
+ setLayoutTopLevelP $ markAnnotated mdeprec
+
+ setLayoutTopLevelP $ markAnnotated mexports
+
+ debugM $ "HsModule.AnnWhere coming"
+ setLayoutTopLevelP $ markApiAnn' an am_main AnnWhere
+
+ setLayoutTopLevelP $ mapM_ markAddApiAnn (al_open $ am_decls $ anns an)
+
+ -- markOptional GHC.AnnOpenC -- Possible '{'
+ -- markManyOptional GHC.AnnSemi -- possible leading semis
+ -- setContextLevel (Set.singleton TopLevel) 2 $ markListWithLayout imports
+ -- markListWithLayout imports
+ markTopLevelList imports
+
+ -- setContextLevel (Set.singleton TopLevel) 2 $ markListWithLayout decls
+ -- markListWithLayout decls
+ -- setLayoutTopLevelP $ markAnnotated decls
+ markTopLevelList decls
+
+ setLayoutTopLevelP $ mapM_ markAddApiAnn (al_close $ am_decls $ anns an)
+ -- markOptional GHC.AnnCloseC -- Possible '}'
+
+ -- markEOF
+ -- eof <- getEofPos
+ -- debugM $ "eof pos:" ++ show (rs2range eof)
+ -- setLayoutTopLevelP $ printStringAtKw' eof ""
+
+-- ---------------------------------------------------------------------
+
+-- TODO:AZ: do we *need* the following, or can we capture it in the AST?
+-- | We can have a list with its own entry point defined. Create a
+-- data structure to capture this, for defining an ExactPrint instance
+data AnnotatedList a = AnnotatedList (Maybe Anchor) a
+ deriving (Eq,Show)
+
+instance (ExactPrint a) => ExactPrint (AnnotatedList a) where
+ getAnnotationEntry (AnnotatedList (Just anc) _) = Entry anc (AnnComments [])
+ getAnnotationEntry (AnnotatedList Nothing _) = NoEntryVal
+
+ exact (AnnotatedList an ls) = do
+ debugM $ "AnnotatedList:an=" ++ show an
+ markAnnotatedWithLayout ls
+
+
+-- ---------------------------------------------------------------------
+-- Start of utility functions
+-- ---------------------------------------------------------------------
+
+printSourceText :: SourceText -> String -> EPP ()
+printSourceText NoSourceText txt = printStringAdvance txt
+printSourceText (SourceText txt) _ = printStringAdvance txt
+
+-- ---------------------------------------------------------------------
+
+printStringAtRs :: RealSrcSpan -> String -> EPP ()
+printStringAtRs ss str = printStringAtKw' ss str
+
+printStringAtSs :: SrcSpan -> String -> EPP ()
+printStringAtSs ss str = printStringAtKw' (realSrcSpan ss) str
+
+-- ---------------------------------------------------------------------
+
+-- AZ:TODO get rid of this
+printStringAtMkw :: Maybe AnnAnchor -> String -> EPP ()
+printStringAtMkw (Just aa) s = printStringAtAA aa s
+printStringAtMkw Nothing s = printStringAtLsDelta (DP 0 1) s
+
+
+printStringAtAA :: AnnAnchor -> String -> EPP ()
+printStringAtAA (AR r) s = printStringAtKw' r s
+printStringAtAA (AD d) s = do
+ pe <- getPriorEndD
+ p1 <- getPosP
+ printStringAtLsDelta d s
+ p2 <- getPosP
+ debugM $ "printStringAtAA:(pe,p1,p2)=" ++ show (pe,p1,p2)
+ setPriorEndASTPD True (p1,p2)
+
+-- Based on Delta.addAnnotationWorker
+printStringAtKw' :: RealSrcSpan -> String -> EPP ()
+printStringAtKw' pa str = do
+ printComments pa
+ pe <- getPriorEndD
+ debugM $ "printStringAtKw':pe=" ++ show pe
+ let p = ss2delta pe pa
+ p' <- adjustDeltaForOffsetM p
+ printStringAtLsDelta p' str
+ setPriorEndASTD True pa
+
+-- ---------------------------------------------------------------------
+
+markExternalSourceText :: SrcSpan -> SourceText -> String -> EPP ()
+markExternalSourceText l NoSourceText txt = printStringAtKw' (realSrcSpan l) txt
+markExternalSourceText l (SourceText txt) _ = printStringAtKw' (realSrcSpan l) txt
+
+-- ---------------------------------------------------------------------
+
+markAddApiAnn :: AddApiAnn -> EPP ()
+markAddApiAnn a@(AddApiAnn kw _) = mark [a] kw
+
+markLocatedMAA :: ApiAnn' a -> (a -> Maybe AddApiAnn) -> EPP ()
+markLocatedMAA ApiAnnNotUsed _ = return ()
+markLocatedMAA (ApiAnn _ a _) f =
+ case f a of
+ Nothing -> return ()
+ Just aa -> markAddApiAnn aa
+
+markLocatedAA :: ApiAnn' a -> (a -> AddApiAnn) -> EPP ()
+markLocatedAA ApiAnnNotUsed _ = return ()
+markLocatedAA (ApiAnn _ a _) f = markKw (f a)
+
+markLocatedAAL :: ApiAnn' a -> (a -> [AddApiAnn]) -> AnnKeywordId -> EPP ()
+markLocatedAAL ApiAnnNotUsed _ _ = return ()
+markLocatedAAL (ApiAnn _ a _) f kw = go (f a)
+ where
+ go [] = return ()
+ go (aa@(AddApiAnn kw' _):as)
+ | kw' == kw = mark [aa] kw
+ | otherwise = go as
+
+markLocatedAALS :: ApiAnn' a -> (a -> [AddApiAnn]) -> AnnKeywordId -> Maybe String -> EPP ()
+markLocatedAALS an f kw Nothing = markLocatedAAL an f kw
+markLocatedAALS ApiAnnNotUsed _ _ _ = return ()
+markLocatedAALS (ApiAnn _ a _) f kw (Just str) = go (f a)
+ where
+ go [] = return ()
+ go (AddApiAnn kw' r:as)
+ | kw' == kw = printStringAtAA r str
+ | otherwise = go as
+
+-- ---------------------------------------------------------------------
+
+markArrow :: ApiAnn' TrailingAnn -> HsArrow GhcPs -> EPP ()
+markArrow ApiAnnNotUsed _ = pure ()
+markArrow an _mult = markKwT (anns an)
+
+-- ---------------------------------------------------------------------
+
+markAnnCloseP :: ApiAnn' AnnPragma -> EPP ()
+markAnnCloseP an = markLocatedAALS an (pure . apr_close) AnnClose (Just "#-}")
+
+markAnnOpenP :: ApiAnn' AnnPragma -> SourceText -> String -> EPP ()
+markAnnOpenP an NoSourceText txt = markLocatedAALS an (pure . apr_open) AnnOpen (Just txt)
+markAnnOpenP an (SourceText txt) _ = markLocatedAALS an (pure . apr_open) AnnOpen (Just txt)
+
+markAnnOpen :: ApiAnn -> SourceText -> String -> EPP ()
+markAnnOpen an NoSourceText txt = markLocatedAALS an id AnnOpen (Just txt)
+markAnnOpen an (SourceText txt) _ = markLocatedAALS an id AnnOpen (Just txt)
+
+markAnnOpen' :: Maybe AnnAnchor -> SourceText -> String -> EPP ()
+markAnnOpen' ms NoSourceText txt = printStringAtMkw ms txt
+markAnnOpen' ms (SourceText txt) _ = printStringAtMkw ms txt
+
+-- ---------------------------------------------------------------------
+
+markOpeningParen, markClosingParen :: ApiAnn' AnnParen -> EPP ()
+markOpeningParen an = markParen an fst
+markClosingParen an = markParen an snd
+
+markParen :: ApiAnn' AnnParen -> (forall a. (a,a) -> a) -> EPP ()
+markParen ApiAnnNotUsed _ = return ()
+markParen (ApiAnn _ (AnnParen pt o c) _) f = markKwA (f $ kw pt) (f (o, c))
+ where
+ kw AnnParens = (AnnOpenP, AnnCloseP)
+ kw AnnParensHash = (AnnOpenPH, AnnClosePH)
+ kw AnnParensSquare = (AnnOpenS, AnnCloseS)
+
+
+markAnnKw :: ApiAnn' a -> (a -> AnnAnchor) -> AnnKeywordId -> EPP ()
+markAnnKw ApiAnnNotUsed _ _ = return ()
+markAnnKw (ApiAnn _ a _) f kw = markKwA kw (f a)
+
+markAnnKwAll :: ApiAnn' a -> (a -> [AnnAnchor]) -> AnnKeywordId -> EPP ()
+markAnnKwAll ApiAnnNotUsed _ _ = return ()
+markAnnKwAll (ApiAnn _ a _) f kw = mapM_ (markKwA kw) (sort (f a))
+
+markAnnKwM :: ApiAnn' a -> (a -> Maybe AnnAnchor) -> AnnKeywordId -> EPP ()
+markAnnKwM ApiAnnNotUsed _ _ = return ()
+markAnnKwM (ApiAnn _ a _) f kw = go (f a)
+ where
+ go Nothing = return ()
+ go (Just s) = markKwA kw s
+
+markALocatedA :: ApiAnn' AnnListItem -> EPP ()
+markALocatedA ApiAnnNotUsed = return ()
+markALocatedA (ApiAnn _ a _) = markTrailing (lann_trailing a)
+
+markApiAnn :: ApiAnn -> AnnKeywordId -> EPP ()
+markApiAnn ApiAnnNotUsed _ = return ()
+markApiAnn (ApiAnn _ a _) kw = mark a kw
+
+markApiAnn' :: ApiAnn' ann -> (ann -> [AddApiAnn]) -> AnnKeywordId -> EPP ()
+markApiAnn' ApiAnnNotUsed _ _ = return ()
+markApiAnn' (ApiAnn _ a _) f kw = mark (f a) kw
+
+markApiAnnAll :: ApiAnn' ann -> (ann -> [AddApiAnn]) -> AnnKeywordId -> EPP ()
+markApiAnnAll ApiAnnNotUsed _ _ = return ()
+markApiAnnAll (ApiAnn _ a _) f kw = mapM_ markKw (sort anns)
+ where
+ anns = filter (\(AddApiAnn ka _) -> ka == kw) (f a)
+
+mark :: [AddApiAnn] -> AnnKeywordId -> EPP ()
+mark anns kw = do
+ case find (\(AddApiAnn k _) -> k == kw) anns of
+ Just aa -> markKw aa
+ Nothing -> case find (\(AddApiAnn k _) -> k == (unicodeAnn kw)) anns of
+ Just aau -> markKw aau
+ Nothing -> return ()
+
+markKwT :: TrailingAnn -> EPP ()
+markKwT (AddSemiAnn ss) = markKwA AnnSemi ss
+markKwT (AddCommaAnn ss) = markKwA AnnComma ss
+markKwT (AddVbarAnn ss) = markKwA AnnVbar ss
+markKwT (AddRarrowAnn ss) = markKwA AnnRarrow ss
+markKwT (AddRarrowAnnU ss) = markKwA AnnRarrowU ss
+-- markKwT (AddLollyAnn ss) = markKwA AnnLolly ss
+-- markKwT (AddLollyAnnU ss) = markKwA AnnLollyU ss
+
+markKw :: AddApiAnn -> EPP ()
+markKw (AddApiAnn kw ss) = markKwA kw ss
+
+-- | This should be the main driver of the process, managing comments
+markKwA :: AnnKeywordId -> AnnAnchor -> EPP ()
+markKwA kw aa = printStringAtAA aa (keywordToString (G kw))
+
+-- ---------------------------------------------------------------------
+
+markAnnList :: ApiAnn' AnnList -> EPP () -> EPP ()
+markAnnList ApiAnnNotUsed action = action
+markAnnList an@(ApiAnn _ ann _) action = do
+ p <- getPosP
+ debugM $ "markAnnList : " ++ showPprUnsafe (p, an)
+ markLocatedMAA an al_open
+ action
+ markLocatedMAA an al_close
+ debugM $ "markAnnList: calling markTrailing with:" ++ showPprUnsafe (al_trailing ann)
+ markTrailing (al_trailing ann)
+
+-- ---------------------------------------------------------------------
+
+-- printTrailingComments :: EPP ()
+-- printTrailingComments = do
+-- cs <- getUnallocatedComments
+-- mapM_ printOneComment cs
+
+-- ---------------------------------------------------------------------
+
+printComments :: RealSrcSpan -> EPP ()
+printComments ss = do
+ cs <- commentAllocation ss
+ debugM $ "printComments: (ss,comment locations): " ++ showPprUnsafe (rs2range ss,map commentAnchor cs)
+ mapM_ printOneComment cs
+
+-- ---------------------------------------------------------------------
+
+printOneComment :: Comment -> EPP ()
+printOneComment c@(Comment _str loc _mo) = do
+ debugM $ "printOneComment:c=" ++ showGhc c
+ dp <-case anchor_op loc of
+ MovedAnchor dp -> return dp
+ _ -> do
+ pe <- getPriorEndD
+ let dp = ss2delta pe (anchor loc)
+ debugM $ "printOneComment:(dp,pe,anchor loc)=" ++ showGhc (dp,pe,ss2pos $ anchor loc)
+ return dp
+ dp'' <- adjustDeltaForOffsetM dp
+ mep <- getExtraDP
+ dp' <- case mep of
+ Nothing -> return dp''
+ Just (Anchor _ (MovedAnchor edp)) -> do
+ -- setExtraDP Nothing
+ debugM $ "printOneComment:edp=" ++ show edp
+ return edp
+ Just (Anchor r _) -> do
+ pe <- getPriorEndD
+ let dp' = ss2delta pe r
+ debugM $ "printOneComment:extraDP(dp,pe,anchor loc)=" ++ showGhc (dp',pe,ss2pos r)
+ return dp
+ LayoutStartCol dOff <- gets dLHS
+ debugM $ "printOneComment:(dp,dp',dOff)=" ++ showGhc (dp,dp',dOff)
+ setPriorEndD (ss2posEnd (anchor loc))
+ printQueuedComment (anchor loc) c dp'
+
+-- ---------------------------------------------------------------------
+
+commentAllocation :: RealSrcSpan -> EPP [Comment]
+commentAllocation ss = do
+ cs <- getUnallocatedComments
+ let (earlier,later) = partition (\(Comment _str loc _mo) -> anchor loc <= ss) cs
+ putUnallocatedComments later
+ -- debugM $ "commentAllocation:(ss,earlier,later)" ++ show (rs2range ss,earlier,later)
+ return earlier
+
+-- ---------------------------------------------------------------------
+
+
+markAnnotatedWithLayout :: ExactPrint ast => ast -> EPP ()
+markAnnotatedWithLayout a = setLayoutBoth $ markAnnotated a
+
+-- ---------------------------------------------------------------------
+
+markTopLevelList :: ExactPrint ast => [ast] -> EPP ()
+markTopLevelList ls = mapM_ (\a -> setLayoutTopLevelP $ markAnnotated a) ls
+
+-- ---------------------------------------------------------------------
+
+instance ExactPrint ModuleName where
+ getAnnotationEntry _ = NoEntryVal
+ exact n = do
+ debugM $ "ModuleName: " ++ showPprUnsafe n
+ withPpr n
+
+-- ---------------------------------------------------------------------
+
+instance ExactPrint (LocatedP WarningTxt) where
+ getAnnotationEntry = entryFromLocatedA
+ exact (L (SrcSpanAnn an _) (WarningTxt (L _ src) ws)) = do
+ markAnnOpenP an src "{-# WARNING"
+ markLocatedAAL an apr_rest AnnOpenS
+ markAnnotated ws
+ markLocatedAAL an apr_rest AnnCloseS
+ markAnnCloseP an
+
+ exact (L (SrcSpanAnn an _) (DeprecatedTxt (L _ src) ws)) = do
+ markAnnOpenP an src "{-# DEPRECATED"
+ markLocatedAAL an apr_rest AnnOpenS
+ markAnnotated ws
+ markLocatedAAL an apr_rest AnnCloseS
+ markAnnCloseP an
+
+-- ---------------------------------------------------------------------
+
+instance ExactPrint (ImportDecl GhcPs) where
+ getAnnotationEntry idecl = fromAnn (ideclExt idecl)
+ exact x@(ImportDecl ApiAnnNotUsed _ _ _ _ _ _ _ _ _) = withPpr x
+ exact (ImportDecl ann@(ApiAnn _ an _) msrc (L lm modname) mpkg _src safeflag qualFlag _impl mAs hiding) = do
+
+ markAnnKw ann importDeclAnnImport AnnImport
+
+ -- "{-# SOURCE" and "#-}"
+ case msrc of
+ SourceText _txt -> do
+ debugM $ "ImportDecl sourcetext"
+ let mo = fmap fst $ importDeclAnnPragma an
+ let mc = fmap snd $ importDeclAnnPragma an
+ markAnnOpen' mo msrc "{-# SOURCE"
+ printStringAtMkw mc "#-}"
+ NoSourceText -> return ()
+ when safeflag (markAnnKwM ann importDeclAnnSafe AnnSafe)
+ case qualFlag of
+ QualifiedPre -- 'qualified' appears in prepositive position.
+ -> printStringAtMkw (importDeclAnnQualified an) "qualified"
+ _ -> return ()
+ case mpkg of
+ Just (StringLiteral src v _) ->
+ printStringAtMkw (importDeclAnnPackage an) (sourceTextToString src (show v))
+ _ -> return ()
+
+ printStringAtKw' (realSrcSpan lm) (moduleNameString modname)
+
+ case qualFlag of
+ QualifiedPost -- 'qualified' appears in postpositive position.
+ -> printStringAtMkw (importDeclAnnQualified an) "qualified"
+ _ -> return ()
+
+ case mAs of
+ Nothing -> return ()
+ Just (L l mn) -> do
+ printStringAtMkw (importDeclAnnAs an) "as"
+ printStringAtKw' (realSrcSpan l) (moduleNameString mn)
+
+ case hiding of
+ Nothing -> return ()
+ Just (_isHiding,lie) -> exact lie
+ -- markTrailingSemi
+
+
+-- ---------------------------------------------------------------------
+
+instance ExactPrint HsDocString where
+ getAnnotationEntry _ = NoEntryVal
+ exact = withPpr -- TODO:AZ use annotations
+
+-- ---------------------------------------------------------------------
+
+instance ExactPrint (HsDecl GhcPs) where
+ getAnnotationEntry (TyClD _ _) = NoEntryVal
+ getAnnotationEntry (InstD _ _) = NoEntryVal
+ getAnnotationEntry (DerivD _ _) = NoEntryVal
+ getAnnotationEntry (ValD _ _) = NoEntryVal
+ getAnnotationEntry (SigD _ _) = NoEntryVal
+ getAnnotationEntry (KindSigD _ _) = NoEntryVal
+ getAnnotationEntry (DefD _ _) = NoEntryVal
+ getAnnotationEntry (ForD _ _) = NoEntryVal
+ getAnnotationEntry (WarningD _ _) = NoEntryVal
+ getAnnotationEntry (AnnD _ _) = NoEntryVal
+ getAnnotationEntry (RuleD _ _) = NoEntryVal
+ getAnnotationEntry (SpliceD _ _) = NoEntryVal
+ getAnnotationEntry (DocD _ _) = NoEntryVal
+ getAnnotationEntry (RoleAnnotD _ _) = NoEntryVal
+
+ exact (TyClD _ d) = markAnnotated d
+ exact (InstD _ d) = markAnnotated d
+ exact (DerivD _ d) = markAnnotated d
+ exact (ValD _ d) = markAnnotated d
+ exact (SigD _ d) = markAnnotated d
+ exact (KindSigD _ d) = markAnnotated d
+ exact (DefD _ d) = markAnnotated d
+ exact (ForD _ d) = markAnnotated d
+ exact (WarningD _ d) = markAnnotated d
+ exact (AnnD _ d) = markAnnotated d
+ exact (RuleD _ d) = markAnnotated d
+ exact (SpliceD _ d) = markAnnotated d
+ exact (DocD _ d) = markAnnotated d
+ exact (RoleAnnotD _ d) = markAnnotated d
+
+-- ---------------------------------------------------------------------
+
+instance ExactPrint (InstDecl GhcPs) where
+ getAnnotationEntry (ClsInstD _ _) = NoEntryVal
+ getAnnotationEntry (DataFamInstD an _) = fromAnn an
+ getAnnotationEntry (TyFamInstD _ _) = NoEntryVal
+
+-- instance Annotate (GHC.InstDecl GHC.GhcPs) where
+
+-- markAST l (GHC.ClsInstD _ cid) = markAST l cid
+-- markAST l (GHC.DataFamInstD _ dfid) = markAST l dfid
+-- markAST l (GHC.TyFamInstD _ tfid) = markAST l tfid
+-- markAST _ (GHC.XInstDecl x) = error $ "got XInstDecl for:" ++ showPprUnsafe x
+
+ exact (ClsInstD _ cid) = markAnnotated cid
+ exact (DataFamInstD an decl) = do
+ exactDataFamInstDecl an TopLevel decl
+ exact (TyFamInstD _ eqn) = do
+ -- exactTyFamInstDecl an TopLevel eqn
+ markAnnotated eqn
+
+-- ---------------------------------------------------------------------
+
+exactDataFamInstDecl :: ApiAnn -> TopLevelFlag -> (DataFamInstDecl GhcPs) -> EPP ()
+exactDataFamInstDecl an top_lvl
+ (DataFamInstDecl ( FamEqn { feqn_tycon = tycon
+ , feqn_bndrs = bndrs
+ , feqn_pats = pats
+ , feqn_fixity = fixity
+ , feqn_rhs = defn }))
+ = exactDataDefn an pp_hdr defn
+ where
+ pp_hdr mctxt = do
+ case top_lvl of
+ TopLevel -> markApiAnn an AnnInstance -- TODO: maybe in toplevel
+ NotTopLevel -> return ()
+ exactHsFamInstLHS an tycon bndrs pats fixity mctxt
+
+-- ---------------------------------------------------------------------
+
+exactTyFamInstDecl :: TopLevelFlag -> (TyFamInstDecl GhcPs) -> EPP ()
+exactTyFamInstDecl top_lvl (TyFamInstDecl { tfid_xtn = an, tfid_eqn = eqn }) = do
+ markApiAnn an AnnType
+ case top_lvl of
+ TopLevel -> markApiAnn an AnnInstance
+ NotTopLevel -> return ()
+ markAnnotated eqn
+
+-- ---------------------------------------------------------------------
+
+instance ExactPrint (DerivDecl GhcPs) where
+ getAnnotationEntry (DerivDecl {deriv_ext = an} ) = fromAnn an
+ exact (DerivDecl an typ ms mov) = do
+ markApiAnn an AnnDeriving
+ mapM_ markAnnotated ms
+ markApiAnn an AnnInstance
+ mapM_ markAnnotated mov
+ markAnnotated typ
+ -- markAST _ (GHC.DerivDecl _ (GHC.HsWC _ (GHC.HsIB _ typ)) ms mov) = do
+ -- mark GHC.AnnDeriving
+ -- markMaybe ms
+ -- mark GHC.AnnInstance
+ -- markMaybe mov
+ -- markLocated typ
+ -- markTrailingSemi
+
+-- ---------------------------------------------------------------------
+
+instance ExactPrint (ForeignDecl GhcPs) where
+ getAnnotationEntry (ForeignImport an _ _ _) = fromAnn an
+ getAnnotationEntry (ForeignExport an _ _ _) = fromAnn an
+
+ exact (ForeignImport an n ty fimport) = do
+ markApiAnn an AnnForeign
+ markApiAnn an AnnImport
+
+ markAnnotated fimport
+
+ markAnnotated n
+ markApiAnn an AnnDcolon
+ markAnnotated ty
+ exact x = error $ "ForDecl: exact for " ++ showAst x
+{-
+ markAST _ (GHC.ForeignImport _ ln (GHC.HsIB _ typ)
+ (GHC.CImport cconv safety@(GHC.L ll _) _mh _imp (GHC.L ls src))) = do
+ mark GHC.AnnForeign
+ mark GHC.AnnImport
+
+ markLocated cconv
+ unless (ll == GHC.noSrcSpan) $ markLocated safety
+ markExternalSourceText ls src ""
+
+ markLocated ln
+ mark GHC.AnnDcolon
+ markLocated typ
+ markTrailingSemi
+
+-}
+
+
+-- ---------------------------------------------------------------------
+
+instance ExactPrint ForeignImport where
+ getAnnotationEntry = const NoEntryVal
+ exact (CImport cconv safety@(L ll _) _mh _imp (L ls src)) = do
+ markAnnotated cconv
+ unless (ll == noSrcSpan) $ markAnnotated safety
+ unless (ls == noSrcSpan) $ markExternalSourceText ls src ""
+
+-- ---------------------------------------------------------------------
+
+instance ExactPrint Safety where
+ getAnnotationEntry = const NoEntryVal
+ exact = withPpr
+
+-- ---------------------------------------------------------------------
+
+instance ExactPrint CCallConv where
+ getAnnotationEntry = const NoEntryVal
+ exact = withPpr
+
+-- ---------------------------------------------------------------------
+
+instance ExactPrint (WarnDecls GhcPs) where
+ getAnnotationEntry (Warnings an _ _) = fromAnn an
+ exact (Warnings an src warns) = do
+ markAnnOpen an src "{-# WARNING" -- Note: might be {-# DEPRECATED
+ markAnnotated warns
+ markLocatedAALS an id AnnClose (Just "#-}")
+
+-- ---------------------------------------------------------------------
+
+instance ExactPrint (WarnDecl GhcPs) where
+ getAnnotationEntry (Warning an _ _) = fromAnn an
+
+ exact (Warning an lns txt) = do
+ markAnnotated lns
+ markApiAnn an AnnOpenS -- "["
+ case txt of
+ WarningTxt _src ls -> markAnnotated ls
+ DeprecatedTxt _src ls -> markAnnotated ls
+ markApiAnn an AnnCloseS -- "]"
+
+-- ---------------------------------------------------------------------
+
+instance ExactPrint StringLiteral where
+ getAnnotationEntry = const NoEntryVal
+
+ exact (StringLiteral src fs mcomma) = do
+ printSourceText src (show (unpackFS fs))
+ mapM_ (\r -> printStringAtKw' r ",") mcomma
+
+-- ---------------------------------------------------------------------
+
+instance ExactPrint FastString where
+ getAnnotationEntry = const NoEntryVal
+
+ -- TODO: https://ghc.haskell.org/trac/ghc/ticket/10313 applies.
+ -- exact fs = printStringAdvance (show (unpackFS fs))
+ exact fs = printStringAdvance (unpackFS fs)
+
+
+-- ---------------------------------------------------------------------
+
+instance ExactPrint (RuleDecls GhcPs) where
+ getAnnotationEntry (HsRules an _ _) = fromAnn an
+ exact (HsRules an src rules) = do
+ case src of
+ NoSourceText -> markLocatedAALS an id AnnOpen (Just "{-# RULES")
+ SourceText srcTxt -> markLocatedAALS an id AnnOpen (Just srcTxt)
+ markAnnotated rules
+ markLocatedAALS an id AnnClose (Just "#-}")
+ -- markTrailingSemi
+
+-- ---------------------------------------------------------------------
+
+instance ExactPrint (RuleDecl GhcPs) where
+ getAnnotationEntry (HsRule {rd_ext = an}) = fromAnn an
+ exact (HsRule an ln act mtybndrs termbndrs lhs rhs) = do
+ debugM "HsRule entered"
+ markAnnotated ln
+ debugM "HsRule after ln"
+ markActivation an ra_rest act
+ debugM "HsRule after act"
+ case mtybndrs of
+ Nothing -> return ()
+ Just bndrs -> do
+ markLocatedMAA an (\a -> fmap fst (ra_tyanns a)) -- AnnForall
+ mapM_ markAnnotated bndrs
+ markLocatedMAA an (\a -> fmap snd (ra_tyanns a)) -- AnnDot
+
+ markLocatedMAA an (\a -> fmap fst (ra_tmanns a)) -- AnnForall
+ mapM_ markAnnotated termbndrs
+ markLocatedMAA an (\a -> fmap snd (ra_tmanns a)) -- AnnDot
+
+ markAnnotated lhs
+ markApiAnn' an ra_rest AnnEqual
+ markAnnotated rhs
+ -- markAST l (GHC.HsRule _ ln act mtybndrs termbndrs lhs rhs) = do
+ -- markLocated ln
+ -- setContext (Set.singleton ExplicitNeverActive) $ markActivation l act
+
+
+ -- mark GHC.AnnForall
+ -- mapM_ markLocated termbndrs
+ -- mark GHC.AnnDot
+
+ -- markLocated lhs
+ -- mark GHC.AnnEqual
+ -- markLocated rhs
+ -- inContext (Set.singleton Intercalate) $ mark GHC.AnnSemi
+ -- markTrailingSemi
+
+markActivation :: ApiAnn' a -> (a -> [AddApiAnn]) -> Activation -> Annotated ()
+markActivation an fn act = do
+ case act of
+ ActiveBefore src phase -> do
+ markApiAnn' an fn AnnOpenS -- '['
+ markApiAnn' an fn AnnTilde -- ~
+ markLocatedAALS an fn AnnVal (Just (toSourceTextWithSuffix src (show phase) ""))
+ markApiAnn' an fn AnnCloseS -- ']'
+ ActiveAfter src phase -> do
+ markApiAnn' an fn AnnOpenS -- '['
+ markLocatedAALS an fn AnnVal (Just (toSourceTextWithSuffix src (show phase) ""))
+ markApiAnn' an fn AnnCloseS -- ']'
+ NeverActive -> do
+ markApiAnn' an fn AnnOpenS -- '['
+ markApiAnn' an fn AnnTilde -- ~
+ markApiAnn' an fn AnnCloseS -- ']'
+ _ -> return ()
+
+-- ---------------------------------------------------------------------
+
+instance ExactPrint (SpliceDecl GhcPs) where
+ getAnnotationEntry = const NoEntryVal
+
+ exact (SpliceDecl _ splice _flag) = do
+ markAnnotated splice
+
+-- ---------------------------------------------------------------------
+
+instance ExactPrint DocDecl where
+ getAnnotationEntry = const NoEntryVal
+
+ exact v =
+ let str =
+ case v of
+ (DocCommentNext ds) -> unpackHDS ds
+ (DocCommentPrev ds) -> unpackHDS ds
+ (DocCommentNamed _s ds) -> unpackHDS ds
+ (DocGroup _i ds) -> unpackHDS ds
+ in
+ printStringAdvance str
+
+-- ---------------------------------------------------------------------
+
+instance ExactPrint (RoleAnnotDecl GhcPs) where
+ getAnnotationEntry (RoleAnnotDecl an _ _) = fromAnn an
+ exact (RoleAnnotDecl an ltycon roles) = do
+ markApiAnn an AnnType
+ markApiAnn an AnnRole
+ markAnnotated ltycon
+ markAnnotated roles
+
+-- ---------------------------------------------------------------------
+
+instance ExactPrint Role where
+ getAnnotationEntry = const NoEntryVal
+ exact = withPpr
+
+-- ---------------------------------------------------------------------
+
+instance ExactPrint (RuleBndr GhcPs) where
+ getAnnotationEntry = const NoEntryVal
+
+{-
+ = RuleBndr (XCRuleBndr pass) (Located (IdP pass))
+ | RuleBndrSig (XRuleBndrSig pass) (Located (IdP pass)) (HsPatSigType pass)
+-}
+ exact (RuleBndr _ ln) = markAnnotated ln
+ exact (RuleBndrSig an ln (HsPS _ ty)) = do
+ markApiAnn an AnnOpenP -- "("
+ markAnnotated ln
+ markApiAnn an AnnDcolon
+ markAnnotated ty
+ markApiAnn an AnnCloseP -- ")"
+
+-- ---------------------------------------------------------------------
+
+-- instance ExactPrint (TyFamInstEqn GhcPs) where
+-- instance (ExactPrint body) => ExactPrint (FamInstEqn GhcPs body) where
+-- getAnnotationEntry = const NoEntryVal
+-- exact (HsIB { hsib_body = FamEqn { feqn_ext = an
+-- , feqn_tycon = tycon
+-- , feqn_bndrs = bndrs
+-- , feqn_pats = pats
+-- , feqn_fixity = fixity
+-- , feqn_rhs = rhs }}) = do
+-- exactHsFamInstLHS an tycon bndrs pats fixity Nothing
+-- markApiAnn an AnnEqual
+-- markAnnotated rhs
+
+instance (ExactPrint body) => ExactPrint (FamEqn GhcPs body) where
+ getAnnotationEntry (FamEqn { feqn_ext = an}) = fromAnn an
+ exact (FamEqn { feqn_ext = an
+ , feqn_tycon = tycon
+ , feqn_bndrs = bndrs
+ , feqn_pats = pats
+ , feqn_fixity = fixity
+ , feqn_rhs = rhs }) = do
+ exactHsFamInstLHS an tycon bndrs pats fixity Nothing
+ markApiAnn an AnnEqual
+ markAnnotated rhs
+
+-- ---------------------------------------------------------------------
+
+exactHsFamInstLHS ::
+ ApiAnn
+ -> LocatedN RdrName
+ -- -> Maybe [LHsTyVarBndr () GhcPs]
+ -> HsOuterTyVarBndrs () GhcPs
+ -> HsTyPats GhcPs
+ -> LexicalFixity
+ -> Maybe (LHsContext GhcPs)
+ -> EPP ()
+exactHsFamInstLHS an thing bndrs typats fixity mb_ctxt = do
+ markApiAnn an AnnForall
+ markAnnotated bndrs
+ markApiAnn an AnnDot
+ mapM_ markAnnotated mb_ctxt
+ exact_pats typats
+ where
+ exact_pats :: HsTyPats GhcPs -> EPP ()
+ exact_pats (patl:patr:pats)
+ | Infix <- fixity
+ = let exact_op_app = do
+ markAnnotated patl
+ markAnnotated thing
+ markAnnotated patr
+ in case pats of
+ [] -> exact_op_app
+ _ -> do
+ markApiAnn an AnnOpenP
+ exact_op_app
+ markApiAnn an AnnCloseP
+ mapM_ markAnnotated pats
+
+ exact_pats pats = do
+ markAnnotated thing
+ markAnnotated pats
+
+-- ---------------------------------------------------------------------
+
+-- instance ExactPrint (LHsTypeArg GhcPs) where
+instance (ExactPrint tm, ExactPrint ty, Outputable tm, Outputable ty)
+ => ExactPrint (HsArg tm ty) where
+ getAnnotationEntry = const NoEntryVal
+
+ exact (HsValArg tm) = markAnnotated tm
+ exact (HsTypeArg ss ty) = printStringAtSs ss "@" >> markAnnotated ty
+ exact x@(HsArgPar _sp) = withPpr x -- Does not appear in original source
+
+-- ---------------------------------------------------------------------
+
+-- instance ExactPrint [LHsTyVarBndr () GhcPs] where
+-- getAnnotationEntry = const NoEntryVal
+-- exact bs = mapM_ markAnnotated bs
+
+-- ---------------------------------------------------------------------
+
+instance ExactPrint (ClsInstDecl GhcPs) where
+ getAnnotationEntry cid = fromAnn (fst $ cid_ext cid)
+
+ exact (ClsInstDecl { cid_ext = (an, sortKey)
+ , cid_poly_ty = inst_ty, cid_binds = binds
+ , cid_sigs = sigs, cid_tyfam_insts = ats
+ , cid_overlap_mode = mbOverlap
+ , cid_datafam_insts = adts })
+ | null sigs, null ats, null adts, isEmptyBag binds -- No "where" part
+ = top_matter
+
+ | otherwise -- Laid out
+ = do
+ top_matter
+ markApiAnn an AnnWhere
+ markApiAnn an AnnOpenC
+ -- = vcat [ top_matter <+> text "where"
+ -- , nest 2 $ pprDeclList $
+ -- map (pprTyFamInstDecl NotTopLevel . unLoc) ats ++
+ -- map (pprDataFamInstDecl NotTopLevel . unLoc) adts ++
+ -- pprLHsBindsForUser binds sigs ]
+ withSortKey sortKey
+ (prepareListAnnotationA ats
+ ++ prepareListAnnotationF (exactDataFamInstDecl an NotTopLevel ) adts
+ ++ prepareListAnnotationA (bagToList binds)
+ ++ prepareListAnnotationA sigs
+ )
+ markApiAnn an AnnCloseC -- '}'
+
+ where
+ top_matter = do
+ markApiAnn an AnnInstance
+ mapM_ markAnnotated mbOverlap
+ markAnnotated inst_ty
+ markApiAnn an AnnWhere -- Optional
+ -- text "instance" <+> ppOverlapPragma mbOverlap
+ -- <+> ppr inst_ty
+
+-- ---------------------------------------------------------------------
+
+instance ExactPrint (TyFamInstDecl GhcPs) where
+ getAnnotationEntry (TyFamInstDecl an _) = fromAnn an
+ exact d@(TyFamInstDecl _an _eqn) =
+ exactTyFamInstDecl TopLevel d
+
+-- ---------------------------------------------------------------------
+
+-- instance (ExactPrint body) => ExactPrint (HsImplicitBndrs GhcPs body) where
+-- getAnnotationEntry (HsIB an _) = fromAnn an
+-- exact (HsIB an t) = markAnnotated t
+
+-- ---------------------------------------------------------------------
+
+instance ExactPrint (LocatedP OverlapMode) where
+ getAnnotationEntry = entryFromLocatedA
+
+ -- NOTE: NoOverlap is only used in the typechecker
+ exact (L (SrcSpanAnn an _) (NoOverlap src)) = do
+ markAnnOpenP an src "{-# NO_OVERLAP"
+ markAnnCloseP an
+
+ exact (L (SrcSpanAnn an _) (Overlappable src)) = do
+ markAnnOpenP an src "{-# OVERLAPPABLE"
+ markAnnCloseP an
+
+ exact (L (SrcSpanAnn an _) (Overlapping src)) = do
+ markAnnOpenP an src "{-# OVERLAPPING"
+ markAnnCloseP an
+
+ exact (L (SrcSpanAnn an _) (Overlaps src)) = do
+ markAnnOpenP an src "{-# OVERLAPS"
+ markAnnCloseP an
+
+ exact (L (SrcSpanAnn an _) (Incoherent src)) = do
+ markAnnOpenP an src "{-# INCOHERENT"
+ markAnnCloseP an
+
+-- ---------------------------------------------------------------------
+
+instance ExactPrint (HsBind GhcPs) where
+ getAnnotationEntry FunBind{} = NoEntryVal
+ getAnnotationEntry PatBind{} = NoEntryVal
+ getAnnotationEntry VarBind{} = NoEntryVal
+ getAnnotationEntry AbsBinds{} = NoEntryVal
+ getAnnotationEntry PatSynBind{} = NoEntryVal
+
+ exact (FunBind _ _ matches _) = do
+ markAnnotated matches
+ exact (PatBind _ pat grhss _) = do
+ markAnnotated pat
+ markAnnotated grhss
+ exact (PatSynBind _ bind) = markAnnotated bind
+
+ exact x = error $ "HsBind: exact for " ++ showAst x
+
+-- ---------------------------------------------------------------------
+
+instance ExactPrint (PatSynBind GhcPs GhcPs) where
+ getAnnotationEntry (PSB { psb_ext = an}) = fromAnn an
+
+ exact (PSB{ psb_ext = an
+ , psb_id = psyn, psb_args = details
+ , psb_def = pat
+ , psb_dir = dir }) = do
+ markApiAnn an AnnPattern
+ case details of
+ InfixCon v1 v2 -> do
+ markAnnotated v1
+ markAnnotated psyn
+ markAnnotated v2
+ PrefixCon tvs vs -> do
+ markAnnotated psyn
+ markAnnotated tvs
+ markAnnotated vs
+ RecCon vs -> do
+ markAnnotated psyn
+ markApiAnn an AnnOpenC -- '{'
+ markAnnotated vs
+ markApiAnn an AnnCloseC -- '}'
+
+ case dir of
+ Unidirectional -> do
+ markApiAnn an AnnLarrow
+ markAnnotated pat
+ ImplicitBidirectional -> do
+ markApiAnn an AnnEqual
+ markAnnotated pat
+ ExplicitBidirectional mg -> do
+ markApiAnn an AnnLarrow
+ markAnnotated pat
+ markApiAnn an AnnWhere
+ markAnnotated mg
+
+ -- case dir of
+ -- GHC.ImplicitBidirectional -> mark GHC.AnnEqual
+ -- _ -> mark GHC.AnnLarrow
+
+ -- markLocated def
+ -- case dir of
+ -- GHC.Unidirectional -> return ()
+ -- GHC.ImplicitBidirectional -> return ()
+ -- GHC.ExplicitBidirectional mg -> do
+ -- mark GHC.AnnWhere
+ -- mark GHC.AnnOpenC -- '{'
+ -- markMatchGroup l mg
+ -- mark GHC.AnnCloseC -- '}'
+
+ -- markTrailingSemi
+
+
+-- ---------------------------------------------------------------------
+
+instance ExactPrint (RecordPatSynField GhcPs) where
+ getAnnotationEntry = const NoEntryVal
+ exact (RecordPatSynField { recordPatSynField = v }) = markAnnotated v
+
+-- ---------------------------------------------------------------------
+
+instance ExactPrint (Match GhcPs (LocatedA (HsCmd GhcPs))) where
+ getAnnotationEntry (Match ann _ _ _) = fromAnn ann
+
+ exact match@(Match ApiAnnNotUsed _ _ _) = withPpr match
+ exact (Match an mctxt pats grhss) = do
+ exactMatch (Match an mctxt pats grhss)
+
+-- -------------------------------------
+
+instance ExactPrint (Match GhcPs (LocatedA (HsExpr GhcPs))) where
+ getAnnotationEntry (Match ann _ _ _) = fromAnn ann
+
+ exact match@(Match ApiAnnNotUsed _ _ _) = withPpr match
+ exact (Match an mctxt pats grhss) = do
+ exactMatch (Match an mctxt pats grhss)
+ -- -- Based on Expr.pprMatch
+
+ -- debugM $ "exact Match entered"
+
+ -- -- herald
+ -- case mctxt of
+ -- FunRhs fun fixity strictness -> do
+ -- debugM $ "exact Match FunRhs:" ++ showPprUnsafe fun
+ -- case strictness of
+ -- SrcStrict -> markApiAnn an AnnBang
+ -- _ -> pure ()
+ -- case fixity of
+ -- Prefix -> do
+ -- markAnnotated fun
+ -- mapM_ markAnnotated pats
+ -- Infix ->
+ -- case pats of
+ -- (p1:p2:rest)
+ -- | null rest -> do
+ -- markAnnotated p1
+ -- markAnnotated fun
+ -- markAnnotated p2
+ -- | otherwise -> do
+ -- markApiAnn an AnnOpenP
+ -- markAnnotated p1
+ -- markAnnotated fun
+ -- markAnnotated p2
+ -- markApiAnn an AnnCloseP
+ -- mapM_ markAnnotated rest
+ -- LambdaExpr -> do
+ -- markApiAnn an AnnLam
+ -- mapM_ markAnnotated pats
+ -- GHC.CaseAlt -> do
+ -- mapM_ markAnnotated pats
+ -- _ -> withPpr mctxt
+
+ -- markAnnotated grhss
+
+-- ---------------------------------------------------------------------
+
+exactMatch :: (ExactPrint (GRHSs GhcPs body)) => (Match GhcPs body) -> Annotated ()
+exactMatch (Match an mctxt pats grhss) = do
+-- Based on Expr.pprMatch
+
+ debugM $ "exact Match entered"
+
+ -- herald
+ case mctxt of
+ FunRhs fun fixity strictness -> do
+ debugM $ "exact Match FunRhs:" ++ showPprUnsafe fun
+ case strictness of
+ SrcStrict -> markApiAnn an AnnBang
+ _ -> pure ()
+ case fixity of
+ Prefix -> do
+ markAnnotated fun
+ markAnnotated pats
+ Infix ->
+ case pats of
+ (p1:p2:rest)
+ | null rest -> do
+ markAnnotated p1
+ markAnnotated fun
+ markAnnotated p2
+ | otherwise -> do
+ markApiAnn an AnnOpenP
+ markAnnotated p1
+ markAnnotated fun
+ markAnnotated p2
+ markApiAnn an AnnCloseP
+ mapM_ markAnnotated rest
+ _ -> panic "FunRhs"
+ LambdaExpr -> do
+ markApiAnn an AnnLam
+ markAnnotated pats
+ GHC.CaseAlt -> do
+ markAnnotated pats
+ _ -> withPpr mctxt
+
+ markAnnotated grhss
+
+-- ---------------------------------------------------------------------
+
+instance ExactPrint (GRHSs GhcPs (LocatedA (HsExpr GhcPs))) where
+ getAnnotationEntry (GRHSs _ _ _) = NoEntryVal
+
+ exact (GRHSs _ grhss binds) = do
+ markAnnotated grhss
+ markAnnotated binds
+
+
+instance ExactPrint (GRHSs GhcPs (LocatedA (HsCmd GhcPs))) where
+ getAnnotationEntry (GRHSs _ _ _) = NoEntryVal
+
+ exact (GRHSs _an grhss binds) = do
+ markAnnotated grhss
+ markAnnotated binds
+
+-- ---------------------------------------------------------------------
+
+instance ExactPrint (HsLocalBinds GhcPs) where
+ getAnnotationEntry (HsValBinds an _) = fromAnn an
+ getAnnotationEntry (HsIPBinds{}) = NoEntryVal
+ getAnnotationEntry (EmptyLocalBinds{}) = NoEntryVal
+
+ exact (HsValBinds an valbinds) = do
+ markLocatedAAL an al_rest AnnWhere
+ let manc = case an of
+ ApiAnnNotUsed -> Nothing
+ _ -> al_anchor $ anns an
+
+ case manc of
+ Just anc -> do
+ when (not $ isEmptyValBinds valbinds) $ setExtraDP (Just anc)
+ _ -> return ()
+
+ markAnnotatedWithLayout valbinds
+
+ exact (HsIPBinds an bs)
+ = markAnnList an (markLocatedAAL an al_rest AnnWhere >> markAnnotated bs)
+ exact (EmptyLocalBinds _) = return ()
+
+
+-- ---------------------------------------------------------------------
+instance ExactPrint (HsValBindsLR GhcPs GhcPs) where
+ getAnnotationEntry _ = NoEntryVal
+
+ exact (ValBinds sortKey binds sigs) = do
+ setLayoutBoth $ withSortKey sortKey
+ (prepareListAnnotationA (bagToList binds)
+ ++ prepareListAnnotationA sigs
+ )
+ exact (XValBindsLR _) = panic "XValBindsLR"
+
+-- ---------------------------------------------------------------------
+
+instance ExactPrint (HsIPBinds GhcPs) where
+ getAnnotationEntry = const NoEntryVal
+
+ exact (IPBinds _ binds) = setLayoutBoth $ markAnnotated binds
+
+-- ---------------------------------------------------------------------
+
+instance ExactPrint (IPBind GhcPs) where
+ getAnnotationEntry (IPBind an _ _) = fromAnn an
+
+ exact (IPBind an (Left lr) rhs) = do
+ markAnnotated lr
+ markApiAnn an AnnEqual
+ markAnnotated rhs
+
+ exact (IPBind _ (Right _) _) = error $ "ExactPrint IPBind: Right only after typechecker"
+
+-- ---------------------------------------------------------------------
+
+instance ExactPrint HsIPName where
+ getAnnotationEntry = const NoEntryVal
+
+ exact (HsIPName fs) = printStringAdvance ("?" ++ (unpackFS fs))
+
+-- ---------------------------------------------------------------------
+
+-- instance ExactPrint (HsValBindsLR GhcPs GhcPs) where
+-- getAnnotationEntry _ = NoEntryVal
+
+-- exact (ValBinds sortKey binds sigs) = do
+-- -- printStringAdvance "ValBinds"
+-- setLayoutBoth $ withSortKey sortKey
+-- (prepareListAnnotationA (bagToList binds)
+-- ++ prepareListAnnotationA sigs
+-- )
+
+-- ---------------------------------------------------------------------
+-- Managing lists which have been separated, e.g. Sigs and Binds
+
+
+-- AZ:TODO: generalise this, and the next one
+-- prepareListAnnotationFamilyD :: [LFamilyDecl GhcPs] -> [(RealSrcSpan,EPP ())]
+-- prepareListAnnotationFamilyD ls
+-- = map (\b -> (realSrcSpan $ getLocA b,exactFamilyDecl NotTopLevel (unLoc b))) ls
+
+prepareListAnnotationF :: (a -> EPP ()) -> [LocatedAn an a] -> [(RealSrcSpan,EPP ())]
+prepareListAnnotationF f ls
+ = map (\b -> (realSrcSpan $ getLocA b, f (unLoc b))) ls
+
+prepareListAnnotationA :: ExactPrint (LocatedAn an a)
+ => [LocatedAn an a] -> [(RealSrcSpan,EPP ())]
+prepareListAnnotationA ls = map (\b -> (realSrcSpan $ getLocA b,markAnnotated b)) ls
+
+
+-- applyListAnnotations :: [(RealSrcSpan, EPP ())] -> EPP ()
+-- applyListAnnotations ls = withSortKey ls
+
+withSortKey :: AnnSortKey -> [(RealSrcSpan, EPP ())] -> EPP ()
+withSortKey annSortKey xs = do
+ debugM $ "withSortKey:annSortKey=" ++ showAst annSortKey
+ let ordered = case annSortKey of
+ NoAnnSortKey -> sortBy orderByFst xs
+ -- Just keys -> error $ "withSortKey: keys" ++ show keys
+ AnnSortKey keys -> orderByKey xs keys
+ -- `debug` ("withSortKey:" ++
+ -- showPprUnsafe (map fst (sortBy (comparing (flip elemIndex keys . fst)) xs),
+ -- map fst xs,
+ -- keys)
+ -- )
+ mapM_ snd ordered
+
+orderByFst :: Ord a => (a, b1) -> (a, b2) -> Ordering
+orderByFst (a,_) (b,_) = compare a b
+
+-- ---------------------------------------------------------------------
+
+instance ExactPrint (Sig GhcPs) where
+ getAnnotationEntry (TypeSig a _ _) = fromAnn a
+ getAnnotationEntry (PatSynSig a _ _) = fromAnn a
+ getAnnotationEntry (ClassOpSig a _ _ _) = fromAnn a
+ getAnnotationEntry (IdSig {}) = NoEntryVal
+ getAnnotationEntry (FixSig a _) = fromAnn a
+ getAnnotationEntry (InlineSig a _ _) = fromAnn a
+ getAnnotationEntry (SpecSig a _ _ _) = fromAnn a
+ getAnnotationEntry (SpecInstSig a _ _) = fromAnn a
+ getAnnotationEntry (MinimalSig a _ _) = fromAnn a
+ getAnnotationEntry (SCCFunSig a _ _ _) = fromAnn a
+ getAnnotationEntry (CompleteMatchSig a _ _ _) = fromAnn a
+
+-- instance Annotate (Sig GhcPs) where
+
+ exact (TypeSig an vars ty) = exactVarSig an vars ty
+
+ exact (PatSynSig an lns typ) = do
+ markLocatedAAL an asRest AnnPattern
+ markAnnotated lns
+ markLocatedAA an asDcolon
+ markAnnotated typ
+
+ exact (ClassOpSig an is_deflt vars ty)
+ | is_deflt = markLocatedAAL an asRest AnnDefault >> exactVarSig an vars ty
+ | otherwise = exactVarSig an vars ty
+
+-- markAST _ (IdSig {}) =
+-- traceM "warning: Introduced after renaming"
+
+ exact (FixSig an (FixitySig _ names (Fixity src v fdir))) = do
+ let fixstr = case fdir of
+ InfixL -> "infixl"
+ InfixR -> "infixr"
+ InfixN -> "infix"
+ markLocatedAALS an id AnnInfix (Just fixstr)
+-- markSourceText src (show v)
+ markLocatedAALS an id AnnVal (Just (sourceTextToString src (show v)))
+ markAnnotated names
+
+
+ exact (InlineSig an ln inl) = do
+ markAnnOpen an (inl_src inl) "{-# INLINE"
+ -- markActivation l (inl_act inl)
+ markActivation an id (inl_act inl)
+ markAnnotated ln
+ -- markWithString AnnClose "#-}" -- '#-}'
+ debugM $ "InlineSig:an=" ++ showAst an
+ p <- getPosP
+ debugM $ "InlineSig: p=" ++ show p
+ markLocatedAALS an id AnnClose (Just "#-}")
+ debugM $ "InlineSig:done"
+
+ exact (SpecSig an ln typs inl) = do
+ markAnnOpen an (inl_src inl) "{-# SPECIALISE" -- Note: may be {-# SPECIALISE_INLINE
+ markActivation an id (inl_act inl)
+ markAnnotated ln
+ markApiAnn an AnnDcolon
+ markAnnotated typs
+ markLocatedAALS an id AnnClose (Just "#-}")
+
+ exact (SpecInstSig an src typ) = do
+ markAnnOpen an src "{-# SPECIALISE"
+ markApiAnn an AnnInstance
+ markAnnotated typ
+ markLocatedAALS an id AnnClose (Just "#-}")
+
+-- markAST _ (SpecInstSig _ src typ) = do
+-- markAnnOpen src "{-# SPECIALISE"
+-- mark AnnInstance
+-- markLHsSigType typ
+-- markWithString AnnClose "#-}" -- '#-}'
+-- markTrailingSemi
+
+ exact (MinimalSig an src formula) = do
+ markAnnOpen an src "{-# MINIMAL"
+ markAnnotated formula
+ markLocatedAALS an id AnnClose (Just "#-}")
+
+-- markAST _ (MinimalSig _ src formula) = do
+-- markAnnOpen src "{-# MINIMAL"
+-- markLocated formula
+-- markWithString AnnClose "#-}"
+-- markTrailingSemi
+
+ exact (SCCFunSig an src ln ml) = do
+ markAnnOpen an src "{-# SCC"
+ markAnnotated ln
+ markAnnotated ml
+ markLocatedAALS an id AnnClose (Just "#-}")
+
+-- markAST _ (CompleteMatchSig _ src (L _ ns) mlns) = do
+-- markAnnOpen src "{-# COMPLETE"
+-- markListIntercalate ns
+-- case mlns of
+-- Nothing -> return ()
+-- Just _ -> do
+-- mark AnnDcolon
+-- markMaybe mlns
+-- markWithString AnnClose "#-}" -- '#-}'
+-- markTrailingSemi
+
+ exact x = error $ "exact Sig for:" ++ showAst x
+
+-- ---------------------------------------------------------------------
+
+exactVarSig :: (ExactPrint a) => ApiAnn' AnnSig -> [LocatedN RdrName] -> a -> EPP ()
+exactVarSig an vars ty = do
+ mapM_ markAnnotated vars
+ markLocatedAA an asDcolon
+ markAnnotated ty
+
+-- ---------------------------------------------------------------------
+
+-- instance ExactPrint (FixitySig GhcPs) where
+-- getAnnotationEntry = const NoEntryVal
+
+-- exact (FixitySig an names (Fixity src v fdir)) = do
+-- let fixstr = case fdir of
+-- InfixL -> "infixl"
+-- InfixR -> "infixr"
+-- InfixN -> "infix"
+-- markAnnotated names
+-- markLocatedAALS an id AnnInfix (Just fixstr)
+-- -- markAST _ (FixSig _ (FixitySig _ lns (Fixity src v fdir))) = do
+-- -- let fixstr = case fdir of
+-- -- InfixL -> "infixl"
+-- -- InfixR -> "infixr"
+-- -- InfixN -> "infix"
+-- -- markWithString AnnInfix fixstr
+-- -- markSourceText src (show v)
+-- -- setContext (Set.singleton InfixOp) $ markListIntercalate lns
+-- -- markTrailingSemi
+-- ---------------------------------------------------------------------
+
+instance ExactPrint (StandaloneKindSig GhcPs) where
+ getAnnotationEntry (StandaloneKindSig an _ _) = fromAnn an
+
+ exact (StandaloneKindSig an vars sig) = do
+ markApiAnn an AnnType
+ markAnnotated vars
+ markApiAnn an AnnDcolon
+ markAnnotated sig
+
+-- ---------------------------------------------------------------------
+
+instance ExactPrint (DefaultDecl GhcPs) where
+ getAnnotationEntry (DefaultDecl an _) = fromAnn an
+
+ exact (DefaultDecl an tys) = do
+ markApiAnn an AnnDefault
+ markApiAnn an AnnOpenP
+ markAnnotated tys
+ markApiAnn an AnnCloseP
+
+-- ---------------------------------------------------------------------
+
+instance ExactPrint (AnnDecl GhcPs) where
+ getAnnotationEntry (HsAnnotation an _ _ _) = fromAnn an
+
+ exact (HsAnnotation an src prov e) = do
+ markAnnOpenP an src "{-# ANN"
+ case prov of
+ (ValueAnnProvenance n) -> markAnnotated n
+ (TypeAnnProvenance n) -> do
+ markLocatedAAL an apr_rest AnnType
+ markAnnotated n
+ ModuleAnnProvenance -> markLocatedAAL an apr_rest AnnModule
+
+ markAnnotated e
+ markAnnCloseP an
+
+-- ---------------------------------------------------------------------
+
+instance ExactPrint (BF.BooleanFormula (LocatedN RdrName)) where
+ getAnnotationEntry = const NoEntryVal
+
+ exact (BF.Var x) = do
+ markAnnotated x
+ exact (BF.Or ls) = markAnnotated ls
+ exact (BF.And ls) = do
+ markAnnotated ls
+ exact (BF.Parens x) = do
+ -- mark AnnOpenP -- '('
+ markAnnotated x
+ -- mark AnnCloseP -- ')'
+
+-- instance (Annotate name) => Annotate (GHC.BooleanFormula (GHC.Located name)) where
+-- markAST _ (GHC.Var x) = do
+-- setContext (Set.singleton PrefixOp) $ markLocated x
+-- inContext (Set.fromList [AddVbar]) $ mark GHC.AnnVbar
+-- inContext (Set.fromList [Intercalate]) $ mark GHC.AnnComma
+-- markAST _ (GHC.Or ls) = markListIntercalateWithFunLevelCtx markLocated 2 AddVbar ls
+-- markAST _ (GHC.And ls) = do
+-- markListIntercalateWithFunLevel markLocated 2 ls
+-- inContext (Set.fromList [AddVbar]) $ mark GHC.AnnVbar
+-- inContext (Set.fromList [Intercalate]) $ mark GHC.AnnComma
+-- markAST _ (GHC.Parens x) = do
+-- mark GHC.AnnOpenP -- '('
+-- markLocated x
+-- mark GHC.AnnCloseP -- ')'
+-- inContext (Set.fromList [AddVbar]) $ mark GHC.AnnVbar
+-- inContext (Set.fromList [Intercalate]) $ mark GHC.AnnComma
+
+-- ---------------------------------------------------------------------
+
+-- instance ExactPrint (LHsSigWcType GhcPs) where
+-- instance ExactPrint (HsWildCardBndrs GhcPs (LHsSigType GhcPs)) where
+instance (ExactPrint body) => ExactPrint (HsWildCardBndrs GhcPs body) where
+ getAnnotationEntry = const NoEntryVal
+ exact (HsWC _ ty) = markAnnotated ty
+
+-- ---------------------------------------------------------------------
+
+instance ExactPrint (GRHS GhcPs (LocatedA (HsExpr GhcPs))) where
+ getAnnotationEntry (GRHS an _ _) = fromAnn an
+
+ exact (GRHS an guards expr) = do
+ debugM $ "GRHS comments:" ++ showGhc (comments an)
+ markAnnKwM an ga_vbar AnnVbar
+ markAnnotated guards
+ debugM $ "GRHS before matchSeparator"
+ markLocatedAA an ga_sep -- Mark the matchSeparator for these GRHSs
+ debugM $ "GRHS after matchSeparator"
+ markAnnotated expr
+ -- markLocatedAA an ga_sep
+
+instance ExactPrint (GRHS GhcPs (LocatedA (HsCmd GhcPs))) where
+ getAnnotationEntry (GRHS ann _ _) = fromAnn ann
+
+ exact (GRHS an guards expr) = do
+ markAnnKwM an ga_vbar AnnVbar
+ markAnnotated guards
+ markLocatedAA an ga_sep -- Mark the matchSeparator for these GRHSs
+ markAnnotated expr
+
+-- ---------------------------------------------------------------------
+
+instance ExactPrint (HsExpr GhcPs) where
+ getAnnotationEntry (HsVar{}) = NoEntryVal
+ getAnnotationEntry (HsUnboundVar an _) = fromAnn an
+ getAnnotationEntry (HsConLikeOut{}) = NoEntryVal
+ getAnnotationEntry (HsRecFld{}) = NoEntryVal
+ getAnnotationEntry (HsOverLabel an _) = fromAnn an
+ getAnnotationEntry (HsIPVar an _) = fromAnn an
+ getAnnotationEntry (HsOverLit an _) = fromAnn an
+ getAnnotationEntry (HsLit an _) = fromAnn an
+ getAnnotationEntry (HsLam _ _) = NoEntryVal
+ getAnnotationEntry (HsLamCase an _) = fromAnn an
+ getAnnotationEntry (HsApp an _ _) = fromAnn an
+ getAnnotationEntry (HsAppType _ _ _) = NoEntryVal
+ getAnnotationEntry (OpApp an _ _ _) = fromAnn an
+ getAnnotationEntry (NegApp an _ _) = fromAnn an
+ getAnnotationEntry (HsPar an _) = fromAnn an
+ getAnnotationEntry (SectionL an _ _) = fromAnn an
+ getAnnotationEntry (SectionR an _ _) = fromAnn an
+ getAnnotationEntry (ExplicitTuple an _ _) = fromAnn an
+ getAnnotationEntry (ExplicitSum an _ _ _) = fromAnn an
+ getAnnotationEntry (HsCase an _ _) = fromAnn an
+ getAnnotationEntry (HsIf an _ _ _) = fromAnn an
+ getAnnotationEntry (HsMultiIf an _) = fromAnn an
+ getAnnotationEntry (HsLet an _ _) = fromAnn an
+ getAnnotationEntry (HsDo an _ _) = fromAnn an
+ getAnnotationEntry (ExplicitList an _) = fromAnn an
+ getAnnotationEntry (RecordCon an _ _) = fromAnn an
+ getAnnotationEntry (RecordUpd an _ _) = fromAnn an
+ getAnnotationEntry (HsGetField an _ _) = fromAnn an
+ getAnnotationEntry (HsProjection an _) = fromAnn an
+ getAnnotationEntry (ExprWithTySig an _ _) = fromAnn an
+ getAnnotationEntry (ArithSeq an _ _) = fromAnn an
+ getAnnotationEntry (HsBracket an _) = fromAnn an
+ getAnnotationEntry (HsRnBracketOut{}) = NoEntryVal
+ getAnnotationEntry (HsTcBracketOut{}) = NoEntryVal
+ getAnnotationEntry (HsSpliceE an _) = fromAnn an
+ getAnnotationEntry (HsProc an _ _) = fromAnn an
+ getAnnotationEntry (HsStatic an _) = fromAnn an
+ getAnnotationEntry (HsTick {}) = NoEntryVal
+ getAnnotationEntry (HsBinTick {}) = NoEntryVal
+ getAnnotationEntry (HsPragE{}) = NoEntryVal
+
+
+ exact (HsVar _ n) = markAnnotated n
+ exact x@(HsUnboundVar an _v) = do
+ case an of
+ ApiAnnNotUsed -> withPpr x
+ ApiAnn _ (ApiAnnUnboundVar (ob,cb) l) _ -> do
+ printStringAtAA ob "`"
+ printStringAtAA l "_"
+ printStringAtAA cb "`"
+ -- exact x@(HsConLikeOut{}) = withPpr x
+ -- exact x@(HsRecFld{}) = withPpr x
+ -- exact x@(HsOverLabel ann _ _) = withPpr x
+ exact (HsIPVar _ (HsIPName n))
+ = printStringAdvance ("?" ++ unpackFS n)
+
+ exact x@(HsOverLit _an ol) = do
+ let str = case ol_val ol of
+ HsIntegral (IL src _ _) -> src
+ HsFractional (FL { fl_text = src }) -> src
+ HsIsString src _ -> src
+ -- markExternalSourceText l str ""
+ case str of
+ SourceText s -> printStringAdvance s
+ NoSourceText -> withPpr x
+
+ exact (HsLit _an lit) = withPpr lit
+ exact (HsLam _ (MG _ (L _ [match]) _)) = do
+ markAnnotated match
+ -- markExpr _ (HsLam _ (MG _ (L _ [match]) _)) = do
+ -- setContext (Set.singleton LambdaExpr) $ do
+ -- -- TODO: Change this, HsLam binds do not need obey layout rules.
+ -- -- And will only ever have a single match
+ -- markLocated match
+ -- markExpr _ (HsLam _ _) = error $ "HsLam with other than one match"
+ exact (HsLam _ _) = error $ "HsLam with other than one match"
+
+ exact (HsLamCase an mg) = do
+ markApiAnn an AnnLam
+ markApiAnn an AnnCase
+ markAnnotated mg
+
+ exact (HsApp _an e1 e2) = do
+ p <- getPosP
+ debugM $ "HsApp entered. p=" ++ show p
+ markAnnotated e1
+ markAnnotated e2
+ exact (HsAppType ss fun arg) = do
+ markAnnotated fun
+ printStringAtSs ss "@"
+ markAnnotated arg
+ exact (OpApp _an e1 e2 e3) = do
+ exact e1
+ exact e2
+ exact e3
+
+ exact (NegApp an e _) = do
+ markApiAnn an AnnMinus
+ markAnnotated e
+
+ exact (HsPar an e) = do
+ markOpeningParen an
+ markAnnotated e
+ debugM $ "HsPar closing paren"
+ markClosingParen an
+ debugM $ "HsPar done"
+
+ -- exact (SectionL an expr op) = do
+ exact (SectionR _an op expr) = do
+ markAnnotated op
+ markAnnotated expr
+ exact (ExplicitTuple an args b) = do
+ if b == Boxed then markApiAnn an AnnOpenP
+ else markApiAnn an AnnOpenPH
+
+ mapM_ markAnnotated args
+
+ if b == Boxed then markApiAnn an AnnCloseP
+ else markApiAnn an AnnClosePH
+ debugM $ "ExplicitTuple done"
+
+ exact (ExplicitSum an _alt _arity expr) = do
+ -- markApiAnn an AnnOpenPH
+ markAnnKw an aesOpen AnnOpenPH
+ markAnnKwAll an aesBarsBefore AnnVbar
+ markAnnotated expr
+ markAnnKwAll an aesBarsAfter AnnVbar
+ markAnnKw an aesClose AnnClosePH
+
+ exact (HsCase an e alts) = do
+ markAnnKw an hsCaseAnnCase AnnCase
+ markAnnotated e
+ markAnnKw an hsCaseAnnOf AnnOf
+ markApiAnn' an hsCaseAnnsRest AnnOpenC
+ markApiAnnAll an hsCaseAnnsRest AnnSemi
+ setLayoutBoth $ markAnnotated alts
+ markApiAnn' an hsCaseAnnsRest AnnCloseC
+
+ -- exact x@(HsCase ApiAnnNotUsed _ _) = withPpr x
+ exact (HsIf an e1 e2 e3) = do
+ markApiAnn an AnnIf
+ markAnnotated e1
+ markApiAnn an AnnThen
+ markAnnotated e2
+ markApiAnn an AnnElse
+ markAnnotated e3
+
+ exact (HsMultiIf an mg) = do
+ markApiAnn an AnnIf
+ markApiAnn an AnnOpenC -- optional
+ markAnnotated mg
+ markApiAnn an AnnCloseC -- optional
+
+ exact (HsLet an binds e) = do
+ setLayoutBoth $ do -- Make sure the 'in' gets indented too
+ markAnnKw an alLet AnnLet
+ debugM $ "HSlet:binds coming"
+ setLayoutBoth $ markAnnotated binds
+ debugM $ "HSlet:binds done"
+ markAnnKw an alIn AnnIn
+ debugM $ "HSlet:expr coming"
+ markAnnotated e
+
+ exact (HsDo an do_or_list_comp stmts) = do
+ debugM $ "HsDo"
+ markAnnList an $ exactDo an do_or_list_comp stmts
+
+ exact (ExplicitList an es) = do
+ debugM $ "ExplicitList start"
+ markLocatedMAA an al_open
+ markAnnotated es
+ markLocatedMAA an al_close
+ debugM $ "ExplicitList end"
+ exact (RecordCon an con_id binds) = do
+ markAnnotated con_id
+ markApiAnn an AnnOpenC
+ markAnnotated binds
+ markApiAnn an AnnCloseC
+ exact (RecordUpd an expr fields) = do
+ markAnnotated expr
+ markApiAnn an AnnOpenC
+ markAnnotated fields
+ markApiAnn an AnnCloseC
+ exact (HsGetField _an expr field) = do
+ markAnnotated expr
+ markAnnotated field
+ exact (HsProjection an flds) = do
+ markAnnKw an apOpen AnnOpenP
+ markAnnotated flds
+ markAnnKw an apClose AnnCloseP
+ exact (ExprWithTySig an expr sig) = do
+ markAnnotated expr
+ markApiAnn an AnnDcolon
+ markAnnotated sig
+ exact (ArithSeq an _ seqInfo) = do
+ markApiAnn an AnnOpenS -- '['
+ case seqInfo of
+ From e -> do
+ markAnnotated e
+ markApiAnn an AnnDotdot
+ FromTo e1 e2 -> do
+ markAnnotated e1
+ markApiAnn an AnnDotdot
+ markAnnotated e2
+ FromThen e1 e2 -> do
+ markAnnotated e1
+ markApiAnn an AnnComma
+ markAnnotated e2
+ markApiAnn an AnnDotdot
+ FromThenTo e1 e2 e3 -> do
+ markAnnotated e1
+ markApiAnn an AnnComma
+ markAnnotated e2
+ markApiAnn an AnnDotdot
+ markAnnotated e3
+ markApiAnn an AnnCloseS -- ']'
+
+
+ exact (HsBracket an (ExpBr _ e)) = do
+ markApiAnn an AnnOpenEQ -- "[|"
+ markApiAnn an AnnOpenE -- "[e|" -- optional
+ markAnnotated e
+ markApiAnn an AnnCloseQ -- "|]"
+ exact (HsBracket an (PatBr _ e)) = do
+ markLocatedAALS an id AnnOpen (Just "[p|")
+ markAnnotated e
+ markApiAnn an AnnCloseQ -- "|]"
+ exact (HsBracket an (DecBrL _ e)) = do
+ markLocatedAALS an id AnnOpen (Just "[d|")
+ markAnnotated e
+ markApiAnn an AnnCloseQ -- "|]"
+ -- -- exact (HsBracket an (DecBrG _ _)) =
+ -- -- traceM "warning: DecBrG introduced after renamer"
+ exact (HsBracket an (TypBr _ e)) = do
+ markLocatedAALS an id AnnOpen (Just "[t|")
+ markAnnotated e
+ markApiAnn an AnnCloseQ -- "|]"
+ exact (HsBracket an (VarBr _ b e)) = do
+ if b
+ then do
+ markApiAnn an AnnSimpleQuote
+ markAnnotated e
+ else do
+ markApiAnn an AnnThTyQuote
+ markAnnotated e
+ exact (HsBracket an (TExpBr _ e)) = do
+ markLocatedAALS an id AnnOpen (Just "[||")
+ markLocatedAALS an id AnnOpenE (Just "[e||")
+ markAnnotated e
+ markLocatedAALS an id AnnClose (Just "||]")
+
+
+ -- exact x@(HsRnBracketOut{}) = withPpr x
+ -- exact x@(HsTcBracketOut{}) = withPpr x
+ exact (HsSpliceE _ sp) = markAnnotated sp
+
+ exact (HsProc an p c) = do
+ debugM $ "HsProc start"
+ markApiAnn an AnnProc
+ markAnnotated p
+ markApiAnn an AnnRarrow
+ debugM $ "HsProc after AnnRarrow"
+ markAnnotated c
+
+ exact (HsStatic an e) = do
+ markApiAnn an AnnStatic
+ markAnnotated e
+
+ -- exact x@(HsTick {}) = withPpr x
+ -- exact x@(HsBinTick {}) = withPpr x
+ exact (HsPragE _ prag e) = do
+ markAnnotated prag
+ markAnnotated e
+ exact x = error $ "exact HsExpr for:" ++ showAst x
+
+-- ---------------------------------------------------------------------
+
+exactDo :: (ExactPrint body)
+ => ApiAnn' AnnList -> (HsStmtContext any) -> body -> EPP ()
+exactDo an (DoExpr m) stmts = exactMdo an m AnnDo >> markAnnotatedWithLayout stmts
+exactDo an GhciStmtCtxt stmts = markLocatedAAL an al_rest AnnDo >> markAnnotatedWithLayout stmts
+exactDo an ArrowExpr stmts = markLocatedAAL an al_rest AnnDo >> markAnnotatedWithLayout stmts
+exactDo an (MDoExpr m) stmts = exactMdo an m AnnMdo >> markAnnotatedWithLayout stmts
+exactDo _ ListComp stmts = markAnnotatedWithLayout stmts
+exactDo _ MonadComp stmts = markAnnotatedWithLayout stmts
+exactDo _ _ _ = panic "pprDo" -- PatGuard, ParStmtCxt
+
+exactMdo :: ApiAnn' AnnList -> Maybe ModuleName -> AnnKeywordId -> EPP ()
+exactMdo an Nothing kw = markLocatedAAL an al_rest kw
+exactMdo an (Just module_name) kw = markLocatedAALS an al_rest kw (Just n)
+ where
+ n = (moduleNameString module_name) ++ "." ++ (keywordToString (G kw))
+
+
+-- ---------------------------------------------------------------------
+instance ExactPrint (HsPragE GhcPs) where
+ getAnnotationEntry HsPragSCC{} = NoEntryVal
+
+ exact (HsPragSCC an st sl) = do
+ markAnnOpenP an st "{-# SCC"
+ let txt = sourceTextToString (sl_st sl) (unpackFS $ sl_fs sl)
+ markLocatedAALS an apr_rest AnnVal (Just txt) -- optional
+ markLocatedAALS an apr_rest AnnValStr (Just txt) -- optional
+ markAnnCloseP an
+
+ -- markExpr _ (GHC.HsPragE _ prag e) = do
+ -- case prag of
+ -- (GHC.HsPragSCC _ src csFStr) -> do
+ -- markAnnOpen src "{-# SCC"
+ -- let txt = sourceTextToString (GHC.sl_st csFStr) (GHC.unpackFS $ GHC.sl_fs csFStr)
+ -- markWithStringOptional GHC.AnnVal txt
+ -- markWithString GHC.AnnValStr txt
+ -- markWithString GHC.AnnClose "#-}"
+ -- markLocated e
+
+ -- (GHC.HsPragTick _ src (str,(v1,v2),(v3,v4)) ((s1,s2),(s3,s4))) -> do
+ -- -- '{-# GENERATED' STRING INTEGER ':' INTEGER '-' INTEGER ':' INTEGER '#-}'
+ -- markAnnOpen src "{-# GENERATED"
+ -- markOffsetWithString GHC.AnnVal 0 (stringLiteralToString str) -- STRING
+
+ -- let
+ -- markOne n v GHC.NoSourceText = markOffsetWithString GHC.AnnVal n (show v)
+ -- markOne n _v (GHC.SourceText s) = markOffsetWithString GHC.AnnVal n s
+
+ -- markOne 1 v1 s1 -- INTEGER
+ -- markOffset GHC.AnnColon 0 -- ':'
+ -- markOne 2 v2 s2 -- INTEGER
+ -- mark GHC.AnnMinus -- '-'
+ -- markOne 3 v3 s3 -- INTEGER
+ -- markOffset GHC.AnnColon 1 -- ':'
+ -- markOne 4 v4 s4 -- INTEGER
+ -- markWithString GHC.AnnClose "#-}"
+ -- markLocated e
+
+-- ---------------------------------------------------------------------
+
+instance ExactPrint (HsSplice GhcPs) where
+ getAnnotationEntry (HsTypedSplice an _ _ _) = fromAnn an
+ getAnnotationEntry (HsUntypedSplice an _ _ _) = fromAnn an
+ getAnnotationEntry (HsQuasiQuote _ _ _ _ _) = NoEntryVal
+ getAnnotationEntry (HsSpliced _ _ _) = NoEntryVal
+
+ exact (HsTypedSplice an DollarSplice _n e) = do
+ markApiAnn an AnnDollarDollar
+ markAnnotated e
+
+ -- = ppr_splice (text "$$") n e empty
+ -- exact (HsTypedSplice _ BareSplice _ _ )
+ -- = panic "Bare typed splice" -- impossible
+ exact (HsUntypedSplice an decoration _n b) = do
+ when (decoration == DollarSplice) $ markApiAnn an AnnDollar
+ markAnnotated b
+
+ -- exact (HsUntypedSplice _ DollarSplice n e)
+ -- = ppr_splice (text "$") n e empty
+ -- exact (HsUntypedSplice _ BareSplice n e)
+ -- = ppr_splice empty n e empty
+
+ exact (HsQuasiQuote _ _ q ss fs) = do
+ -- The quasiquote string does not honour layout offsets. Store
+ -- the colOffset for now.
+ -- TODO: use local?
+ oldOffset <- getLayoutOffsetP
+ setLayoutOffsetP 0
+ printStringAdvance
+ -- Note: Lexer.x does not provide unicode alternative. 2017-02-26
+ ("[" ++ (showPprUnsafe q) ++ "|" ++ (unpackFS fs) ++ "|]")
+ setLayoutOffsetP oldOffset
+ p <- getPosP
+ debugM $ "HsQuasiQuote:after:(p,ss)=" ++ show (p,ss2range ss)
+
+ -- exact (HsSpliced _ _ thing) = ppr thing
+ -- exact (XSplice x) = case ghcPass @p of
+ exact x = error $ "exact HsSplice for:" ++ showAst x
+
+-- ---------------------------------------------------------------------
+
+-- TODO:AZ: combine these instances
+instance ExactPrint (MatchGroup GhcPs (LocatedA (HsExpr GhcPs))) where
+ getAnnotationEntry = const NoEntryVal
+ exact (MG _ matches _) = do
+ -- TODO:AZ use SortKey, in MG ann.
+ markAnnotated matches
+
+instance ExactPrint (MatchGroup GhcPs (LocatedA (HsCmd GhcPs))) where
+ getAnnotationEntry = const NoEntryVal
+ exact (MG _ matches _) = do
+ -- TODO:AZ use SortKey, in MG ann.
+ markAnnotated matches
+
+-- ---------------------------------------------------------------------
+
+instance (ExactPrint body) => ExactPrint (HsRecFields GhcPs body) where
+ getAnnotationEntry = const NoEntryVal
+ exact (HsRecFields fields mdot) = do
+ markAnnotated fields
+ case mdot of
+ Nothing -> return ()
+ Just (L ss _) ->
+ printStringAtSs ss ".."
+ -- Note: mdot contains the SrcSpan where the ".." appears, if present
+
+-- ---------------------------------------------------------------------
+
+-- instance (ExactPrint body) => ExactPrint (HsRecField GhcPs body) where
+instance (ExactPrint body)
+ => ExactPrint (HsRecField' (FieldOcc GhcPs) body) where
+ getAnnotationEntry x = fromAnn (hsRecFieldAnn x)
+ exact (HsRecField an f arg isPun) = do
+ debugM $ "HsRecField"
+ markAnnotated f
+ if isPun then return ()
+ else do
+ markApiAnn an AnnEqual
+ markAnnotated arg
+
+-- ---------------------------------------------------------------------
+
+instance (ExactPrint body)
+ => ExactPrint (HsRecField' (FieldLabelStrings GhcPs) body) where
+ getAnnotationEntry x = fromAnn (hsRecFieldAnn x)
+ exact (HsRecField an f arg isPun) = do
+ debugM $ "HsRecField FieldLabelStrings"
+ markAnnotated f
+ if isPun then return ()
+ else do
+ markApiAnn an AnnEqual
+ markAnnotated arg
+
+-- ---------------------------------------------------------------------
+
+-- instance ExactPrint (HsRecUpdField GhcPs ) where
+instance (ExactPrint body)
+ => ExactPrint (HsRecField' (AmbiguousFieldOcc GhcPs) body) where
+-- instance (ExactPrint body)
+ -- => ExactPrint (HsRecField' (AmbiguousFieldOcc GhcPs) body) where
+ getAnnotationEntry x = fromAnn (hsRecFieldAnn x)
+ exact (HsRecField an f arg isPun) = do
+ debugM $ "HsRecUpdField"
+ markAnnotated f
+ if isPun then return ()
+ else markApiAnn an AnnEqual
+ markAnnotated arg
+
+-- ---------------------------------------------------------------------
+-- instance (ExactPrint body)
+-- => ExactPrint (Either (HsRecField' (AmbiguousFieldOcc GhcPs) body)
+-- (HsRecField' (FieldOcc GhcPs) body)) where
+-- getAnnotationEntry = const NoEntryVal
+-- exact (Left rbinds) = markAnnotated rbinds
+-- exact (Right pbinds) = markAnnotated pbinds
+
+-- ---------------------------------------------------------------------
+-- instance (ExactPrint body)
+-- => ExactPrint
+-- (Either [LocatedA (HsRecField' (AmbiguousFieldOcc GhcPs) body)]
+-- [LocatedA (HsRecField' (FieldOcc GhcPs) body)]) where
+-- getAnnotationEntry = const NoEntryVal
+-- exact (Left rbinds) = markAnnotated rbinds
+-- exact (Right pbinds) = markAnnotated pbinds
+
+-- ---------------------------------------------------------------------
+instance -- (ExactPrint body)
+ (ExactPrint (HsRecField' (a GhcPs) body),
+ ExactPrint (HsRecField' (b GhcPs) body))
+ => ExactPrint
+ (Either [LocatedA (HsRecField' (a GhcPs) body)]
+ [LocatedA (HsRecField' (b GhcPs) body)]) where
+ getAnnotationEntry = const NoEntryVal
+ exact (Left rbinds) = markAnnotated rbinds
+ exact (Right pbinds) = markAnnotated pbinds
+
+-- ---------------------------------------------------------------------
+
+instance ExactPrint (FieldLabelStrings GhcPs) where
+ getAnnotationEntry = const NoEntryVal
+ exact (FieldLabelStrings fs) = markAnnotated fs
+
+-- ---------------------------------------------------------------------
+
+instance ExactPrint (HsFieldLabel GhcPs) where
+ getAnnotationEntry (HsFieldLabel an _) = fromAnn an
+
+ exact (HsFieldLabel an fs) = do
+ markAnnKwM an afDot AnnDot
+ markAnnotated fs
+
+-- ---------------------------------------------------------------------
+
+instance ExactPrint (HsTupArg GhcPs) where
+ getAnnotationEntry (Present an _) = fromAnn an
+ getAnnotationEntry (Missing an) = fromAnn an
+
+ exact (Present _ e) = markAnnotated e
+
+ exact (Missing ApiAnnNotUsed) = return ()
+ exact (Missing _) = printStringAdvance ","
+
+-- ---------------------------------------------------------------------
+
+instance ExactPrint (HsCmdTop GhcPs) where
+ getAnnotationEntry = const NoEntryVal
+ exact (HsCmdTop _ cmd) = markAnnotated cmd
+
+-- ---------------------------------------------------------------------
+
+instance ExactPrint (HsCmd GhcPs) where
+ getAnnotationEntry (HsCmdArrApp an _ _ _ _) = fromAnn an
+ getAnnotationEntry (HsCmdArrForm an _ _ _ _ ) = fromAnn an
+ getAnnotationEntry (HsCmdApp an _ _ ) = fromAnn an
+ getAnnotationEntry (HsCmdLam {}) = NoEntryVal
+ getAnnotationEntry (HsCmdPar an _) = fromAnn an
+ getAnnotationEntry (HsCmdCase an _ _) = fromAnn an
+ getAnnotationEntry (HsCmdLamCase an _) = fromAnn an
+ getAnnotationEntry (HsCmdIf an _ _ _ _) = fromAnn an
+ getAnnotationEntry (HsCmdLet an _ _) = fromAnn an
+ getAnnotationEntry (HsCmdDo an _) = fromAnn an
+
+
+-- ppr_cmd (HsCmdArrApp _ arrow arg HsFirstOrderApp True)
+-- = hsep [ppr_lexpr arrow, larrowt, ppr_lexpr arg]
+-- ppr_cmd (HsCmdArrApp _ arrow arg HsFirstOrderApp False)
+-- = hsep [ppr_lexpr arg, arrowt, ppr_lexpr arrow]
+-- ppr_cmd (HsCmdArrApp _ arrow arg HsHigherOrderApp True)
+-- = hsep [ppr_lexpr arrow, larrowtt, ppr_lexpr arg]
+-- ppr_cmd (HsCmdArrApp _ arrow arg HsHigherOrderApp False)
+-- = hsep [ppr_lexpr arg, arrowtt, ppr_lexpr arrow]
+
+ exact (HsCmdArrApp an arr arg _o isRightToLeft) = do
+ if isRightToLeft
+ then do
+ markAnnotated arr
+ markKw (anns an)
+ markAnnotated arg
+ else do
+ markAnnotated arg
+ markKw (anns an)
+ markAnnotated arr
+-- markAST _ (GHC.HsCmdArrApp _ e1 e2 o isRightToLeft) = do
+-- -- isRightToLeft True => right-to-left (f -< arg)
+-- -- False => left-to-right (arg >- f)
+-- if isRightToLeft
+-- then do
+-- markLocated e1
+-- case o of
+-- GHC.HsFirstOrderApp -> mark GHC.Annlarrowtail
+-- GHC.HsHigherOrderApp -> mark GHC.AnnLarrowtail
+-- else do
+-- markLocated e2
+-- case o of
+-- GHC.HsFirstOrderApp -> mark GHC.Annrarrowtail
+-- GHC.HsHigherOrderApp -> mark GHC.AnnRarrowtail
+
+-- if isRightToLeft
+-- then markLocated e2
+-- else markLocated e1
+
+ exact (HsCmdArrForm an e fixity _mf [arg1,arg2]) = do
+ markLocatedMAA an al_open
+ case fixity of
+ Infix -> do
+ markAnnotated arg1
+ markAnnotated e
+ markAnnotated arg2
+ Prefix -> do
+ markAnnotated e
+ markAnnotated arg1
+ markAnnotated arg2
+ markLocatedMAA an al_close
+-- markAST _ (GHC.HsCmdArrForm _ e fixity _mf cs) = do
+-- -- The AnnOpen should be marked for a prefix usage, not for a postfix one,
+-- -- due to the way checkCmd maps both HsArrForm and OpApp to HsCmdArrForm
+
+-- let isPrefixOp = case fixity of
+-- GHC.Infix -> False
+-- GHC.Prefix -> True
+-- when isPrefixOp $ mark GHC.AnnOpenB -- "(|"
+
+-- -- This may be an infix operation
+-- applyListAnnotationsContexts (LC (Set.singleton PrefixOp) (Set.singleton PrefixOp)
+-- (Set.singleton InfixOp) (Set.singleton InfixOp))
+-- (prepareListAnnotation [e]
+-- ++ prepareListAnnotation cs)
+-- when isPrefixOp $ mark GHC.AnnCloseB -- "|)"
+
+-- markAST _ (GHC.HsCmdApp _ e1 e2) = do
+-- markLocated e1
+-- markLocated e2
+
+ exact (HsCmdLam _ match) = markAnnotated match
+-- markAST l (GHC.HsCmdLam _ match) = do
+-- setContext (Set.singleton LambdaExpr) $ do markMatchGroup l match
+
+ exact (HsCmdPar an e) = do
+ markOpeningParen an
+ markAnnotated e
+ markClosingParen an
+
+ exact (HsCmdCase an e alts) = do
+ markAnnKw an hsCaseAnnCase AnnCase
+ markAnnotated e
+ markAnnKw an hsCaseAnnOf AnnOf
+ markApiAnn' an hsCaseAnnsRest AnnOpenC
+ markApiAnnAll an hsCaseAnnsRest AnnSemi
+ markAnnotated alts
+ markApiAnn' an hsCaseAnnsRest AnnCloseC
+ -- markApiAnn an AnnCase
+ -- markAnnotated e1
+ -- markApiAnn an AnnOf
+ -- markApiAnn an AnnOpenC
+ -- markAnnotated matches
+ -- markApiAnn an AnnCloseC
+
+-- markAST l (GHC.HsCmdCase _ e1 matches) = do
+-- mark GHC.AnnCase
+-- markLocated e1
+-- mark GHC.AnnOf
+-- markOptional GHC.AnnOpenC
+-- setContext (Set.singleton CaseAlt) $ do
+-- markMatchGroup l matches
+-- markOptional GHC.AnnCloseC
+
+-- markAST _ (GHC.HsCmdIf _ _ e1 e2 e3) = do
+-- mark GHC.AnnIf
+-- markLocated e1
+-- markOffset GHC.AnnSemi 0
+-- mark GHC.AnnThen
+-- markLocated e2
+-- markOffset GHC.AnnSemi 1
+-- mark GHC.AnnElse
+-- markLocated e3
+
+-- markAST _ (GHC.HsCmdLet _ (GHC.L _ binds) e) = do
+-- mark GHC.AnnLet
+-- markOptional GHC.AnnOpenC
+-- markLocalBindsWithLayout binds
+-- markOptional GHC.AnnCloseC
+-- mark GHC.AnnIn
+-- markLocated e
+
+ exact (HsCmdDo an es) = do
+ debugM $ "HsCmdDo"
+ markApiAnn' an al_rest AnnDo
+ markAnnotated es
+
+-- markAST _ (GHC.HsCmdDo _ (GHC.L _ es)) = do
+-- mark GHC.AnnDo
+-- markOptional GHC.AnnOpenC
+-- markListWithLayout es
+-- markOptional GHC.AnnCloseC
+
+-- markAST _ (GHC.HsCmdWrap {}) =
+-- traceM "warning: HsCmdWrap introduced after renaming"
+
+-- markAST _ (GHC.XCmd x) = error $ "got XCmd for:" ++ showPprUnsafe x
+
+ exact x = error $ "exact HsCmd for:" ++ showAst x
+
+-- ---------------------------------------------------------------------
+
+-- instance ExactPrint (CmdLStmt GhcPs) where
+-- getAnnotationEntry = const NoEntryVal
+-- exact (L _ a) = markAnnotated a
+
+-- ---------------------------------------------------------------------
+
+-- instance ExactPrint (StmtLR GhcPs GhcPs (LHsCmd GhcPs)) where
+instance (ExactPrint (LocatedA body))
+ => ExactPrint (StmtLR GhcPs GhcPs (LocatedA body)) where
+-- instance ExactPrint (StmtLR GhcPs GhcPs (LocatedA (HsCmd GhcPs))) where
+ getAnnotationEntry (LastStmt _ _ _ _) = NoEntryVal
+ getAnnotationEntry (BindStmt an _ _) = fromAnn an
+ getAnnotationEntry (ApplicativeStmt _ _ _) = NoEntryVal
+ getAnnotationEntry (BodyStmt _ _ _ _) = NoEntryVal
+ getAnnotationEntry (LetStmt an _) = fromAnn an
+ getAnnotationEntry (ParStmt _ _ _ _) = NoEntryVal
+ getAnnotationEntry (TransStmt an _ _ _ _ _ _ _ _) = fromAnn an
+ getAnnotationEntry (RecStmt an _ _ _ _ _ _) = fromAnn an
+
+ -----------------------------------------------------------------
+
+ exact (LastStmt _ body _ _) = do
+ debugM $ "LastStmt"
+ markAnnotated body
+
+ exact (BindStmt an pat body) = do
+ debugM $ "BindStmt"
+ markAnnotated pat
+ markApiAnn an AnnLarrow
+ markAnnotated body
+
+ exact (ApplicativeStmt _ _body _) = do
+ debugM $ "ApplicativeStmt"
+ -- TODO: ApplicativeStmt
+ -- markAnnotated body
+ error $ "need to complete ApplicativeStmt"
+
+ exact (BodyStmt _ body _ _) = do
+ debugM $ "BodyStmt"
+ markAnnotated body
+
+ exact (LetStmt an binds) = do
+ debugM $ "LetStmt"
+ markApiAnn an AnnLet
+ markAnnotated binds
+
+ exact (ParStmt _ pbs _ _) = do
+ debugM $ "ParStmt"
+ markAnnotated pbs
+
+ -- markAST l (GHC.ParStmt _ pbs _ _) = do
+ -- -- Within a given parallel list comprehension,one of the sections to be done
+ -- -- in parallel. It is a normal list comprehension, so has a list of
+ -- -- ParStmtBlock, one for each part of the sub- list comprehension
+
+
+ -- ifInContext (Set.singleton Intercalate)
+ -- (
+
+ -- unsetContext Intercalate $
+ -- markListWithContextsFunction
+ -- (LC (Set.singleton Intercalate) -- only
+ -- Set.empty -- first
+ -- Set.empty -- middle
+ -- (Set.singleton Intercalate) -- last
+ -- ) (markAST l) pbs
+ -- )
+ -- (
+ -- unsetContext Intercalate $
+ -- markListWithContextsFunction
+ -- (LC Set.empty -- only
+ -- (Set.fromList [AddVbar]) -- first
+ -- (Set.fromList [AddVbar]) -- middle
+ -- Set.empty -- last
+ -- ) (markAST l) pbs
+ -- )
+ -- markTrailingSemi
+
+
+-- pprStmt (TransStmt { trS_stmts = stmts, trS_by = by
+-- , trS_using = using, trS_form = form })
+-- = sep $ punctuate comma (map ppr stmts ++ [pprTransStmt by using form])
+
+ exact (TransStmt an form stmts _b using by _ _ _) = do
+ debugM $ "TransStmt"
+ markAnnotated stmts
+ exactTransStmt an by using form
+
+ -- markAST _ (GHC.TransStmt _ form stmts _b using by _ _ _) = do
+ -- setContext (Set.singleton Intercalate) $ mapM_ markLocated stmts
+ -- case form of
+ -- GHC.ThenForm -> do
+ -- mark GHC.AnnThen
+ -- unsetContext Intercalate $ markLocated using
+ -- case by of
+ -- Just b -> do
+ -- mark GHC.AnnBy
+ -- unsetContext Intercalate $ markLocated b
+ -- Nothing -> return ()
+ -- GHC.GroupForm -> do
+ -- mark GHC.AnnThen
+ -- mark GHC.AnnGroup
+ -- case by of
+ -- Just b -> mark GHC.AnnBy >> markLocated b
+ -- Nothing -> return ()
+ -- mark GHC.AnnUsing
+ -- markLocated using
+ -- inContext (Set.singleton AddVbar) $ mark GHC.AnnVbar
+ -- inContext (Set.singleton Intercalate) $ mark GHC.AnnComma
+ -- markTrailingSemi
+
+ exact (RecStmt _ _stmts _ _ _ _ _) = do
+ -- TODO: implement RecStmt
+ debugM $ "RecStmt"
+ error $ "need to test RecStmt"
+
+ -- markAST _ (GHC.RecStmt _ stmts _ _ _ _ _) = do
+ -- mark GHC.AnnRec
+ -- markOptional GHC.AnnOpenC
+ -- markInside GHC.AnnSemi
+ -- markListWithLayout stmts
+ -- markOptional GHC.AnnCloseC
+ -- inContext (Set.singleton AddVbar) $ mark GHC.AnnVbar
+ -- inContext (Set.singleton Intercalate) $ mark GHC.AnnComma
+ -- markTrailingSemi
+
+ -- exact x = error $ "exact CmdLStmt for:" ++ showAst x
+ -- exact x = error $ "exact CmdLStmt for:"
+
+
+-- ---------------------------------------------------------------------
+
+instance ExactPrint (ParStmtBlock GhcPs GhcPs) where
+ getAnnotationEntry = const NoEntryVal
+ exact (ParStmtBlock _ stmts _ _) = markAnnotated stmts
+
+exactTransStmt :: ApiAnn -> Maybe (LHsExpr GhcPs) -> (LHsExpr GhcPs) -> TransForm -> EPP ()
+exactTransStmt an by using ThenForm = do
+ debugM $ "exactTransStmt:ThenForm"
+ markApiAnn an AnnThen
+ markAnnotated using
+ case by of
+ Nothing -> return ()
+ Just b -> do
+ markApiAnn an AnnBy
+ markAnnotated b
+exactTransStmt an by using GroupForm = do
+ debugM $ "exactTransStmt:GroupForm"
+ markApiAnn an AnnThen
+ markApiAnn an AnnGroup
+ case by of
+ Just b -> do
+ markApiAnn an AnnBy
+ markAnnotated b
+ Nothing -> return ()
+ markApiAnn an AnnUsing
+ markAnnotated using
+
+-- ---------------------------------------------------------------------
+
+instance ExactPrint (TyClDecl GhcPs) where
+ getAnnotationEntry (FamDecl { }) = NoEntryVal
+ getAnnotationEntry (SynDecl { tcdSExt = an }) = fromAnn an
+ getAnnotationEntry (DataDecl { tcdDExt = an }) = fromAnn an
+ getAnnotationEntry (ClassDecl { tcdCExt = (an, _, _) }) = fromAnn an
+
+ exact (FamDecl _ decl) = do
+ markAnnotated decl
+
+ exact (SynDecl { tcdSExt = an
+ , tcdLName = ltycon, tcdTyVars = tyvars, tcdFixity = fixity
+ , tcdRhs = rhs }) = do
+ -- There may be arbitrary parens around parts of the constructor that are
+ -- infix.
+ -- Turn these into comments so that they feed into the right place automatically
+ -- annotationsToComments [GHC.AnnOpenP,GHC.AnnCloseP]
+ markApiAnn an AnnType
+
+ -- markTyClass Nothing fixity ln tyvars
+ exactVanillaDeclHead an ltycon tyvars fixity Nothing
+ markApiAnn an AnnEqual
+ markAnnotated rhs
+
+ -- ppr (SynDecl { tcdLName = ltycon, tcdTyVars = tyvars, tcdFixity = fixity
+ -- , tcdRhs = rhs })
+ -- = hang (text "type" <+>
+ -- pp_vanilla_decl_head ltycon tyvars fixity Nothing <+> equals)
+ -- 4 (ppr rhs)
+-- {-
+-- SynDecl { tcdSExt :: XSynDecl pass -- ^ Post renameer, FVs
+-- , tcdLName :: Located (IdP pass) -- ^ Type constructor
+-- , tcdTyVars :: LHsQTyVars pass -- ^ Type variables; for an
+-- -- associated type these
+-- -- include outer binders
+-- , tcdFixity :: LexicalFixity -- ^ Fixity used in the declaration
+-- , tcdRhs :: LHsType pass } -- ^ RHS of type declaration
+
+-- -}
+-- markAST _ (GHC.SynDecl _ ln (GHC.HsQTvs _ tyvars) fixity typ) = do
+-- -- There may be arbitrary parens around parts of the constructor that are
+-- -- infix.
+-- -- Turn these into comments so that they feed into the right place automatically
+-- -- annotationsToComments [GHC.AnnOpenP,GHC.AnnCloseP]
+-- mark GHC.AnnType
+
+-- markTyClass Nothing fixity ln tyvars
+-- mark GHC.AnnEqual
+-- markLocated typ
+-- markTrailingSemi
+
+ exact (DataDecl { tcdDExt = an, tcdLName = ltycon, tcdTyVars = tyvars
+ , tcdFixity = fixity, tcdDataDefn = defn }) =
+ exactDataDefn an (exactVanillaDeclHead an ltycon tyvars fixity) defn
+
+ -- -----------------------------------
+
+ exact (ClassDecl {tcdCExt = (an, sortKey, _),
+ tcdCtxt = context, tcdLName = lclas, tcdTyVars = tyvars,
+ tcdFixity = fixity,
+ tcdFDs = fds,
+ tcdSigs = sigs, tcdMeths = methods,
+ tcdATs = ats, tcdATDefs = at_defs,
+ tcdDocs = _docs})
+ -- TODO: add a test that demonstrates tcdDocs
+ | null sigs && isEmptyBag methods && null ats && null at_defs -- No "where" part
+ = top_matter
+
+ | otherwise -- Laid out
+ = do
+ top_matter
+ -- markApiAnn an AnnWhere
+ markApiAnn an AnnOpenC
+ withSortKey sortKey
+ (prepareListAnnotationA sigs
+ ++ prepareListAnnotationA (bagToList methods)
+ ++ prepareListAnnotationA ats
+ ++ prepareListAnnotationA at_defs
+ -- ++ prepareListAnnotation docs
+ )
+ markApiAnn an AnnCloseC
+ where
+ top_matter = do
+ annotationsToComments (apiAnnAnns an) [AnnOpenP, AnnCloseP]
+ markApiAnn an AnnClass
+ exactVanillaDeclHead an lclas tyvars fixity context
+ unless (null fds) $ do
+ markApiAnn an AnnVbar
+ markAnnotated fds
+ markApiAnn an AnnWhere
+
+-- -- -----------------------------------
+
+-- markAST _ (GHC.ClassDecl _ ctx ln (GHC.HsQTvs _ tyVars) fixity fds
+-- sigs meths ats atdefs docs) = do
+-- mark GHC.AnnClass
+-- markLocated ctx
+
+-- markTyClass Nothing fixity ln tyVars
+
+-- unless (null fds) $ do
+-- mark GHC.AnnVbar
+-- markListIntercalateWithFunLevel markLocated 2 fds
+-- mark GHC.AnnWhere
+-- markOptional GHC.AnnOpenC -- '{'
+-- markInside GHC.AnnSemi
+-- -- AZ:TODO: we end up with both the tyVars and the following body of the
+-- -- class defn in annSortKey for the class. This could cause problems when
+-- -- changing things.
+-- setContext (Set.singleton InClassDecl) $
+-- applyListAnnotationsLayout
+-- (prepareListAnnotation sigs
+-- ++ prepareListAnnotation (GHC.bagToList meths)
+-- ++ prepareListAnnotation ats
+-- ++ prepareListAnnotation atdefs
+-- ++ prepareListAnnotation docs
+-- )
+-- markOptional GHC.AnnCloseC -- '}'
+-- markTrailingSemi
+-- {-
+-- | ClassDecl { tcdCExt :: XClassDecl pass, -- ^ Post renamer, FVs
+-- tcdCtxt :: LHsContext pass, -- ^ Context...
+-- tcdLName :: Located (IdP pass), -- ^ Name of the class
+-- tcdTyVars :: LHsQTyVars pass, -- ^ Class type variables
+-- tcdFixity :: LexicalFixity, -- ^ Fixity used in the declaration
+-- tcdFDs :: [Located (FunDep (Located (IdP pass)))],
+-- -- ^ Functional deps
+-- tcdSigs :: [LSig pass], -- ^ Methods' signatures
+-- tcdMeths :: LHsBinds pass, -- ^ Default methods
+-- tcdATs :: [LFamilyDecl pass], -- ^ Associated types;
+-- tcdATDefs :: [LTyFamDefltEqn pass],
+-- -- ^ Associated type defaults
+-- tcdDocs :: [LDocDecl] -- ^ Haddock docs
+-- }
+
+-- -}
+
+-- markAST _ (GHC.SynDecl _ _ (GHC.XLHsQTyVars _) _ _)
+-- = error "extension hit for TyClDecl"
+-- markAST _ (GHC.DataDecl _ _ (GHC.HsQTvs _ _) _ (GHC.XHsDataDefn _))
+-- = error "extension hit for TyClDecl"
+-- markAST _ (GHC.DataDecl _ _ (GHC.XLHsQTyVars _) _ _)
+-- = error "extension hit for TyClDecl"
+-- markAST _ (GHC.ClassDecl _ _ _ (GHC.XLHsQTyVars _) _ _ _ _ _ _ _)
+-- = error "extension hit for TyClDecl"
+-- markAST _ (GHC.XTyClDecl _)
+-- = error "extension hit for TyClDecl"
+ -- exact x = error $ "exact TyClDecl for:" ++ showAst x
+
+-- ---------------------------------------------------------------------
+
+instance ExactPrint (FunDep GhcPs) where
+ getAnnotationEntry (FunDep an _ _) = fromAnn an
+
+ exact (FunDep an ls rs') = do
+ markAnnotated ls
+ markApiAnn an AnnRarrow
+ markAnnotated rs'
+
+-- ---------------------------------------------------------------------
+
+instance ExactPrint (FamilyDecl GhcPs) where
+ getAnnotationEntry (FamilyDecl { fdExt = an }) = fromAnn an
+
+ exact (FamilyDecl { fdExt = an
+ , fdInfo = info
+ , fdTopLevel = top_level
+ , fdLName = ltycon
+ , fdTyVars = tyvars
+ , fdFixity = fixity
+ , fdResultSig = L _ result
+ , fdInjectivityAnn = mb_inj }) = do
+ -- = vcat [ pprFlavour info <+> pp_top_level <+>
+ -- pp_vanilla_decl_head ltycon tyvars fixity Nothing <+>
+ -- pp_kind <+> pp_inj <+> pp_where
+ -- , nest 2 $ pp_eqns ]
+ exactFlavour an info
+ exact_top_level
+ exactVanillaDeclHead an ltycon tyvars fixity Nothing
+ exact_kind
+ mapM_ markAnnotated mb_inj
+ case info of
+ ClosedTypeFamily mb_eqns -> do
+ markApiAnn an AnnWhere
+ markApiAnn an AnnOpenC
+ case mb_eqns of
+ Nothing -> printStringAdvance ".."
+ Just eqns -> markAnnotated eqns
+ markApiAnn an AnnCloseC
+ _ -> return ()
+ where
+ exact_top_level = case top_level of
+ TopLevel -> markApiAnn an AnnFamily
+ NotTopLevel -> return ()
+
+ exact_kind = case result of
+ NoSig _ -> return ()
+ KindSig _ kind -> markApiAnn an AnnDcolon >> markAnnotated kind
+ TyVarSig _ tv_bndr -> markApiAnn an AnnEqual >> markAnnotated tv_bndr
+
+ -- exact_inj = case mb_inj of
+ -- Just (L _ (InjectivityAnn _ lhs rhs)) ->
+ -- hsep [ vbar, ppr lhs, text "->", hsep (map ppr rhs) ]
+ -- Nothing -> empty
+ -- (pp_where, pp_eqns) = case info of
+ -- ClosedTypeFamily mb_eqns ->
+ -- ( text "where"
+ -- , case mb_eqns of
+ -- Nothing -> text ".."
+ -- Just eqns -> vcat $ map (ppr_fam_inst_eqn . unLoc) eqns )
+ -- _ -> (empty, empty)
+
+exactFlavour :: ApiAnn -> FamilyInfo GhcPs -> EPP ()
+exactFlavour an DataFamily = markApiAnn an AnnData
+exactFlavour an OpenTypeFamily = markApiAnn an AnnType
+exactFlavour an (ClosedTypeFamily {}) = markApiAnn an AnnType
+
+-- instance Outputable (FamilyInfo pass) where
+-- ppr info = pprFlavour info <+> text "family"
+
+-- ---------------------------------------------------------------------
+
+exactDataDefn :: ApiAnn
+ -> (Maybe (LHsContext GhcPs) -> EPP ()) -- Printing the header
+ -> HsDataDefn GhcPs
+ -> EPP ()
+exactDataDefn an exactHdr
+ (HsDataDefn { dd_ext = an2
+ , dd_ND = new_or_data, dd_ctxt = context
+ , dd_cType = mb_ct
+ , dd_kindSig = mb_sig
+ , dd_cons = condecls, dd_derivs = derivings }) = do
+ if new_or_data == DataType
+ then markApiAnn an2 AnnData
+ else markApiAnn an2 AnnNewtype
+ mapM_ markAnnotated mb_ct
+ exactHdr context
+ case mb_sig of
+ Nothing -> return ()
+ Just kind -> do
+ markApiAnn an AnnDcolon
+ markAnnotated kind
+ when (isGadt condecls) $ markApiAnn an AnnWhere
+ exact_condecls an2 condecls
+ mapM_ markAnnotated derivings
+ return ()
+
+exactVanillaDeclHead :: ApiAnn
+ -> LocatedN RdrName
+ -> LHsQTyVars GhcPs
+ -> LexicalFixity
+ -> Maybe (LHsContext GhcPs)
+ -> EPP ()
+exactVanillaDeclHead an thing (HsQTvs { hsq_explicit = tyvars }) fixity context = do
+ let
+ exact_tyvars :: [LHsTyVarBndr () GhcPs] -> EPP ()
+ exact_tyvars (varl:varsr)
+ | fixity == Infix && length varsr > 1 = do
+ -- = hsep [char '(',ppr (unLoc varl), pprInfixOcc (unLoc thing)
+ -- , (ppr.unLoc) (head varsr), char ')'
+ -- , hsep (map (ppr.unLoc) (tail vaprsr))]
+ markApiAnnAll an id AnnOpenP
+ markAnnotated varl
+ markAnnotated thing
+ markAnnotated (head varsr)
+ markApiAnnAll an id AnnCloseP
+ markAnnotated (tail varsr)
+ return ()
+ | fixity == Infix = do
+ -- = hsep [ppr (unLoc varl), pprInfixOcc (unLoc thing)
+ -- , hsep (map (ppr.unLoc) varsr)]
+ markAnnotated varl
+ markAnnotated thing
+ markAnnotated varsr
+ return ()
+ | otherwise = do
+ -- hsep [ pprPrefixOcc (unLoc thing)
+ -- , hsep (map (ppr.unLoc) (varl:varsr))]
+ markAnnotated thing
+ mapM_ markAnnotated (varl:varsr)
+ return ()
+ exact_tyvars [] = do
+ -- pprPrefixOcc (unLoc thing)
+ markAnnotated thing
+ mapM_ markAnnotated context
+ exact_tyvars tyvars
+
+-- ---------------------------------------------------------------------
+
+instance ExactPrint (InjectivityAnn GhcPs) where
+ getAnnotationEntry (InjectivityAnn an _ _) = fromAnn an
+ exact (InjectivityAnn an lhs rhs) = do
+ markApiAnn an AnnVbar
+ markAnnotated lhs
+ markApiAnn an AnnRarrow
+ mapM_ markAnnotated rhs
+ -- Just (L _ (InjectivityAnn _ lhs rhs)) ->
+ -- hsep [ vbar, ppr lhs, text "->", hsep (map ppr rhs) ]
+ -- Nothing -> empty
+
+-- ---------------------------------------------------------------------
+
+-- instance ExactPrint (HsTyVarBndr () GhcPs) where
+-- getAnnotationEntry (UserTyVar an _ _) = fromAnn an
+-- getAnnotationEntry (KindedTyVar an _ _ _) = fromAnn an
+-- exact = withPpr
+
+instance (Typeable flag) => ExactPrint (HsTyVarBndr flag GhcPs) where
+ getAnnotationEntry (UserTyVar an _ _) = fromAnn an
+ getAnnotationEntry (KindedTyVar an _ _ _) = fromAnn an
+
+ exact (UserTyVar an _ n) = do
+ markApiAnnAll an id AnnOpenP
+ markAnnotated n
+ markApiAnnAll an id AnnCloseP
+ exact (KindedTyVar an _ n k) = do
+ markApiAnnAll an id AnnOpenP
+ markAnnotated n
+ markApiAnn an AnnDcolon
+ markAnnotated k
+ markApiAnnAll an id AnnCloseP
+
+-- ---------------------------------------------------------------------
+
+-- NOTE: this is also an alias for LHsKind
+-- instance ExactPrint (LHsType GhcPs) where
+-- getAnnotationEntry = entryFromLocatedA
+-- exact (L _ a) = markAnnotated a
+
+instance ExactPrint (HsType GhcPs) where
+ getAnnotationEntry (HsForAllTy _ _ _) = NoEntryVal
+ getAnnotationEntry (HsQualTy _ _ _) = NoEntryVal
+ getAnnotationEntry (HsTyVar an _ _) = fromAnn an
+ getAnnotationEntry (HsAppTy _ _ _) = NoEntryVal
+ getAnnotationEntry (HsAppKindTy _ _ _) = NoEntryVal
+ getAnnotationEntry (HsFunTy an _ _ _) = fromAnn an
+ getAnnotationEntry (HsListTy an _) = fromAnn an
+ getAnnotationEntry (HsTupleTy an _ _) = fromAnn an
+ getAnnotationEntry (HsSumTy an _) = fromAnn an
+ getAnnotationEntry (HsOpTy _ _ _ _) = NoEntryVal
+ getAnnotationEntry (HsParTy an _) = fromAnn an
+ getAnnotationEntry (HsIParamTy an _ _) = fromAnn an
+ getAnnotationEntry (HsStarTy _ _) = NoEntryVal
+ getAnnotationEntry (HsKindSig an _ _) = fromAnn an
+ getAnnotationEntry (HsSpliceTy _ _) = NoEntryVal
+ getAnnotationEntry (HsDocTy an _ _) = fromAnn an
+ getAnnotationEntry (HsBangTy an _ _) = fromAnn an
+ getAnnotationEntry (HsRecTy an _) = fromAnn an
+ getAnnotationEntry (HsExplicitListTy an _ _) = fromAnn an
+ getAnnotationEntry (HsExplicitTupleTy an _) = fromAnn an
+ getAnnotationEntry (HsTyLit _ _) = NoEntryVal
+ getAnnotationEntry (HsWildCardTy _) = NoEntryVal
+ getAnnotationEntry (XHsType _) = NoEntryVal
+
+
+ exact (HsForAllTy { hst_xforall = _an
+ , hst_tele = tele, hst_body = ty }) = do
+ markAnnotated tele
+ markAnnotated ty
+
+ exact (HsQualTy _ ctxt ty) = do
+ markAnnotated ctxt
+ -- markApiAnn an AnnDarrow
+ markAnnotated ty
+ exact (HsTyVar an promoted name) = do
+ when (promoted == IsPromoted) $ markApiAnn an AnnSimpleQuote
+ markAnnotated name
+
+ exact (HsAppTy _ t1 t2) = markAnnotated t1 >> markAnnotated t2
+ exact (HsAppKindTy ss ty ki) = do
+ markAnnotated ty
+ printStringAtSs ss "@"
+ markAnnotated ki
+ exact (HsFunTy an mult ty1 ty2) = do
+ markAnnotated ty1
+ markArrow an mult
+ markAnnotated ty2
+ exact (HsListTy an tys) = do
+ markOpeningParen an
+ markAnnotated tys
+ markClosingParen an
+ exact (HsTupleTy an _con tys) = do
+ markOpeningParen an
+ markAnnotated tys
+ markClosingParen an
+ exact (HsSumTy an tys) = do
+ markOpeningParen an
+ markAnnotated tys
+ markClosingParen an
+ exact (HsOpTy _an t1 lo t2) = do
+ markAnnotated t1
+ markAnnotated lo
+ markAnnotated t2
+ exact (HsParTy an ty) = do
+ markOpeningParen an
+ markAnnotated ty
+ markClosingParen an
+ exact (HsIParamTy an n t) = do
+ markAnnotated n
+ markApiAnn an AnnDcolon
+ markAnnotated t
+ exact (HsStarTy _an isUnicode)
+ = if isUnicode
+ then printStringAdvance "\x2605" -- Unicode star
+ else printStringAdvance "*"
+ exact (HsKindSig an ty k) = do
+ exact ty
+ markApiAnn an AnnDcolon
+ exact k
+ exact (HsSpliceTy _ splice) = do
+ markAnnotated splice
+ -- exact x@(HsDocTy an _ _) = withPpr x
+ exact (HsBangTy an (HsSrcBang mt _up str) ty) = do
+ case mt of
+ NoSourceText -> return ()
+ SourceText src -> do
+ debugM $ "HsBangTy: src=" ++ showAst src
+ markLocatedAALS an id AnnOpen (Just src)
+ markLocatedAALS an id AnnClose (Just "#-}")
+ debugM $ "HsBangTy: done unpackedness"
+ case str of
+ SrcLazy -> markApiAnn an AnnTilde
+ SrcStrict -> markApiAnn an AnnBang
+ NoSrcStrict -> return ()
+ markAnnotated ty
+ -- exact x@(HsRecTy an _) = withPpr x
+ exact (HsExplicitListTy an prom tys) = do
+ when (isPromoted prom) $ markApiAnn an AnnSimpleQuote
+ markApiAnn an AnnOpenS
+ markAnnotated tys
+ markApiAnn an AnnCloseS
+ exact (HsExplicitTupleTy an tys) = do
+ markApiAnn an AnnSimpleQuote
+ markApiAnn an AnnOpenP
+ markAnnotated tys
+ markApiAnn an AnnCloseP
+ exact (HsTyLit _ lit) = do
+ case lit of
+ (HsNumTy src v) -> printSourceText src (show v)
+ (HsStrTy src v) -> printSourceText src (show v)
+ (HsCharTy src v) -> printSourceText src (show v)
+ exact (HsWildCardTy _) = printStringAdvance "_"
+ exact x = error $ "missing match for HsType:" ++ showAst x
+
+-- ---------------------------------------------------------------------
+
+instance ExactPrint (HsForAllTelescope GhcPs) where
+ getAnnotationEntry (HsForAllVis an _) = fromAnn an
+ getAnnotationEntry (HsForAllInvis an _) = fromAnn an
+
+ exact (HsForAllVis an bndrs) = do
+ markLocatedAA an fst -- AnnForall
+ markAnnotated bndrs
+ markLocatedAA an snd -- AnnRarrow
+
+ exact (HsForAllInvis an bndrs) = do
+ markLocatedAA an fst -- AnnForall
+ markAnnotated bndrs
+ markLocatedAA an snd -- AnnDot
+
+-- ---------------------------------------------------------------------
+
+instance ExactPrint (HsDerivingClause GhcPs) where
+ getAnnotationEntry d@(HsDerivingClause{}) = fromAnn (deriv_clause_ext d)
+
+ exact (HsDerivingClause { deriv_clause_ext = an
+ , deriv_clause_strategy = dcs
+ , deriv_clause_tys = dct }) = do
+ -- = hsep [ text "deriving"
+ -- , pp_strat_before
+ -- , pp_dct dct
+ -- , pp_strat_after ]
+ markApiAnn an AnnDeriving
+ exact_strat_before
+ markAnnotated dct
+ exact_strat_after
+ where
+ -- -- This complexity is to distinguish between
+ -- -- deriving Show
+ -- -- deriving (Show)
+ -- pp_dct [HsIB { hsib_body = ty }]
+ -- = ppr (parenthesizeHsType appPrec ty)
+ -- pp_dct _ = parens (interpp'SP dct)
+
+ -- @via@ is unique in that in comes /after/ the class being derived,
+ -- so we must special-case it.
+ (exact_strat_before, exact_strat_after) =
+ case dcs of
+ Just v@(L _ ViaStrategy{}) -> (pure (), markAnnotated v)
+ _ -> (mapM_ markAnnotated dcs, pure ())
+
+-- ---------------------------------------------------------------------
+
+instance ExactPrint (DerivStrategy GhcPs) where
+ getAnnotationEntry (StockStrategy an) = fromAnn an
+ getAnnotationEntry (AnyclassStrategy an) = fromAnn an
+ getAnnotationEntry (NewtypeStrategy an) = fromAnn an
+ getAnnotationEntry (ViaStrategy (XViaStrategyPs an _)) = fromAnn an
+
+ exact (StockStrategy an) = markApiAnn an AnnStock
+ exact (AnyclassStrategy an) = markApiAnn an AnnAnyclass
+ exact (NewtypeStrategy an) = markApiAnn an AnnNewtype
+ exact (ViaStrategy (XViaStrategyPs an ty))
+ = markApiAnn an AnnVia >> markAnnotated ty
+
+-- ---------------------------------------------------------------------
+
+instance (ExactPrint a) => ExactPrint (LocatedC a) where
+ getAnnotationEntry (L sann _) = fromAnn sann
+
+ exact (L (SrcSpanAnn ApiAnnNotUsed _) a) = markAnnotated a
+ exact (L (SrcSpanAnn (ApiAnn _ (AnnContext ma opens closes) _) _) a) = do
+ -- case ma of
+ -- Just (UnicodeSyntax, rs) -> markKw' AnnDarrowU rs
+ -- Just (NormalSyntax, rs) -> markKw' AnnDarrow rs
+ -- Nothing -> pure ()
+ mapM_ (markKwA AnnOpenP) (sort opens)
+ markAnnotated a
+ mapM_ (markKwA AnnCloseP) (sort closes)
+ case ma of
+ Just (UnicodeSyntax, r) -> markKwA AnnDarrowU r
+ Just (NormalSyntax, r) -> markKwA AnnDarrow r
+ Nothing -> pure ()
+
+-- ---------------------------------------------------------------------
+
+instance ExactPrint (DerivClauseTys GhcPs) where
+ getAnnotationEntry = const NoEntryVal
+
+ exact (DctSingle _ ty) = markAnnotated ty
+ exact (DctMulti _ tys) = do
+ -- parens (interpp'SP tys)
+ markAnnotated tys
+
+-- ---------------------------------------------------------------------
+
+instance ExactPrint (HsSigType GhcPs) where
+ getAnnotationEntry = const NoEntryVal
+
+ exact (HsSig _ bndrs ty) = do
+ markAnnotated bndrs
+ markAnnotated ty
+
+-- ---------------------------------------------------------------------
+
+instance ExactPrint (LocatedN RdrName) where
+ getAnnotationEntry (L sann _) = fromAnn sann
+
+ exact (L (SrcSpanAnn ApiAnnNotUsed l) n) = do
+ p <- getPosP
+ debugM $ "LocatedN RdrName:NOANN: (p,l,str)=" ++ show (p,ss2range l, showPprUnsafe n)
+ printStringAtSs l (showPprUnsafe n)
+ exact (L (SrcSpanAnn (ApiAnn _anchor ann _cs) _ll) n) = do
+ case ann of
+ NameAnn a o l c t -> do
+ markName a o (Just (l,n)) c
+ markTrailing t
+ NameAnnCommas a o cs c t -> do
+ let (kwo,kwc) = adornments a
+ markKw (AddApiAnn kwo o)
+ forM_ cs (\loc -> markKw (AddApiAnn AnnComma loc))
+ markKw (AddApiAnn kwc c)
+ markTrailing t
+ NameAnnOnly a o c t -> do
+ markName a o Nothing c
+ markTrailing t
+ NameAnnRArrow nl t -> do
+ markKw (AddApiAnn AnnRarrow nl)
+ markTrailing t
+ NameAnnQuote q name t -> do
+ debugM $ "NameAnnQuote"
+ markKw (AddApiAnn AnnSimpleQuote q)
+ markAnnotated (L name n)
+ markTrailing t
+ NameAnnTrailing t -> do
+ printStringAdvance (showPprUnsafe n)
+ markTrailing t
+
+markName :: NameAdornment
+ -> AnnAnchor -> Maybe (AnnAnchor,RdrName) -> AnnAnchor -> EPP ()
+markName adorn open mname close = do
+ let (kwo,kwc) = adornments adorn
+ markKw (AddApiAnn kwo open)
+ case mname of
+ Nothing -> return ()
+ Just (name, a) -> printStringAtAA name (showPprUnsafe a)
+ markKw (AddApiAnn kwc close)
+
+adornments :: NameAdornment -> (AnnKeywordId, AnnKeywordId)
+adornments NameParens = (AnnOpenP, AnnCloseP)
+adornments NameParensHash = (AnnOpenPH, AnnClosePH)
+adornments NameBackquotes = (AnnBackquote, AnnBackquote)
+adornments NameSquare = (AnnOpenS, AnnCloseS)
+
+markTrailing :: [TrailingAnn] -> EPP ()
+markTrailing ts = do
+ p <- getPosP
+ debugM $ "markTrailing:" ++ showPprUnsafe (p,ts)
+ mapM_ markKwT (sort ts)
+
+-- ---------------------------------------------------------------------
+
+-- based on pp_condecls in Decls.hs
+exact_condecls :: ApiAnn -> [LConDecl GhcPs] -> EPP ()
+exact_condecls an cs
+ | gadt_syntax -- In GADT syntax
+ -- = hang (text "where") 2 (vcat (map ppr cs))
+ = do
+ -- printStringAdvance "exact_condecls:gadt"
+ mapM_ markAnnotated cs
+ | otherwise -- In H98 syntax
+ -- = equals <+> sep (punctuate (text " |") (map ppr cs))
+ = do
+ -- printStringAdvance "exact_condecls:not gadt"
+ markApiAnn an AnnEqual
+ mapM_ markAnnotated cs
+ where
+ gadt_syntax = case cs of
+ [] -> False
+ (L _ ConDeclH98{} : _) -> False
+ (L _ ConDeclGADT{} : _) -> True
+
+-- ---------------------------------------------------------------------
+
+instance ExactPrint (ConDecl GhcPs) where
+ getAnnotationEntry x@(ConDeclGADT{}) = fromAnn (con_g_ext x)
+ getAnnotationEntry x@(ConDeclH98{}) = fromAnn (con_ext x)
+
+-- based on pprConDecl
+ exact (ConDeclH98 { con_ext = an
+ , con_name = con
+ , con_forall = has_forall
+ , con_ex_tvs = ex_tvs
+ , con_mb_cxt = mcxt
+ , con_args = args
+ , con_doc = doc }) = do
+ -- = sep [ ppr_mbDoc doc
+ -- , pprHsForAll (mkHsForAllInvisTele ex_tvs) mcxt
+ -- , ppr_details args ]
+ mapM_ markAnnotated doc
+ when has_forall $ markApiAnn an AnnForall
+ mapM_ markAnnotated ex_tvs
+ when has_forall $ markApiAnn an AnnDot
+ -- exactHsForall (mkHsForAllInvisTele ex_tvs) mcxt
+ mapM_ markAnnotated mcxt
+ when (isJust mcxt) $ markApiAnn an AnnDarrow
+
+ exact_details args
+
+ -- case args of
+ -- InfixCon _ _ -> return ()
+ -- _ -> markAnnotated con
+ where
+ -- -- In ppr_details: let's not print the multiplicities (they are always 1, by
+ -- -- definition) as they do not appear in an actual declaration.
+ exact_details (InfixCon t1 t2) = do
+ markAnnotated t1
+ markAnnotated con
+ markAnnotated t2
+ exact_details (PrefixCon tyargs tys) = do
+ markAnnotated con
+ markAnnotated tyargs
+ markAnnotated tys
+ exact_details (RecCon fields) = do
+ markAnnotated con
+ markAnnotated fields
+
+ -- -----------------------------------
+
+ exact (ConDeclGADT { con_g_ext = an
+ , con_names = cons
+ , con_bndrs = bndrs
+ , con_mb_cxt = mcxt, con_g_args = args
+ , con_res_ty = res_ty, con_doc = doc }) = do
+ mapM_ markAnnotated doc
+ mapM_ markAnnotated cons
+ markApiAnn an AnnDcolon
+ annotationsToComments (apiAnnAnns an) [AnnOpenP, AnnCloseP]
+ -- when has_forall $ markApiAnn an AnnForall
+ markAnnotated bndrs
+ -- mapM_ markAnnotated qvars
+ -- when has_forall $ markApiAnn an AnnDot
+ mapM_ markAnnotated mcxt
+ when (isJust mcxt) $ markApiAnn an AnnDarrow
+ -- mapM_ markAnnotated args
+ case args of
+ (PrefixConGADT args') -> mapM_ markAnnotated args'
+ (RecConGADT fields) -> markAnnotated fields
+ -- mapM_ markAnnotated (unLoc fields)
+ markAnnotated res_ty
+ -- markAST _ (GHC.ConDeclGADT _ lns (GHC.L l forall) qvars mbCxt args typ _) = do
+ -- setContext (Set.singleton PrefixOp) $ markListIntercalate lns
+ -- mark GHC.AnnDcolon
+ -- annotationsToComments [GHC.AnnOpenP]
+ -- markLocated (GHC.L l (ResTyGADTHook forall qvars))
+ -- markMaybe mbCxt
+ -- markHsConDeclDetails False True lns args
+ -- markLocated typ
+ -- markManyOptional GHC.AnnCloseP
+ -- markTrailingSemi
+
+-- pprConDecl (ConDeclGADT { con_names = cons, con_qvars = qvars
+-- , con_mb_cxt = mcxt, con_args = args
+-- , con_res_ty = res_ty, con_doc = doc })
+-- = ppr_mbDoc doc <+> ppr_con_names cons <+> dcolon
+-- <+> (sep [pprHsForAll (mkHsForAllInvisTele qvars) mcxt,
+-- ppr_arrow_chain (get_args args ++ [ppr res_ty]) ])
+-- where
+-- get_args (PrefixCon args) = map ppr args
+-- get_args (RecCon fields) = [pprConDeclFields (unLoc fields)]
+-- get_args (InfixCon {}) = pprPanic "pprConDecl:GADT" (ppr_con_names cons)
+
+-- ppr_arrow_chain (a:as) = sep (a : map (arrow <+>) as)
+-- ppr_arrow_chain [] = empty
+
+-- ppr_con_names :: (OutputableBndr a) => [GenLocated l a] -> SDoc
+-- ppr_con_names = pprWithCommas (pprPrefixOcc . unLoc)
+
+
+-- ---------------------------------------------------------------------
+
+-- exactHsForall :: HsForAllTelescope GhcPs
+-- -> Maybe (LHsContext GhcPs) -> EPP ()
+-- exactHsForall = exactHsForAllExtra False
+
+-- exactHsForAllExtra :: Bool
+-- -> HsForAllTelescope GhcPs
+-- -> Maybe (LHsContext GhcPs) -> EPP ()
+-- exactHsForAllExtra show_extra Nothing = return ()
+-- exactHsForAllExtra show_extra lctxt@(Just ctxt)
+-- | not show_extra = markAnnotated ctxt
+-- -- | null ctxt = char '_' <+> darrow
+-- | null ctxt = return ()
+-- | otherwise = parens (sep (punctuate comma ctxt')) <+> darrow
+-- where
+-- ctxt' = map ppr ctxt ++ [char '_']
+
+-- ---------------------------------------------------------------------
+
+instance ExactPrint Void where
+ getAnnotationEntry = const NoEntryVal
+ exact _ = return ()
+
+-- ---------------------------------------------------------------------
+
+instance (Typeable flag) => ExactPrint (HsOuterTyVarBndrs flag GhcPs) where
+ getAnnotationEntry (HsOuterImplicit _) = NoEntryVal
+ getAnnotationEntry (HsOuterExplicit an _) = fromAnn an
+
+ exact (HsOuterImplicit _) = pure ()
+ exact (HsOuterExplicit an bndrs) = do
+ markLocatedAA an fst -- "forall"
+ markAnnotated bndrs
+ markLocatedAA an snd -- "."
+
+-- ---------------------------------------------------------------------
+
+instance ExactPrint (ConDeclField GhcPs) where
+ getAnnotationEntry f@(ConDeclField{}) = fromAnn (cd_fld_ext f)
+
+ exact (ConDeclField an names ftype mdoc) = do
+ markAnnotated names
+ markApiAnn an AnnDcolon
+ markAnnotated ftype
+ mapM_ markAnnotated mdoc
+
+-- ---------------------------------------------------------------------
+
+instance ExactPrint (FieldOcc GhcPs) where
+ getAnnotationEntry = const NoEntryVal
+ exact (FieldOcc _ n) = markAnnotated n
+
+-- ---------------------------------------------------------------------
+
+instance ExactPrint (AmbiguousFieldOcc GhcPs) where
+ getAnnotationEntry = const NoEntryVal
+ exact (Unambiguous _ n) = markAnnotated n
+ exact (Ambiguous _ n) = markAnnotated n
+
+-- ---------------------------------------------------------------------
+
+instance (ExactPrint a) => ExactPrint (HsScaled GhcPs a) where
+ getAnnotationEntry = const NoEntryVal
+ exact (HsScaled _arr t) = markAnnotated t
+
+-- ---------------------------------------------------------------------
+
+-- instance ExactPrint (LHsContext GhcPs) where
+-- getAnnotationEntry (L (SrcSpanAnn ann _) _) = fromAnn ann
+-- exact = withPpr
+
+-- ---------------------------------------------------------------------
+
+instance ExactPrint (LocatedP CType) where
+ getAnnotationEntry = entryFromLocatedA
+
+ exact (L (SrcSpanAnn ApiAnnNotUsed _) ct) = withPpr ct
+ exact (L (SrcSpanAnn an _ll)
+ (CType stp mh (stct,ct))) = do
+ markAnnOpenP an stp "{-# CTYPE"
+ case mh of
+ Nothing -> return ()
+ Just (Header srcH _h) ->
+ markLocatedAALS an apr_rest AnnHeader (Just (toSourceTextWithSuffix srcH "" ""))
+ markLocatedAALS an apr_rest AnnVal (Just (toSourceTextWithSuffix stct (unpackFS ct) ""))
+ markAnnCloseP an
+
+-- instance Annotate GHC.CType where
+-- markAST _ (GHC.CType src mh f) = do
+-- -- markWithString GHC.AnnOpen src
+-- markAnnOpen src ""
+-- case mh of
+-- Nothing -> return ()
+-- Just (GHC.Header srcH _h) ->
+-- -- markWithString GHC.AnnHeader srcH
+-- markWithString GHC.AnnHeader (toSourceTextWithSuffix srcH "" "")
+-- -- markWithString GHC.AnnVal (fst f)
+-- markSourceText (fst f) (GHC.unpackFS $ snd f)
+-- markWithString GHC.AnnClose "#-}"
+
+-- ---------------------------------------------------------------------
+
+instance ExactPrint (SourceText, RuleName) where
+ -- We end up at the right place from the Located wrapper
+ getAnnotationEntry = const NoEntryVal
+
+ exact (st, rn)
+ = printStringAdvance (toSourceTextWithSuffix st (unpackFS rn) "")
+
+
+-- =====================================================================
+-- LocatedL instances start --
+--
+-- Each is dealt with specifically, as they have
+-- different wrapping annotations in the al_rest zone.
+--
+-- In future, the annotation could perhaps be improved, with an
+-- 'al_pre' and 'al_post' set of annotations to be simply sorted and
+-- applied.
+-- ---------------------------------------------------------------------
+
+-- instance (ExactPrint body) => ExactPrint (LocatedL body) where
+-- getAnnotationEntry = entryFromLocatedA
+-- exact (L (SrcSpanAnn an _) b) = do
+-- markLocatedMAA an al_open
+-- markApiAnnAll an al_rest AnnSemi
+-- markAnnotated b
+-- markLocatedMAA an al_close
+
+instance ExactPrint (LocatedL [LocatedA (IE GhcPs)]) where
+ getAnnotationEntry = entryFromLocatedA
+
+ exact (L (SrcSpanAnn ann _) ies) = do
+ debugM $ "LocatedL [LIE"
+ markLocatedAAL ann al_rest AnnHiding
+ p <- getPosP
+ debugM $ "LocatedL [LIE:p=" ++ showPprUnsafe p
+ markAnnList ann (markAnnotated ies)
+
+-- AZ:TODO: combine with next instance
+instance ExactPrint (LocatedL [LocatedA (Match GhcPs (LocatedA (HsExpr GhcPs)))]) where
+ getAnnotationEntry = entryFromLocatedA
+ exact (L la a) = do
+ debugM $ "LocatedL [LMatch"
+ -- TODO: markAnnList?
+ markApiAnnAll (ann la) al_rest AnnWhere
+ markLocatedMAA (ann la) al_open
+ markApiAnnAll (ann la) al_rest AnnSemi
+ markAnnotated a
+ markLocatedMAA (ann la) al_close
+
+instance ExactPrint (LocatedL [LocatedA (Match GhcPs (LocatedA (HsCmd GhcPs)))]) where
+ getAnnotationEntry = entryFromLocatedA
+ exact (L la a) = do
+ debugM $ "LocatedL [LMatch"
+ -- TODO: markAnnList?
+ markApiAnnAll (ann la) al_rest AnnWhere
+ markLocatedMAA (ann la) al_open
+ markApiAnnAll (ann la) al_rest AnnSemi
+ markAnnotated a
+ markLocatedMAA (ann la) al_close
+
+-- instance ExactPrint (LocatedL [ExprLStmt GhcPs]) where
+instance ExactPrint (LocatedL [LocatedA (StmtLR GhcPs GhcPs (LocatedA (HsExpr GhcPs)))]) where
+ getAnnotationEntry = entryFromLocatedA
+ exact (L (SrcSpanAnn an _) stmts) = do
+ debugM $ "LocatedL [ExprLStmt"
+ markAnnList an $ do
+ -- markLocatedMAA an al_open
+ case snocView stmts of
+ Just (initStmts, ls@(L _ (LastStmt _ _body _ _))) -> do
+ debugM $ "LocatedL [ExprLStmt: snocView"
+ markAnnotated ls
+ markAnnotated initStmts
+ _ -> markAnnotated stmts
+ -- x -> error $ "pprDo:ListComp" ++ showAst x
+ -- markLocatedMAA an al_close
+
+-- instance ExactPrint (LocatedL [CmdLStmt GhcPs]) where
+instance ExactPrint (LocatedL [LocatedA (StmtLR GhcPs GhcPs (LocatedA (HsCmd GhcPs)))]) where
+ getAnnotationEntry = entryFromLocatedA
+ exact (L (SrcSpanAnn ann _) es) = do
+ debugM $ "LocatedL [CmdLStmt"
+ markLocatedMAA ann al_open
+ mapM_ markAnnotated es
+ markLocatedMAA ann al_close
+
+instance ExactPrint (LocatedL [LocatedA (ConDeclField GhcPs)]) where
+ getAnnotationEntry = entryFromLocatedA
+ exact (L (SrcSpanAnn an _) fs) = do
+ debugM $ "LocatedL [LConDeclField"
+ markAnnList an (mapM_ markAnnotated fs) -- AZ:TODO get rid of mapM_
+
+instance ExactPrint (LocatedL (BF.BooleanFormula (LocatedN RdrName))) where
+ getAnnotationEntry = entryFromLocatedA
+ exact (L (SrcSpanAnn an _) bf) = do
+ debugM $ "LocatedL [LBooleanFormula"
+ markAnnList an (markAnnotated bf)
+
+-- ---------------------------------------------------------------------
+-- LocatedL instances end --
+-- =====================================================================
+
+instance ExactPrint (IE GhcPs) where
+ getAnnotationEntry (IEVar _ _) = NoEntryVal
+ getAnnotationEntry (IEThingAbs an _) = fromAnn an
+ getAnnotationEntry (IEThingAll an _) = fromAnn an
+ getAnnotationEntry (IEThingWith an _ _ _) = fromAnn an
+ getAnnotationEntry (IEModuleContents an _)= fromAnn an
+ getAnnotationEntry (IEGroup _ _ _) = NoEntryVal
+ getAnnotationEntry (IEDoc _ _) = NoEntryVal
+ getAnnotationEntry (IEDocNamed _ _) = NoEntryVal
+
+ exact (IEVar _ ln) = markAnnotated ln
+ exact (IEThingAbs _ thing) = markAnnotated thing
+ exact (IEThingAll an thing) = do
+ markAnnotated thing
+ markApiAnn an AnnOpenP
+ markApiAnn an AnnDotdot
+ markApiAnn an AnnCloseP
+
+ exact (IEThingWith an thing wc withs) = do
+ markAnnotated thing
+ markApiAnn an AnnOpenP
+ case wc of
+ NoIEWildcard -> markAnnotated withs
+ IEWildcard pos -> do
+ let (bs, as) = splitAt pos withs
+ markAnnotated bs
+ markApiAnn an AnnDotdot
+ markApiAnn an AnnComma
+ markAnnotated as
+ markApiAnn an AnnCloseP
+
+ exact (IEModuleContents an (L lm mn)) = do
+ markApiAnn an AnnModule
+ printStringAtSs lm (moduleNameString mn)
+
+ -- exact (IEGroup _ _ _) = NoEntryVal
+ -- exact (IEDoc _ _) = NoEntryVal
+ -- exact (IEDocNamed _ _) = NoEntryVal
+ exact x = error $ "missing match for IE:" ++ showAst x
+
+-- ---------------------------------------------------------------------
+
+instance ExactPrint (IEWrappedName RdrName) where
+ getAnnotationEntry = const NoEntryVal
+
+ exact (IEName n) = markAnnotated n
+ exact (IEPattern r n) = do
+ printStringAtAA r "pattern"
+ markAnnotated n
+ exact (IEType r n) = do
+ printStringAtAA r "type"
+ markAnnotated n
+
+-- markIEWrapped :: ApiAnn -> LIEWrappedName RdrName -> EPP ()
+-- markIEWrapped an (L _ (IEName n))
+-- = markAnnotated n
+-- markIEWrapped an (L _ (IEPattern n))
+-- = markApiAnn an AnnPattern >> markAnnotated n
+-- markIEWrapped an (L _ (IEType n))
+-- = markApiAnn an AnnType >> markAnnotated n
+
+-- ---------------------------------------------------------------------
+
+-- instance ExactPrint (LocatedA (Pat GhcPs)) where
+-- -- getAnnotationEntry (L (SrcSpanAnn ann _) _) = fromAnn ann
+-- getAnnotationEntry = entryFromLocatedA
+-- exact (L _ a) = do
+-- debugM $ "exact:LPat:" ++ showPprUnsafe a
+-- markAnnotated a
+
+instance ExactPrint (Pat GhcPs) where
+ getAnnotationEntry (WildPat _) = NoEntryVal
+ getAnnotationEntry (VarPat _ _) = NoEntryVal
+ getAnnotationEntry (LazyPat an _) = fromAnn an
+ getAnnotationEntry (AsPat an _ _) = fromAnn an
+ getAnnotationEntry (ParPat an _) = fromAnn an
+ getAnnotationEntry (BangPat an _) = fromAnn an
+ getAnnotationEntry (ListPat an _) = fromAnn an
+ getAnnotationEntry (TuplePat an _ _) = fromAnn an
+ getAnnotationEntry (SumPat an _ _ _) = fromAnn an
+ getAnnotationEntry (ConPat an _ _) = fromAnn an
+ getAnnotationEntry (ViewPat an _ _) = fromAnn an
+ getAnnotationEntry (SplicePat _ _) = NoEntryVal
+ getAnnotationEntry (LitPat _ _) = NoEntryVal
+ getAnnotationEntry (NPat an _ _ _) = fromAnn an
+ getAnnotationEntry (NPlusKPat an _ _ _ _ _) = fromAnn an
+ getAnnotationEntry (SigPat an _ _) = fromAnn an
+
+ exact (WildPat _) = do
+ anchor <- getAnchorU
+ debugM $ "WildPat:anchor=" ++ show anchor
+ printStringAtRs anchor "_"
+ exact (VarPat _ n) = do
+ -- The parser inserts a placeholder value for a record pun rhs. This must be
+ -- filtered.
+ let pun_RDR = "pun-right-hand-side"
+ when (showPprUnsafe n /= pun_RDR) $ markAnnotated n
+ -- | LazyPat an pat)
+ exact (AsPat an n pat) = do
+ markAnnotated n
+ markApiAnn an AnnAt
+ markAnnotated pat
+ exact (ParPat an pat) = do
+ markAnnKw an ap_open AnnOpenP
+ markAnnotated pat
+ markAnnKw an ap_close AnnCloseP
+
+ -- | BangPat an pat)
+ exact (ListPat an pats) = markAnnList an (markAnnotated pats)
+
+ exact (TuplePat an pats boxity) = do
+ case boxity of
+ Boxed -> markApiAnn an AnnOpenP
+ Unboxed -> markApiAnn an AnnOpenPH
+ markAnnotated pats
+ case boxity of
+ Boxed -> markApiAnn an AnnCloseP
+ Unboxed -> markApiAnn an AnnClosePH
+
+ exact (SumPat an pat _alt _arity) = do
+ markLocatedAAL an sumPatParens AnnOpenPH
+ markAnnKwAll an sumPatVbarsBefore AnnVbar
+ markAnnotated pat
+ markAnnKwAll an sumPatVbarsAfter AnnVbar
+ markLocatedAAL an sumPatParens AnnClosePH
+ -- markPat _ (GHC.SumPat _ pat alt arity) = do
+ -- markWithString GHC.AnnOpen "(#"
+ -- replicateM_ (alt - 1) $ mark GHC.AnnVbar
+ -- markLocated pat
+ -- replicateM_ (arity - alt) $ mark GHC.AnnVbar
+ -- markWithString GHC.AnnClose "#)"
+
+ -- | ConPat an con args)
+ exact (ConPat an con details) = exactUserCon an con details
+ exact (ViewPat an expr pat) = do
+ markAnnotated expr
+ markApiAnn an AnnRarrow
+ markAnnotated pat
+ exact (SplicePat _ splice) = markAnnotated splice
+ exact (LitPat _ lit) = printStringAdvance (hsLit2String lit)
+ exact (NPat an ol mn _) = do
+ when (isJust mn) $ markApiAnn an AnnMinus
+ markAnnotated ol
+
+ -- | NPlusKPat an n lit1 lit2 _ _)
+ exact (SigPat an pat sig) = do
+ markAnnotated pat
+ markApiAnn an AnnDcolon
+ markAnnotated sig
+ -- exact x = withPpr x
+ exact x = error $ "missing match for Pat:" ++ showAst x
+
+-- instance Annotate (GHC.Pat GHC.GhcPs) where
+-- markAST loc typ = do
+-- markPat loc typ
+-- inContext (Set.fromList [Intercalate]) $ mark GHC.AnnComma `debug` ("AnnComma in Pat")
+-- where
+-- markPat l (GHC.WildPat _) = markExternal l GHC.AnnVal "_"
+-- markPat l (GHC.VarPat _ n) = do
+-- -- The parser inserts a placeholder value for a record pun rhs. This must be
+-- -- filtered out until https://ghc.haskell.org/trac/ghc/ticket/12224 is
+-- -- resolved, particularly for pretty printing where annotations are added.
+-- let pun_RDR = "pun-right-hand-side"
+-- when (showPprUnsafe n /= pun_RDR) $
+-- unsetContext Intercalate $ setContext (Set.singleton PrefixOp) $ markAST l (GHC.unLoc n)
+-- -- unsetContext Intercalate $ setContext (Set.singleton PrefixOp) $ markLocated n
+-- markPat _ (GHC.LazyPat _ p) = do
+-- mark GHC.AnnTilde
+-- markLocated p
+
+-- markPat _ (GHC.AsPat _ ln p) = do
+-- markLocated ln
+-- mark GHC.AnnAt
+-- markLocated p
+
+-- markPat _ (GHC.ParPat _ p) = do
+-- mark GHC.AnnOpenP
+-- markLocated p
+-- mark GHC.AnnCloseP
+
+-- markPat _ (GHC.BangPat _ p) = do
+-- mark GHC.AnnBang
+-- markLocated p
+
+-- markPat _ (GHC.ListPat _ ps) = do
+-- mark GHC.AnnOpenS
+-- markListIntercalateWithFunLevel markLocated 2 ps
+-- mark GHC.AnnCloseS
+
+-- markPat _ (GHC.TuplePat _ pats b) = do
+-- if b == GHC.Boxed then mark GHC.AnnOpenP
+-- else markWithString GHC.AnnOpen "(#"
+-- markListIntercalateWithFunLevel markLocated 2 pats
+-- if b == GHC.Boxed then mark GHC.AnnCloseP
+-- else markWithString GHC.AnnClose "#)"
+
+-- markPat _ (GHC.SumPat _ pat alt arity) = do
+-- markWithString GHC.AnnOpen "(#"
+-- replicateM_ (alt - 1) $ mark GHC.AnnVbar
+-- markLocated pat
+-- replicateM_ (arity - alt) $ mark GHC.AnnVbar
+-- markWithString GHC.AnnClose "#)"
+
+-- markPat _ (GHC.ConPatIn n dets) = do
+-- markHsConPatDetails n dets
+
+-- markPat _ GHC.ConPatOut {} =
+-- traceM "warning: ConPatOut Introduced after renaming"
+
+-- markPat _ (GHC.ViewPat _ e pat) = do
+-- markLocated e
+-- mark GHC.AnnRarrow
+-- markLocated pat
+
+-- markPat l (GHC.SplicePat _ s) = do
+-- markAST l s
+
+-- markPat l (GHC.LitPat _ lp) = markAST l lp
+
+-- markPat _ (GHC.NPat _ ol mn _) = do
+-- when (isJust mn) $ mark GHC.AnnMinus
+-- markLocated ol
+
+-- markPat _ (GHC.NPlusKPat _ ln ol _ _ _) = do
+-- markLocated ln
+-- markWithString GHC.AnnVal "+" -- "+"
+-- markLocated ol
+
+
+-- markPat _ (GHC.SigPat _ pat ty) = do
+-- markLocated pat
+-- mark GHC.AnnDcolon
+-- markLHsSigWcType ty
+
+-- markPat _ GHC.CoPat {} =
+-- traceM "warning: CoPat introduced after renaming"
+
+-- markPat _ (GHC.XPat (GHC.L l p)) = markPat l p
+-- -- markPat _ (GHC.XPat x) = error $ "got XPat for:" ++ showPprUnsafe x
+
+
+-- ---------------------------------------------------------------------
+
+instance ExactPrint (HsPatSigType GhcPs) where
+ getAnnotationEntry = const NoEntryVal
+
+ exact (HsPS _ ty) = markAnnotated ty
+
+-- ---------------------------------------------------------------------
+
+instance ExactPrint (HsOverLit GhcPs) where
+ getAnnotationEntry = const NoEntryVal
+
+ exact ol =
+ let str = case ol_val ol of
+ HsIntegral (IL src _ _) -> src
+ HsFractional (FL{ fl_text = src }) -> src
+ HsIsString src _ -> src
+ in
+ case str of
+ SourceText s -> printStringAdvance s
+ NoSourceText -> return ()
+
+-- ---------------------------------------------------------------------
+
+hsLit2String :: HsLit GhcPs -> String
+hsLit2String lit =
+ case lit of
+ HsChar src v -> toSourceTextWithSuffix src v ""
+ -- It should be included here
+ -- https://github.com/ghc/ghc/blob/master/compiler/parser/Lexer.x#L1471
+ HsCharPrim src p -> toSourceTextWithSuffix src p "#"
+ HsString src v -> toSourceTextWithSuffix src v ""
+ HsStringPrim src v -> toSourceTextWithSuffix src v ""
+ HsInt _ (IL src _ v) -> toSourceTextWithSuffix src v ""
+ HsIntPrim src v -> toSourceTextWithSuffix src v ""
+ HsWordPrim src v -> toSourceTextWithSuffix src v ""
+ HsInt64Prim src v -> toSourceTextWithSuffix src v ""
+ HsWord64Prim src v -> toSourceTextWithSuffix src v ""
+ HsInteger src v _ -> toSourceTextWithSuffix src v ""
+ HsRat _ fl@(FL{fl_text = src }) _ -> toSourceTextWithSuffix src fl ""
+ HsFloatPrim _ fl@(FL{fl_text = src }) -> toSourceTextWithSuffix src fl "#"
+ HsDoublePrim _ fl@(FL{fl_text = src }) -> toSourceTextWithSuffix src fl "##"
+ -- (XLit x) -> error $ "got XLit for:" ++ showPprUnsafe x
+
+toSourceTextWithSuffix :: (Show a) => SourceText -> a -> String -> String
+toSourceTextWithSuffix (NoSourceText) alt suffix = show alt ++ suffix
+toSourceTextWithSuffix (SourceText txt) _alt suffix = txt ++ suffix
+
+sourceTextToString :: SourceText -> String -> String
+sourceTextToString NoSourceText alt = alt
+sourceTextToString (SourceText txt) _ = txt
+
+-- ---------------------------------------------------------------------
+
+exactUserCon :: (ExactPrint con) => ApiAnn -> con -> HsConPatDetails GhcPs -> EPP ()
+exactUserCon _ c (InfixCon p1 p2) = markAnnotated p1 >> markAnnotated c >> markAnnotated p2
+exactUserCon an c details = do
+ markAnnotated c
+ markApiAnn an AnnOpenC
+ exactConArgs details
+ markApiAnn an AnnCloseC
+
+
+exactConArgs ::HsConPatDetails GhcPs -> EPP ()
+exactConArgs (PrefixCon tyargs pats) = markAnnotated tyargs >> markAnnotated pats
+exactConArgs (InfixCon p1 p2) = markAnnotated p1 >> markAnnotated p2
+exactConArgs (RecCon rpats) = markAnnotated rpats
+
+-- ---------------------------------------------------------------------
+
+entryFromLocatedA :: LocatedAn ann a -> Entry
+entryFromLocatedA (L la _) = fromAnn la
+
+-- =====================================================================
+-- Utility stuff
+-- ---------------------------------------------------------------------
+
+-- |This should be the final point where things are mode concrete,
+-- before output.
+-- NOTE: despite the name, this is the ghc-exactprint final output for
+-- the PRINT phase.
+printStringAtLsDelta :: (Monad m, Monoid w) => DeltaPos -> String -> EP w m ()
+printStringAtLsDelta cl s = do
+ p <- getPosP
+ colOffset <- getLayoutOffsetP
+ if isGoodDeltaWithOffset cl colOffset
+ then do
+ printStringAt (undelta p cl colOffset) s
+ `debug` ("printStringAtLsDelta:(pos,s):" ++ show (undelta p cl colOffset,s))
+ else return () `debug` ("printStringAtLsDelta:bad delta for (mc,s):" ++ show (cl,s))
+
+-- ---------------------------------------------------------------------
+
+isGoodDeltaWithOffset :: DeltaPos -> LayoutStartCol -> Bool
+isGoodDeltaWithOffset dp colOffset = isGoodDelta (DP l c)
+ where (l,c) = undelta (0,0) dp colOffset
+
+printQueuedComment :: (Monad m, Monoid w) => RealSrcSpan -> Comment -> DeltaPos -> EP w m ()
+printQueuedComment loc Comment{commentContents} dp = do
+ p <- getPosP
+ colOffset <- getLayoutOffsetP
+ let (dr,dc) = undelta (0,0) dp colOffset
+ -- do not lose comments against the left margin
+ when (isGoodDelta (DP dr (max 0 dc))) $ do
+ printCommentAt (undelta p dp colOffset) commentContents
+ setPriorEndASTD False loc
+ p' <- getPosP
+ debugM $ "printQueuedComment: (p,p',dp,colOffset,undelta)=" ++ show (p,p',dp,colOffset,undelta p dp colOffset)
+
+{-
+-- Print version
+printQueuedComment :: (Monad m, Monoid w) => Comment -> DeltaPos -> EP w m ()
+printQueuedComment Comment{commentContents} dp = do
+ p <- getPos
+ colOffset <- getLayoutOffset
+ let (dr,dc) = undelta (0,0) dp colOffset
+ -- do not lose comments against the left margin
+ when (isGoodDelta (DP (dr,max 0 dc))) $
+ printCommentAt (undelta p dp colOffset) commentContents
+
+-}
+
+-- ---------------------------------------------------------------------
+
+-- withContext :: (Monad m, Monoid w)
+-- => [(KeywordId, DeltaPos)]
+-- -> Annotation
+-- -> EP w m a -> EP w m a
+-- withContext kds an x = withKds kds (withOffset an x)
+
+-- ---------------------------------------------------------------------
+--
+-- | Given an annotation associated with a specific SrcSpan,
+-- determines a new offset relative to the previous offset
+--
+withOffset :: (Monad m, Monoid w) => Annotation -> (EP w m a -> EP w m a)
+withOffset a =
+ local (\s -> s { epAnn = a, epContext = pushAcs (epContext s) })
+
+------------------------------------------------------------------------
+
+setLayoutBoth :: (Monad m, Monoid w) => EP w m () -> EP w m ()
+setLayoutBoth k = do
+ oldLHS <- gets dLHS
+ oldAnchorOffset <- getLayoutOffsetP
+ debugM $ "setLayoutBoth: (oldLHS,oldAnchorOffset)=" ++ show (oldLHS,oldAnchorOffset)
+ modify (\a -> a { dMarkLayout = True
+ , pMarkLayout = True } )
+ let reset = do
+ debugM $ "setLayoutBoth:reset: (oldLHS,oldAnchorOffset)=" ++ show (oldLHS,oldAnchorOffset)
+ modify (\a -> a { dMarkLayout = False
+ , dLHS = oldLHS
+ , pMarkLayout = False
+ , pLHS = oldAnchorOffset} )
+ k <* reset
+
+-- Use 'local', designed for this
+setLayoutTopLevelP :: (Monad m, Monoid w) => EP w m () -> EP w m ()
+setLayoutTopLevelP k = do
+ debugM $ "setLayoutTopLevelP entered"
+ oldAnchorOffset <- getLayoutOffsetP
+ modify (\a -> a { pMarkLayout = False
+ , pLHS = 1} )
+ k
+ debugM $ "setLayoutTopLevelP:resetting"
+ setLayoutOffsetP oldAnchorOffset
+
+------------------------------------------------------------------------
+
+getPosP :: (Monad m, Monoid w) => EP w m Pos
+getPosP = gets epPos
+
+setPosP :: (Monad m, Monoid w) => Pos -> EP w m ()
+setPosP l = do
+ debugM $ "setPosP:" ++ show l
+ modify (\s -> s {epPos = l})
+
+getExtraDP :: (Monad m, Monoid w) => EP w m (Maybe Anchor)
+getExtraDP = gets uExtraDP
+
+setExtraDP :: (Monad m, Monoid w) => Maybe Anchor -> EP w m ()
+setExtraDP md = do
+ debugM $ "setExtraDP:" ++ show md
+ modify (\s -> s {uExtraDP = md})
+
+getPriorEndD :: (Monad m, Monoid w) => EP w m Pos
+getPriorEndD = gets dPriorEndPosition
+
+getAnchorU :: (Monad m, Monoid w) => EP w m RealSrcSpan
+getAnchorU = gets uAnchorSpan
+
+setPriorEndD :: (Monad m, Monoid w) => Pos -> EP w m ()
+setPriorEndD pe = do
+ -- setLayoutStartIfNeededD (snd pe)
+ setPriorEndNoLayoutD pe
+
+setPriorEndNoLayoutD :: (Monad m, Monoid w) => Pos -> EP w m ()
+setPriorEndNoLayoutD pe = do
+ debugM $ "setPriorEndNoLayout:pe=" ++ show pe
+ modify (\s -> s { dPriorEndPosition = pe })
+
+setPriorEndASTD :: (Monad m, Monoid w) => Bool -> RealSrcSpan -> EP w m ()
+setPriorEndASTD layout pe = setPriorEndASTPD layout (rs2range pe)
+
+setPriorEndASTPD :: (Monad m, Monoid w) => Bool -> (Pos,Pos) -> EP w m ()
+setPriorEndASTPD layout pe@(fm,to) = do
+ debugM $ "setPriorEndASTD:pe=" ++ show pe
+ when layout $ setLayoutStartD (snd fm)
+ modify (\s -> s { dPriorEndPosition = to } )
+
+setLayoutStartD :: (Monad m, Monoid w) => Int -> EP w m ()
+setLayoutStartD p = do
+ EPState{dMarkLayout} <- get
+ when dMarkLayout $ do
+ debugM $ "setLayoutStartD: setting dLHS=" ++ show p
+ modify (\s -> s { dMarkLayout = False
+ , dLHS = LayoutStartCol p})
+
+setAnchorU :: (Monad m, Monoid w) => RealSrcSpan -> EP w m ()
+setAnchorU rss = do
+ debugM $ "setAnchorU:" ++ show (rs2range rss)
+ modify (\s -> s { uAnchorSpan = rss })
+
+getUnallocatedComments :: (Monad m, Monoid w) => EP w m [Comment]
+getUnallocatedComments = gets epComments
+
+putUnallocatedComments :: (Monad m, Monoid w) => [Comment] -> EP w m ()
+putUnallocatedComments cs = modify (\s -> s { epComments = cs } )
+
+getLayoutOffsetP :: (Monad m, Monoid w) => EP w m LayoutStartCol
+getLayoutOffsetP = gets pLHS
+
+setLayoutOffsetP :: (Monad m, Monoid w) => LayoutStartCol -> EP w m ()
+setLayoutOffsetP c = do
+ debugM $ "setLayoutOffsetP:" ++ show c
+ modify (\s -> s { pLHS = c })
+
+-- getEofPos :: (Monad m, Monoid w) => EP w m RealSrcSpan
+-- getEofPos = do
+-- as <- gets epApiAnns
+-- case apiAnnEofPos as of
+-- Nothing -> return placeholderRealSpan
+-- Just ss -> return ss
+
+-- ---------------------------------------------------------------------
+-------------------------------------------------------------------------
+-- |First move to the given location, then call exactP
+-- exactPC :: (Data ast, Monad m, Monoid w) => GHC.Located ast -> EP w m a -> EP w m a
+-- exactPC :: (Data ast, Data (GHC.SrcSpanLess ast), GHC.HasSrcSpan ast, Monad m, Monoid w)
+-- exactPC :: (Data ast, Monad m, Monoid w) => GHC.Located ast -> EP w m a -> EP w m a
+-- exactPC ast action =
+-- do
+-- return () `debug` ("exactPC entered for:" ++ show (mkAnnKey ast))
+-- ma <- getAndRemoveAnnotation ast
+-- let an@Ann{ annEntryDelta=edp
+-- , annPriorComments=comments
+-- , annFollowingComments=fcomments
+-- , annsDP=kds
+-- } = fromMaybe annNone ma
+-- PrintOptions{epAstPrint} <- ask
+-- r <- withContext kds an
+-- (mapM_ (uncurry printQueuedComment) comments
+-- >> advance edp
+-- >> censorM (epAstPrint ast) action
+-- <* mapM_ (uncurry printQueuedComment) fcomments)
+-- return r `debug` ("leaving exactPCfor:" ++ show (mkAnnKey ast))
+
+-- censorM :: (Monoid w, Monad m) => (w -> m w) -> EP w m a -> EP w m a
+-- censorM f m = passM (liftM (\x -> (x,f)) m)
+
+-- passM :: (Monad m) => EP w m (a, w -> m w) -> EP w m a
+-- passM m = RWST $ \r s -> do
+-- ~((a, f),s', EPWriter w) <- runRWST m r s
+-- w' <- f w
+-- return (a, s', EPWriter w')
+
+advance :: (Monad m, Monoid w) => DeltaPos -> EP w m ()
+advance dp = do
+ p <- getPosP
+ colOffset <- getLayoutOffsetP
+ debugM $ "advance:(p,dp,colOffset,ws)=" ++ show (p,dp,colOffset,undelta p dp colOffset)
+ printWhitespace (undelta p dp colOffset)
+
+{-
+Version from Print.advance
+advance :: (Monad m, Monoid w) => DeltaPos -> EP w m ()
+advance cl = do
+ p <- getPos
+ colOffset <- getLayoutOffset
+ printWhitespace (undelta p cl colOffset)
+-}
+
+-- ---------------------------------------------------------------------
+
+adjustDeltaForOffsetM :: DeltaPos -> EPP DeltaPos
+adjustDeltaForOffsetM dp = do
+ colOffset <- gets dLHS
+ return (adjustDeltaForOffset 0 colOffset dp)
+
+-- adjustDeltaForOffset :: Int -> LayoutStartCol -> DeltaPos -> DeltaPos
+-- adjustDeltaForOffset _ _colOffset dp@(DP (0,_)) = dp -- same line
+-- adjustDeltaForOffset d (LayoutStartCol colOffset) (DP (l,c)) = DP (l,c - colOffset - d)
+
+-- ---------------------------------------------------------------------
+-- Printing functions
+
+printString :: (Monad m, Monoid w) => Bool -> String -> EP w m ()
+printString layout str = do
+ EPState{epPos = (_,c), pMarkLayout} <- get
+ PrintOptions{epTokenPrint, epWhitespacePrint} <- ask
+ when (pMarkLayout && layout) $ do
+ debugM $ "printString: setting pLHS to " ++ show c
+ modify (\s -> s { pLHS = LayoutStartCol c, pMarkLayout = False } )
+
+ -- Advance position, taking care of any newlines in the string
+ let strDP@(DP cr _cc) = dpFromString str
+ p <- getPosP
+ colOffset <- getLayoutOffsetP
+ debugM $ "printString:(p,colOffset,strDP,cr)=" ++ show (p,colOffset,strDP,cr)
+ if cr == 0
+ then setPosP (undelta p strDP colOffset)
+ else setPosP (undelta p strDP 1)
+
+ -- Debug stuff
+ -- pp <- getPosP
+ -- debugM $ "printString: (p,pp,str)" ++ show (p,pp,str)
+ -- Debug end
+
+ --
+ if not layout && c == 0
+ then lift (epWhitespacePrint str) >>= \s -> tell EPWriter { output = s}
+ else lift (epTokenPrint str) >>= \s -> tell EPWriter { output = s}
+
+
+{-
+
+-- Print.printString
+printString :: (Monad m, Monoid w) => Bool -> String -> EP w m ()
+printString layout str = do
+ EPState{epPos = (_,c), epMarkLayout} <- get
+ PrintOptions{epTokenPrint, epWhitespacePrint} <- ask
+ when (epMarkLayout && layout) $
+ modify (\s -> s { epLHS = LayoutStartCol c, epMarkLayout = False } )
+
+ -- Advance position, taking care of any newlines in the string
+ let strDP@(DP (cr,_cc)) = dpFromString str
+ p <- getPos
+ colOffset <- getLayoutOffset
+ if cr == 0
+ then setPos (undelta p strDP colOffset)
+ else setPos (undelta p strDP 1)
+
+ --
+ if not layout && c == 0
+ then lift (epWhitespacePrint str) >>= \s -> tell EPWriter { output = s}
+ else lift (epTokenPrint str) >>= \s -> tell EPWriter { output = s}
+
+-}
+
+--------------------------------------------------------
+
+printStringAdvance :: String -> EPP ()
+printStringAdvance str = do
+ ss <- getAnchorU
+ printStringAtKw' ss str
+
+--------------------------------------------------------
+
+newLine :: (Monad m, Monoid w) => EP w m ()
+newLine = do
+ (l,_) <- getPosP
+ printString False "\n"
+ setPosP (l+1,1)
+
+padUntil :: (Monad m, Monoid w) => Pos -> EP w m ()
+padUntil (l,c) = do
+ (l1,c1) <- getPosP
+ if | l1 == l && c1 <= c -> printString False $ replicate (c - c1) ' '
+ | l1 < l -> newLine >> padUntil (l,c)
+ | otherwise -> return ()
+
+printWhitespace :: (Monad m, Monoid w) => Pos -> EP w m ()
+printWhitespace = padUntil
+
+printCommentAt :: (Monad m, Monoid w) => Pos -> String -> EP w m ()
+printCommentAt p str = do
+ debugM $ "printCommentAt: (pos,str)" ++ show (p,str)
+ printWhitespace p >> printString False str
+
+printStringAt :: (Monad m, Monoid w) => Pos -> String -> EP w m ()
+printStringAt p str = printWhitespace p >> printString True str
diff --git a/utils/check-exact/Lookup.hs b/utils/check-exact/Lookup.hs
new file mode 100644
index 0000000000..8edf4ac1f0
--- /dev/null
+++ b/utils/check-exact/Lookup.hs
@@ -0,0 +1,137 @@
+module Lookup
+ (
+ keywordToString
+ , KeywordId(..)
+ , Comment(..)
+ ) where
+
+-- import Language.Haskell.ExactPrint.Types
+import GHC (AnnKeywordId(..))
+-- import GHC.Utils.Outputable hiding ( (<>) )
+-- import Data.Data (Data)
+-- import GHC.Types.SrcLoc
+-- import GHC.Driver.Session
+import Types
+
+-- | Maps `AnnKeywordId` to the corresponding String representation.
+-- There is no specific mapping for the following constructors.
+-- `AnnOpen`, `AnnClose`, `AnnVal`, `AnnPackageName`, `AnnHeader`, `AnnFunId`,
+-- `AnnInfix`
+keywordToString :: KeywordId -> String
+keywordToString kw =
+ let mkErr x = error $ "keywordToString: missing case for:" ++ show x
+ in
+ case kw of
+ -- Specifically handle all cases so that there are pattern match
+ -- warnings if new constructors are added.
+ AnnComment _ -> mkErr kw
+ AnnString _ -> mkErr kw
+ AnnSemiSep -> ";"
+ (G AnnAnyclass) -> "anyclass"
+ (G AnnOpen ) -> mkErr kw
+ (G AnnClose ) -> mkErr kw
+ (G AnnVal ) -> mkErr kw
+ (G AnnPackageName) -> mkErr kw
+ (G AnnHeader ) -> mkErr kw
+ (G AnnFunId ) -> mkErr kw
+ (G AnnInfix ) -> mkErr kw
+ (G AnnValStr ) -> mkErr kw
+ (G AnnName ) -> mkErr kw
+ (G AnnAs ) -> "as"
+ (G AnnAt ) -> "@"
+ (G AnnBang ) -> "!"
+ (G AnnBackquote ) -> "`"
+ (G AnnBy ) -> "by"
+ (G AnnCase ) -> "case"
+ (G AnnClass ) -> "class"
+ (G AnnCloseB ) -> "|)"
+ (G AnnCloseBU ) -> "⦈"
+ (G AnnCloseC ) -> "}"
+ (G AnnCloseP ) -> ")"
+ (G AnnClosePH ) -> "#)"
+ (G AnnCloseQ ) -> "|]"
+ (G AnnCloseQU ) -> "⟧"
+ (G AnnCloseS ) -> "]"
+ (G AnnColon ) -> ":"
+ (G AnnComma ) -> ","
+ (G AnnCommaTuple ) -> ","
+ (G AnnDarrow ) -> "=>"
+ (G AnnData ) -> "data"
+ (G AnnDcolon ) -> "::"
+ (G AnnDefault ) -> "default"
+ (G AnnDeriving ) -> "deriving"
+ (G AnnDo ) -> "do"
+ (G AnnDot ) -> "."
+ (G AnnDotdot ) -> ".."
+ (G AnnElse ) -> "else"
+ (G AnnEqual ) -> "="
+ (G AnnExport ) -> "export"
+ (G AnnFamily ) -> "family"
+ (G AnnForall ) -> "forall"
+ (G AnnForeign ) -> "foreign"
+ (G AnnGroup ) -> "group"
+ (G AnnHiding ) -> "hiding"
+ (G AnnIf ) -> "if"
+ (G AnnImport ) -> "import"
+ (G AnnIn ) -> "in"
+ (G AnnInstance ) -> "instance"
+ (G AnnLam ) -> "\\"
+ (G AnnLarrow ) -> "<-"
+ (G AnnLet ) -> "let"
+ -- (G AnnLolly ) -> "#->"
+ (G AnnLollyU ) -> "⊸"
+ (G AnnMdo ) -> "mdo"
+ (G AnnMinus ) -> "-"
+ (G AnnModule ) -> "module"
+ (G AnnNewtype ) -> "newtype"
+ (G AnnOf ) -> "of"
+ (G AnnOpenB ) -> "(|"
+ (G AnnOpenBU ) -> "⦇"
+ (G AnnOpenC ) -> "{"
+ (G AnnOpenE ) -> "[e|"
+ (G AnnOpenEQ ) -> "[|"
+ (G AnnOpenEQU ) -> "⟦"
+ (G AnnOpenP ) -> "("
+ (G AnnOpenPH ) -> "(#"
+ -- (G AnnOpenPE ) -> "$("
+ -- (G AnnOpenPTE ) -> "$$("
+ (G AnnOpenS ) -> "["
+ (G AnnPattern ) -> "pattern"
+ (G AnnPercent ) -> "%"
+ (G AnnPercentOne) -> "%1"
+ (G AnnProc ) -> "proc"
+ (G AnnQualified ) -> "qualified"
+ (G AnnRarrow ) -> "->"
+ (G AnnRec ) -> "rec"
+ (G AnnRole ) -> "role"
+ (G AnnSafe ) -> "safe"
+ (G AnnSemi ) -> ";"
+ (G AnnSignature) -> "signature"
+ (G AnnStock ) -> "stock"
+ (G AnnStatic ) -> "static"
+ (G AnnThen ) -> "then"
+ (G AnnTilde ) -> "~"
+ (G AnnType ) -> "type"
+ (G AnnUnit ) -> "()"
+ (G AnnUsing ) -> "using"
+ (G AnnVbar ) -> "|"
+ (G AnnWhere ) -> "where"
+ (G Annlarrowtail ) -> "-<"
+ (G Annrarrowtail ) -> ">-"
+ (G AnnLarrowtail ) -> "-<<"
+ (G AnnRarrowtail ) -> ">>-"
+ (G AnnSimpleQuote ) -> "'"
+ (G AnnThTyQuote ) -> "''"
+ (G AnnDollar ) -> "$"
+ (G AnnDollarDollar ) -> "$$"
+ (G AnnDarrowU) -> "⇒"
+ (G AnnDcolonU) -> "∷"
+ (G AnnForallU) -> "∀"
+ (G AnnLarrowU) -> "←"
+ (G AnnLarrowtailU) -> "⤛"
+ (G AnnRarrowU) -> "→"
+ (G AnnRarrowtailU) -> "⤜"
+ (G AnnlarrowtailU) -> "⤙"
+ (G AnnrarrowtailU) -> "⤚"
+ AnnTypeApp -> "@"
+ (G AnnVia) -> "via"
diff --git a/utils/check-exact/Main.hs b/utils/check-exact/Main.hs
new file mode 100644
index 0000000000..80c1908ce0
--- /dev/null
+++ b/utils/check-exact/Main.hs
@@ -0,0 +1,238 @@
+{-# LANGUAGE ScopedTypeVariables #-}
+
+-- import Data.List
+-- import GHC.Types.SrcLoc
+import GHC hiding (moduleName)
+import GHC.Driver.Ppr
+import GHC.Driver.Session
+import GHC.Hs.Dump
+-- import qualified Control.Monad.IO.Class as GHC
+-- import GHC.Types.SourceText
+-- import GHC.Hs.Exact hiding (ExactPrint())
+-- import GHC.Utils.Outputable hiding (space)
+import System.Environment( getArgs )
+import System.Exit
+import System.FilePath
+import ExactPrint
+-- exactPrint = undefined
+-- showPprUnsafe = undefined
+
+-- ---------------------------------------------------------------------
+
+_tt :: IO ()
+-- _tt = testOneFile "/home/alanz/mysrc/git.haskell.org/ghc/_build/stage1/lib"
+_tt = testOneFile "/home/alanz/mysrc/git.haskell.org/worktree/exactprint/_build/stage1/lib"
+-- _tt = testOneFile "/home/alanz/mysrc/git.haskell.org/worktree/epw/_build/stage1/lib"
+
+ -- "../../testsuite/tests/printer/Ppr001.hs"
+ -- "../../testsuite/tests/printer/Ppr002.hs"
+ -- "../../testsuite/tests/printer/Ppr002a.hs"
+ -- "../../testsuite/tests/printer/Ppr003.hs"
+ -- "../../testsuite/tests/printer/Ppr004.hs"
+ -- "../../testsuite/tests/printer/Ppr005.hs"
+ -- "../../testsuite/tests/qualifieddo/should_compile/qdocompile001.hs"
+ -- "../../testsuite/tests/printer/Ppr006.hs"
+ -- "../../testsuite/tests/printer/Ppr007.hs"
+ -- "../../testsuite/tests/printer/Ppr008.hs"
+ -- "../../testsuite/tests/hiefile/should_compile/hie008.hs"
+ -- "../../testsuite/tests/printer/Ppr009.hs"
+ -- "../../testsuite/tests/printer/Ppr011.hs"
+ -- "../../testsuite/tests/printer/Ppr012.hs"
+ -- "../../testsuite/tests/printer/Ppr013.hs"
+ -- "../../testsuite/tests/printer/Ppr014.hs"
+ -- "../../testsuite/tests/printer/Ppr015.hs"
+ -- "../../testsuite/tests/printer/Ppr016.hs"
+ -- "../../testsuite/tests/printer/Ppr017.hs"
+ -- "../../testsuite/tests/printer/Ppr018.hs"
+ -- "../../testsuite/tests/printer/Ppr019.hs"
+ -- "../../testsuite/tests/printer/Ppr020.hs"
+ -- "../../testsuite/tests/printer/Ppr021.hs"
+ -- "../../testsuite/tests/printer/Ppr022.hs"
+ -- "../../testsuite/tests/printer/Ppr023.hs"
+ -- "../../testsuite/tests/printer/Ppr024.hs"
+ -- "../../testsuite/tests/printer/Ppr025.hs"
+ -- "../../testsuite/tests/printer/Ppr026.hs"
+ -- "../../testsuite/tests/printer/Ppr027.hs"
+ -- "../../testsuite/tests/printer/Ppr028.hs"
+ -- "../../testsuite/tests/printer/Ppr029.hs"
+ -- "../../testsuite/tests/printer/Ppr030.hs"
+ -- "../../testsuite/tests/printer/Ppr031.hs"
+ -- "../../testsuite/tests/printer/Ppr032.hs"
+ -- "../../testsuite/tests/printer/Ppr033.hs"
+ -- "../../testsuite/tests/printer/Ppr034.hs"
+ -- "../../testsuite/tests/printer/Ppr035.hs"
+ -- "../../testsuite/tests/printer/Ppr036.hs"
+ -- "../../testsuite/tests/printer/Ppr037.hs"
+ -- "../../testsuite/tests/printer/Ppr038.hs"
+ -- "../../testsuite/tests/printer/Ppr039.hs"
+ -- "../../testsuite/tests/printer/Ppr040.hs"
+ -- "../../testsuite/tests/printer/Ppr041.hs"
+ -- "../../testsuite/tests/printer/Ppr042.hs"
+ -- "../../testsuite/tests/printer/Ppr043.hs"
+ -- "../../testsuite/tests/printer/Ppr044.hs"
+ -- "../../testsuite/tests/printer/Ppr045.hs"
+ -- "../../testsuite/tests/printer/Ppr046.hs"
+ -- Not tested, the GENERATED pragma is getting removed "../../testsuite/tests/printer/Ppr047.hs"
+ -- "../../testsuite/tests/printer/Ppr048.hs"
+ -- "../../testsuite/tests/printer/Ppr049.hs"
+ -- "../../testsuite/tests/printer/T13050p.hs"
+ -- "../../testsuite/tests/printer/T13199.hs"
+ -- "../../testsuite/tests/printer/T13550.hs"
+ -- "../../testsuite/tests/printer/T13942.hs"
+ -- "../../testsuite/tests/printer/T14289b.hs"
+ -- "../../testsuite/tests/printer/T14289c.hs"
+ -- "../../testsuite/tests/printer/T14289.hs"
+ -- "../../testsuite/tests/printer/T14306.hs"
+ -- "../../testsuite/tests/printer/T14343b.hs"
+ -- "../../testsuite/tests/printer/T14343.hs"
+ -- "../../testsuite/tests/printer/T15761.hs"
+ -- "../../testsuite/tests/printer/Test17519.hs"
+ -- "../../testsuite/tests/printer/T18052a.hs"
+ -- "../../testsuite/tests/printer/T18247a.hs"
+ -- "../../testsuite/tests/printer/Ppr050.hs"
+ -- "../../testsuite/tests/printer/Ppr051.hs"
+ -- "../../testsuite/tests/printer/Ppr052.hs"
+ -- "../../testsuite/tests/typecheck/should_fail/T17566c.hs"
+ -- "../../testsuite/tests/hiefile/should_compile/Constructors.hs"
+ -- "../../testsuite/tests/printer/StarBinderAnns.hs"
+ -- "../../testsuite/tests/typecheck/should_fail/StrictBinds.hs"
+ -- "../../testsuite/tests/printer/Test10276.hs"
+ -- "../../testsuite/tests/printer/Test10278.hs"
+ -- "../../testsuite/tests/printer/Test12417.hs"
+ -- "../../testsuite/tests/parser/should_compile/T14189.hs"
+ -- "../../testsuite/tests/printer/Test16212.hs"
+ -- "../../testsuite/tests/printer/Test10312.hs"
+ -- "../../testsuite/tests/printer/Test10354.hs"
+ -- "../../testsuite/tests/printer/Test10357.hs"
+ -- "../../testsuite/tests/printer/Test10399.hs"
+ -- "../../testsuite/tests/printer/Test11018.hs"
+ -- "../../testsuite/tests/printer/Test11332.hs"
+ -- "../../testsuite/tests/printer/Test16230.hs"
+ -- "../../testsuite/tests/printer/Test16236.hs"
+ -- "../../testsuite/tests/printer/AnnotationLet.hs"
+ -- "../../testsuite/tests/printer/AnnotationTuple.hs"
+ -- "../../testsuite/tests/ghc-api/annotations/CommentsTest.hs"
+ -- "../../testsuite/tests/hiefile/should_compile/Scopes.hs"
+ -- "../../testsuite/tests/printer/Ppr053.hs"
+ -- "../../testsuite/tests/printer/Ppr054.hs"
+ -- "../../testsuite/tests/printer/Ppr055.hs"
+ -- "../../testsuite/tests/hiefile/should_run/PatTypes.hs"
+ -- "./cases/LocalDecls2.expected.hs"
+ -- "./cases/WhereIn3a.hs"
+ -- "./cases/AddLocalDecl1.hs"
+ -- "./cases/LayoutIn1.hs"
+ -- "./cases/EmptyWheres.hs"
+ -- "../../testsuite/tests/printer/PprRecordDotSyntax1.hs"
+ -- "../../testsuite/tests/printer/PprRecordDotSyntax2.hs"
+ -- "../../testsuite/tests/printer/PprRecordDotSyntax3.hs"
+ -- "../../testsuite/tests/printer/PprRecordDotSyntax4.hs"
+ "../../testsuite/tests/printer/PprRecordDotSyntaxA.hs"
+ -- "./cases/Windows.hs"
+
+-- exact = ppr
+
+-- ---------------------------------------------------------------------
+
+usage :: String
+usage = unlines
+ [ "usage: check-exact (libdir) (file)"
+ , ""
+ , "where libdir is the GHC library directory (e.g. the output of"
+ , "ghc --print-libdir) and file is the file to parse."
+ ]
+
+main :: IO()
+main = do
+ args <- getArgs
+ case args of
+ [libdir,fileName] -> testOneFile libdir fileName
+ _ -> putStrLn usage
+
+testOneFile :: FilePath -> String -> IO ()
+testOneFile libdir fileName = do
+ p <- parseOneFile libdir fileName
+ -- putStrLn $ "\n\ngot p"
+ let
+ origAst = showSDocUnsafe
+ $ showAstData BlankSrcSpanFile NoBlankApiAnnotations
+ (pm_parsed_source p)
+ anns' = pm_annotations p
+ -- pped = pragmas ++ "\n" ++ (exactPrint $ pm_parsed_source p)
+ pped = exactPrint (pm_parsed_source p) anns'
+ -- pragmas = getPragmas anns'
+
+ newFile = dropExtension fileName <.> "ppr" <.> takeExtension fileName
+ astFile = fileName <.> "ast"
+ newAstFile = fileName <.> "ast.new"
+
+ -- putStrLn $ "\n\nabout to writeFile"
+ writeFile astFile origAst
+ -- putStrLn $ "\n\nabout to pp"
+ writeFile newFile pped
+
+ -- putStrLn $ "anns':" ++ showPprUnsafe (apiAnnRogueComments anns')
+
+ p' <- parseOneFile libdir newFile
+
+ let newAstStr :: String
+ newAstStr = showSDocUnsafe
+ $ showAstData BlankSrcSpanFile NoBlankApiAnnotations
+ (pm_parsed_source p')
+ writeFile newAstFile newAstStr
+
+ -- putStrLn $ "\n\nanns':" ++ showPprUnsafe (apiAnnRogueComments anns')
+
+ if origAst == newAstStr
+ then do
+ -- putStrLn "ASTs matched"
+ exitSuccess
+ else do
+ putStrLn "exactPrint AST Match Failed"
+ putStrLn "\n===================================\nOrig\n\n"
+ putStrLn origAst
+ putStrLn "\n===================================\nNew\n\n"
+ putStrLn newAstStr
+ putStrLn "\n===================================\n\n"
+ exitFailure
+
+
+parseOneFile :: FilePath -> FilePath -> IO ParsedModule
+parseOneFile libdir fileName = do
+ let modByFile m =
+ case ml_hs_file $ ms_location m of
+ Nothing -> False
+ Just fn -> fn == fileName
+ runGhc (Just libdir) $ do
+ dflags <- getSessionDynFlags
+ let dflags2 = dflags `gopt_set` Opt_KeepRawTokenStream
+ _ <- setSessionDynFlags dflags2
+ addTarget Target { targetId = TargetFile fileName Nothing
+ , targetAllowObjCode = True
+ , targetContents = Nothing }
+ _ <- load LoadAllTargets
+ graph <- getModuleGraph
+ let
+ modSum = case filter modByFile (mgModSummaries graph) of
+ [x] -> x
+ xs -> error $ "Can't find module, got:"
+ ++ show (map (ml_hs_file . ms_location) xs)
+ -- toks <- getRichTokenStream (ms_mod modSum)
+ -- toks <- getTokenStream (ms_mod modSum)
+ -- GHC.liftIO $ putStrLn $ "toks=" ++ showPprUnsafe toks
+ parseModule modSum
+
+-- getPragmas :: ApiAnns -> String
+-- getPragmas anns' = pragmaStr
+-- where
+-- tokComment (L _ (AnnBlockComment s)) = s
+-- tokComment (L _ (AnnLineComment s)) = s
+-- tokComment _ = ""
+
+-- comments' = map tokComment $ sortRealLocated $ apiAnnRogueComments anns'
+-- pragmas = filter (\c -> isPrefixOf "{-#" c ) comments'
+-- pragmaStr = intercalate "\n" pragmas
+
+-- pp :: (Outputable a) => a -> String
+-- pp a = showPpr unsafeGlobalDynFlags a
+
+-- ---------------------------------------------------------------------
diff --git a/utils/check-exact/Parsers.hs b/utils/check-exact/Parsers.hs
new file mode 100644
index 0000000000..403ee3e55d
--- /dev/null
+++ b/utils/check-exact/Parsers.hs
@@ -0,0 +1,332 @@
+{-# LANGUAGE FlexibleContexts #-}
+{-# LANGUAGE ViewPatterns #-}
+-----------------------------------------------------------------------------
+-- |
+-- This module rexposes wrapped parsers from the GHC API. Along with
+-- returning the parse result, the corresponding annotations are also
+-- returned such that it is then easy to modify the annotations and print
+-- the result.
+--
+----------------------------------------------------------------------------
+module Parsers (
+ -- * Utility
+ Parser
+ , ParseResult
+ , withDynFlags
+ , CppOptions(..)
+ , defaultCppOptions
+
+ -- * Module Parsers
+ , parseModule
+ , parseModuleFromString
+ , parseModuleWithOptions
+ , parseModuleWithCpp
+
+ -- * Basic Parsers
+ , parseExpr
+ , parseImport
+ , parseType
+ , parseDecl
+ , parsePattern
+ , parseStmt
+
+ , parseWith
+
+ -- * Internal
+
+ , ghcWrapper
+
+ , initDynFlags
+ , initDynFlagsPure
+ , parseModuleFromStringInternal
+ , parseModuleApiAnnsWithCpp
+ , parseModuleApiAnnsWithCppInternal
+ , postParseTransform
+ ) where
+
+-- import Language.Haskell.GHC.ExactPrint.Annotate
+-- import Language.Haskell.GHC.ExactPrint.Delta
+import Preprocess
+import Types
+
+import Control.Monad.RWS
+-- import Data.Data (Data)
+
+
+-- import GHC.Paths (libdir)
+
+import qualified GHC hiding (parseModule)
+import qualified Control.Monad.IO.Class as GHC
+import qualified GHC.Data.FastString as GHC
+import qualified GHC.Data.StringBuffer as GHC
+import qualified GHC.Driver.Config as GHC
+import qualified GHC.Driver.Session as GHC
+import qualified GHC.Parser as GHC
+import qualified GHC.Parser.Header as GHC
+import qualified GHC.Parser.Lexer as GHC
+import qualified GHC.Parser.PostProcess as GHC
+import qualified GHC.Parser.Errors.Ppr as GHC
+import qualified GHC.Types.SrcLoc as GHC
+import qualified GHC.Utils.Error as GHC
+
+import qualified GHC.LanguageExtensions as LangExt
+
+-- import qualified Data.Map as Map
+
+{-# ANN module "HLint: ignore Eta reduce" #-}
+{-# ANN module "HLint: ignore Redundant do" #-}
+{-# ANN module "HLint: ignore Reduce duplication" #-}
+-- ---------------------------------------------------------------------
+
+-- | Wrapper function which returns Annotations along with the parsed
+-- element.
+parseWith :: GHC.DynFlags
+ -> FilePath
+ -> GHC.P w
+ -> String
+ -> ParseResult w
+parseWith dflags fileName parser s =
+ case runParser parser dflags fileName s of
+ GHC.PFailed pst -> Left (fmap GHC.pprError $ GHC.getErrorMessages pst)
+ GHC.POk (mkApiAnns -> apianns) pmod -> Right (apianns, pmod)
+
+
+parseWithECP :: (GHC.DisambECP w)
+ => GHC.DynFlags
+ -> FilePath
+ -> GHC.P GHC.ECP
+ -> String
+ -> ParseResult (GHC.LocatedA w)
+parseWithECP dflags fileName parser s =
+ -- case runParser ff dflags fileName s of
+ -- case runParser (parser >>= \p -> GHC.runECP_P p) dflags fileName s of
+ case runParser (parser >>= \p -> GHC.runPV $ GHC.unECP p) dflags fileName s of
+ GHC.PFailed pst -> Left (fmap GHC.pprError $ GHC.getErrorMessages pst)
+ GHC.POk (mkApiAnns -> apianns) pmod -> Right (apianns, pmod)
+
+-- ---------------------------------------------------------------------
+
+runParser :: GHC.P a -> GHC.DynFlags -> FilePath -> String -> GHC.ParseResult a
+runParser parser flags filename str = GHC.unP parser parseState
+ where
+ location = GHC.mkRealSrcLoc (GHC.mkFastString filename) 1 1
+ buffer = GHC.stringToStringBuffer str
+ parseState = GHC.initParserState (GHC.initParserOpts flags) buffer location
+
+-- ---------------------------------------------------------------------
+
+-- | Provides a safe way to consume a properly initialised set of
+-- 'DynFlags'.
+--
+-- @
+-- myParser fname expr = withDynFlags (\\d -> parseExpr d fname expr)
+-- @
+withDynFlags :: FilePath -> (GHC.DynFlags -> a) -> IO a
+withDynFlags libdir action = ghcWrapper libdir $ do
+ dflags <- GHC.getSessionDynFlags
+ void $ GHC.setSessionDynFlags dflags
+ return (action dflags)
+
+-- ---------------------------------------------------------------------
+
+parseFile :: GHC.DynFlags -> FilePath -> String -> GHC.ParseResult (GHC.Located GHC.HsModule)
+parseFile = runParser GHC.parseModule
+
+-- ---------------------------------------------------------------------
+
+type ParseResult a = Either GHC.ErrorMessages (GHC.ApiAnns, a)
+
+type Parser a = GHC.DynFlags -> FilePath -> String
+ -> ParseResult a
+
+parseExpr :: Parser (GHC.LHsExpr GHC.GhcPs)
+parseExpr df fp = parseWithECP df fp GHC.parseExpression
+
+parseImport :: Parser (GHC.LImportDecl GHC.GhcPs)
+parseImport df fp = parseWith df fp GHC.parseImport
+
+parseType :: Parser (GHC.LHsType GHC.GhcPs)
+parseType df fp = parseWith df fp GHC.parseType
+
+-- safe, see D1007
+parseDecl :: Parser (GHC.LHsDecl GHC.GhcPs)
+parseDecl df fp = parseWith df fp GHC.parseDeclaration
+
+parseStmt :: Parser (GHC.ExprLStmt GHC.GhcPs)
+parseStmt df fp = parseWith df fp GHC.parseStatement
+
+parsePattern :: Parser (GHC.LPat GHC.GhcPs)
+parsePattern df fp = parseWith df fp GHC.parsePattern
+
+-- ---------------------------------------------------------------------
+--
+
+-- | This entry point will also work out which language extensions are
+-- required and perform CPP processing if necessary.
+--
+-- @
+-- parseModule = parseModuleWithCpp defaultCppOptions
+-- @
+--
+-- Note: 'GHC.ParsedSource' is a synonym for 'GHC.Located' ('GHC.HsModule' 'GhcPs')
+parseModule :: FilePath -> FilePath -> IO (ParseResult GHC.ParsedSource)
+parseModule libdir file = parseModuleWithCpp libdir defaultCppOptions file
+
+
+-- | This entry point will work out which language extensions are
+-- required but will _not_ perform CPP processing.
+-- In contrast to `parseModoule` the input source is read from the provided
+-- string; the `FilePath` parameter solely exists to provide a name
+-- in source location annotations.
+parseModuleFromString
+ :: FilePath -- GHC libdir
+ -> FilePath
+ -> String
+ -> IO (ParseResult GHC.ParsedSource)
+parseModuleFromString libdir fp s = ghcWrapper libdir $ do
+ dflags <- initDynFlagsPure fp s
+ return $ parseModuleFromStringInternal dflags fp s
+
+-- | Internal part of 'parseModuleFromString'.
+parseModuleFromStringInternal :: Parser GHC.ParsedSource
+parseModuleFromStringInternal dflags fileName str =
+ let (str1, lp) = stripLinePragmas str
+ res = case runParser GHC.parseModule dflags fileName str1 of
+ GHC.PFailed pst -> Left (fmap GHC.pprError $ GHC.getErrorMessages pst)
+ GHC.POk x pmod -> Right (mkApiAnns x, lp, dflags, pmod)
+ in postParseTransform res
+
+parseModuleWithOptions :: FilePath -- ^ GHC libdir
+ -> FilePath
+ -> IO (ParseResult GHC.ParsedSource)
+parseModuleWithOptions libdir fp =
+ parseModuleWithCpp libdir defaultCppOptions fp
+
+
+-- | Parse a module with specific instructions for the C pre-processor.
+parseModuleWithCpp
+ :: FilePath -- ^ GHC libdir
+ -> CppOptions
+ -> FilePath -- ^ File to be parsed
+ -> IO (ParseResult GHC.ParsedSource)
+parseModuleWithCpp libdir cpp fp = do
+ res <- parseModuleApiAnnsWithCpp libdir cpp fp
+ return $ postParseTransform res
+
+-- ---------------------------------------------------------------------
+
+-- | Low level function which is used in the internal tests.
+-- It is advised to use 'parseModule' or 'parseModuleWithCpp' instead of
+-- this function.
+parseModuleApiAnnsWithCpp
+ :: FilePath -- ^ GHC libdir
+ -> CppOptions
+ -> FilePath -- ^ File to be parsed
+ -> IO
+ ( Either
+ GHC.ErrorMessages
+ (GHC.ApiAnns, [Comment], GHC.DynFlags, GHC.ParsedSource)
+ )
+parseModuleApiAnnsWithCpp libdir cppOptions file = ghcWrapper libdir $ do
+ dflags <- initDynFlags file
+ parseModuleApiAnnsWithCppInternal cppOptions dflags file
+
+-- | Internal function. Default runner of GHC.Ghc action in IO.
+ghcWrapper :: FilePath -> GHC.Ghc a -> IO a
+ghcWrapper libdir a =
+ GHC.defaultErrorHandler GHC.defaultFatalMessager GHC.defaultFlushOut
+ $ GHC.runGhc (Just libdir) a
+
+-- | Internal function. Exposed if you want to muck with DynFlags
+-- before parsing.
+parseModuleApiAnnsWithCppInternal
+ :: GHC.GhcMonad m
+ => CppOptions
+ -> GHC.DynFlags
+ -> FilePath
+ -> m
+ ( Either
+ GHC.ErrorMessages
+ (GHC.ApiAnns, [Comment], GHC.DynFlags, GHC.ParsedSource)
+ )
+parseModuleApiAnnsWithCppInternal cppOptions dflags file = do
+ let useCpp = GHC.xopt LangExt.Cpp dflags
+ (fileContents, injectedComments, dflags') <-
+ if useCpp
+ then do
+ (contents,dflags1) <- getPreprocessedSrcDirect cppOptions file
+ cppComments <- getCppTokensAsComments cppOptions file
+ return (contents,cppComments,dflags1)
+ else do
+ txt <- GHC.liftIO $ readFileGhc file
+ let (contents1,lp) = stripLinePragmas txt
+ return (contents1,lp,dflags)
+ return $
+ case parseFile dflags' file fileContents of
+ GHC.PFailed pst -> Left (fmap GHC.pprError $ GHC.getErrorMessages pst)
+ GHC.POk (mkApiAnns -> apianns) pmod ->
+ Right $ (apianns, injectedComments, dflags', pmod)
+
+-- | Internal function. Exposed if you want to muck with DynFlags
+-- before parsing. Or after parsing.
+postParseTransform
+ :: Either a (GHC.ApiAnns, [Comment], GHC.DynFlags, GHC.ParsedSource)
+ -> Either a (GHC.ApiAnns, GHC.ParsedSource)
+postParseTransform parseRes = fmap mkAnns parseRes
+ where
+ mkAnns (apianns, _cs, _, m) = (apianns, m)
+ -- (relativiseApiAnnsWithOptions opts cs m apianns, m)
+
+-- | Internal function. Initializes DynFlags value for parsing.
+--
+-- Passes "-hide-all-packages" to the GHC API to prevent parsing of
+-- package environment files. However this only works if there is no
+-- invocation of `setSessionDynFlags` before calling `initDynFlags`.
+-- See ghc tickets #15513, #15541.
+initDynFlags :: GHC.GhcMonad m => FilePath -> m GHC.DynFlags
+initDynFlags file = do
+ dflags0 <- GHC.getSessionDynFlags
+ src_opts <- GHC.liftIO $ GHC.getOptionsFromFile dflags0 file
+ (dflags1, _, _) <- GHC.parseDynamicFilePragma dflags0 src_opts
+ -- Turn this on last to avoid T10942
+ let dflags2 = dflags1 `GHC.gopt_set` GHC.Opt_KeepRawTokenStream
+ -- Prevent parsing of .ghc.environment.* "package environment files"
+ (dflags3, _, _) <- GHC.parseDynamicFlagsCmdLine
+ dflags2
+ [GHC.noLoc "-hide-all-packages"]
+ _ <- GHC.setSessionDynFlags dflags3
+ return dflags3
+
+-- | Requires GhcMonad constraint because there is
+-- no pure variant of `parseDynamicFilePragma`. Yet, in constrast to
+-- `initDynFlags`, it does not (try to) read the file at filepath, but
+-- solely depends on the module source in the input string.
+--
+-- Passes "-hide-all-packages" to the GHC API to prevent parsing of
+-- package environment files. However this only works if there is no
+-- invocation of `setSessionDynFlags` before calling `initDynFlagsPure`.
+-- See ghc tickets #15513, #15541.
+initDynFlagsPure :: GHC.GhcMonad m => FilePath -> String -> m GHC.DynFlags
+initDynFlagsPure fp s = do
+ -- I was told we could get away with using the unsafeGlobalDynFlags.
+ -- as long as `parseDynamicFilePragma` is impure there seems to be
+ -- no reason to use it.
+ dflags0 <- GHC.getSessionDynFlags
+ let pragmaInfo = GHC.getOptions dflags0 (GHC.stringToStringBuffer $ s) fp
+ (dflags1, _, _) <- GHC.parseDynamicFilePragma dflags0 pragmaInfo
+ -- Turn this on last to avoid T10942
+ let dflags2 = dflags1 `GHC.gopt_set` GHC.Opt_KeepRawTokenStream
+ -- Prevent parsing of .ghc.environment.* "package environment files"
+ (dflags3, _, _) <- GHC.parseDynamicFlagsCmdLine
+ dflags2
+ [GHC.noLoc "-hide-all-packages"]
+ _ <- GHC.setSessionDynFlags dflags3
+ return dflags3
+
+-- ---------------------------------------------------------------------
+
+mkApiAnns :: GHC.PState -> GHC.ApiAnns
+mkApiAnns pstate
+ = GHC.ApiAnns {
+ GHC.apiAnnRogueComments = GHC.comment_q pstate
+ }
diff --git a/utils/check-exact/Preprocess.hs b/utils/check-exact/Preprocess.hs
new file mode 100644
index 0000000000..aa474df2b1
--- /dev/null
+++ b/utils/check-exact/Preprocess.hs
@@ -0,0 +1,312 @@
+{-# LANGUAGE RecordWildCards #-}
+{-# LANGUAGE OverloadedStrings #-}
+-- | This module provides support for CPP, interpreter directives and line
+-- pragmas.
+module Preprocess
+ (
+ stripLinePragmas
+ , getCppTokensAsComments
+ , getPreprocessedSrcDirect
+ , readFileGhc
+
+ , CppOptions(..)
+ , defaultCppOptions
+ ) where
+
+import qualified GHC as GHC hiding (parseModule)
+
+import qualified Control.Monad.IO.Class as GHC
+import qualified GHC.Data.Bag as GHC
+import qualified GHC.Data.FastString as GHC
+import qualified GHC.Data.StringBuffer as GHC
+import qualified GHC.Driver.Config as GHC
+import qualified GHC.Driver.Env as GHC
+import qualified GHC.Driver.Phases as GHC
+import qualified GHC.Driver.Pipeline as GHC
+import qualified GHC.Fingerprint.Type as GHC
+import qualified GHC.Parser.Errors.Ppr as GHC
+import qualified GHC.Parser.Lexer as GHC
+import qualified GHC.Settings as GHC
+import qualified GHC.Types.SourceError as GHC
+import qualified GHC.Types.SourceFile as GHC
+import qualified GHC.Types.SrcLoc as GHC
+import qualified GHC.Utils.Error as GHC
+import qualified GHC.Utils.Fingerprint as GHC
+import GHC.Types.SrcLoc (mkSrcSpan, mkSrcLoc)
+import GHC.Data.FastString (mkFastString)
+
+import Data.List hiding (find)
+import Data.Maybe
+import Types
+import Utils
+import qualified Data.Set as Set
+
+
+-- import Debug.Trace
+--
+{-# ANN module ("HLint: ignore Eta reduce" :: String) #-}
+{-# ANN module ("HLint: ignore Redundant do" :: String) #-}
+{-# ANN module ("HLint: ignore Reduce duplication" :: String) #-}
+
+-- ---------------------------------------------------------------------
+
+data CppOptions = CppOptions
+ { cppDefine :: [String] -- ^ CPP #define macros
+ , cppInclude :: [FilePath] -- ^ CPP Includes directory
+ , cppFile :: [FilePath] -- ^ CPP pre-include file
+ }
+
+defaultCppOptions :: CppOptions
+defaultCppOptions = CppOptions [] [] []
+
+-- ---------------------------------------------------------------------
+-- | Remove GHC style line pragams (@{-# LINE .. #-}@) and convert them into comments.
+stripLinePragmas :: String -> (String, [Comment])
+stripLinePragmas = unlines' . unzip . findLines . lines
+ where
+ unlines' (a, b) = (unlines a, catMaybes b)
+
+findLines :: [String] -> [(String, Maybe Comment)]
+findLines = zipWith checkLine [1..]
+
+checkLine :: Int -> String -> (String, Maybe Comment)
+checkLine line s
+ | "{-# LINE" `isPrefixOf` s =
+ let (pragma, res) = getPragma s
+ size = length pragma
+ mSrcLoc = mkSrcLoc (mkFastString "LINE")
+ ss = mkSrcSpan (mSrcLoc line 1) (mSrcLoc line (size+1))
+ in (res, Just $ mkComment pragma (GHC.spanAsAnchor ss))
+ -- Deal with shebang/cpp directives too
+ -- x | "#" `isPrefixOf` s = ("",Just $ Comment ((line, 1), (line, length s)) s)
+ | "#!" `isPrefixOf` s =
+ let mSrcLoc = mkSrcLoc (mkFastString "SHEBANG")
+ ss = mkSrcSpan (mSrcLoc line 1) (mSrcLoc line (length s))
+ in
+ ("",Just $ mkComment s (GHC.spanAsAnchor ss))
+ | otherwise = (s, Nothing)
+
+getPragma :: String -> (String, String)
+getPragma [] = error "Input must not be empty"
+getPragma s@(x:xs)
+ | "#-}" `isPrefixOf` s = ("#-}", " " ++ drop 3 s)
+ | otherwise =
+ let (prag, remline) = getPragma xs
+ in (x:prag, ' ':remline)
+
+-- ---------------------------------------------------------------------
+
+-- | Replacement for original 'getRichTokenStream' which will return
+-- the tokens for a file processed by CPP.
+-- See bug <http://ghc.haskell.org/trac/ghc/ticket/8265>
+getCppTokensAsComments :: GHC.GhcMonad m
+ => CppOptions -- ^ Preprocessor Options
+ -> FilePath -- ^ Path to source file
+ -> m [Comment]
+getCppTokensAsComments cppOptions sourceFile = do
+ source <- GHC.liftIO $ GHC.hGetStringBuffer sourceFile
+ let startLoc = GHC.mkRealSrcLoc (GHC.mkFastString sourceFile) 1 1
+ (_txt,strSrcBuf,flags2') <- getPreprocessedSrcDirectPrim cppOptions sourceFile
+ let flags2 = GHC.initParserOpts flags2'
+ -- hash-ifdef tokens
+ directiveToks <- GHC.liftIO $ getPreprocessorAsComments sourceFile
+ -- Tokens without hash-ifdef
+ nonDirectiveToks <- tokeniseOriginalSrc startLoc flags2 source
+ case GHC.lexTokenStream flags2 strSrcBuf startLoc of
+ GHC.POk _ ts ->
+ do
+ let toks = GHC.addSourceToTokens startLoc source ts
+ cppCommentToks = getCppTokens directiveToks nonDirectiveToks toks
+ return $ filter goodComment
+ $ map (tokComment . GHC.commentToAnnotation . toRealLocated . fst) cppCommentToks
+ GHC.PFailed pst -> parseError pst
+
+goodComment :: Comment -> Bool
+goodComment (Comment "" _ _) = False
+goodComment _ = True
+
+
+toRealLocated :: GHC.Located a -> GHC.RealLocated a
+toRealLocated (GHC.L (GHC.RealSrcSpan s _) x) = GHC.L s x
+toRealLocated (GHC.L _ x) = GHC.L badRealSrcSpan x
+
+-- ---------------------------------------------------------------------
+
+-- | Combine the three sets of tokens to produce a single set that
+-- represents the code compiled, and will regenerate the original
+-- source file.
+-- [@directiveToks@] are the tokens corresponding to preprocessor
+-- directives, converted to comments
+-- [@origSrcToks@] are the tokenised source of the original code, with
+-- the preprocessor directives stripped out so that
+-- the lexer does not complain
+-- [@postCppToks@] are the tokens that the compiler saw originally
+-- NOTE: this scheme will only work for cpp in -nomacro mode
+getCppTokens ::
+ [(GHC.Located GHC.Token, String)]
+ -> [(GHC.Located GHC.Token, String)]
+ -> [(GHC.Located GHC.Token, String)]
+ -> [(GHC.Located GHC.Token, String)]
+getCppTokens directiveToks origSrcToks postCppToks = toks
+ where
+ locFn (GHC.L l1 _,_) (GHC.L l2 _,_) = compare (rs l1) (rs l2)
+ m1Toks = mergeBy locFn postCppToks directiveToks
+
+ -- We must now find the set of tokens that are in origSrcToks, but
+ -- not in m1Toks
+
+ -- GHC.Token does not have Ord, can't use a set directly
+ origSpans = map (\(GHC.L l _,_) -> rs l) origSrcToks
+ m1Spans = map (\(GHC.L l _,_) -> rs l) m1Toks
+ missingSpans = Set.fromList origSpans Set.\\ Set.fromList m1Spans
+
+ missingToks = filter (\(GHC.L l _,_) -> Set.member (rs l) missingSpans) origSrcToks
+
+ missingAsComments = map mkCommentTok missingToks
+ where
+ mkCommentTok :: (GHC.Located GHC.Token,String) -> (GHC.Located GHC.Token,String)
+ mkCommentTok (GHC.L l _,s) = (GHC.L l (GHC.ITlineComment s placeholderBufSpan),s)
+
+ toks = mergeBy locFn directiveToks missingAsComments
+
+-- ---------------------------------------------------------------------
+
+tokeniseOriginalSrc ::
+ GHC.GhcMonad m
+ => GHC.RealSrcLoc -> GHC.ParserOpts -> GHC.StringBuffer
+ -> m [(GHC.Located GHC.Token, String)]
+tokeniseOriginalSrc startLoc flags buf = do
+ let src = stripPreprocessorDirectives buf
+ case GHC.lexTokenStream flags src startLoc of
+ GHC.POk _ ts -> return $ GHC.addSourceToTokens startLoc src ts
+ GHC.PFailed pst -> parseError pst
+
+-- ---------------------------------------------------------------------
+
+-- | Strip out the CPP directives so that the balance of the source
+-- can tokenised.
+stripPreprocessorDirectives :: GHC.StringBuffer -> GHC.StringBuffer
+stripPreprocessorDirectives buf = buf'
+ where
+ srcByLine = lines $ sbufToString buf
+ noDirectivesLines = map (\line -> if line /= [] && head line == '#' then "" else line) srcByLine
+ buf' = GHC.stringToStringBuffer $ unlines noDirectivesLines
+
+-- ---------------------------------------------------------------------
+
+sbufToString :: GHC.StringBuffer -> String
+sbufToString sb@(GHC.StringBuffer _buf len _cur) = GHC.lexemeToString sb len
+
+-- ---------------------------------------------------------------------
+getPreprocessedSrcDirect :: (GHC.GhcMonad m)
+ => CppOptions
+ -> FilePath
+ -> m (String, GHC.DynFlags)
+getPreprocessedSrcDirect cppOptions src =
+ (\(s,_,d) -> (s,d)) <$> getPreprocessedSrcDirectPrim cppOptions src
+
+getPreprocessedSrcDirectPrim :: (GHC.GhcMonad m)
+ => CppOptions
+ -> FilePath
+ -> m (String, GHC.StringBuffer, GHC.DynFlags)
+getPreprocessedSrcDirectPrim cppOptions src_fn = do
+ hsc_env <- GHC.getSession
+ let dfs = GHC.hsc_dflags hsc_env
+ new_env = hsc_env { GHC.hsc_dflags = injectCppOptions cppOptions dfs }
+ -- (dflags', hspp_fn) <-
+ r <- GHC.liftIO $ GHC.preprocess new_env src_fn Nothing (Just (GHC.Cpp GHC.HsSrcFile))
+ case r of
+ Left err -> error $ showErrorMessages err
+ Right (dflags', hspp_fn) -> do
+ buf <- GHC.liftIO $ GHC.hGetStringBuffer hspp_fn
+ txt <- GHC.liftIO $ readFileGhc hspp_fn
+ return (txt, buf, dflags')
+
+showErrorMessages :: GHC.ErrorMessages -> String
+showErrorMessages msgs = intercalate "\n" $ map show $ GHC.bagToList msgs
+
+injectCppOptions :: CppOptions -> GHC.DynFlags -> GHC.DynFlags
+injectCppOptions CppOptions{..} dflags =
+ foldr addOptP dflags (map mkDefine cppDefine ++ map mkIncludeDir cppInclude ++ map mkInclude cppFile)
+ where
+ mkDefine = ("-D" ++)
+ mkIncludeDir = ("-I" ++)
+ mkInclude = ("-include" ++)
+
+
+addOptP :: String -> GHC.DynFlags -> GHC.DynFlags
+addOptP f = alterToolSettings $ \s -> s
+ { GHC.toolSettings_opt_P = f : GHC.toolSettings_opt_P s
+ , GHC.toolSettings_opt_P_fingerprint = fingerprintStrings (f : GHC.toolSettings_opt_P s)
+ }
+alterToolSettings :: (GHC.ToolSettings -> GHC.ToolSettings) -> GHC.DynFlags -> GHC.DynFlags
+alterToolSettings f dynFlags = dynFlags { GHC.toolSettings = f (GHC.toolSettings dynFlags) }
+
+fingerprintStrings :: [String] -> GHC.Fingerprint
+fingerprintStrings ss = GHC.fingerprintFingerprints $ map GHC.fingerprintString ss
+
+-- ---------------------------------------------------------------------
+
+-- | Get the preprocessor directives as comment tokens from the
+-- source.
+getPreprocessorAsComments :: FilePath -> IO [(GHC.Located GHC.Token, String)]
+getPreprocessorAsComments srcFile = do
+ fcontents <- readFileGhc srcFile
+ let directives = filter (\(_lineNum,line) -> line /= [] && head line == '#')
+ $ zip [1..] (lines fcontents)
+
+ let mkTok (lineNum,line) = (GHC.L l (GHC.ITlineComment line placeholderBufSpan),line)
+ where
+ start = GHC.mkSrcLoc (GHC.mkFastString srcFile) lineNum 1
+ end = GHC.mkSrcLoc (GHC.mkFastString srcFile) lineNum (length line)
+ l = GHC.mkSrcSpan start end
+
+ let toks = map mkTok directives
+ return toks
+
+placeholderBufSpan :: GHC.PsSpan
+placeholderBufSpan = pspan
+ where
+ bl = GHC.BufPos 0
+ pspan = GHC.PsSpan GHC.placeholderRealSpan (GHC.BufSpan bl bl)
+
+-- ---------------------------------------------------------------------
+
+parseError :: (GHC.MonadIO m) => GHC.PState -> m b
+parseError pst = do
+ let
+ -- (warns,errs) = GHC.getMessages pst dflags
+ -- throw $ GHC.mkSrcErr (GHC.unitBag $ GHC.mkPlainErrMsg dflags sspan err)
+ GHC.throwErrors (fmap GHC.pprError (GHC.getErrorMessages pst))
+
+-- ---------------------------------------------------------------------
+
+readFileGhc :: FilePath -> IO String
+readFileGhc file = do
+ buf@(GHC.StringBuffer _ len _) <- GHC.hGetStringBuffer file
+ return (GHC.lexemeToString buf len)
+
+-- ---------------------------------------------------------------------
+
+-- Copied over from MissingH, the dependency cause travis to fail
+
+{- | Merge two sorted lists using into a single, sorted whole,
+allowing the programmer to specify the comparison function.
+
+QuickCheck test property:
+
+prop_mergeBy xs ys =
+ mergeBy cmp (sortBy cmp xs) (sortBy cmp ys) == sortBy cmp (xs ++ ys)
+ where types = xs :: [ (Int, Int) ]
+ cmp (x1,_) (x2,_) = compare x1 x2
+-}
+mergeBy :: (a -> a -> Ordering) -> [a] -> [a] -> [a]
+mergeBy _cmp [] ys = ys
+mergeBy _cmp xs [] = xs
+mergeBy cmp (allx@(x:xs)) (ally@(y:ys))
+ -- Ordering derives Eq, Ord, so the comparison below is valid.
+ -- Explanation left as an exercise for the reader.
+ -- Someone please put this code out of its misery.
+ | (x `cmp` y) <= EQ = x : mergeBy cmp xs ally
+ | otherwise = y : mergeBy cmp allx ys
+
diff --git a/utils/check-exact/README b/utils/check-exact/README
new file mode 100644
index 0000000000..b27f0fbd55
--- /dev/null
+++ b/utils/check-exact/README
@@ -0,0 +1,24 @@
+
+This programme is intended to be used by any GHC developers working on
+the AST and/or pretty printer by providing a way to check that using
+exact print on the ParsedSource reproduces the original source.
+Except for stripping trailing whitespace on lines, and discarding
+tabs.
+
+This utility is also intended to be used in tests, so that when new features are
+added the ability to round-trip the AST via exact is tested.
+
+Usage
+
+In a test Makefile
+
+ $(CHECK_EXACT) "`'$(TEST_HC)' $(TEST_HC_OPTS) --print-libdir | tr -d '\r'`" FileToParse.hs
+
+AZ: update the rest here
+See examples in (REPO_HOME)/testsuite/tests/printer/Makefile
+
+The utility generates the following files for ToBeTested.hs
+
+ - ToBeTested.ppr.hs : the ppr result
+ - ToBeTested.hs.ast : the AST of the original source
+ - ToBeTested.hs.ast.new : the AST of the re-parsed ppr source
diff --git a/utils/check-exact/Test.hs b/utils/check-exact/Test.hs
new file mode 100644
index 0000000000..57c09cc737
--- /dev/null
+++ b/utils/check-exact/Test.hs
@@ -0,0 +1,840 @@
+{-# LANGUAGE RankNTypes #-}
+{-# LANGUAGE ScopedTypeVariables #-}
+{-# LANGUAGE StandaloneDeriving #-}
+{-# LANGUAGE DeriveDataTypeable #-}
+{-# OPTIONS_GHC -Wno-incomplete-uni-patterns #-}
+{-# OPTIONS_GHC -Wno-incomplete-patterns #-}
+{-# OPTIONS_GHC -Wno-orphans #-}
+
+import Data.List
+import Data.Data
+import GHC.Types.Name.Occurrence
+import GHC.Types.Name.Reader
+import GHC hiding (moduleName)
+import GHC.Driver.Ppr
+import GHC.Driver.Session
+import GHC.Hs.Dump
+import GHC.Data.Bag
+import System.Environment( getArgs )
+import System.Exit
+import System.FilePath
+
+import Types
+import Utils
+import ExactPrint
+import Transform
+import Parsers
+
+import GHC.Parser.Lexer
+import GHC.Data.FastString
+import GHC.Types.SrcLoc
+
+-- ---------------------------------------------------------------------
+
+_tt :: IO ()
+-- _tt = testOneFile changers "/home/alanz/mysrc/git.haskell.org/ghc/_build/stage1/lib"
+_tt = testOneFile changers "/home/alanz/mysrc/git.haskell.org/worktree/exactprint/_build/stage1/lib"
+-- _tt = testOneFile changers "/home/alanz/mysrc/git.haskell.org/worktree/epw/_build/stage1/lib"
+ "cases/RenameCase1.hs" changeRenameCase1
+ -- "cases/LayoutLet2.hs" changeLayoutLet2
+ -- "cases/LayoutLet3.hs" changeLayoutLet3
+ -- "cases/LayoutLet4.hs" changeLayoutLet3
+ -- "cases/Rename1.hs" changeRename1
+ -- "cases/Rename2.hs" changeRename2
+ -- "cases/LayoutIn1.hs" changeLayoutIn1
+ -- "cases/LayoutIn3.hs" changeLayoutIn3
+ -- "cases/LayoutIn3a.hs" changeLayoutIn3
+ -- "cases/LayoutIn3b.hs" changeLayoutIn3
+ -- "cases/LayoutIn4.hs" changeLayoutIn4
+ -- "cases/LocToName.hs" changeLocToName
+ -- "cases/LetIn1.hs" changeLetIn1
+ -- "cases/WhereIn4.hs" changeWhereIn4
+ -- "cases/AddDecl1.hs" changeAddDecl1
+ -- "cases/AddDecl2.hs" changeAddDecl2
+ -- "cases/AddDecl3.hs" changeAddDecl3
+ -- "cases/LocalDecls.hs" changeLocalDecls
+ -- "cases/LocalDecls2.hs" changeLocalDecls2
+ -- "cases/WhereIn3a.hs" changeWhereIn3a
+ -- "cases/WhereIn3b.hs" changeWhereIn3b
+ -- "cases/AddLocalDecl1.hs" addLocaLDecl1
+ -- "cases/AddLocalDecl2.hs" addLocaLDecl2
+ -- "cases/AddLocalDecl3.hs" addLocaLDecl3
+ -- "cases/AddLocalDecl4.hs" addLocaLDecl4
+ -- "cases/AddLocalDecl5.hs" addLocaLDecl5
+ -- "cases/AddLocalDecl6.hs" addLocaLDecl6
+ -- "cases/RmDecl1.hs" rmDecl1
+ -- "cases/RmDecl2.hs" rmDecl2
+ -- "cases/RmDecl3.hs" rmDecl3
+ -- "cases/RmDecl4.hs" rmDecl4
+ -- "cases/RmDecl5.hs" rmDecl5
+ -- "cases/RmDecl6.hs" rmDecl6
+ -- "cases/RmDecl7.hs" rmDecl7
+ -- "cases/RmTypeSig1.hs" rmTypeSig1
+ -- "cases/RmTypeSig2.hs" rmTypeSig2
+ -- "cases/AddHiding1.hs" addHiding1
+ -- "cases/AddHiding2.hs" addHiding2
+
+-- cloneT does not need a test, function can be retired
+
+
+-- exact = ppr
+
+changers :: [(String, Changer)]
+changers =
+ [("noChange", noChange)
+ ,("changeRenameCase1", changeRenameCase1)
+ ,("changeLayoutLet2", changeLayoutLet2)
+ ,("changeLayoutLet3", changeLayoutLet3)
+ ,("changeLayoutIn1", changeLayoutIn1)
+ ,("changeLayoutIn3", changeLayoutIn3)
+ ,("changeLayoutIn4", changeLayoutIn4)
+ ,("changeLocToName", changeLocToName)
+ ,("changeRename1", changeRename1)
+ ,("changeRename2", changeRename2)
+ ,("changeWhereIn4", changeWhereIn4)
+ ,("changeLetIn1", changeLetIn1)
+ ,("changeAddDecl1", changeAddDecl1)
+ ,("changeAddDecl2", changeAddDecl2)
+ ,("changeAddDecl3", changeAddDecl3)
+ ,("changeLocalDecls", changeLocalDecls)
+ ,("changeLocalDecls2", changeLocalDecls2)
+ ,("changeWhereIn3a", changeWhereIn3a)
+ ,("changeWhereIn3b", changeWhereIn3b)
+ ,("addLocaLDecl1", addLocaLDecl1)
+ ,("addLocaLDecl2", addLocaLDecl2)
+ ,("addLocaLDecl3", addLocaLDecl3)
+ ,("addLocaLDecl4", addLocaLDecl4)
+ ,("addLocaLDecl5", addLocaLDecl5)
+ ,("addLocaLDecl6", addLocaLDecl6)
+ ,("rmDecl1", rmDecl1)
+ ,("rmDecl2", rmDecl2)
+ ,("rmDecl3", rmDecl3)
+ ,("rmDecl4", rmDecl4)
+ ,("rmDecl5", rmDecl5)
+ ,("rmDecl6", rmDecl6)
+ ,("rmDecl7", rmDecl7)
+ ,("rmTypeSig1", rmTypeSig1)
+ ,("rmTypeSig2", rmTypeSig2)
+ ,("addHiding1", addHiding1)
+ ,("addHiding2", addHiding2)
+ ,("addHiding2", addHiding2)
+ ]
+
+-- ---------------------------------------------------------------------
+
+usage :: String
+usage = unlines
+ [ "usage: check-ppr (libdir) (file)"
+ , ""
+ , "where libdir is the GHC library directory (e.g. the output of"
+ , "ghc --print-libdir) and file is the file to parse."
+ ]
+
+main :: IO()
+main = do
+ args <- getArgs
+ case args of
+ [libdir,fileName] -> testOneFile changers libdir fileName noChange
+ _ -> putStrLn usage
+
+deriving instance Data Token
+deriving instance Data PsSpan
+deriving instance Data BufSpan
+deriving instance Data BufPos
+
+testOneFile :: [(String, Changer)] -> FilePath -> String -> Changer -> IO ()
+testOneFile _ libdir fileName changer = do
+ (p,_toks) <- parseOneFile libdir fileName
+ -- putStrLn $ "\n\ngot p" ++ showAst (take 4 $ reverse toks)
+ let
+ origAst = ppAst (pm_parsed_source p)
+ anns' = pm_annotations p
+ -- pped = pragmas ++ "\n" ++ (exactPrint $ pm_parsed_source p)
+ pped = exactPrint (pm_parsed_source p) anns'
+ -- pragmas = getPragmas anns'
+
+ newFile = dropExtension fileName <.> "ppr" <.> takeExtension fileName
+ newFileChanged = dropExtension fileName <.> "changed" <.> takeExtension fileName
+ newFileExpected = dropExtension fileName <.> "expected" <.> takeExtension fileName
+ astFile = fileName <.> "ast"
+ newAstFile = fileName <.> "ast.new"
+ changedAstFile = fileName <.> "ast.changed"
+
+ -- pped' <- exactprintWithChange changeRenameCase1 (pm_parsed_source p) anns'
+ (pped', ast') <- exactprintWithChange libdir changer (pm_parsed_source p) anns'
+ -- putStrLn $ "\n\nabout to writeFile"
+ writeFile changedAstFile (ppAst ast')
+ writeFile astFile origAst
+ -- putStrLn $ "\n\nabout to pp"
+ writeFile newFile pped
+ writeFile newFileChanged pped'
+
+ -- putStrLn $ "anns':" ++ showPprUnsafe (apiAnnRogueComments anns')
+
+ (p',_) <- parseOneFile libdir newFile
+
+ let newAstStr :: String
+ newAstStr = ppAst (pm_parsed_source p')
+ writeFile newAstFile newAstStr
+ expectedSource <- readFile newFileExpected
+ changedSource <- readFile newFileChanged
+
+ -- putStrLn $ "\n\nanns':" ++ showPprUnsafe (apiAnnRogueComments anns')
+
+ let
+ origAstOk = origAst == newAstStr
+ changedSourceOk = expectedSource == changedSource
+ if origAstOk && changedSourceOk
+ then do
+ -- putStrLn "ASTs matched"
+ exitSuccess
+ else if not origAstOk
+ then do
+ putStrLn "AST Match Failed"
+ -- putStrLn "\n===================================\nOrig\n\n"
+ -- putStrLn origAst
+ putStrLn "\n===================================\nNew\n\n"
+ putStrLn newAstStr
+ exitFailure
+ else do
+ putStrLn "Changed AST Source Mismatch"
+ putStrLn "\n===================================\nExpected\n\n"
+ putStrLn expectedSource
+ putStrLn "\n===================================\nChanged\n\n"
+ putStrLn changedSource
+ putStrLn "\n===================================\n"
+ putStrLn $ show changedSourceOk
+ exitFailure
+
+ppAst :: Data a => a -> String
+ppAst ast = showSDocUnsafe $ showAstData BlankSrcSpanFile NoBlankApiAnnotations ast
+
+parseOneFile :: FilePath -> FilePath -> IO (ParsedModule, [Located Token])
+parseOneFile libdir fileName = do
+ let modByFile m =
+ case ml_hs_file $ ms_location m of
+ Nothing -> False
+ Just fn -> fn == fileName
+ runGhc (Just libdir) $ do
+ dflags <- getSessionDynFlags
+ let dflags2 = dflags `gopt_set` Opt_KeepRawTokenStream
+ _ <- setSessionDynFlags dflags2
+ addTarget Target { targetId = TargetFile fileName Nothing
+ , targetAllowObjCode = True
+ , targetContents = Nothing }
+ _ <- load LoadAllTargets
+ graph <- getModuleGraph
+ let
+ modSum = case filter modByFile (mgModSummaries graph) of
+ [x] -> x
+ xs -> error $ "Can't find module, got:"
+ ++ show (map (ml_hs_file . ms_location) xs)
+ pm <- GHC.parseModule modSum
+ toks <- getTokenStream (ms_mod modSum)
+ return (pm, toks)
+
+ -- getTokenStream :: GhcMonad m => Module -> m [Located Token]
+
+-- getPragmas :: ApiAnns -> String
+-- getPragmas anns' = pragmaStr
+-- where
+-- tokComment (L _ (AnnBlockComment s)) = s
+-- tokComment (L _ (AnnLineComment s)) = s
+-- tokComment _ = ""
+
+-- comments' = map tokComment $ sortRealLocated $ apiAnnRogueComments anns'
+-- pragmas = filter (\c -> isPrefixOf "{-#" c ) comments'
+-- pragmaStr = intercalate "\n" pragmas
+
+-- pp :: (Outputable a) => a -> String
+-- pp a = showPpr unsafeGlobalDynFlags a
+
+-- ---------------------------------------------------------------------
+
+exactprintWithChange :: FilePath -> Changer -> ParsedSource -> ApiAnns -> IO (String, ParsedSource)
+exactprintWithChange libdir f p anns = do
+ debugM $ "exactprintWithChange:anns=" ++ showGhc (apiAnnRogueComments anns)
+ (anns',p') <- f libdir anns p
+ return (exactPrint p' anns', p')
+
+
+-- First param is libdir
+type Changer = FilePath -> (ApiAnns -> ParsedSource -> IO (ApiAnns,ParsedSource))
+
+noChange :: Changer
+noChange _libdir ans parsed = return (ans,parsed)
+
+changeRenameCase1 :: Changer
+changeRenameCase1 _libdir ans parsed = return (ans,rename "bazLonger" [((3,15),(3,18))] parsed)
+
+changeLayoutLet2 :: Changer
+changeLayoutLet2 _libdir ans parsed = return (ans,rename "xxxlonger" [((7,5),(7,8)),((8,24),(8,27))] parsed)
+
+changeLayoutLet3 :: Changer
+changeLayoutLet3 _libdir ans parsed = return (ans,rename "xxxlonger" [((7,5),(7,8)),((9,14),(9,17))] parsed)
+
+changeLayoutIn1 :: Changer
+changeLayoutIn1 _libdir ans parsed = return (ans,rename "square" [((7,17),(7,19)),((7,24),(7,26))] parsed)
+
+changeLayoutIn3 :: Changer
+changeLayoutIn3 _libdir ans parsed = return (ans,rename "anotherX" [((7,13),(7,14)),((7,37),(7,38)),((8,37),(8,38))] parsed)
+
+changeLayoutIn4 :: Changer
+changeLayoutIn4 _libdir ans parsed = return (ans,rename "io" [((7,8),(7,13)),((7,28),(7,33))] parsed)
+
+changeLocToName :: Changer
+changeLocToName _libdir ans parsed = return (ans,rename "LocToName.newPoint" [((20,1),(20,11)),((20,28),(20,38)),((24,1),(24,11))] parsed)
+
+
+changeRename1 :: Changer
+changeRename1 _libdir ans parsed = return (ans,rename "bar2" [((3,1),(3,4))] parsed)
+
+changeRename2 :: Changer
+changeRename2 _libdir ans parsed = return (ans,rename "joe" [((2,1),(2,5))] parsed)
+
+rename :: (Data a) => String -> [(Pos, Pos)] -> a -> a
+rename newNameStr spans' a
+ = everywhere (mkT replaceRdr) a
+ where
+ newName = mkRdrUnqual (mkVarOcc newNameStr)
+
+ cond :: SrcSpan -> Bool
+ cond ln = ss2range ln `elem` spans'
+
+ replaceRdr :: LocatedN RdrName -> LocatedN RdrName
+ replaceRdr (L ln _)
+ | cond (locA ln) = L ln newName
+ replaceRdr x = x
+
+-- ---------------------------------------------------------------------
+
+changeWhereIn4 :: Changer
+changeWhereIn4 _libdir ans parsed
+ = return (ans,everywhere (mkT replace) parsed)
+ where
+ replace :: LocatedN RdrName -> LocatedN RdrName
+ replace (L ln _n)
+ | ss2range (locA ln) == ((12,16),(12,17)) = L ln (mkRdrUnqual (mkVarOcc "p_2"))
+ replace x = x
+
+-- ---------------------------------------------------------------------
+
+changeLetIn1 :: Changer
+changeLetIn1 _libdir ans parsed
+ = return (ans,everywhere (mkT replace) parsed)
+ where
+ replace :: HsExpr GhcPs -> HsExpr GhcPs
+ replace (HsLet (ApiAnn anc (AnnsLet l _i) cs) localDecls expr)
+ =
+ let (HsValBinds x (ValBinds xv bagDecls sigs)) = localDecls
+ [l2,_l1] = map wrapDecl $ bagToList bagDecls
+ bagDecls' = listToBag $ concatMap decl2Bind [l2]
+ (L (SrcSpanAnn _ le) e) = expr
+ a = (SrcSpanAnn (ApiAnn (Anchor (realSrcSpan le) (MovedAnchor (DP 0 1))) mempty noCom) le)
+ expr' = L a e
+ in (HsLet (ApiAnn anc (AnnsLet l (AD (DP 1 0))) cs) (HsValBinds x (ValBinds xv bagDecls' sigs)) expr')
+
+ replace x = x
+-- ---------------------------------------------------------------------
+
+-- | Add a declaration to AddDecl
+changeAddDecl1 :: Changer
+changeAddDecl1 libdir ans top = do
+ Right (_, decl) <- withDynFlags libdir (\df -> parseDecl df "<interactive>" "nn = n2")
+ let decl' = setEntryDP' decl (DP 2 0)
+
+ let (p',(_,_),_) = runTransform mempty doAddDecl
+ doAddDecl = everywhereM (mkM replaceTopLevelDecls) top
+ replaceTopLevelDecls :: ParsedSource -> Transform ParsedSource
+ replaceTopLevelDecls m = insertAtStart m decl'
+ return (ans,p')
+
+-- ---------------------------------------------------------------------
+changeAddDecl2 :: Changer
+changeAddDecl2 libdir ans top = do
+ Right (_, decl) <- withDynFlags libdir (\df -> parseDecl df "<interactive>" "nn = n2")
+ let decl' = setEntryDP' decl (DP 2 0)
+ let top' = anchorEof top
+
+ let (p',(_,_),_) = runTransform mempty doAddDecl
+ doAddDecl = everywhereM (mkM replaceTopLevelDecls) top'
+ replaceTopLevelDecls :: ParsedSource -> Transform ParsedSource
+ replaceTopLevelDecls m = insertAtEnd m decl'
+ return (ans,p')
+
+-- ---------------------------------------------------------------------
+changeAddDecl3 :: Changer
+changeAddDecl3 libdir ans top = do
+ Right (_, decl) <- withDynFlags libdir (\df -> parseDecl df "<interactive>" "nn = n2")
+ let decl' = setEntryDP' decl (DP 2 0)
+
+ let (p',(_,_),_) = runTransform mempty doAddDecl
+ doAddDecl = everywhereM (mkM replaceTopLevelDecls) top
+ f d (l1:l2:ls) = l1:d:l2':ls
+ where
+ l2' = setEntryDP' l2 (DP 2 0)
+ replaceTopLevelDecls :: ParsedSource -> Transform ParsedSource
+ replaceTopLevelDecls m = insertAt f m decl'
+ return (ans,p')
+
+-- ---------------------------------------------------------------------
+
+-- | Add a local declaration with signature to LocalDecl
+changeLocalDecls :: Changer
+changeLocalDecls libdir ans (L l p) = do
+ Right (_, s@(L ls (SigD _ sig))) <- withDynFlags libdir (\df -> parseDecl df "sig" "nn :: Int")
+ Right (_, d@(L ld (ValD _ decl))) <- withDynFlags libdir (\df -> parseDecl df "decl" "nn = 2")
+ let decl' = setEntryDP' (L ld decl) (DP 1 0)
+ let sig' = setEntryDP' (L ls sig) (DP 0 0)
+ let (p',(_,_),_w) = runTransform mempty doAddLocal
+ doAddLocal = everywhereM (mkM replaceLocalBinds) p
+ replaceLocalBinds :: LMatch GhcPs (LHsExpr GhcPs)
+ -> Transform (LMatch GhcPs (LHsExpr GhcPs))
+ replaceLocalBinds (L lm (Match an mln pats (GRHSs _ rhs (HsValBinds van (ValBinds _ binds sigs))))) = do
+ let oldDecls = sortLocatedA $ map wrapDecl (bagToList binds) ++ map wrapSig sigs
+ let decls = s:d:oldDecls
+ let oldDecls' = captureLineSpacing oldDecls
+ let oldBinds = concatMap decl2Bind oldDecls'
+ (os:oldSigs) = concatMap decl2Sig oldDecls'
+ os' = setEntryDP' os (DP 2 0)
+ let sortKey = captureOrder decls
+ let (ApiAnn anc (AnnList (Just (Anchor anc2 _)) a b c dd) cs) = van
+ let van' = (ApiAnn anc (AnnList (Just (Anchor anc2 (MovedAnchor (DP 1 4)))) a b c dd) cs)
+ let binds' = (HsValBinds van'
+ (ValBinds sortKey (listToBag $ decl':oldBinds)
+ (sig':os':oldSigs)))
+ return (L lm (Match an mln pats (GRHSs noExtField rhs binds')))
+ replaceLocalBinds x = return x
+ return (ans,L l p')
+
+-- ---------------------------------------------------------------------
+
+-- | Add a local declaration with signature to LocalDecl, where there was no
+-- prior local decl. So it adds a "where" annotation.
+changeLocalDecls2 :: Changer
+changeLocalDecls2 libdir ans (L l p) = do
+ Right (_, d@(L ld (ValD _ decl))) <- withDynFlags libdir (\df -> parseDecl df "decl" "nn = 2")
+ Right (_, s@(L ls (SigD _ sig))) <- withDynFlags libdir (\df -> parseDecl df "sig" "nn :: Int")
+ let decl' = setEntryDP' (L ld decl) (DP 1 0)
+ let sig' = setEntryDP' (L ls sig) (DP 0 2)
+ let (p',(_,_),_w) = runTransform mempty doAddLocal
+ doAddLocal = everywhereM (mkM replaceLocalBinds) p
+ replaceLocalBinds :: LMatch GhcPs (LHsExpr GhcPs)
+ -> Transform (LMatch GhcPs (LHsExpr GhcPs))
+ replaceLocalBinds (L lm (Match ma mln pats (GRHSs _ rhs EmptyLocalBinds{}))) = do
+ newSpan <- uniqueSrcSpanT
+ let anc = (Anchor (rs newSpan) (MovedAnchor (DP 1 2)))
+ let anc2 = (Anchor (rs newSpan) (MovedAnchor (DP 1 4)))
+ let an = ApiAnn anc
+ (AnnList (Just anc2) Nothing Nothing
+ [(undeltaSpan (rs newSpan) AnnWhere (DP 0 0))] [])
+ noCom
+ let decls = [s,d]
+ let sortKey = captureOrder decls
+ let binds = (HsValBinds an (ValBinds sortKey (listToBag $ [decl'])
+ [sig']))
+ return (L lm (Match ma mln pats (GRHSs noExtField rhs binds)))
+ replaceLocalBinds x = return x
+ return (ans,L l p')
+
+-- ---------------------------------------------------------------------
+
+-- | Check that balanceCommentsList is idempotent
+changeWhereIn3a :: Changer
+changeWhereIn3a _libdir ans (L l p) = do
+ let decls0 = hsmodDecls p
+ (decls,(_,_),w) = runTransform mempty (balanceCommentsList decls0)
+ (_de0:_:de1:_d2:_) = decls
+ debugM $ unlines w
+ debugM $ "changeWhereIn3a:de1:" ++ showAst de1
+ let p2 = p { hsmodDecls = decls}
+ return (ans,L l p2)
+
+-- ---------------------------------------------------------------------
+
+changeWhereIn3b :: Changer
+changeWhereIn3b _libdir ans (L l p) = do
+ let decls0 = hsmodDecls p
+ (decls,(_,_),w) = runTransform mempty (balanceCommentsList decls0)
+ (de0:_:de1:d2:_) = decls
+ de0' = setEntryDP' de0 (DP 2 0)
+ de1' = setEntryDP' de1 (DP 2 0)
+ d2' = setEntryDP' d2 (DP 2 0)
+ decls' = d2':de1':de0':(tail decls)
+ debugM $ unlines w
+ debugM $ "changeWhereIn3b:de1':" ++ showAst de1'
+ let p2 = p { hsmodDecls = decls'}
+ return (ans,L l p2)
+
+-- ---------------------------------------------------------------------
+
+addLocaLDecl1 :: Changer
+addLocaLDecl1 libdir ans lp = do
+ Right (_, (L ld (ValD _ decl))) <- withDynFlags libdir (\df -> parseDecl df "decl" "nn = 2")
+ let decl' = setEntryDP' (L ld decl) (DP 1 4)
+ doAddLocal = do
+ (de1:d2:d3:_) <- hsDecls lp
+ (de1'',d2') <- balanceComments de1 d2
+ (de1',_) <- modifyValD (getLocA de1'') de1'' $ \_m d -> do
+ return ((wrapDecl decl' : d),Nothing)
+ replaceDecls lp [de1', d2', d3]
+
+ (lp',(_,_),w) <- runTransformT mempty doAddLocal
+ debugM $ "addLocaLDecl1:" ++ intercalate "\n" w
+ return (ans,lp')
+
+-- ---------------------------------------------------------------------
+
+addLocaLDecl2 :: Changer
+addLocaLDecl2 libdir ans lp = do
+ Right (_, newDecl) <- withDynFlags libdir (\df -> parseDecl df "decl" "nn = 2")
+ let
+ doAddLocal = do
+ (de1:d2:_) <- hsDecls lp
+ (de1'',d2') <- balanceComments de1 d2
+
+ (parent',_) <- modifyValD (getLocA de1) de1'' $ \_m (d:ds) -> do
+ newDecl' <- transferEntryDP' d newDecl
+ let d' = setEntryDP' d (DP 1 0)
+ return ((newDecl':d':ds),Nothing)
+
+ replaceDecls lp [parent',d2']
+
+ (lp',(_,_),_w) <- runTransformT mempty doAddLocal
+ debugM $ "log:[\n" ++ intercalate "\n" _w ++ "]log end\n"
+ return (ans,lp')
+
+-- ---------------------------------------------------------------------
+
+addLocaLDecl3 :: Changer
+addLocaLDecl3 libdir ans lp = do
+ Right (_, newDecl) <- withDynFlags libdir (\df -> parseDecl df "decl" "nn = 2")
+ -- Right (_, newDecl@(L ld (ValD _ decl))) <- withDynFlags libdir (\df -> parseDecl df "decl" "jj = 2")
+ let
+ doAddLocal = do
+ (de1:d2:_) <- hsDecls lp
+ (de1'',d2') <- balanceComments de1 d2
+
+ (parent',_) <- modifyValD (getLocA de1) de1'' $ \_m (d:ds) -> do
+ let newDecl' = setEntryDP' newDecl (DP 1 0)
+ return (((d:ds) ++ [newDecl']),Nothing)
+
+ replaceDecls (anchorEof lp) [parent',d2']
+
+ (lp',(_,_),_w) <- runTransformT mempty doAddLocal
+ debugM $ "log:[\n" ++ intercalate "\n" _w ++ "]log end\n"
+ return (ans,lp')
+
+-- ---------------------------------------------------------------------
+
+addLocaLDecl4 :: Changer
+addLocaLDecl4 libdir ans lp = do
+ Right (_, newDecl) <- withDynFlags libdir (\df -> parseDecl df "decl" "nn = 2")
+ Right (_, newSig) <- withDynFlags libdir (\df -> parseDecl df "sig" "nn :: Int")
+ -- putStrLn $ "addLocaLDecl4:lp=" ++ showGhc lp
+ let
+ doAddLocal = do
+ (parent:ds) <- hsDecls lp
+
+ let newDecl' = setEntryDP' newDecl (DP 1 0)
+ let newSig' = setEntryDP' newSig (DP 1 4)
+
+ (parent',_) <- modifyValD (getLocA parent) parent $ \_m decls -> do
+ return ((decls++[newSig',newDecl']),Nothing)
+
+ replaceDecls (anchorEof lp) (parent':ds)
+
+ (lp',(_,_),_w) <- runTransformT mempty doAddLocal
+ debugM $ "log:[\n" ++ intercalate "\n" _w ++ "]log end\n"
+ return (ans,lp')
+
+
+-- ---------------------------------------------------------------------
+
+addLocaLDecl5 :: Changer
+addLocaLDecl5 _libdir ans lp = do
+ let
+ doAddLocal = do
+ decls <- hsDecls lp
+ [s1,de1,d2,d3] <- balanceCommentsList decls
+
+ let d3' = setEntryDP' d3 (DP 2 0)
+
+ (de1',_) <- modifyValD (getLocA de1) de1 $ \_m _decls -> do
+ let d2' = setEntryDP' d2 (DP 1 0)
+ return ([d2'],Nothing)
+ replaceDecls lp [s1,de1',d3']
+
+ (lp',(_,_),_w) <- runTransformT mempty doAddLocal
+ debugM $ "log:[\n" ++ intercalate "\n" _w ++ "]log end\n"
+ return (ans,lp')
+
+-- ---------------------------------------------------------------------
+
+addLocaLDecl6 :: Changer
+addLocaLDecl6 libdir ans lp = do
+ Right (_, newDecl) <- withDynFlags libdir (\df -> parseDecl df "decl" "x = 3")
+ let
+ newDecl' = setEntryDP' newDecl (DP 1 4)
+ doAddLocal = do
+ decls0 <- hsDecls lp
+ [de1'',d2] <- balanceCommentsList decls0
+
+ let de1 = captureMatchLineSpacing de1''
+ let L _ (ValD _ (FunBind _ _ (MG _ (L _ ms) _) _)) = de1
+ let [ma1,_ma2] = ms
+
+ (de1',_) <- modifyValD (getLocA ma1) de1 $ \_m decls -> do
+ return ((newDecl' : decls),Nothing)
+ replaceDecls lp [de1', d2]
+
+ (lp',(_,_),_w) <- runTransformT mempty doAddLocal
+ debugM $ "log:[\n" ++ intercalate "\n" _w ++ "]log end\n"
+ return (ans,lp')
+
+-- ---------------------------------------------------------------------
+
+rmDecl1 :: Changer
+rmDecl1 _libdir ans lp = do
+ let doRmDecl = do
+ tlDecs0 <- hsDecls lp
+ tlDecs <- balanceCommentsList $ captureLineSpacing tlDecs0
+ let (de1:_s1:_d2:ds) = tlDecs
+
+ replaceDecls lp (de1:ds)
+
+ (lp',(_,_),_w) <- runTransformT mempty doRmDecl
+ debugM $ "log:[\n" ++ intercalate "\n" _w ++ "]log end\n"
+ return (ans,lp')
+
+-- ---------------------------------------------------------------------
+
+rmDecl2 :: Changer
+rmDecl2 _libdir ans lp = do
+ let
+ doRmDecl = do
+ let
+ go :: GHC.LHsExpr GhcPs -> Transform (GHC.LHsExpr GhcPs)
+ go e@(GHC.L _ (GHC.HsLet{})) = do
+ decs0 <- hsDecls e
+ decs <- balanceCommentsList $ captureLineSpacing decs0
+ e' <- replaceDecls e (init decs)
+ return e'
+ go x = return x
+
+ everywhereM (mkM go) lp
+
+ let (lp',(_,_),_w) = runTransform mempty doRmDecl
+ debugM $ "log:[\n" ++ intercalate "\n" _w ++ "]log end\n"
+ return (ans,lp')
+
+-- ---------------------------------------------------------------------
+
+rmDecl3 :: Changer
+rmDecl3 _libdir ans lp = do
+ let
+ doRmDecl = do
+ [de1,d2] <- hsDecls lp
+
+ (de1',Just sd1) <- modifyValD (getLocA de1) de1 $ \_m [sd1] -> do
+ let sd1' = setEntryDP' sd1 (DP 2 0)
+ return ([],Just sd1')
+
+ replaceDecls lp [de1',sd1,d2]
+
+ (lp',(_,_),_w) <- runTransformT mempty doRmDecl
+ debugM $ "log:[\n" ++ intercalate "\n" _w ++ "]log end\n"
+ return (ans,lp')
+
+-- ---------------------------------------------------------------------
+
+rmDecl4 :: Changer
+rmDecl4 _libdir ans lp = do
+ let
+ doRmDecl = do
+ [de1] <- hsDecls lp
+
+ (de1',Just sd1) <- modifyValD (getLocA de1) de1 $ \_m [sd1,sd2] -> do
+ sd2' <- transferEntryDP' sd1 sd2
+
+ let sd1' = setEntryDP' sd1 (DP 2 0)
+ return ([sd2'],Just sd1')
+
+ replaceDecls (anchorEof lp) [de1',sd1]
+
+ (lp',(_,_),_w) <- runTransformT mempty doRmDecl
+ debugM $ "log:[\n" ++ intercalate "\n" _w ++ "]log end\n"
+ return (ans,lp')
+
+-- ---------------------------------------------------------------------
+
+rmDecl5 :: Changer
+rmDecl5 _libdir ans lp = do
+ let
+ doRmDecl = do
+ let
+ go :: HsExpr GhcPs -> Transform (HsExpr GhcPs)
+ go (HsLet a lb expr) = do
+ decs <- hsDeclsValBinds lb
+ let dec = last decs
+ _ <- transferEntryDPT (head decs) dec
+ lb' <- replaceDeclsValbinds WithoutWhere lb [dec]
+ return (HsLet a lb' expr)
+ go x = return x
+
+ everywhereM (mkM go) lp
+
+ let (lp',(_,_),_w) = runTransform mempty doRmDecl
+ debugM $ "log:[\n" ++ intercalate "\n" _w ++ "]log end\n"
+ return (ans,lp')
+
+-- ---------------------------------------------------------------------
+
+rmDecl6 :: Changer
+rmDecl6 _libdir ans lp = do
+ let
+ doRmDecl = do
+ [de1] <- hsDecls lp
+
+ (de1',_) <- modifyValD (getLocA de1) de1 $ \_m subDecs -> do
+ let (ss1:_sd1:sd2:sds) = subDecs
+ sd2' <- transferEntryDP' ss1 sd2
+
+ return (sd2':sds,Nothing)
+
+ replaceDecls lp [de1']
+
+ (lp',(_,_),_w) <- runTransformT mempty doRmDecl
+ debugM $ "log:[\n" ++ intercalate "\n" _w ++ "]log end\n"
+ return (ans,lp')
+
+-- ---------------------------------------------------------------------
+
+rmDecl7 :: Changer
+rmDecl7 _libdir ans lp = do
+ let
+ doRmDecl = do
+ tlDecs <- hsDecls lp
+ [s1,de1,d2,d3] <- balanceCommentsList tlDecs
+
+ d3' <- transferEntryDP' d2 d3
+
+ replaceDecls lp [s1,de1,d3']
+
+ (lp',(_,_),_w) <- runTransformT mempty doRmDecl
+ debugM $ "log:[\n" ++ intercalate "\n" _w ++ "]log end\n"
+ return (ans,lp')
+
+-- ---------------------------------------------------------------------
+
+rmTypeSig1 :: Changer
+rmTypeSig1 _libdir ans lp = do
+ let doRmDecl = do
+ tlDecs <- hsDecls lp
+ let (s0:de1:d2) = tlDecs
+ s1 = captureTypeSigSpacing s0
+ (L l (SigD x1 (TypeSig x2 [n1,n2] typ))) = s1
+ n2' <- transferEntryDP n1 n2
+ let s1' = (L l (SigD x1 (TypeSig x2 [n2'] typ)))
+ replaceDecls lp (s1':de1:d2)
+
+ let (lp',(_,_),_w) = runTransform mempty doRmDecl
+ debugM $ "log:[\n" ++ intercalate "\n" _w ++ "]log end\n"
+ return (ans,lp')
+
+-- ---------------------------------------------------------------------
+
+rmTypeSig2 :: Changer
+rmTypeSig2 _libdir ans lp = do
+ let doRmDecl = do
+ tlDecs <- hsDecls lp
+ let [de1] = tlDecs
+
+ (de1',_) <- modifyValD (getLocA de1) de1 $ \_m [s,d] -> do
+ d' <- transferEntryDPT s d
+ return ([d'],Nothing)
+ replaceDecls lp [de1']
+
+ let (lp',(_,_),_w) = runTransform mempty doRmDecl
+ debugM $ "log:[\n" ++ intercalate "\n" _w ++ "]log end\n"
+ return (ans,lp')
+
+-- ---------------------------------------------------------------------
+
+addHiding1 :: Changer
+addHiding1 _libdir ans (L l p) = do
+ let doTransform = do
+ l0 <- uniqueSrcSpanT
+ l1 <- uniqueSrcSpanT
+ l2 <- uniqueSrcSpanT
+ let
+ [L li imp1,imp2] = hsmodImports p
+ n1 = L (noAnnSrcSpanDP0 l1) (mkVarUnqual (mkFastString "n1"))
+ n2 = L (noAnnSrcSpanDP0 l2) (mkVarUnqual (mkFastString "n2"))
+ v1 = L (addComma $ noAnnSrcSpanDP0 l1) (IEVar noExtField (L (noAnnSrcSpanDP0 l1) (IEName n1)))
+ v2 = L ( noAnnSrcSpanDP0 l2) (IEVar noExtField (L (noAnnSrcSpanDP0 l2) (IEName n2)))
+ impHiding = L (SrcSpanAnn (ApiAnn (Anchor (realSrcSpan l0) m0)
+ (AnnList Nothing
+ (Just (AddApiAnn AnnOpenP d1))
+ (Just (AddApiAnn AnnCloseP d0))
+ [(AddApiAnn AnnHiding d1)]
+ [])
+ noCom) l0) [v1,v2]
+ imp1' = imp1 { ideclHiding = Just (True,impHiding)}
+ p' = p { hsmodImports = [L li imp1',imp2]}
+ return (L l p')
+
+ let (lp',(_ans',_),_w) = runTransform mempty doTransform
+ debugM $ "log:[\n" ++ intercalate "\n" _w ++ "]log end\n"
+ return (ans,lp')
+
+-- ---------------------------------------------------------------------
+
+addHiding2 :: Changer
+addHiding2 _libdir ans (L l p) = do
+ let doTransform = do
+ l1 <- uniqueSrcSpanT
+ l2 <- uniqueSrcSpanT
+ let
+ [L li imp1] = hsmodImports p
+ Just (_,L lh ns) = ideclHiding imp1
+ lh' = (SrcSpanAnn (ApiAnn (Anchor (realSrcSpan (locA lh)) m0)
+ (AnnList Nothing
+ (Just (AddApiAnn AnnOpenP d1))
+ (Just (AddApiAnn AnnCloseP d0))
+ [(AddApiAnn AnnHiding d1)]
+ [])
+ noCom) (locA lh))
+ n1 = L (noAnnSrcSpanDP0 l1) (mkVarUnqual (mkFastString "n1"))
+ n2 = L (noAnnSrcSpanDP0 l2) (mkVarUnqual (mkFastString "n2"))
+ v1 = L (addComma $ noAnnSrcSpanDP0 l1) (IEVar noExtField (L (noAnnSrcSpanDP0 l1) (IEName n1)))
+ v2 = L ( noAnnSrcSpanDP0 l2) (IEVar noExtField (L (noAnnSrcSpanDP0 l2) (IEName n2)))
+ L ln n = last ns
+ n' = L (addComma ln) n
+ imp1' = imp1 { ideclHiding = Just (True,L lh' (init ns ++ [n',v1,v2]))}
+ p' = p { hsmodImports = [L li imp1']}
+ return (L l p')
+
+ let (lp',(_ans',_),_w) = runTransform mempty doTransform
+ debugM $ "log:[\n" ++ intercalate "\n" _w ++ "]log end\n"
+ return (ans,lp')
+
+
+-- ---------------------------------------------------------------------
+-- From SYB
+
+-- | Apply transformation on each level of a tree.
+--
+-- Just like 'everything', this is stolen from SYB package.
+everywhere :: (forall a. Data a => a -> a) -> (forall a. Data a => a -> a)
+everywhere f = f . gmapT (everywhere f)
+
+-- | Create generic transformation.
+--
+-- Another function stolen from SYB package.
+mkT :: (Typeable a, Typeable b) => (b -> b) -> (a -> a)
+mkT f = case cast f of
+ Just f' -> f'
+ Nothing -> id
+
+-- ---------------------------------------------------------------------
diff --git a/utils/check-exact/Transform.hs b/utils/check-exact/Transform.hs
new file mode 100644
index 0000000000..2901356879
--- /dev/null
+++ b/utils/check-exact/Transform.hs
@@ -0,0 +1,1513 @@
+{-# LANGUAGE FlexibleContexts #-}
+{-# LANGUAGE FlexibleInstances #-}
+{-# LANGUAGE GeneralizedNewtypeDeriving #-}
+{-# LANGUAGE NamedFieldPuns #-}
+{-# LANGUAGE RankNTypes #-}
+{-# LANGUAGE RecordWildCards #-}
+{-# LANGUAGE ScopedTypeVariables #-}
+{-# LANGUAGE ViewPatterns #-}
+-----------------------------------------------------------------------------
+-- |
+-- Module : Language.Haskell.GHC.ExactPrint.Transform
+--
+-- This module is currently under heavy development, and no promises are made
+-- about API stability. Use with care.
+--
+-- We welcome any feedback / contributions on this, as it is the main point of
+-- the library.
+--
+-----------------------------------------------------------------------------
+module Transform
+ (
+ -- * The Transform Monad
+ Transform
+ , TransformT(..)
+ , hoistTransform
+ , runTransform
+ , runTransformT
+ , runTransformFrom
+ , runTransformFromT
+
+ -- * Transform monad operations
+ , logTr
+ , logDataWithAnnsTr
+ , getAnnsT, putAnnsT, modifyAnnsT
+ , uniqueSrcSpanT
+
+ , cloneT
+ , graftT
+
+ , getEntryDPT
+ , setEntryDPT
+ , transferEntryDPT
+ , setPrecedingLinesDeclT
+ , setPrecedingLinesT
+ , addSimpleAnnT
+ , addTrailingCommaT
+ , removeTrailingCommaT
+
+ -- ** Managing declarations, in Transform monad
+ , HasTransform (..)
+ , HasDecls (..)
+ , hasDeclsSybTransform
+ , hsDeclsGeneric
+ , hsDeclsPatBind, hsDeclsPatBindD
+ , replaceDeclsPatBind, replaceDeclsPatBindD
+ , modifyDeclsT
+ , modifyValD
+ -- *** Utility, does not manage layout
+ , hsDeclsValBinds, replaceDeclsValbinds
+ , WithWhere(..)
+
+ -- ** New gen functions
+ , noAnnSrcSpanDP
+ , noAnnSrcSpanDP0
+ , noAnnSrcSpanDP1
+ , noAnnSrcSpanDPn
+ , d0, d1, dn
+ , m0, m1, mn
+ , addComma
+
+ -- ** Managing lists, Transform monad
+ , insertAt
+ , insertAtStart
+ , insertAtEnd
+ , insertAfter
+ , insertBefore
+
+ -- *** Low level operations used in 'HasDecls'
+ , balanceComments
+ , balanceCommentsList
+ , balanceCommentsList'
+ , balanceTrailingComments
+ , moveTrailingComments
+ , anchorEof
+
+ -- ** Managing lists, pure functions
+ , captureOrder
+ , captureLineSpacing
+ , captureMatchLineSpacing
+ , captureTypeSigSpacing
+
+ -- * Operations
+ , isUniqueSrcSpan
+
+ -- * Pure functions
+ , mergeAnns
+ , mergeAnnList
+ , setPrecedingLinesDecl
+ , setPrecedingLines
+ , getEntryDP
+ , setEntryDP
+ , setEntryDP'
+ , transferEntryDP
+ , transferEntryDP'
+ , addTrailingComma
+ , wrapSig, wrapDecl
+ , decl2Sig, decl2Bind
+ , deltaAnchor
+ ) where
+
+import Types
+import Utils
+
+import Control.Monad.RWS
+import qualified Control.Monad.Fail as Fail
+
+import GHC hiding (parseModule, parsedSource)
+import GHC.Data.Bag
+import GHC.Data.FastString
+
+-- import qualified Data.Generics as SYB
+
+import Data.Data
+import Data.List
+import Data.Maybe
+
+import qualified Data.Map as Map
+
+import Data.Functor.Identity
+import Control.Monad.State
+import Control.Monad.Writer
+
+-- import Debug.Trace
+
+------------------------------------------------------------------------------
+-- Transformation of source elements
+
+-- | Monad type for updating the AST and managing the annotations at the same
+-- time. The W state is used to generate logging information if required.
+type Transform = TransformT Identity
+
+-- |Monad transformer version of 'Transform' monad
+newtype TransformT m a = TransformT { unTransformT :: RWST () [String] (Anns,Int) m a }
+ deriving (Monad,Applicative,Functor
+ ,MonadReader ()
+ ,MonadWriter [String]
+ ,MonadState (Anns,Int)
+ ,MonadTrans
+ )
+
+instance Fail.MonadFail m => Fail.MonadFail (TransformT m) where
+ fail msg = TransformT $ RWST $ \_ _ -> Fail.fail msg
+
+-- | Run a transformation in the 'Transform' monad, returning the updated
+-- annotations and any logging generated via 'logTr'
+runTransform :: Anns -> Transform a -> (a,(Anns,Int),[String])
+runTransform ans f = runTransformFrom 0 ans f
+
+runTransformT :: Anns -> TransformT m a -> m (a,(Anns,Int),[String])
+runTransformT ans f = runTransformFromT 0 ans f
+
+-- | Run a transformation in the 'Transform' monad, returning the updated
+-- annotations and any logging generated via 'logTr', allocating any new
+-- SrcSpans from the provided initial value.
+runTransformFrom :: Int -> Anns -> Transform a -> (a,(Anns,Int),[String])
+runTransformFrom seed ans f = runRWS (unTransformT f) () (ans,seed)
+
+-- |Run a monad transformer stack for the 'TransformT' monad transformer
+runTransformFromT :: Int -> Anns -> TransformT m a -> m (a,(Anns,Int),[String])
+runTransformFromT seed ans f = runRWST (unTransformT f) () (ans,seed)
+
+-- | Change inner monad of 'TransformT'.
+hoistTransform :: (forall x. m x -> n x) -> TransformT m a -> TransformT n a
+hoistTransform nt (TransformT m) = TransformT (mapRWST nt m)
+
+-- |Log a string to the output of the Monad
+logTr :: (Monad m) => String -> TransformT m ()
+logTr str = tell [str]
+
+-- |Log a representation of the given AST with annotations to the output of the
+-- Monad
+logDataWithAnnsTr :: (Monad m) => (Data a) => String -> a -> TransformT m ()
+logDataWithAnnsTr str ast = do
+ logTr $ str ++ showAst ast
+
+-- |Access the 'Anns' being modified in this transformation
+getAnnsT :: (Monad m) => TransformT m Anns
+getAnnsT = gets fst
+
+-- |Replace the 'Anns' after any changes
+putAnnsT :: (Monad m) => Anns -> TransformT m ()
+putAnnsT ans = do
+ (_,col) <- get
+ put (ans,col)
+
+-- |Change the stored 'Anns'
+modifyAnnsT :: (Monad m) => (Anns -> Anns) -> TransformT m ()
+modifyAnnsT f = do
+ ans <- getAnnsT
+ putAnnsT (f ans)
+
+-- ---------------------------------------------------------------------
+
+-- |Once we have 'Anns', a 'SrcSpan' is used purely as part of an 'AnnKey'
+-- to index into the 'Anns'. If we need to add new elements to the AST, they
+-- need their own 'SrcSpan' for this.
+uniqueSrcSpanT :: (Monad m) => TransformT m SrcSpan
+uniqueSrcSpanT = do
+ (an,col) <- get
+ put (an,col + 1 )
+ let pos = mkSrcLoc (mkFastString "ghc-exactprint") (-1) col
+ return $ mkSrcSpan pos pos
+
+-- |Test whether a given 'SrcSpan' was generated by 'uniqueSrcSpanT'
+isUniqueSrcSpan :: SrcSpan -> Bool
+isUniqueSrcSpan ss = srcSpanStartLine' ss == -1
+
+srcSpanStartLine' :: SrcSpan -> Int
+srcSpanStartLine' (RealSrcSpan s _) = srcSpanStartLine s
+srcSpanStartLine' _ = 0
+
+-- ---------------------------------------------------------------------
+-- |Make a copy of an AST element, replacing the existing SrcSpans with new
+-- ones, and duplicating the matching annotations.
+cloneT :: (Data a,Monad m) => a -> TransformT m (a, [(SrcSpan, SrcSpan)])
+cloneT ast = do
+ runWriterT $ everywhereM (return `ext2M` replaceLocated) ast
+ where
+ replaceLocated :: forall loc a m. (Typeable loc,Data a,Monad m)
+ => (GenLocated loc a) -> WriterT [(SrcSpan, SrcSpan)] (TransformT m) (GenLocated loc a)
+ replaceLocated (L l t) = do
+ case cast l :: Maybe SrcSpan of
+ Just ss -> do
+ newSpan <- lift uniqueSrcSpanT
+ lift $ modifyAnnsT (\anns -> case Map.lookup (mkAnnKey (L ss t)) anns of
+ Nothing -> anns
+ Just an -> Map.insert (mkAnnKey (L newSpan t)) an anns)
+ tell [(ss, newSpan)]
+ return $ fromJust . cast $ L newSpan t
+ Nothing -> return (L l t)
+
+-- ---------------------------------------------------------------------
+-- |Slightly more general form of cloneT
+graftT :: (Data a,Monad m) => Anns -> a -> TransformT m a
+graftT origAnns = everywhereM (return `ext2M` replaceLocated)
+ where
+ replaceLocated :: forall loc a m. (Typeable loc, Data a, Monad m)
+ => GenLocated loc a -> TransformT m (GenLocated loc a)
+ replaceLocated (L l t) = do
+ case cast l :: Maybe SrcSpan of
+ Just ss -> do
+ newSpan <- uniqueSrcSpanT
+ modifyAnnsT (\anns -> case Map.lookup (mkAnnKey (L ss t)) origAnns of
+ Nothing -> anns
+ Just an -> Map.insert (mkAnnKey (L newSpan t)) an anns)
+ return $ fromJust $ cast $ L newSpan t
+ Nothing -> return (L l t)
+
+-- ---------------------------------------------------------------------
+
+-- |If a list has been re-ordered or had items added, capture the new order in
+-- the appropriate 'AnnSortKey' attached to the 'Annotation' for the list.
+captureOrder :: [LocatedA b] -> AnnSortKey
+captureOrder ls = AnnSortKey $ map (rs . getLocA) ls
+
+-- ---------------------------------------------------------------------
+
+captureMatchLineSpacing :: LHsDecl GhcPs -> LHsDecl GhcPs
+captureMatchLineSpacing (L l (ValD x (FunBind a b (MG c (L d ms ) e) f)))
+ = L l (ValD x (FunBind a b (MG c (L d ms') e) f))
+ where
+ ms' :: [LMatch GhcPs (LHsExpr GhcPs)]
+ ms' = captureLineSpacing ms
+captureMatchLineSpacing d = d
+
+captureLineSpacing :: Monoid t
+ => [LocatedAn t e] -> [GenLocated (SrcSpanAnn' (ApiAnn' t)) e]
+captureLineSpacing [] = []
+captureLineSpacing [d] = [d]
+captureLineSpacing (de1:d2:ds) = de1:captureLineSpacing (d2':ds)
+ where
+ (l1,_) = ss2pos $ rs $ getLocA de1
+ (l2,_) = ss2pos $ rs $ getLocA d2
+ d2' = setEntryDP' d2 (DP (l2-l1) 0)
+
+-- ---------------------------------------------------------------------
+
+captureTypeSigSpacing :: LHsDecl GhcPs -> LHsDecl GhcPs
+captureTypeSigSpacing (L l (SigD x (TypeSig (ApiAnn anc (AnnSig dc rs') cs) ns (HsWC xw ty))))
+ = (L l (SigD x (TypeSig (ApiAnn anc (AnnSig dc' rs') cs) ns (HsWC xw ty'))))
+ where
+ -- we want DPs for the distance from the end of the ns to the
+ -- AnnDColon, and to the start of the ty
+ AddApiAnn kw dca = dc
+ rd = case last ns of
+ L (SrcSpanAnn ApiAnnNotUsed ll) _ -> realSrcSpan ll
+ L (SrcSpanAnn (ApiAnn anc' _ _) _) _ -> anchor anc' -- TODO MovedAnchor?
+ -- DP (line, col) = ss2delta (ss2pos $ anchor $ getLoc lc) r
+ dc' = case dca of
+ AR r -> AddApiAnn kw (AD $ ss2delta (ss2posEnd rd) r)
+ AD _ -> AddApiAnn kw dca
+
+ -- ---------------------------------
+
+ ty' :: LHsSigType GhcPs
+ ty' = case ty of
+ (L (SrcSpanAnn ApiAnnNotUsed ll) b)
+ -> let
+ op = case dca of
+ AR r -> MovedAnchor (ss2delta (ss2posEnd r) (realSrcSpan ll))
+ AD _ -> MovedAnchor (DP 0 1)
+ in (L (SrcSpanAnn (ApiAnn (Anchor (realSrcSpan ll) op) mempty noCom) ll) b)
+ (L (SrcSpanAnn (ApiAnn (Anchor r op) a c) ll) b)
+ -> let
+ op' = case op of
+ MovedAnchor _ -> op
+ _ -> case dca of
+ AR dcr -> MovedAnchor (ss2delta (ss2posEnd dcr) r)
+ AD _ -> MovedAnchor (DP 0 1)
+ in (L (SrcSpanAnn (ApiAnn (Anchor r op') a c) ll) b)
+
+captureTypeSigSpacing s = s
+
+-- ---------------------------------------------------------------------
+
+-- |Pure function to convert a 'LHsDecl' to a 'LHsBind'. This does
+-- nothing to any annotations that may be attached to either of the elements.
+-- It is used as a utility function in 'replaceDecls'
+decl2Bind :: LHsDecl GhcPs -> [LHsBind GhcPs]
+decl2Bind (L l (ValD _ s)) = [L l s]
+decl2Bind _ = []
+
+-- |Pure function to convert a 'LSig' to a 'LHsBind'. This does
+-- nothing to any annotations that may be attached to either of the elements.
+-- It is used as a utility function in 'replaceDecls'
+decl2Sig :: LHsDecl GhcPs -> [LSig GhcPs]
+decl2Sig (L l (SigD _ s)) = [L l s]
+decl2Sig _ = []
+
+-- ---------------------------------------------------------------------
+
+-- |Convert a 'LSig' into a 'LHsDecl'
+wrapSig :: LSig GhcPs -> LHsDecl GhcPs
+wrapSig (L l s) = L l (SigD NoExtField s)
+
+-- ---------------------------------------------------------------------
+
+-- |Convert a 'LHsBind' into a 'LHsDecl'
+wrapDecl :: LHsBind GhcPs -> LHsDecl GhcPs
+wrapDecl (L l s) = L l (ValD NoExtField s)
+
+-- ---------------------------------------------------------------------
+
+-- |Create a simple 'Annotation' without comments, and attach it to the first
+-- parameter.
+addSimpleAnnT :: (Data a,Monad m)
+ => Located a -> DeltaPos -> [(KeywordId, DeltaPos)] -> TransformT m ()
+addSimpleAnnT ast dp kds = do
+ let ann = annNone { annEntryDelta = dp
+ , annsDP = kds
+ }
+ modifyAnnsT (Map.insert (mkAnnKey ast) ann)
+
+-- ---------------------------------------------------------------------
+
+-- |Add a trailing comma annotation, unless there is already one
+addTrailingCommaT :: (Data a,Monad m) => Located a -> TransformT m ()
+addTrailingCommaT ast = do
+ modifyAnnsT (addTrailingComma ast (DP 0 0))
+
+-- ---------------------------------------------------------------------
+
+-- |Remove a trailing comma annotation, if there is one one
+removeTrailingCommaT :: (Data a,Monad m) => Located a -> TransformT m ()
+removeTrailingCommaT ast = do
+ modifyAnnsT (removeTrailingComma ast)
+
+-- ---------------------------------------------------------------------
+
+-- |'Transform' monad version of 'getEntryDP'
+getEntryDPT :: (Data a,Monad m) => Located a -> TransformT m DeltaPos
+getEntryDPT ast = do
+ anns <- getAnnsT
+ return (getEntryDP anns ast)
+
+-- ---------------------------------------------------------------------
+
+-- |'Transform' monad version of 'getEntryDP'
+setEntryDPT :: (Data a,Monad m) => LocatedA a -> DeltaPos -> TransformT m ()
+setEntryDPT ast dp = do
+ modifyAnnsT (setEntryDP ast dp)
+
+-- ---------------------------------------------------------------------
+
+-- |'Transform' monad version of 'transferEntryDP'
+transferEntryDPT :: (Data a,Data b,Monad m) => LocatedA a -> LocatedA b -> TransformT m (LocatedA b)
+transferEntryDPT _a b = do
+ return b
+ -- modifyAnnsT (transferEntryDP a b)
+
+-- ---------------------------------------------------------------------
+
+-- |'Transform' monad version of 'setPrecedingLinesDecl'
+setPrecedingLinesDeclT :: (Monad m) => LHsDecl GhcPs -> Int -> Int -> TransformT m ()
+setPrecedingLinesDeclT ld n c =
+ modifyAnnsT (setPrecedingLinesDecl ld n c)
+
+-- ---------------------------------------------------------------------
+
+-- |'Transform' monad version of 'setPrecedingLines'
+setPrecedingLinesT :: (Data a,Monad m) => LocatedA a -> Int -> Int -> TransformT m ()
+setPrecedingLinesT ld n c =
+ modifyAnnsT (setPrecedingLines ld n c)
+
+-- ---------------------------------------------------------------------
+
+-- | Left bias pair union
+mergeAnns :: Anns -> Anns -> Anns
+mergeAnns
+ = Map.union
+
+-- |Combine a list of annotations
+mergeAnnList :: [Anns] -> Anns
+mergeAnnList [] = error "mergeAnnList must have at lease one entry"
+mergeAnnList (x:xs) = foldr mergeAnns x xs
+
+-- ---------------------------------------------------------------------
+
+-- |Unwrap a HsDecl and call setPrecedingLines on it
+-- ++AZ++ TODO: get rid of this, it is a synonym only
+setPrecedingLinesDecl :: LHsDecl GhcPs -> Int -> Int -> Anns -> Anns
+setPrecedingLinesDecl ld n c ans = setPrecedingLines ld n c ans
+
+-- ---------------------------------------------------------------------
+
+-- | Adjust the entry annotations to provide an `n` line preceding gap
+setPrecedingLines :: (Data a) => LocatedA a -> Int -> Int -> Anns -> Anns
+setPrecedingLines ast n c anne = setEntryDP ast (DP n c) anne
+
+-- ---------------------------------------------------------------------
+
+-- |Return the true entry 'DeltaPos' from the annotation for a given AST
+-- element. This is the 'DeltaPos' ignoring any comments.
+getEntryDP :: (Data a) => Anns -> Located a -> DeltaPos
+getEntryDP anns ast =
+ case Map.lookup (mkAnnKey ast) anns of
+ Nothing -> DP 0 0
+ Just ann -> annTrueEntryDelta ann
+
+-- ---------------------------------------------------------------------
+
+setEntryDPDecl :: LHsDecl GhcPs -> DeltaPos -> LHsDecl GhcPs
+setEntryDPDecl decl@(L _ (ValD x (FunBind a b (MG c (L d ms ) e) f))) dp
+ = L l' (ValD x (FunBind a b (MG c (L d ms') e) f))
+ where
+ L l' _ = setEntryDP' decl dp
+ ms' :: [LMatch GhcPs (LHsExpr GhcPs)]
+ ms' = case ms of
+ [] -> []
+ (m0':ms0) -> setEntryDP' m0' dp : ms0
+setEntryDPDecl d dp = setEntryDP' d dp
+
+-- ---------------------------------------------------------------------
+
+-- |Set the true entry 'DeltaPos' from the annotation for a given AST
+-- element. This is the 'DeltaPos' ignoring any comments.
+-- setEntryDP' :: (Data a) => LocatedA a -> DeltaPos -> LocatedA a
+setEntryDP' :: (Monoid t) => LocatedAn t a -> DeltaPos -> LocatedAn t a
+setEntryDP' (L (SrcSpanAnn ApiAnnNotUsed l) a) dp
+ = L (SrcSpanAnn
+ (ApiAnn (Anchor (realSrcSpan l) (MovedAnchor dp)) mempty noCom)
+ l) a
+setEntryDP' (L (SrcSpanAnn (ApiAnn (Anchor r _) an (AnnComments [])) l) a) dp
+ = L (SrcSpanAnn
+ (ApiAnn (Anchor r (MovedAnchor dp)) an (AnnComments []))
+ l) a
+setEntryDP' (L (SrcSpanAnn (ApiAnn (Anchor r _) an cs) l) a) dp
+ = case sort (priorComments cs) of
+ [] ->
+ L (SrcSpanAnn
+ (ApiAnn (Anchor r (MovedAnchor dp)) an cs)
+ l) a
+ (L ca c:cs') ->
+ L (SrcSpanAnn
+ (ApiAnn (Anchor r (MovedAnchor edp)) an cs'')
+ l) a
+ where
+ cs'' = setPriorComments cs (L (Anchor (anchor ca) (MovedAnchor dp)) c:cs')
+ lc = head $ reverse $ (L ca c:cs')
+ DP line col = ss2delta (ss2pos $ anchor $ getLoc lc) r
+ -- TODO: this adjustment by 1 happens all over the place. Generalise it
+ edp' = if line == 0 then DP line col
+ else DP line (col - 1)
+ edp = edp' `debug` ("setEntryDP' :" ++ showGhc (edp', (ss2pos $ anchor $ getLoc lc), r))
+ -- edp = if line == 0 then DP (line, col)
+ -- else DP (line, col - 1)
+
+-- |Set the true entry 'DeltaPos' from the annotation for a given AST
+-- element. This is the 'DeltaPos' ignoring any comments.
+setEntryDP :: (Data a) => LocatedA a -> DeltaPos -> Anns -> Anns
+setEntryDP _ast _dp anns = anns
+
+-- ---------------------------------------------------------------------
+
+addAnnAnchorDelta :: LayoutStartCol -> RealSrcSpan -> AnnAnchor -> AnnAnchor
+addAnnAnchorDelta _off _anc (AD d) = AD d
+addAnnAnchorDelta off anc (AR r)
+ = AD (adjustDeltaForOffset 0 off (ss2deltaEnd anc r))
+
+-- Set the entry DP for an element coming after an existing keyword annotation
+setEntryDPFromAnchor :: LayoutStartCol -> AnnAnchor -> LocatedA t -> LocatedA t
+setEntryDPFromAnchor _off (AD _) (L la a) = L la a
+setEntryDPFromAnchor off (AR anc) ll@(L la _) = setEntryDP' ll dp'
+ where
+ r = case la of
+ (SrcSpanAnn ApiAnnNotUsed l) -> realSrcSpan l
+ (SrcSpanAnn (ApiAnn (Anchor r' _) _ _) _) -> r'
+ dp' = adjustDeltaForOffset 0 off (ss2deltaEnd anc r)
+
+-- ---------------------------------------------------------------------
+
+-- -- |When setting an entryDP, the leading comment needs to be adjusted too
+-- setCommentEntryDP :: Annotation -> DeltaPos -> Annotation
+-- -- setCommentEntryDP ann dp = error $ "setCommentEntryDP:ann'=" ++ show ann'
+-- setCommentEntryDP ann dp = ann'
+-- where
+-- ann' = case (annPriorComments ann) of
+-- [] -> ann
+-- [(pc,_)] -> ann { annPriorComments = [(pc,dp)] }
+-- ((pc,_):pcs) -> ann { annPriorComments = ((pc,dp):pcs) }
+
+-- ---------------------------------------------------------------------
+
+-- |Take the annEntryDelta associated with the first item and associate it with the second.
+-- Also transfer any comments occuring before it.
+transferEntryDP :: (Monad m, Monoid t) => LocatedAn t a -> LocatedAn t b -> TransformT m (LocatedAn t b)
+transferEntryDP (L (SrcSpanAnn ApiAnnNotUsed l1) _) (L (SrcSpanAnn ApiAnnNotUsed _) b) = do
+ logTr $ "transferEntryDP': ApiAnnNotUsed,ApiAnnNotUsed"
+ return (L (SrcSpanAnn ApiAnnNotUsed l1) b)
+transferEntryDP (L (SrcSpanAnn (ApiAnn anc _an cs) _l1) _) (L (SrcSpanAnn ApiAnnNotUsed l2) b) = do
+ logTr $ "transferEntryDP': ApiAnn,ApiAnnNotUsed"
+ return (L (SrcSpanAnn (ApiAnn anc mempty cs) l2) b)
+transferEntryDP (L (SrcSpanAnn (ApiAnn anc1 _an1 cs1) _l1) _) (L (SrcSpanAnn (ApiAnn _anc2 an2 cs2) l2) b) = do
+ logTr $ "transferEntryDP': ApiAnn,ApiAnn"
+ -- Problem: if the original had preceding comments, blindly
+ -- transferring the location is not correct
+ case priorComments cs1 of
+ [] -> return (L (SrcSpanAnn (ApiAnn anc1 an2 cs2) l2) b)
+ -- TODO: what happens if the receiving side already has comments?
+ (L anc _:_) -> do
+ logDataWithAnnsTr "transferEntryDP':priorComments anc=" anc
+ return (L (SrcSpanAnn (ApiAnn (kludgeAnchor anc) an2 cs2) l2) b)
+transferEntryDP (L (SrcSpanAnn ApiAnnNotUsed _l1) _) (L (SrcSpanAnn (ApiAnn anc2 an2 cs2) l2) b) = do
+ logTr $ "transferEntryDP': ApiAnnNotUsed,ApiAnn"
+ return (L (SrcSpanAnn (ApiAnn anc2' an2 cs2) l2) b)
+ where
+ anc2' = case anc2 of
+ Anchor _a op -> Anchor (realSrcSpan l2) op
+
+-- |Take the annEntryDelta associated with the first item and associate it with the second.
+-- Also transfer any comments occuring before it.
+-- TODO: call transferEntryDP, and use pushDeclDP
+transferEntryDP' :: (Monad m) => LHsDecl GhcPs -> LHsDecl GhcPs -> TransformT m (LHsDecl GhcPs)
+transferEntryDP' la lb = do
+ (L l2 b) <- transferEntryDP la lb
+ return (L l2 (pushDeclDP b (DP 0 0)))
+
+-- There is an off-by-one in DPs. I *think* it has to do wether we
+-- calculate the final position when applying it against the stored
+-- final pos or against another RealSrcSpan. Must get to the bottom
+-- of it and come up with a canonical DP. This function adjusts a
+-- "comment space" DP to a "enterAnn" space one
+kludgeAnchor :: Anchor -> Anchor
+kludgeAnchor a@(Anchor _ (MovedAnchor (DP 0 _))) = a
+kludgeAnchor (Anchor a (MovedAnchor (DP r c))) = (Anchor a (MovedAnchor (DP r (c - 1))))
+kludgeAnchor a = a
+
+pushDeclDP :: HsDecl GhcPs -> DeltaPos -> HsDecl GhcPs
+pushDeclDP (ValD x (FunBind a b (MG c (L d ms ) e) f)) dp
+ = ValD x (FunBind a b (MG c (L d' ms') e) f)
+ where
+ L d' _ = setEntryDP' (L d ms) dp
+ ms' :: [LMatch GhcPs (LHsExpr GhcPs)]
+ ms' = case ms of
+ [] -> []
+ (m0':ms0) -> setEntryDP' m0' dp : ms0
+pushDeclDP d _dp = d
+
+-- ---------------------------------------------------------------------
+
+addTrailingComma :: (Data a) => Located a -> DeltaPos -> Anns -> Anns
+addTrailingComma a dp anns =
+ case Map.lookup (mkAnnKey a) anns of
+ Nothing -> anns
+ Just an ->
+ case find isAnnComma (annsDP an) of
+ Nothing -> Map.insert (mkAnnKey a) (an { annsDP = annsDP an ++ [(G AnnComma,dp)]}) anns
+ Just _ -> anns
+ where
+ isAnnComma (G AnnComma,_) = True
+ isAnnComma _ = False
+
+-- ---------------------------------------------------------------------
+
+removeTrailingComma :: (Data a) => Located a -> Anns -> Anns
+removeTrailingComma a anns =
+ case Map.lookup (mkAnnKey a) anns of
+ Nothing -> anns
+ Just an ->
+ case find isAnnComma (annsDP an) of
+ Nothing -> anns
+ Just _ -> Map.insert (mkAnnKey a) (an { annsDP = filter (not.isAnnComma) (annsDP an) }) anns
+ where
+ isAnnComma (G AnnComma,_) = True
+ isAnnComma _ = False
+
+-- ---------------------------------------------------------------------
+
+balanceCommentsList :: (Monad m) => [LHsDecl GhcPs] -> TransformT m [LHsDecl GhcPs]
+balanceCommentsList [] = return []
+balanceCommentsList [x] = return [x]
+balanceCommentsList (a:b:ls) = do
+ (a',b') <- balanceComments a b
+ r <- balanceCommentsList (b':ls)
+ return (a':r)
+
+-- |The relatavise phase puts all comments appearing between the end of one AST
+-- item and the beginning of the next as 'annPriorComments' for the second one.
+-- This function takes two adjacent AST items and moves any 'annPriorComments'
+-- from the second one to the 'annFollowingComments' of the first if they belong
+-- to it instead. This is typically required before deleting or duplicating
+-- either of the AST elements.
+balanceComments :: (Monad m)
+ => LHsDecl GhcPs -> LHsDecl GhcPs
+ -> TransformT m (LHsDecl GhcPs, LHsDecl GhcPs)
+balanceComments first second = do
+ -- ++AZ++ : replace the nested casts with appropriate gmapM
+ -- logTr $ "balanceComments entered"
+ -- logDataWithAnnsTr "first" first
+ case first of
+ (L l (ValD x fb@(FunBind{}))) -> do
+ (L l' fb',second') <- balanceCommentsFB (L l fb) second
+ return (L l' (ValD x fb'), second')
+ _ -> balanceComments' first second
+
+-- |Once 'balanceComments' has been called to move trailing comments to a
+-- 'FunBind', these need to be pushed down from the top level to the last
+-- 'Match' if that 'Match' needs to be manipulated.
+balanceCommentsFB :: (Data b,Monad m)
+ => LHsBind GhcPs -> LocatedA b -> TransformT m (LHsBind GhcPs, LocatedA b)
+balanceCommentsFB (L lf (FunBind x n (MG mx (L lm matches) o) t)) second = do
+ logTr $ "balanceCommentsFB entered: " ++ showGhc (ss2range $ locA lf)
+ matches' <- balanceCommentsList' matches
+ let (m,ms) = case reverse matches' of
+ (m':ms') -> (m',ms')
+ _ -> error "balanceCommentsFB"
+ (m',second') <- balanceComments' m second
+ m'' <- balanceCommentsMatch m'
+ logTr $ "balanceCommentsMatch done"
+ return (L lf (FunBind x n (MG mx (L lm (reverse (m'':ms))) o) t), second')
+balanceCommentsFB f s = balanceComments' f s
+
+-- | Move comments on the same line as the end of the match into the
+-- GRHS, prior to the binds
+balanceCommentsMatch :: (Monad m)
+ => LMatch GhcPs (LHsExpr GhcPs) -> TransformT m (LMatch GhcPs (LHsExpr GhcPs))
+balanceCommentsMatch (L l (Match am mctxt pats (GRHSs xg grhss binds))) = do
+ logTr $ "balanceCommentsMatch: (loc1)=" ++ showGhc (ss2range (locA l))
+ logTr $ "balanceCommentsMatch: (move',stay')=" ++ showAst (move',stay')
+ logTr $ "balanceCommentsMatch: (logInfo)=" ++ showAst (logInfo)
+ logTr $ "balanceCommentsMatch: (loc1)=" ++ showGhc (ss2range (locA l))
+ logTr $ "balanceCommentsMatch: (anc1,cs1f)=" ++ showAst (anc1,cs1f)
+ logTr $ "balanceCommentsMatch: (l'', grhss')=" ++ showAst (l'', grhss')
+ return (L l'' (Match am mctxt pats (GRHSs xg grhss' binds')))
+ where
+ simpleBreak (r,_) = r /= 0
+ (SrcSpanAnn an1 _loc1) = l
+ anc1 = addCommentOrigDeltas $ apiAnnComments an1
+ cs1f = getFollowingComments anc1
+ -- (move',stay') = break simpleBreak (commentsDeltas (anchorFromLocatedA (L l ())) cs1f)
+ (move',stay') = break simpleBreak (trailingCommentsDeltas (anchorFromLocatedA (L l ())) cs1f)
+ move = map snd move'
+ stay = map snd stay'
+ (l'', grhss', binds', logInfo)
+ = case reverse grhss of
+ [] -> (l, [], binds, (AnnComments [], SrcSpanAnn ApiAnnNotUsed noSrcSpan))
+ (L lg g@(GRHS ApiAnnNotUsed _grs _rhs):gs) -> (l, reverse (L lg g:gs), binds, (AnnComments [], SrcSpanAnn ApiAnnNotUsed noSrcSpan))
+ (L lg (GRHS ag grs rhs):gs) ->
+ let
+ anc1' = setFollowingComments anc1 stay
+ an1' = setCommentsSrcAnn l anc1'
+
+ -- ---------------------------------
+ (moved,bindsm) = pushTrailingComments WithWhere (AnnCommentsBalanced [] move) binds
+ -- ---------------------------------
+
+ (ApiAnn anc an lgc) = ag
+ lgc' = splitComments (realSrcSpan lg) $ addCommentOrigDeltas lgc
+ ag' = if moved
+ then ApiAnn anc an lgc'
+ else ApiAnn anc an (lgc' <> (AnnCommentsBalanced [] move))
+ -- ag' = ApiAnn anc an lgc'
+
+ in (an1', (reverse $ (L lg (GRHS ag' grs rhs):gs)), bindsm, (anc1',an1'))
+
+pushTrailingComments :: WithWhere -> ApiAnnComments -> HsLocalBinds GhcPs -> (Bool, HsLocalBinds GhcPs)
+pushTrailingComments _ _cs b@EmptyLocalBinds{} = (False, b)
+pushTrailingComments _ _cs (HsIPBinds _ _) = error "TODO: pushTrailingComments:HsIPBinds"
+pushTrailingComments w cs lb@(HsValBinds an _)
+ = (True, HsValBinds an' vb)
+ where
+ (decls, _, _ws1) = runTransform mempty (hsDeclsValBinds lb)
+ (an', decls') = case reverse decls of
+ [] -> (addCommentsToApiAnn (spanHsLocaLBinds lb) an cs, decls)
+ (L la d:ds) -> (an, L (addCommentsToSrcAnn la cs) d:ds)
+ (vb,_ws2) = case runTransform mempty (replaceDeclsValbinds w lb decls') of
+ ((HsValBinds _ vb'), _, ws2') -> (vb', ws2')
+ _ -> (ValBinds NoAnnSortKey emptyBag [], [])
+
+
+balanceCommentsList' :: (Monad m) => [LocatedA a] -> TransformT m [LocatedA a]
+balanceCommentsList' [] = return []
+balanceCommentsList' [x] = return [x]
+balanceCommentsList' (a:b:ls) = do
+ logTr $ "balanceCommentsList' entered"
+ (a',b') <- balanceComments' a b
+ r <- balanceCommentsList' (b':ls)
+ return (a':r)
+
+-- |Prior to moving an AST element, make sure any trailing comments belonging to
+-- it are attached to it, and not the following element. Of necessity this is a
+-- heuristic process, to be tuned later. Possibly a variant should be provided
+-- with a passed-in decision function.
+-- The initial situation is that all comments for a given anchor appear as prior comments
+-- Many of these should in fact be following comments for the previous anchor
+balanceComments' :: (Monad m) => LocatedA a -> LocatedA b -> TransformT m (LocatedA a, LocatedA b)
+balanceComments' la1 la2 = do
+ logTr $ "balanceComments': (loc1,loc2)=" ++ showGhc (ss2range loc1,ss2range loc2)
+ logTr $ "balanceComments': (anchorFromLocatedA la1)=" ++ showGhc (anchorFromLocatedA la1)
+ logTr $ "balanceComments': (sort cs2b)=" ++ showAst (sort cs2b)
+ logTr $ "balanceComments': (move',stay')=" ++ showAst (move',stay')
+ logTr $ "balanceComments': (move'',stay'')=" ++ showAst (move'',stay'')
+ logTr $ "balanceComments': (move,stay)=" ++ showAst (move,stay)
+ return (la1', la2')
+ where
+ simpleBreak n (r,_) = r > n
+ L (SrcSpanAnn an1 loc1) f = la1
+ L (SrcSpanAnn an2 loc2) s = la2
+ anc1 = addCommentOrigDeltas $ apiAnnComments an1
+ anc2 = addCommentOrigDeltas $ apiAnnComments an2
+ cs1f = getFollowingComments anc1
+ cs2b = priorComments anc2
+ (stay'',move') = break (simpleBreak 1) (priorCommentsDeltas (anchorFromLocatedA la2) cs2b)
+ -- Need to also check for comments more closely attached to la1,
+ -- ie trailing on the same line
+ (move'',stay') = break (simpleBreak 0) (trailingCommentsDeltas (anchorFromLocatedA la1) (map snd stay''))
+ move = map snd (move'' ++ move')
+ stay = map snd stay'
+ cs1 = setFollowingComments anc1 (sort $ cs1f ++ move)
+ cs2 = setPriorComments anc2 stay
+
+ an1' = setCommentsSrcAnn (getLoc la1) cs1
+ an2' = setCommentsSrcAnn (getLoc la2) cs2
+ la1' = L an1' f
+ la2' = L an2' s
+
+-- | Like commentsDeltas, but calculates the delta from the end of the anchor, not the start
+trailingCommentsDeltas :: RealSrcSpan -> [LAnnotationComment]
+ -> [(Int, LAnnotationComment)]
+trailingCommentsDeltas _ [] = []
+trailingCommentsDeltas anc (la@(L l _):las)
+ = deltaComment anc la : trailingCommentsDeltas (anchor l) las
+ where
+ deltaComment anc' (L loc c) = (abs(ll - al), L loc c)
+ where
+ (al,_) = ss2posEnd anc'
+ (ll,_) = ss2pos (anchor loc)
+
+-- AZ:TODO: this is identical to commentsDeltas
+priorCommentsDeltas :: RealSrcSpan -> [LAnnotationComment]
+ -> [(Int, LAnnotationComment)]
+priorCommentsDeltas anc cs = go anc (reverse $ sort cs)
+ where
+ go :: RealSrcSpan -> [LAnnotationComment] -> [(Int, LAnnotationComment)]
+ go _ [] = []
+ go anc' (la@(L l _):las) = deltaComment anc' la : go (anchor l) las
+
+ deltaComment :: RealSrcSpan -> LAnnotationComment -> (Int, LAnnotationComment)
+ deltaComment anc' (L loc c) = (abs(ll - al), L loc c)
+ where
+ (al,_) = ss2pos anc'
+ (ll,_) = ss2pos (anchor loc)
+
+
+-- | Split comments into ones occuring before the end of the reference
+-- span, and those after it.
+splitComments :: RealSrcSpan -> ApiAnnComments -> ApiAnnComments
+splitComments p (AnnComments cs) = cs'
+ where
+ cmp (L (Anchor l _) _) = ss2pos l < ss2posEnd p
+ (before, after) = break cmp cs
+ cs' = case after of
+ [] -> AnnComments cs
+ _ -> AnnCommentsBalanced before after
+splitComments p (AnnCommentsBalanced cs ts) = AnnCommentsBalanced cs' ts'
+ where
+ cmp (L (Anchor l _) _) = ss2pos l < ss2posEnd p
+ (before, after) = break cmp cs
+ cs' = before
+ ts' = after <> ts
+
+-- | A GHC comment includes the span of the preceding (non-comment)
+-- token. Takes an original list of comments, and converts the
+-- 'Anchor's to have a have a `MovedAnchor` operation based on the
+-- original locations.
+commentOrigDeltas :: [LAnnotationComment] -> [LAnnotationComment]
+commentOrigDeltas [] = []
+commentOrigDeltas lcs@(L _ (GHC.AnnComment _ pt):_) = go pt lcs
+ -- TODO:AZ: we now have deltas wrt *all* tokens, not just preceding
+ -- non-comment. Simplify this.
+ where
+ go :: RealSrcSpan -> [LAnnotationComment] -> [LAnnotationComment]
+ go _ [] = []
+ go p (L (Anchor la _) (GHC.AnnComment t pp):ls)
+ = L (Anchor la op) (GHC.AnnComment t pp) : go p' ls
+ where
+ p' = p
+ (r,c) = ss2posEnd pp
+ op' = if r == 0
+ then MovedAnchor (ss2delta (r,c+1) la)
+ else MovedAnchor (ss2delta (r,c) la)
+ op = if t == AnnEofComment && op' == MovedAnchor (DP 0 0)
+ then MovedAnchor (DP 1 0)
+ else op'
+
+addCommentOrigDeltas :: ApiAnnComments -> ApiAnnComments
+addCommentOrigDeltas (AnnComments cs) = AnnComments (commentOrigDeltas cs)
+addCommentOrigDeltas (AnnCommentsBalanced pcs fcs)
+ = AnnCommentsBalanced (commentOrigDeltas pcs) (commentOrigDeltas fcs)
+
+addCommentOrigDeltasAnn :: (ApiAnn' a) -> (ApiAnn' a)
+addCommentOrigDeltasAnn ApiAnnNotUsed = ApiAnnNotUsed
+addCommentOrigDeltasAnn (ApiAnn e a cs) = ApiAnn e a (addCommentOrigDeltas cs)
+
+-- TODO: this is replicating functionality in ExactPrint. Sort out the
+-- import loop`
+anchorFromLocatedA :: LocatedA a -> RealSrcSpan
+anchorFromLocatedA (L (SrcSpanAnn an loc) _)
+ = case an of
+ ApiAnnNotUsed -> realSrcSpan loc
+ (ApiAnn anc _ _) -> anchor anc
+
+-- ---------------------------------------------------------------------
+
+balanceSameLineComments :: (Monad m)
+ => LMatch GhcPs (LHsExpr GhcPs) -> TransformT m (LMatch GhcPs (LHsExpr GhcPs))
+balanceSameLineComments (L la (Match anm mctxt pats (GRHSs x grhss lb))) = do
+ logTr $ "balanceSameLineComments: (la)=" ++ showGhc (ss2range $ locA la)
+ logTr $ "balanceSameLineComments: [logInfo]=" ++ showAst logInfo
+ return (L la' (Match anm mctxt pats (GRHSs x grhss' lb)))
+ where
+ simpleBreak n (r,_) = r > n
+ (la',grhss', logInfo) = case reverse grhss of
+ [] -> (la,grhss,[])
+ (L lg g@(GRHS ApiAnnNotUsed _gs _rhs):grs) -> (la,reverse $ (L lg g):grs,[])
+ (L lg (GRHS ga gs rhs):grs) -> (la'',reverse $ (L lg (GRHS ga' gs rhs)):grs,[(gac,(csp,csf))])
+ where
+ (SrcSpanAnn an1 _loc1) = la
+ anc1 = addCommentOrigDeltas $ apiAnnComments an1
+ (ApiAnn anc an _) = ga :: ApiAnn' GrhsAnn
+ (csp,csf) = case anc1 of
+ AnnComments cs -> ([],cs)
+ AnnCommentsBalanced p f -> (p,f)
+ (move',stay') = break (simpleBreak 0) (trailingCommentsDeltas (anchor anc) csf)
+ move = map snd move'
+ stay = map snd stay'
+ cs1 = AnnCommentsBalanced csp stay
+
+ gac = addCommentOrigDeltas $ apiAnnComments ga
+ gfc = getFollowingComments gac
+ gac' = setFollowingComments gac (sort $ gfc ++ move)
+ ga' = (ApiAnn anc an gac')
+
+ an1' = setCommentsSrcAnn la cs1
+ la'' = an1'
+
+-- ---------------------------------------------------------------------
+
+
+-- |After moving an AST element, make sure any comments that may belong
+-- with the following element in fact do. Of necessity this is a heuristic
+-- process, to be tuned later. Possibly a variant should be provided with a
+-- passed-in decision function.
+balanceTrailingComments :: (Monad m) => (Data a,Data b) => Located a -> Located b
+ -> TransformT m [(Comment, DeltaPos)]
+balanceTrailingComments first second = do
+ let
+ k1 = mkAnnKey first
+ k2 = mkAnnKey second
+ moveComments p ans = (ans',move)
+ where
+ an1 = gfromJust "balanceTrailingComments k1" $ Map.lookup k1 ans
+ an2 = gfromJust "balanceTrailingComments k2" $ Map.lookup k2 ans
+ cs1f = annFollowingComments an1
+ (move,stay) = break p cs1f
+ an1' = an1 { annFollowingComments = stay }
+ ans' = Map.insert k1 an1' $ Map.insert k2 an2 ans
+
+ simpleBreak (_,DP r _c) = r > 0
+
+ ans <- getAnnsT
+ let (ans',mov) = moveComments simpleBreak ans
+ putAnnsT ans'
+ return mov
+
+-- ---------------------------------------------------------------------
+
+-- ++AZ++ TODO: This needs to be renamed/reworked, based on what it actually gets used for
+-- |Move any 'annFollowingComments' values from the 'Annotation' associated to
+-- the first parameter to that of the second.
+moveTrailingComments :: (Data a,Data b)
+ => Located a -> Located b -> Transform ()
+moveTrailingComments first second = do
+ let
+ k1 = mkAnnKey first
+ k2 = mkAnnKey second
+ moveComments ans = ans'
+ where
+ an1 = gfromJust "moveTrailingComments k1" $ Map.lookup k1 ans
+ an2 = gfromJust "moveTrailingComments k2" $ Map.lookup k2 ans
+ cs1f = annFollowingComments an1
+ cs2f = annFollowingComments an2
+ an1' = an1 { annFollowingComments = [] }
+ an2' = an2 { annFollowingComments = cs1f ++ cs2f }
+ ans' = Map.insert k1 an1' $ Map.insert k2 an2' ans
+
+ modifyAnnsT moveComments
+
+-- ---------------------------------------------------------------------
+
+anchorEof :: ParsedSource -> ParsedSource
+anchorEof (L l m@(HsModule an _lo _mn _exps _imps _decls _ _)) = L l (m { hsmodAnn = an' })
+ where
+ an' = addCommentOrigDeltasAnn an
+
+-- ---------------------------------------------------------------------
+
+-- | Take an anchor and a preceding location, and generate an
+-- equivalent one with a 'MovedAnchor' delta.
+deltaAnchor :: Anchor -> RealSrcSpan -> Anchor
+deltaAnchor (Anchor anc _) ss = Anchor anc (MovedAnchor dp)
+ where
+ dp = ss2delta (ss2pos anc) ss
+
+-- ---------------------------------------------------------------------
+
+-- | Create a @SrcSpanAnn@ with a @MovedAnchor@ operation using the
+-- given @DeltaPos@.
+noAnnSrcSpanDP :: (Monoid ann) => SrcSpan -> DeltaPos -> SrcSpanAnn' (ApiAnn' ann)
+noAnnSrcSpanDP l dp
+ = SrcSpanAnn (ApiAnn (Anchor (realSrcSpan l) (MovedAnchor dp)) mempty noCom) l
+
+noAnnSrcSpanDP0 :: (Monoid ann) => SrcSpan -> SrcSpanAnn' (ApiAnn' ann)
+noAnnSrcSpanDP0 l = noAnnSrcSpanDP l (DP 0 0)
+
+noAnnSrcSpanDP1 :: (Monoid ann) => SrcSpan -> SrcSpanAnn' (ApiAnn' ann)
+noAnnSrcSpanDP1 l = noAnnSrcSpanDP l (DP 0 1)
+
+noAnnSrcSpanDPn :: (Monoid ann) => SrcSpan -> Int -> SrcSpanAnn' (ApiAnn' ann)
+noAnnSrcSpanDPn l s = noAnnSrcSpanDP l (DP 0 s)
+
+d0 :: AnnAnchor
+d0 = AD $ DP 0 0
+
+d1 :: AnnAnchor
+d1 = AD $ DP 0 1
+
+dn :: Int -> AnnAnchor
+dn n = AD $ DP 0 n
+
+m0 :: AnchorOperation
+m0 = MovedAnchor $ DP 0 0
+
+m1 :: AnchorOperation
+m1 = MovedAnchor $ DP 0 1
+
+mn :: Int -> AnchorOperation
+mn n = MovedAnchor $ DP 0 n
+
+addComma :: SrcSpanAnnA -> SrcSpanAnnA
+addComma (SrcSpanAnn ApiAnnNotUsed l)
+ = (SrcSpanAnn (ApiAnn (spanAsAnchor l) (AnnListItem [AddCommaAnn d0]) noCom) l)
+addComma (SrcSpanAnn (ApiAnn anc (AnnListItem as) cs) l)
+ = (SrcSpanAnn (ApiAnn anc (AnnListItem (AddCommaAnn d0:as)) cs) l)
+
+-- ---------------------------------------------------------------------
+
+-- | Insert a declaration into an AST element having sub-declarations
+-- (@HasDecls@) according to the given location function.
+insertAt :: (HasDecls ast)
+ => (LHsDecl GhcPs
+ -> [LHsDecl GhcPs]
+ -> [LHsDecl GhcPs])
+ -> ast
+ -> LHsDecl GhcPs
+ -> Transform ast
+insertAt f t decl = do
+ oldDecls <- hsDecls t
+ replaceDecls t (f decl oldDecls)
+
+-- |Insert a declaration at the beginning or end of the subdecls of the given
+-- AST item
+insertAtStart, insertAtEnd :: (HasDecls ast)
+ => ast
+ -> LHsDecl GhcPs
+ -> Transform ast
+
+insertAtStart = insertAt (:)
+insertAtEnd = insertAt (\x xs -> xs ++ [x])
+
+-- |Insert a declaration at a specific location in the subdecls of the given
+-- AST item
+insertAfter, insertBefore :: (HasDecls (LocatedA ast))
+ => LocatedA old
+ -> LocatedA ast
+ -> LHsDecl GhcPs
+ -> Transform (LocatedA ast)
+insertAfter (getLocA -> k) = insertAt findAfter
+ where
+ findAfter x xs =
+ case span (\(L l _) -> locA l /= k) xs of
+ ([],[]) -> [x]
+ (fs,[]) -> fs++[x]
+ (fs, b:bs) -> fs ++ (b : x : bs)
+ -- let (fs, b:bs) = span (\(L l _) -> locA l /= k) xs
+ -- in fs ++ (b : x : bs)
+insertBefore (getLocA -> k) = insertAt findBefore
+ where
+ findBefore x xs =
+ let (fs, bs) = span (\(L l _) -> locA l /= k) xs
+ in fs ++ (x : bs)
+
+-- =====================================================================
+-- start of HasDecls instances
+-- =====================================================================
+
+-- |Provide a means to get and process the immediate child declartions of a
+-- given AST element.
+class (Data t) => HasDecls t where
+-- ++AZ++: TODO: add tests to confirm that hsDecls followed by replaceDecls is idempotent
+
+ -- | Return the 'HsDecl's that are directly enclosed in the
+ -- given syntax phrase. They are always returned in the wrapped 'HsDecl'
+ -- form, even if orginating in local decls. This is safe, as annotations
+ -- never attach to the wrapper, only to the wrapped item.
+ hsDecls :: (Monad m) => t -> TransformT m [LHsDecl GhcPs]
+
+ -- | Replace the directly enclosed decl list by the given
+ -- decl list. Runs in the 'Transform' monad to be able to update list order
+ -- annotations, and rebalance comments and other layout changes as needed.
+ --
+ -- For example, a call on replaceDecls for a wrapped 'FunBind' having no
+ -- where clause will convert
+ --
+ -- @
+ -- -- |This is a function
+ -- foo = x -- comment1
+ -- @
+ -- in to
+ --
+ -- @
+ -- -- |This is a function
+ -- foo = x -- comment1
+ -- where
+ -- nn = 2
+ -- @
+ replaceDecls :: (Monad m) => t -> [LHsDecl GhcPs] -> TransformT m t
+
+-- ---------------------------------------------------------------------
+
+instance HasDecls ParsedSource where
+ hsDecls (L _ (HsModule _ _lo _mn _exps _imps decls _ _)) = return decls
+ replaceDecls (L l (HsModule a lo mname exps imps _decls deps haddocks)) decls
+ = do
+ logTr "replaceDecls LHsModule"
+ -- modifyAnnsT (captureOrder m decls)
+ return (L l (HsModule a lo mname exps imps decls deps haddocks))
+
+-- ---------------------------------------------------------------------
+
+instance HasDecls (LocatedA (Match GhcPs (LocatedA (HsExpr GhcPs)))) where
+ hsDecls (L _ (Match _ _ _ (GRHSs _ _ lb))) = hsDeclsValBinds lb
+
+ replaceDecls (L l (Match xm c p (GRHSs xr rhs binds))) []
+ = do
+ logTr "replaceDecls LMatch empty decls"
+ binds'' <- replaceDeclsValbinds WithoutWhere binds []
+ return (L l (Match xm c p (GRHSs xr rhs binds'')))
+
+ replaceDecls m@(L l (Match xm c p (GRHSs xr rhs binds))) newBinds
+ = do
+ logTr "replaceDecls LMatch nonempty decls"
+ -- Need to throw in a fresh where clause if the binds were empty,
+ -- in the annotations.
+ (l', rhs') <- case binds of
+ EmptyLocalBinds{} -> do
+ logTr $ "replaceDecls LMatch empty binds"
+ modifyAnnsT (setPrecedingLines (ghead "LMatch.replaceDecls" newBinds) 1 4)
+
+ -- only move the comment if the original where clause was empty.
+ -- toMove <- balanceTrailingComments m m
+ -- insertCommentBefore (mkAnnKey m) toMove (matchApiAnn AnnWhere)
+ -- TODO: move trailing comments on the same line to before the binds
+ logDataWithAnnsTr "Match.replaceDecls:balancing comments:m" m
+ L l' m' <- balanceSameLineComments m
+ logDataWithAnnsTr "Match.replaceDecls:(m1')" (L l' m')
+ return (l', grhssGRHSs $ m_grhss m')
+ _ -> return (l, rhs)
+ binds'' <- replaceDeclsValbinds WithWhere binds newBinds
+ logDataWithAnnsTr "Match.replaceDecls:binds'" binds''
+ return (L l' (Match xm c p (GRHSs xr rhs' binds'')))
+
+-- ---------------------------------------------------------------------
+
+instance HasDecls (LocatedA (HsExpr GhcPs)) where
+ hsDecls (L _ (HsLet _ decls _ex)) = hsDeclsValBinds decls
+ hsDecls _ = return []
+
+ replaceDecls (L ll (HsLet x binds ex)) newDecls
+ = do
+ logTr "replaceDecls HsLet"
+ let lastAnc = realSrcSpan $ spanHsLocaLBinds binds
+ -- TODO: may be an intervening comment, take account for lastAnc
+ let (x', ex',newDecls') = case x of
+ ApiAnnNotUsed -> (x, ex, newDecls)
+ (ApiAnn a (AnnsLet l i) cs) ->
+ let
+ off = case l of
+ (AR r) -> LayoutStartCol $ snd $ ss2pos r
+ (AD (DP 0 _)) -> LayoutStartCol 0
+ (AD (DP _ c)) -> LayoutStartCol c
+ ex'' = setEntryDPFromAnchor off i ex
+ newDecls'' = case newDecls of
+ [] -> newDecls
+ (d:ds) -> setEntryDPDecl d (DP 0 0) : ds
+ in ( ApiAnn a (AnnsLet l (addAnnAnchorDelta off lastAnc i)) cs
+ , ex''
+ , newDecls'')
+ binds' <- replaceDeclsValbinds WithoutWhere binds newDecls'
+ return (L ll (HsLet x' binds' ex'))
+
+ -- TODO: does this make sense? Especially as no hsDecls for HsPar
+ replaceDecls (L l (HsPar x e)) newDecls
+ = do
+ logTr "replaceDecls HsPar"
+ e' <- replaceDecls e newDecls
+ return (L l (HsPar x e'))
+ replaceDecls old _new = error $ "replaceDecls (LHsExpr GhcPs) undefined for:" ++ showGhc old
+
+-- ---------------------------------------------------------------------
+
+-- | Extract the immediate declarations for a 'PatBind' wrapped in a 'ValD'. This
+-- cannot be a member of 'HasDecls' because a 'FunBind' is not idempotent
+-- for 'hsDecls' \/ 'replaceDecls'. 'hsDeclsPatBindD' \/ 'replaceDeclsPatBindD' is
+-- idempotent.
+hsDeclsPatBindD :: (Monad m) => LHsDecl GhcPs -> TransformT m [LHsDecl GhcPs]
+hsDeclsPatBindD (L l (ValD _ d)) = hsDeclsPatBind (L l d)
+hsDeclsPatBindD x = error $ "hsDeclsPatBindD called for:" ++ showGhc x
+
+-- | Extract the immediate declarations for a 'PatBind'. This
+-- cannot be a member of 'HasDecls' because a 'FunBind' is not idempotent
+-- for 'hsDecls' \/ 'replaceDecls'. 'hsDeclsPatBind' \/ 'replaceDeclsPatBind' is
+-- idempotent.
+hsDeclsPatBind :: (Monad m) => LHsBind GhcPs -> TransformT m [LHsDecl GhcPs]
+hsDeclsPatBind (L _ (PatBind _ _ (GRHSs _ _grhs lb) _)) = hsDeclsValBinds lb
+hsDeclsPatBind x = error $ "hsDeclsPatBind called for:" ++ showGhc x
+
+-- -------------------------------------
+
+-- | Replace the immediate declarations for a 'PatBind' wrapped in a 'ValD'. This
+-- cannot be a member of 'HasDecls' because a 'FunBind' is not idempotent
+-- for 'hsDecls' \/ 'replaceDecls'. 'hsDeclsPatBindD' \/ 'replaceDeclsPatBindD' is
+-- idempotent.
+replaceDeclsPatBindD :: (Monad m) => LHsDecl GhcPs -> [LHsDecl GhcPs]
+ -> TransformT m (LHsDecl GhcPs)
+replaceDeclsPatBindD (L l (ValD x d)) newDecls = do
+ (L _ d') <- replaceDeclsPatBind (L l d) newDecls
+ return (L l (ValD x d'))
+replaceDeclsPatBindD x _ = error $ "replaceDeclsPatBindD called for:" ++ showGhc x
+
+-- | Replace the immediate declarations for a 'PatBind'. This
+-- cannot be a member of 'HasDecls' because a 'FunBind' is not idempotent
+-- for 'hsDecls' \/ 'replaceDecls'. 'hsDeclsPatBind' \/ 'replaceDeclsPatBind' is
+-- idempotent.
+replaceDeclsPatBind :: (Monad m) => LHsBind GhcPs -> [LHsDecl GhcPs]
+ -> TransformT m (LHsBind GhcPs)
+replaceDeclsPatBind (L l (PatBind x a (GRHSs xr rhss binds) b)) newDecls
+ = do
+ logTr "replaceDecls PatBind"
+ -- Need to throw in a fresh where clause if the binds were empty,
+ -- in the annotations.
+ case binds of
+ EmptyLocalBinds{} -> do
+ let
+ addWhere _mkds =
+ error "TBD"
+ modifyAnnsT addWhere
+ modifyAnnsT (setPrecedingLines (ghead "LMatch.replaceDecls" newDecls) 1 4)
+
+ _ -> return ()
+
+ -- modifyAnnsT (captureOrderAnnKey (mkAnnKey p) newDecls)
+ binds'' <- replaceDeclsValbinds WithWhere binds newDecls
+ -- let binds' = L (getLoc binds) binds''
+ return (L l (PatBind x a (GRHSs xr rhss binds'') b))
+replaceDeclsPatBind x _ = error $ "replaceDeclsPatBind called for:" ++ showGhc x
+
+-- ---------------------------------------------------------------------
+
+instance HasDecls (LocatedA (Stmt GhcPs (LocatedA (HsExpr GhcPs)))) where
+ hsDecls (L _ (LetStmt _ lb)) = hsDeclsValBinds lb
+ hsDecls (L _ (LastStmt _ e _ _)) = hsDecls e
+ hsDecls (L _ (BindStmt _ _pat e)) = hsDecls e
+ hsDecls (L _ (BodyStmt _ e _ _)) = hsDecls e
+ hsDecls _ = return []
+
+ replaceDecls (L l (LetStmt x lb)) newDecls
+ = do
+ -- modifyAnnsT (captureOrder s newDecls)
+ lb'' <- replaceDeclsValbinds WithWhere lb newDecls
+ -- let lb' = L (getLoc lb) lb''
+ return (L l (LetStmt x lb''))
+ replaceDecls (L l (LastStmt x e d se)) newDecls
+ = do
+ e' <- replaceDecls e newDecls
+ return (L l (LastStmt x e' d se))
+ replaceDecls (L l (BindStmt x pat e)) newDecls
+ = do
+ e' <- replaceDecls e newDecls
+ return (L l (BindStmt x pat e'))
+
+ replaceDecls (L l (BodyStmt x e a b)) newDecls
+ = do
+ e' <- replaceDecls e newDecls
+ return (L l (BodyStmt x e' a b))
+ replaceDecls x _newDecls = return x
+
+-- =====================================================================
+-- end of HasDecls instances
+-- =====================================================================
+
+-- ---------------------------------------------------------------------
+
+-- |Do a transformation on an AST fragment by providing a function to process
+-- the general case and one specific for a 'LHsBind'. This is required
+-- because a 'FunBind' may have multiple 'Match' items, so we cannot
+-- gurantee that 'replaceDecls' after 'hsDecls' is idempotent.
+hasDeclsSybTransform :: (Data t2,Monad m)
+ => (forall t. HasDecls t => t -> m t)
+ -- ^Worker function for the general case
+ -> (LHsBind GhcPs -> m (LHsBind GhcPs))
+ -- ^Worker function for FunBind/PatBind
+ -> t2 -- ^Item to be updated
+ -> m t2
+hasDeclsSybTransform workerHasDecls workerBind t = trf t
+ where
+ trf = mkM parsedSource
+ `extM` lmatch
+ `extM` lexpr
+ `extM` lstmt
+ `extM` lhsbind
+ `extM` lvald
+
+ parsedSource (p::ParsedSource) = workerHasDecls p
+
+ lmatch (lm::LMatch GhcPs (LHsExpr GhcPs))
+ = workerHasDecls lm
+
+ lexpr (le::LHsExpr GhcPs)
+ = workerHasDecls le
+
+ lstmt (d::LStmt GhcPs (LHsExpr GhcPs))
+ = workerHasDecls d
+
+ lhsbind (b@(L _ FunBind{}):: LHsBind GhcPs)
+ = workerBind b
+ lhsbind b@(L _ PatBind{})
+ = workerBind b
+ lhsbind x = return x
+
+ lvald (L l (ValD x d)) = do
+ (L _ d') <- lhsbind (L l d)
+ return (L l (ValD x d'))
+ lvald x = return x
+
+-- ---------------------------------------------------------------------
+
+-- |A 'FunBind' wraps up one or more 'Match' items. 'hsDecls' cannot
+-- return anything for these as there is not meaningful 'replaceDecls' for it.
+-- This function provides a version of 'hsDecls' that returns the 'FunBind'
+-- decls too, where they are needed for analysis only.
+hsDeclsGeneric :: (Data t,Monad m) => t -> TransformT m [LHsDecl GhcPs]
+hsDeclsGeneric t = q t
+ where
+ q = return []
+ `mkQ` parsedSource
+ `extQ` lmatch
+ `extQ` lexpr
+ `extQ` lstmt
+ `extQ` lhsbind
+ `extQ` lhsbindd
+ `extQ` llocalbinds
+ `extQ` localbinds
+
+ parsedSource (p::ParsedSource) = hsDecls p
+
+ lmatch (lm::LMatch GhcPs (LHsExpr GhcPs)) = hsDecls lm
+
+ lexpr (le::LHsExpr GhcPs) = hsDecls le
+
+ lstmt (d::LStmt GhcPs (LHsExpr GhcPs)) = hsDecls d
+
+ -- ---------------------------------
+
+ lhsbind :: (Monad m) => LHsBind GhcPs -> TransformT m [LHsDecl GhcPs]
+ lhsbind (L _ (FunBind _ _ (MG _ (L _ matches) _) _)) = do
+ dss <- mapM hsDecls matches
+ return (concat dss)
+ lhsbind p@(L _ (PatBind{})) = do
+ hsDeclsPatBind p
+ lhsbind _ = return []
+
+ -- ---------------------------------
+
+ lhsbindd (L l (ValD _ d)) = lhsbind (L l d)
+ lhsbindd _ = return []
+
+ -- ---------------------------------
+
+ llocalbinds :: (Monad m) => Located (HsLocalBinds GhcPs) -> TransformT m [LHsDecl GhcPs]
+ llocalbinds (L _ ds) = localbinds ds
+
+ -- ---------------------------------
+
+ localbinds :: (Monad m) => HsLocalBinds GhcPs -> TransformT m [LHsDecl GhcPs]
+ localbinds d = hsDeclsValBinds d
+
+-- ---------------------------------------------------------------------
+
+-- |Look up the annotated order and sort the decls accordingly
+-- TODO:AZ: this should be pure
+orderedDecls :: (Monad m)
+ => AnnSortKey -> [LHsDecl GhcPs] -> TransformT m [LHsDecl GhcPs]
+orderedDecls sortKey decls = do
+ case sortKey of
+ NoAnnSortKey -> do
+ -- return decls
+ return $ sortBy (\a b -> compare (realSrcSpan $ getLocA a) (realSrcSpan $ getLocA b)) decls
+ AnnSortKey keys -> do
+ let ds = map (\s -> (rs $ getLocA s,s)) decls
+ ordered = map snd $ orderByKey ds keys
+ return ordered
+
+-- ---------------------------------------------------------------------
+
+hsDeclsValBinds :: (Monad m) => HsLocalBinds GhcPs -> TransformT m [LHsDecl GhcPs]
+hsDeclsValBinds lb = case lb of
+ HsValBinds _ (ValBinds sortKey bs sigs) -> do
+ let
+ bds = map wrapDecl (bagToList bs)
+ sds = map wrapSig sigs
+ orderedDecls sortKey (bds ++ sds)
+ HsValBinds _ (XValBindsLR _) -> error $ "hsDecls.XValBindsLR not valid"
+ HsIPBinds {} -> return []
+ EmptyLocalBinds {} -> return []
+
+data WithWhere = WithWhere
+ | WithoutWhere
+ deriving (Eq,Show)
+
+-- | Utility function for returning decls to 'HsLocalBinds'. Use with
+-- care, as this does not manage the declaration order, the
+-- ordering should be done by the calling function from the 'HsLocalBinds'
+-- context in the AST.
+replaceDeclsValbinds :: (Monad m)
+ => WithWhere
+ -> HsLocalBinds GhcPs -> [LHsDecl GhcPs]
+ -> TransformT m (HsLocalBinds GhcPs)
+replaceDeclsValbinds _ _ [] = do
+ return (EmptyLocalBinds NoExtField)
+replaceDeclsValbinds w b@(HsValBinds a _) new
+ = do
+ logTr "replaceDeclsValbinds"
+ let oldSpan = spanHsLocaLBinds b
+ an <- oldWhereAnnotation a w (realSrcSpan oldSpan)
+ let decs = listToBag $ concatMap decl2Bind new
+ let sigs = concatMap decl2Sig new
+ let sortKey = captureOrder new
+ return (HsValBinds an (ValBinds sortKey decs sigs))
+replaceDeclsValbinds _ (HsIPBinds {}) _new = error "undefined replaceDecls HsIPBinds"
+replaceDeclsValbinds w (EmptyLocalBinds _) new
+ = do
+ logTr "replaceDecls HsLocalBinds"
+ an <- newWhereAnnotation w
+ let newBinds = concatMap decl2Bind new
+ newSigs = concatMap decl2Sig new
+ let decs = listToBag $ newBinds
+ let sigs = newSigs
+ let sortKey = captureOrder new
+ return (HsValBinds an (ValBinds sortKey decs sigs))
+
+oldWhereAnnotation :: (Monad m)
+ => ApiAnn' AnnList -> WithWhere -> RealSrcSpan -> TransformT m (ApiAnn' AnnList)
+oldWhereAnnotation ApiAnnNotUsed ww _oldSpan = do
+ newSpan <- uniqueSrcSpanT
+ let w = case ww of
+ WithWhere -> [AddApiAnn AnnWhere (AD (DP 0 0))]
+ WithoutWhere -> []
+ let anc2' = Anchor (rs newSpan) (MovedAnchor (DP 0 1))
+ (anc, anc2) <- do
+ newSpan' <- uniqueSrcSpanT
+ return ( Anchor (rs newSpan') (MovedAnchor (DP 1 2))
+ , anc2')
+ let an = ApiAnn anc
+ (AnnList (Just anc2) Nothing Nothing w [])
+ noCom
+ return an
+oldWhereAnnotation (ApiAnn anc an cs) ww _oldSpan = do
+ -- TODO: when we set DP (0,0) for the HsValBinds ApiAnnAnchor, change the AnnList anchor to have the correct DP too
+ let (AnnList ancl o c _r t) = an
+ let w = case ww of
+ WithWhere -> [AddApiAnn AnnWhere (AD (DP 0 0))]
+ WithoutWhere -> []
+ (anc', ancl') <- do
+ case ww of
+ WithWhere -> return (anc, ancl)
+ WithoutWhere -> return (anc, ancl)
+ let an' = ApiAnn anc'
+ (AnnList ancl' o c w t)
+ cs
+ return an'
+
+newWhereAnnotation :: (Monad m) => WithWhere -> TransformT m (ApiAnn' AnnList)
+newWhereAnnotation ww = do
+ newSpan <- uniqueSrcSpanT
+ let anc = Anchor (rs newSpan) (MovedAnchor (DP 1 2))
+ let anc2 = Anchor (rs newSpan) (MovedAnchor (DP 1 4))
+ let w = case ww of
+ WithWhere -> [AddApiAnn AnnWhere (AD (DP 0 0))]
+ WithoutWhere -> []
+ let an = ApiAnn anc
+ (AnnList (Just anc2) Nothing Nothing w [])
+ noCom
+ return an
+
+-- ---------------------------------------------------------------------
+
+type Decl = LHsDecl GhcPs
+type PMatch = LMatch GhcPs (LHsExpr GhcPs)
+
+-- |Modify a 'LHsBind' wrapped in a 'ValD'. For a 'PatBind' the
+-- declarations are extracted and returned after modification. For a
+-- 'FunBind' the supplied 'SrcSpan' is used to identify the specific
+-- 'Match' to be transformed, for when there are multiple of them.
+modifyValD :: forall m t. (HasTransform m)
+ => SrcSpan
+ -> Decl
+ -> (PMatch -> [Decl] -> m ([Decl], Maybe t))
+ -> m (Decl,Maybe t)
+modifyValD p pb@(L ss (ValD _ (PatBind {} ))) f =
+ if (locA ss) == p
+ then do
+ ds <- liftT $ hsDeclsPatBindD pb
+ (ds',r) <- f (error "modifyValD.PatBind should not touch Match") ds
+ pb' <- liftT $ replaceDeclsPatBindD pb ds'
+ return (pb',r)
+ else return (pb,Nothing)
+modifyValD p ast f = do
+ (ast',r) <- runStateT (everywhereM (mkM doModLocal) ast) Nothing
+ return (ast',r)
+ where
+ doModLocal :: PMatch -> StateT (Maybe t) m PMatch
+ doModLocal (match@(L ss _) :: PMatch) = do
+ if (locA ss) == p
+ then do
+ ds <- lift $ liftT $ hsDecls match
+ (ds',r) <- lift $ f match ds
+ put r
+ match' <- lift $ liftT $ replaceDecls match ds'
+ return match'
+ else return match
+
+-- ---------------------------------------------------------------------
+
+-- |Used to integrate a @Transform@ into other Monad stacks
+class (Monad m) => (HasTransform m) where
+ liftT :: Transform a -> m a
+
+instance Monad m => HasTransform (TransformT m) where
+ liftT = hoistTransform (return . runIdentity)
+
+-- ---------------------------------------------------------------------
+
+-- | Apply a transformation to the decls contained in @t@
+modifyDeclsT :: (HasDecls t,HasTransform m)
+ => ([LHsDecl GhcPs] -> m [LHsDecl GhcPs])
+ -> t -> m t
+modifyDeclsT action t = do
+ decls <- liftT $ hsDecls t
+ decls' <- action decls
+ liftT $ replaceDecls t decls'
+
+-- ---------------------------------------------------------------------
diff --git a/utils/check-exact/Types.hs b/utils/check-exact/Types.hs
new file mode 100644
index 0000000000..46ce9b4291
--- /dev/null
+++ b/utils/check-exact/Types.hs
@@ -0,0 +1,331 @@
+{-# LANGUAGE DeriveDataTypeable #-}
+{-# LANGUAGE FlexibleInstances #-}
+{-# LANGUAGE GeneralizedNewtypeDeriving #-}
+{-# LANGUAGE MultiWayIf #-}
+{-# LANGUAGE NamedFieldPuns #-}
+{-# LANGUAGE RankNTypes #-}
+{-# LANGUAGE StandaloneDeriving #-}
+{-# LANGUAGE TypeSynonymInstances #-}
+{-# LANGUAGE ViewPatterns #-}
+
+module Types
+ where
+
+import GHC hiding (AnnComment)
+-- import GHC.Hs.Extension
+-- import GHC.Parser.Lexer (AddApiAnn(..))
+-- import GHC.Types.Basic hiding (EP)
+-- import GHC.Types.Name.Reader
+-- import GHC.Types.SrcLoc
+import GHC.Utils.Outputable hiding ( (<>) )
+-- import GHC.Driver.Session
+import GHC.Driver.Ppr
+-- import Control.Monad.Identity
+-- import Control.Monad.RWS
+import Data.Data (Data, toConstr,cast)
+-- import Data.Foldable
+-- import Data.List (sortBy, elemIndex)
+-- import Data.Maybe (fromMaybe)
+-- import Data.Ord (comparing)
+
+import qualified Data.Map as Map
+import qualified Data.Set as Set
+
+-- import qualified GHC
+-- import Lookup
+
+-- ---------------------------------------------------------------------
+
+-- ---------------------------------------------------------------------
+-- | This structure holds a complete set of annotations for an AST
+type Anns = Map.Map AnnKey Annotation
+
+emptyAnns :: Anns
+emptyAnns = Map.empty
+
+-- | For every @Located a@, use the @SrcSpan@ and constructor name of
+-- a as the key, to store the standard annotation.
+-- These are used to maintain context in the AP and EP monads
+data AnnKey = AnnKey RealSrcSpan AnnConName
+ deriving (Eq, Data, Ord)
+-- deriving instance Ord SrcSpan
+
+-- More compact Show instance
+instance Show AnnKey where
+ show (AnnKey ss cn) = "AnnKey " ++ showPprUnsafe ss ++ " " ++ show cn
+
+mkAnnKeyPrim :: (Data a) => Located a -> AnnKey
+mkAnnKeyPrim (L l a) = AnnKey (realSrcSpan l) (annGetConstr a)
+
+mkAnnKeyPrimA :: (Data a) => LocatedA a -> AnnKey
+mkAnnKeyPrimA (L l a) = AnnKey (realSrcSpan $ locA l) (annGetConstr a)
+
+-- Holds the name of a constructor
+data AnnConName = CN { unConName :: String }
+ deriving (Eq, Ord, Data)
+
+-- More compact show instance
+instance Show AnnConName where
+ show (CN s) = "CN " ++ show s
+
+annGetConstr :: (Data a) => a -> AnnConName
+annGetConstr a = CN (show $ toConstr a)
+
+-- |Make an unwrapped @AnnKey@ for the @LHsDecl@ case, a normal one otherwise.
+mkAnnKey :: (Data a) => Located a -> AnnKey
+mkAnnKey ld =
+ case cast ld :: Maybe (LHsDecl GhcPs) of
+ Just d -> declFun mkAnnKeyPrimA d
+ Nothing -> mkAnnKeyPrim ld
+
+
+type Pos = (Int,Int)
+
+deltaRow, deltaColumn :: DeltaPos -> Int
+deltaRow (DP r _) = r
+deltaColumn (DP _ c) = c
+
+-- ---------------------------------------------------------------------
+
+annNone :: Annotation
+annNone = Ann (DP 0 0) [] [] [] Nothing Nothing
+
+data Annotation = Ann
+ {
+ -- The first three fields relate to interfacing up into the AST
+ annEntryDelta :: !DeltaPos
+ -- ^ Offset used to get to the start of the SrcSpan, from whatever the prior
+ -- output was, including all annPriorComments (field below).
+ , annPriorComments :: ![(Comment, DeltaPos)]
+ -- ^ Comments coming after the last non-comment output of the preceding
+ -- element but before the SrcSpan being annotated by this Annotation. If
+ -- these are changed then annEntryDelta (field above) must also change to
+ -- match.
+ , annFollowingComments :: ![(Comment, DeltaPos)]
+ -- ^ Comments coming after the last output for the element subject to this
+ -- Annotation. These will only be added by AST transformations, and care
+ -- must be taken not to disturb layout of following elements.
+
+ -- The next three fields relate to interacing down into the AST
+ , annsDP :: ![(KeywordId, DeltaPos)]
+ -- ^ Annotations associated with this element.
+ , annSortKey :: !(Maybe [RealSrcSpan])
+ -- ^ Captures the sort order of sub elements. This is needed when the
+ -- sub-elements have been split (as in a HsLocalBind which holds separate
+ -- binds and sigs) or for infix patterns where the order has been
+ -- re-arranged. It is captured explicitly so that after the Delta phase a
+ -- SrcSpan is used purely as an index into the annotations, allowing
+ -- transformations of the AST including the introduction of new Located
+ -- items or re-arranging existing ones.
+ , annCapturedSpan :: !(Maybe AnnKey)
+ -- ^ Occasionally we must calculate a SrcSpan for an unlocated list of
+ -- elements which we must remember for the Print phase. e.g. the statements
+ -- in a HsLet or HsDo. These must be managed as a group because they all
+ -- need eo be vertically aligned for the Haskell layout rules, and this
+ -- guarantees this property in the presence of AST edits.
+
+ } deriving (Eq)
+
+-- ---------------------------------------------------------------------
+
+declFun :: (forall a . Data a => LocatedA a -> b) -> LHsDecl GhcPs -> b
+declFun f (L l de) =
+ case de of
+ TyClD _ d -> f (L l d)
+ InstD _ d -> f (L l d)
+ DerivD _ d -> f (L l d)
+ ValD _ d -> f (L l d)
+ SigD _ d -> f (L l d)
+ KindSigD _ d -> f (L l d)
+ DefD _ d -> f (L l d)
+ ForD _ d -> f (L l d)
+ WarningD _ d -> f (L l d)
+ AnnD _ d -> f (L l d)
+ RuleD _ d -> f (L l d)
+ SpliceD _ d -> f (L l d)
+ DocD _ d -> f (L l d)
+ RoleAnnotD _ d -> f (L l d)
+
+-- ---------------------------------------------------------------------
+
+data ACS' a = ACS
+ { acs :: !(Map.Map a Int) -- ^ how many levels each AstContext should
+ -- propagate down the AST. Removed when it hits zero
+ } deriving (Show)
+
+instance Semigroup (ACS' AstContext) where
+ ACS a <> ACS b = ACS (Map.unionWith max a b)
+ -- For Data.Map, mappend == union, which is a left-biased replace
+ -- for key collisions
+
+instance Monoid (ACS' AstContext) where
+ mempty = ACS mempty
+
+type AstContextSet = ACS' AstContext
+-- data AstContextSet = ACS
+-- { acs :: !(Map.Map AstContext Int) -- ^ how many levels each AstContext should
+-- -- propagate down the AST. Removed when it
+-- -- hits zero
+-- } deriving (Show)
+
+defaultACS :: AstContextSet
+defaultACS = ACS Map.empty
+
+-- instance Outputable AstContextSet where
+instance (Show a) => Outputable (ACS' a) where
+ ppr x = text $ show x
+
+data AstContext = -- LambdaExpr
+ CaseAlt
+ | NoPrecedingSpace
+ | HasHiding
+ | AdvanceLine
+ | NoAdvanceLine
+ | Intercalate -- This item may have a list separator following
+ | InIE -- possible 'type' or 'pattern'
+ | PrefixOp
+ | PrefixOpDollar
+ | InfixOp -- RdrName may be used as an infix operator
+ | ListStart -- Identifies first element of a list in layout, so its indentation can me managed differently
+ | ListItem -- Identifies subsequent elements of a list in layout
+ | TopLevelDecl -- top level declaration
+ | NoDarrow
+ | AddVbar
+ | Deriving
+ | Parens -- TODO: Not currently used?
+ | ExplicitNeverActive
+ | InGadt
+ | InRecCon
+ | InClassDecl
+ | InSpliceDecl
+ | LeftMost -- Is this the leftmost operator in a chain of OpApps?
+ | InTypeApp -- HsTyVar in a TYPEAPP context. Has AnnAt
+ -- TODO:AZ: do we actually need this?
+
+ -- Next four used to identify current list context
+ | CtxOnly
+ | CtxFirst
+ | CtxMiddle
+ | CtxLast
+ | CtxPos Int -- 0 for first, increasing for subsequent
+
+ -- Next are used in tellContext to push context up the tree
+ | FollowingLine
+ deriving (Eq, Ord, Show)
+
+
+data ListContexts = LC { lcOnly,lcInitial,lcMiddle,lcLast :: !(Set.Set AstContext) }
+ deriving (Eq,Show)
+
+-- ---------------------------------------------------------------------
+
+data Rigidity = NormalLayout | RigidLayout deriving (Eq, Ord, Show)
+
+-- -- ---------------------------------------------------------------------
+-- -- | This structure holds a complete set of annotations for an AST
+-- type Anns = Map.Map AnnKey Annotation
+
+-- emptyAnns :: Anns
+-- emptyAnns = Map.empty
+
+-- -- | For every @Located a@, use the @SrcSpan@ and constructor name of
+-- -- a as the key, to store the standard annotation.
+-- -- These are used to maintain context in the AP and EP monads
+-- data AnnKey = AnnKey SrcSpan AnnConName
+-- deriving (Eq, Data, Ord)
+-- deriving instance Ord SrcSpan
+
+-- -- More compact Show instance
+-- instance Show AnnKey where
+-- show (AnnKey ss cn) = "AnnKey " ++ showPprUnsafe ss ++ " " ++ show cn
+
+-- mkAnnKeyPrim :: (Data a) => Located a -> AnnKey
+-- mkAnnKeyPrim (L l a) = AnnKey l (annGetConstr a)
+
+-- mkAnnKeyPrimA :: (Data a) => LocatedA a -> AnnKey
+-- mkAnnKeyPrimA (L l a) = AnnKey (locA l) (annGetConstr a)
+
+-- -- Holds the name of a constructor
+-- data AnnConName = CN { unConName :: String }
+-- deriving (Eq, Ord, Data)
+
+-- -- More compact show instance
+-- instance Show AnnConName where
+-- show (CN s) = "CN " ++ show s
+
+-- annGetConstr :: (Data a) => a -> AnnConName
+-- annGetConstr a = CN (show $ toConstr a)
+
+-- -- |Make an unwrapped @AnnKey@ for the @LHsDecl@ case, a normal one otherwise.
+-- mkAnnKey :: (Data a) => Located a -> AnnKey
+-- mkAnnKey ld =
+-- case cast ld :: Maybe (LHsDecl GhcPs) of
+-- Just d -> declFun mkAnnKeyPrimA d
+-- Nothing -> mkAnnKeyPrim ld
+
+
+-- type Pos = (Int,Int)
+
+-- -- | A relative positions, row then column
+-- newtype DeltaPos = DP (Int,Int) deriving (Show,Eq,Ord,Data)
+
+-- deltaRow, deltaColumn :: DeltaPos -> Int
+-- deltaRow (DP (r, _)) = r
+-- deltaColumn (DP (_, c)) = c
+
+-- ---------------------------------------------------------------------
+
+-- | A Haskell comment. The @AnnKeywordId@ is present if it has been converted
+-- from an @AnnKeywordId@ because the annotation must be interleaved into the
+-- stream and does not have a well-defined position
+data Comment = Comment
+ {
+ commentContents :: !String -- ^ The contents of the comment including separators
+
+ -- AZ:TODO: commentIdentifier is a misnomer, should be commentSrcSpan, it is
+ -- the thing we use to decide where in the output stream the comment should
+ -- go.
+ , commentAnchor :: !Anchor
+ , commentOrigin :: !(Maybe AnnKeywordId) -- ^ We sometimes turn syntax into comments in order to process them properly.
+ }
+ deriving (Eq)
+
+instance Show Comment where
+ show (Comment cs ss o) = "(Comment " ++ show cs ++ " " ++ showPprUnsafe ss ++ " " ++ show o ++ ")"
+
+instance Ord Comment where
+ compare (Comment _ ss1 _) (Comment _ ss2 _) = compare (anchor ss1) (anchor ss2)
+
+instance Outputable Comment where
+ ppr x = text (show x)
+
+-- | The different syntactic elements which are not represented in the
+-- AST.
+data KeywordId = G AnnKeywordId -- ^ A normal keyword
+ | AnnSemiSep -- ^ A separating comma
+ | AnnTypeApp -- ^ Visible type application annotation
+ | AnnComment Comment
+ | AnnString String -- ^ Used to pass information from
+ -- Delta to Print when we have to work
+ -- out details from the original
+ -- SrcSpan.
+ deriving (Eq)
+
+instance Show KeywordId where
+ show (G gc) = "(G " ++ show gc ++ ")"
+ show AnnSemiSep = "AnnSemiSep"
+ show AnnTypeApp = "AnnTypeApp"
+ show (AnnComment dc) = "(AnnComment " ++ show dc ++ ")"
+ show (AnnString s) = "(AnnString " ++ s ++ ")"
+
+-- | Marks the start column of a layout block.
+newtype LayoutStartCol = LayoutStartCol { getLayoutStartCol :: Int }
+ deriving (Eq, Num)
+
+instance Show LayoutStartCol where
+ show (LayoutStartCol sc) = "(LayoutStartCol " ++ show sc ++ ")"
+-- ---------------------------------------------------------------------
+
+-- Duplicated here so it can be used in show instances
+showGhc :: (Outputable a) => a -> String
+showGhc = showPprUnsafe
diff --git a/utils/check-exact/Utils.hs b/utils/check-exact/Utils.hs
new file mode 100644
index 0000000000..23f166514f
--- /dev/null
+++ b/utils/check-exact/Utils.hs
@@ -0,0 +1,596 @@
+{-# LANGUAGE FlexibleContexts #-}
+{-# LANGUAGE NamedFieldPuns #-}
+{-# LANGUAGE RankNTypes #-}
+{-# LANGUAGE ScopedTypeVariables #-}
+
+module Utils
+ -- (
+ -- -- * Manipulating Positons
+ -- ss2pos
+ -- , ss2posEnd
+ -- , undelta
+ -- , isPointSrcSpan
+ -- , pos2delta
+ -- , ss2delta
+ -- , addDP
+ -- , spanLength
+ -- , isGoodDelta
+ -- ) where
+ where
+import Control.Monad.State
+-- import qualified Data.ByteString as B
+-- import GHC.Generics hiding (Fixity)
+import Data.Function
+import Data.Ord (comparing)
+
+import GHC.Hs.Dump
+-- import Language.Haskell.GHC.ExactPrint.Types
+import Lookup
+
+-- import GHC.Data.Bag
+-- import GHC.Driver.Session
+-- import GHC.Data.FastString
+import GHC hiding (AnnComment)
+import qualified GHC
+-- import qualified Name as GHC
+-- import qualified NameSet as GHC
+-- import GHC.Utils.Outputable
+import GHC.Types.Name
+import GHC.Types.Name.Reader
+import GHC.Types.SrcLoc
+import GHC.Driver.Ppr
+import GHC.Data.FastString
+-- import GHC.Types.Var
+-- import GHC.Types.Name.Occurrence
+
+-- import qualified OccName(OccName(..),occNameString,pprNameSpaceBrief)
+import qualified GHC.Types.Name.Occurrence as OccName (OccName(..),pprNameSpaceBrief)
+
+import Control.Arrow
+
+import qualified Data.Map as Map
+import qualified Data.Set as Set
+import Data.Data hiding ( Fixity )
+import Data.List
+
+import Debug.Trace
+import Types
+
+-- ---------------------------------------------------------------------
+-- ---------------------------------------------------------------------
+-- ---------------------------------------------------------------------
+
+-- |Global switch to enable debug tracing in ghc-exactprint Delta / Print
+debugEnabledFlag :: Bool
+-- debugEnabledFlag = True
+debugEnabledFlag = False
+
+-- |Global switch to enable debug tracing in ghc-exactprint Pretty
+debugPEnabledFlag :: Bool
+debugPEnabledFlag = True
+-- debugPEnabledFlag = False
+
+-- |Provide a version of trace that comes at the end of the line, so it can
+-- easily be commented out when debugging different things.
+debug :: c -> String -> c
+debug c s = if debugEnabledFlag
+ then trace s c
+ else c
+
+-- |Provide a version of trace for the Pretty module, which can be enabled
+-- separately from 'debug' and 'debugM'
+debugP :: String -> c -> c
+debugP s c = if debugPEnabledFlag
+ then trace s c
+ else c
+
+debugM :: Monad m => String -> m ()
+debugM s = when debugEnabledFlag $ traceM s
+
+
+-- ---------------------------------------------------------------------
+
+warn :: c -> String -> c
+-- warn = flip trace
+warn c _ = c
+
+-- | A good delta has no negative values.
+isGoodDelta :: DeltaPos -> Bool
+isGoodDelta (DP ro co) = ro >= 0 && co >= 0
+
+
+-- | Create a delta from the current position to the start of the given
+-- @SrcSpan@.
+ss2delta :: Pos -> RealSrcSpan -> DeltaPos
+ss2delta ref ss = pos2delta ref (ss2pos ss)
+
+-- | create a delta from the end of a current span. The +1 is because
+-- the stored position ends up one past the span, this is prior to
+-- that adjustment
+ss2deltaEnd :: RealSrcSpan -> RealSrcSpan -> DeltaPos
+ss2deltaEnd rrs ss = ss2delta ref ss
+ where
+ (r,c) = ss2posEnd rrs
+ ref = if r == 0
+ then (r,c+1)
+ else (r,c)
+
+-- | create a delta from the start of a current span. The +1 is
+-- because the stored position ends up one past the span, this is
+-- prior to that adjustment
+ss2deltaStart :: RealSrcSpan -> RealSrcSpan -> DeltaPos
+ss2deltaStart rrs ss = ss2delta ref ss
+ where
+ (r,c) = ss2pos rrs
+ ref = if r == 0
+ -- then (r,c+1)
+ then (r,c)
+ else (r,c)
+
+-- | Convert the start of the second @Pos@ to be an offset from the
+-- first. The assumption is the reference starts before the second @Pos@
+pos2delta :: Pos -> Pos -> DeltaPos
+pos2delta (refl,refc) (l,c) = DP lo co
+ where
+ lo = l - refl
+ co = if lo == 0 then c - refc
+ else c
+
+-- | Apply the delta to the current position, taking into account the
+-- current column offset if advancing to a new line
+undelta :: Pos -> DeltaPos -> LayoutStartCol -> Pos
+undelta (l,c) (DP dl dc) (LayoutStartCol co) = (fl,fc)
+ where
+ fl = l + dl
+ fc = if dl == 0 then c + dc
+ else co + dc
+
+undeltaSpan :: RealSrcSpan -> AnnKeywordId -> DeltaPos -> AddApiAnn
+undeltaSpan anchor kw dp = AddApiAnn kw (AR sp)
+ where
+ (l,c) = undelta (ss2pos anchor) dp (LayoutStartCol 0)
+ len = length (keywordToString (G kw))
+ sp = range2rs ((l,c),(l,c+len))
+
+-- | Add together two @DeltaPos@ taking into account newlines
+--
+-- > DP (0, 1) `addDP` DP (0, 2) == DP (0, 3)
+-- > DP (0, 9) `addDP` DP (1, 5) == DP (1, 5)
+-- > DP (1, 4) `addDP` DP (1, 3) == DP (2, 3)
+addDP :: DeltaPos -> DeltaPos -> DeltaPos
+addDP (DP a b) (DP c d) =
+ if c >= 1 then DP (a+c) d
+ else DP a (b+d)
+
+-- | "Subtract" two @DeltaPos@ from each other, in the sense of calculating the
+-- remaining delta for the second after the first has been applied.
+-- invariant : if c = a `addDP` b
+-- then a `stepDP` c == b
+--
+-- Cases where first DP is <= than second
+-- > DP (0, 1) `addDP` DP (0, 2) == DP (0, 1)
+-- > DP (1, 1) `addDP` DP (2, 0) == DP (1, 0)
+-- > DP (1, 3) `addDP` DP (1, 4) == DP (0, 1)
+-- > DP (1, 4) `addDP` DP (1, 4) == DP (1, 4)
+--
+-- Cases where first DP is > than second
+-- > DP (0, 3) `addDP` DP (0, 2) == DP (0,1) -- advance one at least
+-- > DP (3, 3) `addDP` DP (2, 4) == DP (1, 4) -- go one line forward and to expected col
+-- > DP (3, 3) `addDP` DP (0, 4) == DP (0, 1) -- maintain col delta at least
+-- > DP (1, 21) `addDP` DP (1, 4) == DP (1, 4) -- go one line forward and to expected col
+stepDP :: DeltaPos -> DeltaPos -> DeltaPos
+stepDP (DP a b) (DP c d)
+ | (a,b) == (c,d) = DP a b
+ | a == c = if b < d then DP 0 (d - b)
+ else if d == 0
+ then DP 1 0
+ else DP c d
+ | a < c = DP (c - a) d
+ | otherwise = DP 1 d
+
+-- ---------------------------------------------------------------------
+
+adjustDeltaForOffset :: Int -> LayoutStartCol -> DeltaPos -> DeltaPos
+adjustDeltaForOffset _ _colOffset dp@(DP 0 _) = dp -- same line
+adjustDeltaForOffset d (LayoutStartCol colOffset) (DP l c) = DP l (c - colOffset - d)
+
+-- ---------------------------------------------------------------------
+
+ss2pos :: RealSrcSpan -> Pos
+ss2pos ss = (srcSpanStartLine ss,srcSpanStartCol ss)
+
+ss2posEnd :: RealSrcSpan -> Pos
+ss2posEnd ss = (srcSpanEndLine ss,srcSpanEndCol ss)
+
+ss2range :: SrcSpan -> (Pos,Pos)
+ss2range ss = (ss2pos $ rs ss, ss2posEnd $ rs ss)
+
+rs2range :: RealSrcSpan -> (Pos,Pos)
+rs2range ss = (ss2pos ss, ss2posEnd ss)
+
+rs :: SrcSpan -> RealSrcSpan
+rs (RealSrcSpan s _) = s
+rs _ = badRealSrcSpan
+
+range2rs :: (Pos,Pos) -> RealSrcSpan
+range2rs (s,e) = mkRealSrcSpan (mkLoc s) (mkLoc e)
+ where
+ mkLoc (l,c) = mkRealSrcLoc (fsLit "ghc-exactprint") l c
+
+badRealSrcSpan :: RealSrcSpan
+badRealSrcSpan = mkRealSrcSpan bad bad
+ where
+ bad = mkRealSrcLoc (fsLit "ghc-exactprint-nospan") 0 0
+
+spanLength :: RealSrcSpan -> Int
+spanLength = (-) <$> srcSpanEndCol <*> srcSpanStartCol
+
+-- ---------------------------------------------------------------------
+-- | Checks whether a SrcSpan has zero length.
+isPointSrcSpan :: RealSrcSpan -> Bool
+isPointSrcSpan ss = spanLength ss == 0
+ && srcSpanStartLine ss == srcSpanEndLine ss
+
+-- ---------------------------------------------------------------------
+
+-- |Given a list of items and a list of keys, returns a list of items
+-- ordered by their position in the list of keys.
+orderByKey :: [(RealSrcSpan,a)] -> [RealSrcSpan] -> [(RealSrcSpan,a)]
+orderByKey keys order
+ -- AZ:TODO: if performance becomes a problem, consider a Map of the order
+ -- SrcSpan to an index, and do a lookup instead of elemIndex.
+
+ -- Items not in the ordering are placed to the start
+ = sortBy (comparing (flip elemIndex order . fst)) keys
+
+-- ---------------------------------------------------------------------
+
+isListComp :: HsStmtContext name -> Bool
+isListComp cts = case cts of
+ ListComp -> True
+ MonadComp -> True
+
+ DoExpr {} -> False
+ MDoExpr {} -> False
+ ArrowExpr -> False
+ GhciStmtCtxt -> False
+
+ PatGuard {} -> False
+ ParStmtCtxt {} -> False
+ TransStmtCtxt {} -> False
+
+-- ---------------------------------------------------------------------
+
+isGadt :: [LConDecl (GhcPass p)] -> Bool
+isGadt [] = False
+isGadt ((L _ (ConDeclGADT{})):_) = True
+isGadt _ = False
+
+-- ---------------------------------------------------------------------
+
+-- Is a RdrName of type Exact? SYB query, so can be extended to other types too
+isExactName :: (Data name) => name -> Bool
+isExactName = False `mkQ` isExact
+
+-- ---------------------------------------------------------------------
+
+ghcCommentText :: LAnnotationComment -> String
+ghcCommentText (L _ (GHC.AnnComment (AnnDocCommentNext s) _)) = s
+ghcCommentText (L _ (GHC.AnnComment (AnnDocCommentPrev s) _)) = s
+ghcCommentText (L _ (GHC.AnnComment (AnnDocCommentNamed s) _)) = s
+ghcCommentText (L _ (GHC.AnnComment (AnnDocSection _ s) _)) = s
+ghcCommentText (L _ (GHC.AnnComment (AnnDocOptions s) _)) = s
+ghcCommentText (L _ (GHC.AnnComment (AnnLineComment s) _)) = s
+ghcCommentText (L _ (GHC.AnnComment (AnnBlockComment s) _)) = s
+ghcCommentText (L _ (GHC.AnnComment (AnnEofComment) _)) = ""
+
+tokComment :: LAnnotationComment -> Comment
+tokComment t@(L lt _) = mkComment (normaliseCommentText $ ghcCommentText t) lt
+
+mkComment :: String -> Anchor -> Comment
+mkComment c anc = Comment c anc Nothing
+
+-- Windows comments include \r in them from the lexer.
+normaliseCommentText :: String -> String
+normaliseCommentText [] = []
+normaliseCommentText ('\r':xs) = normaliseCommentText xs
+normaliseCommentText (x:xs) = x:normaliseCommentText xs
+
+-- | Makes a comment which originates from a specific keyword.
+mkKWComment :: AnnKeywordId -> AnnAnchor -> Comment
+mkKWComment kw (AR ss)
+ = Comment (keywordToString $ G kw) (Anchor ss UnchangedAnchor) (Just kw)
+mkKWComment kw (AD dp)
+ = Comment (keywordToString $ G kw) (Anchor placeholderRealSpan (MovedAnchor dp)) (Just kw)
+
+comment2dp :: (Comment, DeltaPos) -> (KeywordId, DeltaPos)
+comment2dp = first AnnComment
+
+
+rogueComments :: ApiAnns -> [Comment]
+rogueComments as = extractRogueComments as
+ -- where
+ -- go :: Comment -> (Comment, DeltaPos)
+ -- go c@(Comment _str loc _mo) = (c, ss2delta (1,1) loc)
+
+-- extractComments :: ApiAnns -> [Comment]
+-- extractComments anns
+-- -- cm has type :: Map RealSrcSpan [LAnnotationComment]
+-- -- = map tokComment . sortRealLocated . concat $ Map.elems (apiAnnComments anns)
+-- = []
+
+extractRogueComments :: ApiAnns -> [Comment]
+extractRogueComments anns
+ -- cm has type :: Map RealSrcSpan [LAnnotationComment]
+ = map tokComment $ sortAnchorLocated (apiAnnRogueComments anns)
+
+sortAnchorLocated :: [GenLocated Anchor a] -> [GenLocated Anchor a]
+sortAnchorLocated = sortBy (compare `on` (anchor . getLoc))
+
+
+getAnnotationEP :: (Data a) => Located a -> Anns -> Maybe Annotation
+getAnnotationEP la as =
+ Map.lookup (mkAnnKey la) as
+
+-- | The "true entry" is the distance from the last concrete element to the
+-- start of the current element.
+annTrueEntryDelta :: Annotation -> DeltaPos
+annTrueEntryDelta Ann{annEntryDelta, annPriorComments} =
+ foldr addDP (DP 0 0) (map (\(a, b) -> addDP b (dpFromString $ commentContents a)) annPriorComments )
+ `addDP` annEntryDelta
+
+-- | Take an annotation and a required "true entry" and calculate an equivalent
+-- one relative to the last comment in the annPriorComments.
+annCommentEntryDelta :: Annotation -> DeltaPos -> DeltaPos
+annCommentEntryDelta Ann{annPriorComments} trueDP = dp
+ where
+ commentDP =
+ foldr addDP (DP 0 0) (map (\(a, b) -> addDP b (dpFromString $ commentContents a)) annPriorComments )
+ dp = stepDP commentDP trueDP
+
+-- | Return the DP of the first item that generates output, either a comment or the entry DP
+annLeadingCommentEntryDelta :: Annotation -> DeltaPos
+annLeadingCommentEntryDelta Ann{annPriorComments,annEntryDelta} = dp
+ where
+ dp = case annPriorComments of
+ [] -> annEntryDelta
+ ((_,ed):_) -> ed
+
+-- | Calculates the distance from the start of a string to the end of
+-- a string.
+dpFromString :: String -> DeltaPos
+dpFromString xs = dpFromString' xs 0 0
+ where
+ dpFromString' "" line col = DP line col
+ dpFromString' ('\n': cs) line _ = dpFromString' cs (line + 1) 0
+ dpFromString' (_:cs) line col = dpFromString' cs line (col + 1)
+
+-- ---------------------------------------------------------------------
+
+isSymbolRdrName :: RdrName -> Bool
+isSymbolRdrName n = isSymOcc $ rdrNameOcc n
+
+rdrName2String :: RdrName -> String
+rdrName2String r =
+ case isExact_maybe r of
+ Just n -> name2String n
+ Nothing ->
+ case r of
+ Unqual occ -> occNameString occ
+ Qual modname occ -> moduleNameString modname ++ "."
+ ++ occNameString occ
+ Orig _ occ -> occNameString occ
+ Exact n -> getOccString n
+
+name2String :: Name -> String
+name2String = showPprUnsafe
+
+-- ---------------------------------------------------------------------
+
+-- | Put the provided context elements into the existing set with fresh level
+-- counts
+setAcs :: Set.Set AstContext -> AstContextSet -> AstContextSet
+setAcs ctxt acs = setAcsWithLevel ctxt 3 acs
+
+-- | Put the provided context elements into the existing set with given level
+-- counts
+-- setAcsWithLevel :: Set.Set AstContext -> Int -> AstContextSet -> AstContextSet
+-- setAcsWithLevel ctxt level (ACS a) = ACS a'
+-- where
+-- upd s (k,v) = Map.insert k v s
+-- a' = foldl' upd a $ zip (Set.toList ctxt) (repeat level)
+setAcsWithLevel :: (Ord a) => Set.Set a -> Int -> ACS' a -> ACS' a
+setAcsWithLevel ctxt level (ACS a) = ACS a'
+ where
+ upd s (k,v) = Map.insert k v s
+ a' = foldl' upd a $ zip (Set.toList ctxt) (repeat level)
+
+-- ---------------------------------------------------------------------
+-- | Remove the provided context element from the existing set
+-- unsetAcs :: AstContext -> AstContextSet -> AstContextSet
+unsetAcs :: (Ord a) => a -> ACS' a -> ACS' a
+unsetAcs ctxt (ACS a) = ACS $ Map.delete ctxt a
+
+-- ---------------------------------------------------------------------
+
+-- | Are any of the contexts currently active?
+-- inAcs :: Set.Set AstContext -> AstContextSet -> Bool
+inAcs :: (Ord a) => Set.Set a -> ACS' a -> Bool
+inAcs ctxt (ACS a) = not $ Set.null $ Set.intersection ctxt (Set.fromList $ Map.keys a)
+
+-- | propagate the ACS down a level, dropping all values which hit zero
+-- pushAcs :: AstContextSet -> AstContextSet
+pushAcs :: ACS' a -> ACS' a
+pushAcs (ACS a) = ACS $ Map.mapMaybe f a
+ where
+ f n
+ | n <= 1 = Nothing
+ | otherwise = Just (n - 1)
+
+-- |Sometimes we have to pass the context down unchanged. Bump each count up by
+-- one so that it is unchanged after a @pushAcs@ call.
+-- bumpAcs :: AstContextSet -> AstContextSet
+bumpAcs :: ACS' a -> ACS' a
+bumpAcs (ACS a) = ACS $ Map.mapMaybe f a
+ where
+ f n = Just (n + 1)
+
+-- ---------------------------------------------------------------------
+
+occAttributes :: OccName.OccName -> String
+occAttributes o = "(" ++ ns ++ vo ++ tv ++ tc ++ d ++ ds ++ s ++ v ++ ")"
+ where
+ -- ns = (showSDocUnsafe $ OccName.pprNameSpaceBrief $ occNameSpace o) ++ ", "
+ ns = (showSDocUnsafe $ OccName.pprNameSpaceBrief $ occNameSpace o) ++ ", "
+ vo = if isVarOcc o then "Var " else ""
+ tv = if isTvOcc o then "Tv " else ""
+ tc = if isTcOcc o then "Tc " else ""
+ d = if isDataOcc o then "Data " else ""
+ ds = if isDataSymOcc o then "DataSym " else ""
+ s = if isSymOcc o then "Sym " else ""
+ v = if isValOcc o then "Val " else ""
+
+{-
+data NameSpace = VarName -- Variables, including "real" data constructors
+ | DataName -- "Source" data constructors
+ | TvName -- Type variables
+ | TcClsName -- Type constructors and classes; Haskell has them
+ -- in the same name space for now.
+-}
+
+ -- ---------------------------------------------------------------------
+
+locatedAnAnchor :: LocatedAn a t -> RealSrcSpan
+locatedAnAnchor (L (SrcSpanAnn ApiAnnNotUsed l) _) = realSrcSpan l
+locatedAnAnchor (L (SrcSpanAnn (ApiAnn a _ _) _) _) = anchor a
+
+ -- ---------------------------------------------------------------------
+
+-- showSDoc_ :: SDoc -> String
+-- showSDoc_ = showSDoc unsafeGlobalDynFlags
+
+-- showSDocDebug_ :: SDoc -> String
+-- showSDocDebug_ = showSDocDebug unsafeGlobalDynFlags
+
+
+ -- ---------------------------------------------------------------------
+
+showAst :: (Data a) => a -> String
+showAst ast
+ = showSDocUnsafe
+ $ showAstData NoBlankSrcSpan NoBlankApiAnnotations ast
+
+-- ---------------------------------------------------------------------
+-- Putting these here for the time being, to avoid import loops
+
+ghead :: String -> [a] -> a
+ghead info [] = error $ "ghead "++info++" []"
+ghead _info (h:_) = h
+
+glast :: String -> [a] -> a
+glast info [] = error $ "glast " ++ info ++ " []"
+glast _info h = last h
+
+gtail :: String -> [a] -> [a]
+gtail info [] = error $ "gtail " ++ info ++ " []"
+gtail _info h = tail h
+
+gfromJust :: String -> Maybe a -> a
+gfromJust _info (Just h) = h
+gfromJust info Nothing = error $ "gfromJust " ++ info ++ " Nothing"
+
+-- ---------------------------------------------------------------------
+
+-- Copied from syb for the test
+
+
+-- | Generic queries of type \"r\",
+-- i.e., take any \"a\" and return an \"r\"
+--
+type GenericQ r = forall a. Data a => a -> r
+
+
+-- | Make a generic query;
+-- start from a type-specific case;
+-- return a constant otherwise
+--
+mkQ :: ( Typeable a
+ , Typeable b
+ )
+ => r
+ -> (b -> r)
+ -> a
+ -> r
+(r `mkQ` br) a = case cast a of
+ Just b -> br b
+ Nothing -> r
+
+-- | Make a generic monadic transformation;
+-- start from a type-specific case;
+-- resort to return otherwise
+--
+mkM :: ( Monad m
+ , Typeable a
+ , Typeable b
+ )
+ => (b -> m b)
+ -> a
+ -> m a
+mkM = extM return
+
+-- | Flexible type extension
+ext0 :: (Typeable a, Typeable b) => c a -> c b -> c a
+ext0 def ext = maybe def id (gcast ext)
+
+
+-- | Extend a generic query by a type-specific case
+extQ :: ( Typeable a
+ , Typeable b
+ )
+ => (a -> q)
+ -> (b -> q)
+ -> a
+ -> q
+extQ f g a = maybe (f a) g (cast a)
+
+-- | Flexible type extension
+ext2 :: (Data a, Typeable t)
+ => c a
+ -> (forall d1 d2. (Data d1, Data d2) => c (t d1 d2))
+ -> c a
+ext2 def ext = maybe def id (dataCast2 ext)
+
+
+-- | Extend a generic monadic transformation by a type-specific case
+extM :: ( Monad m
+ , Typeable a
+ , Typeable b
+ )
+ => (a -> m a) -> (b -> m b) -> a -> m a
+extM def ext = unM ((M def) `ext0` (M ext))
+
+-- | Type extension of monadic transformations for type constructors
+ext2M :: (Monad m, Data d, Typeable t)
+ => (forall e. Data e => e -> m e)
+ -> (forall d1 d2. (Data d1, Data d2) => t d1 d2 -> m (t d1 d2))
+ -> d -> m d
+ext2M def ext = unM ((M def) `ext2` (M ext))
+
+-- | The type constructor for transformations
+newtype M m x = M { unM :: x -> m x }
+
+-- | Generic monadic transformations,
+-- i.e., take an \"a\" and compute an \"a\"
+--
+type GenericM m = forall a. Data a => a -> m a
+
+-- | Monadic variation on everywhere
+everywhereM :: forall m. Monad m => GenericM m -> GenericM m
+
+-- Bottom-up order is also reflected in order of do-actions
+everywhereM f = go
+ where
+ go :: GenericM m
+ go x = do
+ x' <- gmapM go x
+ f x'
diff --git a/utils/check-exact/cases/AddDecl1.expected.hs b/utils/check-exact/cases/AddDecl1.expected.hs
new file mode 100644
index 0000000000..88ef0fdd7d
--- /dev/null
+++ b/utils/check-exact/cases/AddDecl1.expected.hs
@@ -0,0 +1,13 @@
+module AddDecl where
+
+nn = n2
+
+-- Adding a declaration to an existing file
+
+-- | Do foo
+foo a b = a + b
+
+-- | Do bar
+bar x y = {- baz -} foo (x+y) x
+
+-- end of file
diff --git a/utils/check-exact/cases/AddDecl1.hs b/utils/check-exact/cases/AddDecl1.hs
new file mode 100644
index 0000000000..45c0cb3864
--- /dev/null
+++ b/utils/check-exact/cases/AddDecl1.hs
@@ -0,0 +1,11 @@
+module AddDecl where
+
+-- Adding a declaration to an existing file
+
+-- | Do foo
+foo a b = a + b
+
+-- | Do bar
+bar x y = {- baz -} foo (x+y) x
+
+-- end of file
diff --git a/utils/check-exact/cases/AddDecl2.expected.hs b/utils/check-exact/cases/AddDecl2.expected.hs
new file mode 100644
index 0000000000..2bbbcf5b37
--- /dev/null
+++ b/utils/check-exact/cases/AddDecl2.expected.hs
@@ -0,0 +1,13 @@
+module AddDecl where
+
+-- Adding a declaration to an existing file
+
+-- | Do foo
+foo a b = a + b
+
+-- | Do bar
+bar x y = {- baz -} foo (x+y) x
+
+nn = n2
+
+-- end of file
diff --git a/utils/check-exact/cases/AddDecl2.hs b/utils/check-exact/cases/AddDecl2.hs
new file mode 100644
index 0000000000..45c0cb3864
--- /dev/null
+++ b/utils/check-exact/cases/AddDecl2.hs
@@ -0,0 +1,11 @@
+module AddDecl where
+
+-- Adding a declaration to an existing file
+
+-- | Do foo
+foo a b = a + b
+
+-- | Do bar
+bar x y = {- baz -} foo (x+y) x
+
+-- end of file
diff --git a/utils/check-exact/cases/AddDecl3.expected.hs b/utils/check-exact/cases/AddDecl3.expected.hs
new file mode 100644
index 0000000000..dd3044fcc5
--- /dev/null
+++ b/utils/check-exact/cases/AddDecl3.expected.hs
@@ -0,0 +1,13 @@
+module AddDecl where
+
+-- Adding a declaration to an existing file
+
+-- | Do foo
+foo a b = a + b
+
+nn = n2
+
+-- | Do bar
+bar x y = {- baz -} foo (x+y) x
+
+-- end of file
diff --git a/utils/check-exact/cases/AddDecl3.hs b/utils/check-exact/cases/AddDecl3.hs
new file mode 100644
index 0000000000..45c0cb3864
--- /dev/null
+++ b/utils/check-exact/cases/AddDecl3.hs
@@ -0,0 +1,11 @@
+module AddDecl where
+
+-- Adding a declaration to an existing file
+
+-- | Do foo
+foo a b = a + b
+
+-- | Do bar
+bar x y = {- baz -} foo (x+y) x
+
+-- end of file
diff --git a/utils/check-exact/cases/AddHiding1.expected.hs b/utils/check-exact/cases/AddHiding1.expected.hs
new file mode 100644
index 0000000000..f3c8f17c8b
--- /dev/null
+++ b/utils/check-exact/cases/AddHiding1.expected.hs
@@ -0,0 +1,8 @@
+module AddHiding1 where
+
+import Data.Maybe hiding (n1,n2)
+
+import Data.Maybe hiding (n1,n2)
+
+f = 1
+
diff --git a/utils/check-exact/cases/AddHiding1.hs b/utils/check-exact/cases/AddHiding1.hs
new file mode 100644
index 0000000000..abcd47879a
--- /dev/null
+++ b/utils/check-exact/cases/AddHiding1.hs
@@ -0,0 +1,8 @@
+module AddHiding1 where
+
+import Data.Maybe
+
+import Data.Maybe hiding (n1,n2)
+
+f = 1
+
diff --git a/utils/check-exact/cases/AddHiding2.expected.hs b/utils/check-exact/cases/AddHiding2.expected.hs
new file mode 100644
index 0000000000..d62005227b
--- /dev/null
+++ b/utils/check-exact/cases/AddHiding2.expected.hs
@@ -0,0 +1,5 @@
+module AddHiding2 where
+
+import Data.Maybe hiding (f1,f2,n1,n2)
+
+f = 1
diff --git a/utils/check-exact/cases/AddHiding2.hs b/utils/check-exact/cases/AddHiding2.hs
new file mode 100644
index 0000000000..f5f551a9cb
--- /dev/null
+++ b/utils/check-exact/cases/AddHiding2.hs
@@ -0,0 +1,5 @@
+module AddHiding2 where
+
+import Data.Maybe hiding (f1,f2)
+
+f = 1
diff --git a/utils/check-exact/cases/AddLocalDecl1.expected.hs b/utils/check-exact/cases/AddLocalDecl1.expected.hs
new file mode 100644
index 0000000000..023e2ea05d
--- /dev/null
+++ b/utils/check-exact/cases/AddLocalDecl1.expected.hs
@@ -0,0 +1,15 @@
+module AddLocalDecl1 where
+
+-- |This is a function
+foo = x -- comment1
+ where
+ nn = 2
+-- trailing 1
+
+-- |Another fun
+x = a -- comment2
+ where
+ a = 3
+-- trailing 2
+
+y = 3
diff --git a/utils/check-exact/cases/AddLocalDecl1.hs b/utils/check-exact/cases/AddLocalDecl1.hs
new file mode 100644
index 0000000000..3bb4953c51
--- /dev/null
+++ b/utils/check-exact/cases/AddLocalDecl1.hs
@@ -0,0 +1,13 @@
+module AddLocalDecl1 where
+
+-- |This is a function
+foo = x -- comment1
+-- trailing 1
+
+-- |Another fun
+x = a -- comment2
+ where
+ a = 3
+-- trailing 2
+
+y = 3
diff --git a/utils/check-exact/cases/AddLocalDecl2.expected.hs b/utils/check-exact/cases/AddLocalDecl2.expected.hs
new file mode 100644
index 0000000000..ff25b79157
--- /dev/null
+++ b/utils/check-exact/cases/AddLocalDecl2.expected.hs
@@ -0,0 +1,11 @@
+module AddLocalDecl2 where
+
+-- |This is a function
+foo = x -- comment 0
+ where nn = 2
+ p = 2 -- comment 1
+
+-- |Another fun
+bar = a -- comment 2
+ where nn = 2
+ p = 2 -- comment 3
diff --git a/utils/check-exact/cases/AddLocalDecl2.hs b/utils/check-exact/cases/AddLocalDecl2.hs
new file mode 100644
index 0000000000..7609f657ed
--- /dev/null
+++ b/utils/check-exact/cases/AddLocalDecl2.hs
@@ -0,0 +1,10 @@
+module AddLocalDecl2 where
+
+-- |This is a function
+foo = x -- comment 0
+ where p = 2 -- comment 1
+
+-- |Another fun
+bar = a -- comment 2
+ where nn = 2
+ p = 2 -- comment 3
diff --git a/utils/check-exact/cases/AddLocalDecl3.expected.hs b/utils/check-exact/cases/AddLocalDecl3.expected.hs
new file mode 100644
index 0000000000..deaf1e7cb8
--- /dev/null
+++ b/utils/check-exact/cases/AddLocalDecl3.expected.hs
@@ -0,0 +1,13 @@
+module AddLocalDecl3 where
+
+-- |This is a function
+foo = x -- comment 0
+ where p = 2 -- comment 1
+ nn = 2
+ -- comment f
+
+-- |Another fun
+bar = a -- comment 2
+ where p = 2 -- comment 3
+ nn = 2
+ -- comment b
diff --git a/utils/check-exact/cases/AddLocalDecl3.hs b/utils/check-exact/cases/AddLocalDecl3.hs
new file mode 100644
index 0000000000..eb14013031
--- /dev/null
+++ b/utils/check-exact/cases/AddLocalDecl3.hs
@@ -0,0 +1,12 @@
+module AddLocalDecl3 where
+
+-- |This is a function
+foo = x -- comment 0
+ where p = 2 -- comment 1
+ -- comment f
+
+-- |Another fun
+bar = a -- comment 2
+ where p = 2 -- comment 3
+ nn = 2
+ -- comment b
diff --git a/utils/check-exact/cases/AddLocalDecl4.expected.hs b/utils/check-exact/cases/AddLocalDecl4.expected.hs
new file mode 100644
index 0000000000..b3c1445d0d
--- /dev/null
+++ b/utils/check-exact/cases/AddLocalDecl4.expected.hs
@@ -0,0 +1,6 @@
+module AddLocalDecl4 where
+
+toplevel x = c * x
+ where
+ nn :: Int
+ nn = 2
diff --git a/utils/check-exact/cases/AddLocalDecl4.hs b/utils/check-exact/cases/AddLocalDecl4.hs
new file mode 100644
index 0000000000..2ec2c0bf73
--- /dev/null
+++ b/utils/check-exact/cases/AddLocalDecl4.hs
@@ -0,0 +1,3 @@
+module AddLocalDecl4 where
+
+toplevel x = c * x
diff --git a/utils/check-exact/cases/AddLocalDecl5.expected.hs b/utils/check-exact/cases/AddLocalDecl5.expected.hs
new file mode 100644
index 0000000000..5e66dc5a6b
--- /dev/null
+++ b/utils/check-exact/cases/AddLocalDecl5.expected.hs
@@ -0,0 +1,9 @@
+module AddLocalDecl5 where
+
+toplevel :: Integer -> Integer
+toplevel x = c * x
+ where
+ -- c,d :: Integer
+ c = 7
+
+d = 9
diff --git a/utils/check-exact/cases/AddLocalDecl5.hs b/utils/check-exact/cases/AddLocalDecl5.hs
new file mode 100644
index 0000000000..9f07e1071b
--- /dev/null
+++ b/utils/check-exact/cases/AddLocalDecl5.hs
@@ -0,0 +1,8 @@
+module AddLocalDecl5 where
+
+toplevel :: Integer -> Integer
+toplevel x = c * x
+
+-- c,d :: Integer
+c = 7
+d = 9
diff --git a/utils/check-exact/cases/AddLocalDecl6.expected.hs b/utils/check-exact/cases/AddLocalDecl6.expected.hs
new file mode 100644
index 0000000000..9cedb7d63f
--- /dev/null
+++ b/utils/check-exact/cases/AddLocalDecl6.expected.hs
@@ -0,0 +1,12 @@
+module AddLocalDecl6 where
+
+foo [] = 1 -- comment 0
+ where
+ x = 3
+foo xs = 2 -- comment 1
+
+bar [] = 1 -- comment 2
+ where
+ x = 3
+bar xs = 2 -- comment 3
+
diff --git a/utils/check-exact/cases/AddLocalDecl6.hs b/utils/check-exact/cases/AddLocalDecl6.hs
new file mode 100644
index 0000000000..d0bdffca41
--- /dev/null
+++ b/utils/check-exact/cases/AddLocalDecl6.hs
@@ -0,0 +1,10 @@
+module AddLocalDecl6 where
+
+foo [] = 1 -- comment 0
+foo xs = 2 -- comment 1
+
+bar [] = 1 -- comment 2
+ where
+ x = 3
+bar xs = 2 -- comment 3
+
diff --git a/utils/check-exact/cases/EmptyWheres.hs b/utils/check-exact/cases/EmptyWheres.hs
new file mode 100644
index 0000000000..edc0570012
--- /dev/null
+++ b/utils/check-exact/cases/EmptyWheres.hs
@@ -0,0 +1,9 @@
+module EmptyWheres where
+
+x = 2 where
+y = 3
+
+instance Foo1 Int where
+
+ff = ff where g = g where
+type T = Int
diff --git a/utils/check-exact/cases/LayoutIn1.expected.hs b/utils/check-exact/cases/LayoutIn1.expected.hs
new file mode 100644
index 0000000000..2b23b21853
--- /dev/null
+++ b/utils/check-exact/cases/LayoutIn1.expected.hs
@@ -0,0 +1,9 @@
+module LayoutIn1 where
+
+--Layout rule applies after 'where','let','do' and 'of'
+
+--In this Example: rename 'sq' to 'square'.
+
+sumSquares x y= square x + square y where sq x= x^pow
+ --There is a comment.
+ pow=2
diff --git a/utils/check-exact/cases/LayoutIn1.hs b/utils/check-exact/cases/LayoutIn1.hs
new file mode 100644
index 0000000000..3ea1f8402c
--- /dev/null
+++ b/utils/check-exact/cases/LayoutIn1.hs
@@ -0,0 +1,9 @@
+module LayoutIn1 where
+
+--Layout rule applies after 'where','let','do' and 'of'
+
+--In this Example: rename 'sq' to 'square'.
+
+sumSquares x y= sq x + sq y where sq x= x^pow
+ --There is a comment.
+ pow=2
diff --git a/utils/check-exact/cases/LayoutIn3.expected.hs b/utils/check-exact/cases/LayoutIn3.expected.hs
new file mode 100644
index 0000000000..900d6daf63
--- /dev/null
+++ b/utils/check-exact/cases/LayoutIn3.expected.hs
@@ -0,0 +1,13 @@
+module LayoutIn3 where
+
+--Layout rule applies after 'where','let','do' and 'of'
+
+--In this Example: rename 'x' after 'let' to 'anotherX'.
+
+foo x = let anotherX = 12 in (let y = 3
+ z = 2 in anotherX * y * z * w) where y = 2
+ --there is a comment.
+ w = x
+ where
+ x = let y = 5 in y + 3
+
diff --git a/utils/check-exact/cases/LayoutIn3.hs b/utils/check-exact/cases/LayoutIn3.hs
new file mode 100644
index 0000000000..c8c110d65c
--- /dev/null
+++ b/utils/check-exact/cases/LayoutIn3.hs
@@ -0,0 +1,13 @@
+module LayoutIn3 where
+
+--Layout rule applies after 'where','let','do' and 'of'
+
+--In this Example: rename 'x' after 'let' to 'anotherX'.
+
+foo x = let x = 12 in (let y = 3
+ z = 2 in x * y * z * w) where y = 2
+ --there is a comment.
+ w = x
+ where
+ x = let y = 5 in y + 3
+
diff --git a/utils/check-exact/cases/LayoutIn3a.expected.hs b/utils/check-exact/cases/LayoutIn3a.expected.hs
new file mode 100644
index 0000000000..c0a552c0d0
--- /dev/null
+++ b/utils/check-exact/cases/LayoutIn3a.expected.hs
@@ -0,0 +1,13 @@
+module LayoutIn3a where
+
+--Layout rule applies after 'where','let','do' and 'of'
+
+--In this Example: rename 'x' after 'let' to 'anotherX'.
+
+foo x = let anotherX = 12 in (
+ anotherX ) where y = 2
+ --there is a comment.
+ w = x
+ where
+ x = let y = 5 in y + 3
+
diff --git a/utils/check-exact/cases/LayoutIn3a.hs b/utils/check-exact/cases/LayoutIn3a.hs
new file mode 100644
index 0000000000..58b36b07f8
--- /dev/null
+++ b/utils/check-exact/cases/LayoutIn3a.hs
@@ -0,0 +1,13 @@
+module LayoutIn3a where
+
+--Layout rule applies after 'where','let','do' and 'of'
+
+--In this Example: rename 'x' after 'let' to 'anotherX'.
+
+foo x = let x = 12 in (
+ x ) where y = 2
+ --there is a comment.
+ w = x
+ where
+ x = let y = 5 in y + 3
+
diff --git a/utils/check-exact/cases/LayoutIn3b.expected.hs b/utils/check-exact/cases/LayoutIn3b.expected.hs
new file mode 100644
index 0000000000..057d9d346a
--- /dev/null
+++ b/utils/check-exact/cases/LayoutIn3b.expected.hs
@@ -0,0 +1,12 @@
+module LayoutIn3b where
+
+--Layout rule applies after 'where','let','do' and 'of'
+
+--In this Example: rename 'x' after 'let' to 'anotherX'.
+
+foo x = let anotherX = 12 in ( anotherX ) where y = 2
+ --there is a comment.
+ w = x
+ where
+ x = let y = 5 in y + 3
+
diff --git a/utils/check-exact/cases/LayoutIn3b.hs b/utils/check-exact/cases/LayoutIn3b.hs
new file mode 100644
index 0000000000..32bc294ae4
--- /dev/null
+++ b/utils/check-exact/cases/LayoutIn3b.hs
@@ -0,0 +1,12 @@
+module LayoutIn3b where
+
+--Layout rule applies after 'where','let','do' and 'of'
+
+--In this Example: rename 'x' after 'let' to 'anotherX'.
+
+foo x = let x = 12 in ( x ) where y = 2
+ --there is a comment.
+ w = x
+ where
+ x = let y = 5 in y + 3
+
diff --git a/utils/check-exact/cases/LayoutIn4.expected.hs b/utils/check-exact/cases/LayoutIn4.expected.hs
new file mode 100644
index 0000000000..531478da48
--- /dev/null
+++ b/utils/check-exact/cases/LayoutIn4.expected.hs
@@ -0,0 +1,13 @@
+module LayoutIn4 where
+
+--Layout rule applies after 'where','let','do' and 'of'
+
+--In this Example: rename 'ioFun' to 'io'
+
+main = io "hello" where io s= do let k = reverse s
+--There is a comment
+ s <- getLine
+ let q = (k ++ s)
+ putStr q
+ putStr "foo"
+
diff --git a/utils/check-exact/cases/LayoutIn4.hs b/utils/check-exact/cases/LayoutIn4.hs
new file mode 100644
index 0000000000..d99d05649d
--- /dev/null
+++ b/utils/check-exact/cases/LayoutIn4.hs
@@ -0,0 +1,13 @@
+module LayoutIn4 where
+
+--Layout rule applies after 'where','let','do' and 'of'
+
+--In this Example: rename 'ioFun' to 'io'
+
+main = ioFun "hello" where ioFun s= do let k = reverse s
+ --There is a comment
+ s <- getLine
+ let q = (k ++ s)
+ putStr q
+ putStr "foo"
+
diff --git a/utils/check-exact/cases/LayoutLet2.expected.hs b/utils/check-exact/cases/LayoutLet2.expected.hs
new file mode 100644
index 0000000000..8da499ce3a
--- /dev/null
+++ b/utils/check-exact/cases/LayoutLet2.expected.hs
@@ -0,0 +1,8 @@
+module LayoutLet2 where
+
+-- Simple let expression, rename xxx to something longer or shorter
+-- and the let/in layout should adjust accordingly
+-- In this case the tokens for xxx + a + b should also shift out
+
+foo xxxlonger = let a = 1
+ b = 2 in xxxlonger + a + b
diff --git a/utils/check-exact/cases/LayoutLet2.hs b/utils/check-exact/cases/LayoutLet2.hs
new file mode 100644
index 0000000000..378aa587a8
--- /dev/null
+++ b/utils/check-exact/cases/LayoutLet2.hs
@@ -0,0 +1,8 @@
+module LayoutLet2 where
+
+-- Simple let expression, rename xxx to something longer or shorter
+-- and the let/in layout should adjust accordingly
+-- In this case the tokens for xxx + a + b should also shift out
+
+foo xxx = let a = 1
+ b = 2 in xxx + a + b
diff --git a/utils/check-exact/cases/LayoutLet3.expected.hs b/utils/check-exact/cases/LayoutLet3.expected.hs
new file mode 100644
index 0000000000..797cf5f483
--- /dev/null
+++ b/utils/check-exact/cases/LayoutLet3.expected.hs
@@ -0,0 +1,10 @@
+module LayoutLet3 where
+
+-- Simple let expression, rename xxx to something longer or shorter
+-- and the let/in layout should adjust accordingly
+-- In this case the tokens for xxx + a + b should also shift out
+
+foo xxxlonger = let a = 1
+ b = 2
+ in xxxlonger + a + b
+
diff --git a/utils/check-exact/cases/LayoutLet3.hs b/utils/check-exact/cases/LayoutLet3.hs
new file mode 100644
index 0000000000..5ba80aff6a
--- /dev/null
+++ b/utils/check-exact/cases/LayoutLet3.hs
@@ -0,0 +1,10 @@
+module LayoutLet3 where
+
+-- Simple let expression, rename xxx to something longer or shorter
+-- and the let/in layout should adjust accordingly
+-- In this case the tokens for xxx + a + b should also shift out
+
+foo xxx = let a = 1
+ b = 2
+ in xxx + a + b
+
diff --git a/utils/check-exact/cases/LayoutLet4.expected.hs b/utils/check-exact/cases/LayoutLet4.expected.hs
new file mode 100644
index 0000000000..b3c52f424e
--- /dev/null
+++ b/utils/check-exact/cases/LayoutLet4.expected.hs
@@ -0,0 +1,12 @@
+module LayoutLet4 where
+
+-- Simple let expression, rename xxx to something longer or shorter
+-- and the let/in layout should adjust accordingly
+-- In this case the tokens for xxx + a + b should also shift out
+
+foo xxxlonger = let a = 1
+ b = 2
+ in xxxlonger + a + b
+
+bar = 3
+
diff --git a/utils/check-exact/cases/LayoutLet4.hs b/utils/check-exact/cases/LayoutLet4.hs
new file mode 100644
index 0000000000..28fe599432
--- /dev/null
+++ b/utils/check-exact/cases/LayoutLet4.hs
@@ -0,0 +1,12 @@
+module LayoutLet4 where
+
+-- Simple let expression, rename xxx to something longer or shorter
+-- and the let/in layout should adjust accordingly
+-- In this case the tokens for xxx + a + b should also shift out
+
+foo xxx = let a = 1
+ b = 2
+ in xxx + a + b
+
+bar = 3
+
diff --git a/utils/check-exact/cases/LetIn1.expected.hs b/utils/check-exact/cases/LetIn1.expected.hs
new file mode 100644
index 0000000000..d233115ee6
--- /dev/null
+++ b/utils/check-exact/cases/LetIn1.expected.hs
@@ -0,0 +1,18 @@
+module LetIn1 where
+
+--A definition can be demoted to the local 'where' binding of a friend declaration,
+--if it is only used by this friend declaration.
+
+--Demoting a definition narrows down the scope of the definition.
+--In this example, demote the local 'pow' to 'sq'
+--This example also aims to test the demoting a local declaration in 'let'.
+
+sumSquares x y = let sq 0=0
+ sq z=z^pow
+ in sq x + sq y
+
+
+anotherFun 0 y = sq y
+ where sq x = x^2
+
+
diff --git a/utils/check-exact/cases/LetIn1.hs b/utils/check-exact/cases/LetIn1.hs
new file mode 100644
index 0000000000..f1109b8f03
--- /dev/null
+++ b/utils/check-exact/cases/LetIn1.hs
@@ -0,0 +1,19 @@
+module LetIn1 where
+
+--A definition can be demoted to the local 'where' binding of a friend declaration,
+--if it is only used by this friend declaration.
+
+--Demoting a definition narrows down the scope of the definition.
+--In this example, demote the local 'pow' to 'sq'
+--This example also aims to test the demoting a local declaration in 'let'.
+
+sumSquares x y = let sq 0=0
+ sq z=z^pow
+ pow=2
+ in sq x + sq y
+
+
+anotherFun 0 y = sq y
+ where sq x = x^2
+
+
diff --git a/utils/check-exact/cases/LocToName.expected.hs b/utils/check-exact/cases/LocToName.expected.hs
new file mode 100644
index 0000000000..0b1484873a
--- /dev/null
+++ b/utils/check-exact/cases/LocToName.expected.hs
@@ -0,0 +1,25 @@
+module LocToName where
+
+{-
+
+
+
+
+
+
+
+
+-}
+
+
+
+
+
+
+
+LocToName.newPoint (x:xs) = x ^2 + LocToName.newPoint xs
+ -- where sq x = x ^pow
+ -- pow = 2
+
+LocToName.newPoint [] = 0
+
diff --git a/utils/check-exact/cases/LocToName.hs b/utils/check-exact/cases/LocToName.hs
new file mode 100644
index 0000000000..89a0acea12
--- /dev/null
+++ b/utils/check-exact/cases/LocToName.hs
@@ -0,0 +1,25 @@
+module LocToName where
+
+{-
+
+
+
+
+
+
+
+
+-}
+
+
+
+
+
+
+
+sumSquares (x:xs) = x ^2 + sumSquares xs
+ -- where sq x = x ^pow
+ -- pow = 2
+
+sumSquares [] = 0
+
diff --git a/utils/check-exact/cases/LocalDecls.expected.hs b/utils/check-exact/cases/LocalDecls.expected.hs
new file mode 100644
index 0000000000..7c41178ba0
--- /dev/null
+++ b/utils/check-exact/cases/LocalDecls.expected.hs
@@ -0,0 +1,11 @@
+module LocalDecls where
+
+foo a = bar a
+ where
+ nn :: Int
+ nn = 2
+
+ bar :: Int -> Int
+ bar x = x + 2
+
+ baz = 4
diff --git a/utils/check-exact/cases/LocalDecls.hs b/utils/check-exact/cases/LocalDecls.hs
new file mode 100644
index 0000000000..ebb774ac63
--- /dev/null
+++ b/utils/check-exact/cases/LocalDecls.hs
@@ -0,0 +1,8 @@
+module LocalDecls where
+
+foo a = bar a
+ where
+ bar :: Int -> Int
+ bar x = x + 2
+
+ baz = 4
diff --git a/utils/check-exact/cases/LocalDecls2.expected.hs b/utils/check-exact/cases/LocalDecls2.expected.hs
new file mode 100644
index 0000000000..d2353e94c5
--- /dev/null
+++ b/utils/check-exact/cases/LocalDecls2.expected.hs
@@ -0,0 +1,8 @@
+module LocalDecls2 where
+
+foo a = bar a
+ where
+ nn :: Int
+ nn = 2
+
+
diff --git a/utils/check-exact/cases/LocalDecls2.hs b/utils/check-exact/cases/LocalDecls2.hs
new file mode 100644
index 0000000000..92a8649649
--- /dev/null
+++ b/utils/check-exact/cases/LocalDecls2.hs
@@ -0,0 +1,3 @@
+module LocalDecls2 where
+
+foo a = bar a
diff --git a/utils/check-exact/cases/Rename1.expected.hs b/utils/check-exact/cases/Rename1.expected.hs
new file mode 100644
index 0000000000..353a7420e2
--- /dev/null
+++ b/utils/check-exact/cases/Rename1.expected.hs
@@ -0,0 +1,6 @@
+module Rename1 where
+
+bar2 x y =
+ do c <- getChar
+ return c
+
diff --git a/utils/check-exact/cases/Rename1.hs b/utils/check-exact/cases/Rename1.hs
new file mode 100644
index 0000000000..1ad343afd3
--- /dev/null
+++ b/utils/check-exact/cases/Rename1.hs
@@ -0,0 +1,6 @@
+module Rename1 where
+
+foo x y =
+ do c <- getChar
+ return c
+
diff --git a/utils/check-exact/cases/Rename2.expected.hs b/utils/check-exact/cases/Rename2.expected.hs
new file mode 100644
index 0000000000..6be3ff6e0a
--- /dev/null
+++ b/utils/check-exact/cases/Rename2.expected.hs
@@ -0,0 +1,4 @@
+
+joe x = case (odd x) of
+ True -> "Odd"
+ False -> "Even"
diff --git a/utils/check-exact/cases/Rename2.hs b/utils/check-exact/cases/Rename2.hs
new file mode 100644
index 0000000000..29fea060c2
--- /dev/null
+++ b/utils/check-exact/cases/Rename2.hs
@@ -0,0 +1,4 @@
+
+foo' x = case (odd x) of
+ True -> "Odd"
+ False -> "Even"
diff --git a/utils/check-exact/cases/RenameCase1.expected.hs b/utils/check-exact/cases/RenameCase1.expected.hs
new file mode 100644
index 0000000000..dad6765012
--- /dev/null
+++ b/utils/check-exact/cases/RenameCase1.expected.hs
@@ -0,0 +1,5 @@
+module RenameCase1 where
+
+foo x = case (bazLonger x) of
+ 1 -> "a"
+ _ -> "b"
diff --git a/utils/check-exact/cases/RenameCase1.hs b/utils/check-exact/cases/RenameCase1.hs
new file mode 100644
index 0000000000..22d549367a
--- /dev/null
+++ b/utils/check-exact/cases/RenameCase1.hs
@@ -0,0 +1,5 @@
+module RenameCase1 where
+
+foo x = case (baz x) of
+ 1 -> "a"
+ _ -> "b"
diff --git a/utils/check-exact/cases/RmDecl1.expected.hs b/utils/check-exact/cases/RmDecl1.expected.hs
new file mode 100644
index 0000000000..6bb503aede
--- /dev/null
+++ b/utils/check-exact/cases/RmDecl1.expected.hs
@@ -0,0 +1,9 @@
+module RmDecl1 where
+
+sumSquares x = x * p
+ where p=2 {-There is a comment-}
+
+{- foo bar -}
+anotherFun 0 y = sq y
+ where sq x = x^2
+
diff --git a/utils/check-exact/cases/RmDecl1.hs b/utils/check-exact/cases/RmDecl1.hs
new file mode 100644
index 0000000000..15cd9f1e04
--- /dev/null
+++ b/utils/check-exact/cases/RmDecl1.hs
@@ -0,0 +1,13 @@
+module RmDecl1 where
+
+sumSquares x = x * p
+ where p=2 {-There is a comment-}
+
+sq :: Int -> Int -> Int
+sq pow 0 = 0
+sq pow z = z^pow --there is a comment
+
+{- foo bar -}
+anotherFun 0 y = sq y
+ where sq x = x^2
+
diff --git a/utils/check-exact/cases/RmDecl2.expected.hs b/utils/check-exact/cases/RmDecl2.expected.hs
new file mode 100644
index 0000000000..d77b760dca
--- /dev/null
+++ b/utils/check-exact/cases/RmDecl2.expected.hs
@@ -0,0 +1,9 @@
+module RmDecl2 where
+
+sumSquares x y = let sq 0=0
+ sq z=z^pow
+ in sq x + sq y
+
+anotherFun 0 y = sq y
+ where sq x = x^2
+
diff --git a/utils/check-exact/cases/RmDecl2.hs b/utils/check-exact/cases/RmDecl2.hs
new file mode 100644
index 0000000000..2f0dbd3ace
--- /dev/null
+++ b/utils/check-exact/cases/RmDecl2.hs
@@ -0,0 +1,10 @@
+module RmDecl2 where
+
+sumSquares x y = let sq 0=0
+ sq z=z^pow
+ pow=2
+ in sq x + sq y
+
+anotherFun 0 y = sq y
+ where sq x = x^2
+
diff --git a/utils/check-exact/cases/RmDecl3.expected.hs b/utils/check-exact/cases/RmDecl3.expected.hs
new file mode 100644
index 0000000000..ca14f33ad5
--- /dev/null
+++ b/utils/check-exact/cases/RmDecl3.expected.hs
@@ -0,0 +1,9 @@
+module RmDecl3 where
+
+-- Remove last declaration from a where clause, where should disappear too
+ff y = y + zz
+
+zz = 1
+
+foo = 3
+-- EOF
diff --git a/utils/check-exact/cases/RmDecl3.hs b/utils/check-exact/cases/RmDecl3.hs
new file mode 100644
index 0000000000..280bccf259
--- /dev/null
+++ b/utils/check-exact/cases/RmDecl3.hs
@@ -0,0 +1,9 @@
+module RmDecl3 where
+
+-- Remove last declaration from a where clause, where should disappear too
+ff y = y + zz
+ where
+ zz = 1
+
+foo = 3
+-- EOF
diff --git a/utils/check-exact/cases/RmDecl4.expected.hs b/utils/check-exact/cases/RmDecl4.expected.hs
new file mode 100644
index 0000000000..e7c71dbd08
--- /dev/null
+++ b/utils/check-exact/cases/RmDecl4.expected.hs
@@ -0,0 +1,10 @@
+module RmDecl4 where
+
+-- Remove first declaration from a where clause, last should still be indented
+ff y = y + zz + xx
+ where
+ xx = 2
+
+zz = 1
+
+-- EOF
diff --git a/utils/check-exact/cases/RmDecl4.hs b/utils/check-exact/cases/RmDecl4.hs
new file mode 100644
index 0000000000..532b738763
--- /dev/null
+++ b/utils/check-exact/cases/RmDecl4.hs
@@ -0,0 +1,9 @@
+module RmDecl4 where
+
+-- Remove first declaration from a where clause, last should still be indented
+ff y = y + zz + xx
+ where
+ zz = 1
+ xx = 2
+
+-- EOF
diff --git a/utils/check-exact/cases/RmDecl5.expected.hs b/utils/check-exact/cases/RmDecl5.expected.hs
new file mode 100644
index 0000000000..67ac8ddfab
--- /dev/null
+++ b/utils/check-exact/cases/RmDecl5.expected.hs
@@ -0,0 +1,5 @@
+module RmDecl5 where
+
+sumSquares x y = let pow=2
+ in sq x + sq y
+
diff --git a/utils/check-exact/cases/RmDecl5.hs b/utils/check-exact/cases/RmDecl5.hs
new file mode 100644
index 0000000000..40f86199ce
--- /dev/null
+++ b/utils/check-exact/cases/RmDecl5.hs
@@ -0,0 +1,7 @@
+module RmDecl5 where
+
+sumSquares x y = let sq 0=0
+ sq z=z^pow
+ pow=2
+ in sq x + sq y
+
diff --git a/utils/check-exact/cases/RmDecl6.expected.hs b/utils/check-exact/cases/RmDecl6.expected.hs
new file mode 100644
index 0000000000..a2bd7d0443
--- /dev/null
+++ b/utils/check-exact/cases/RmDecl6.expected.hs
@@ -0,0 +1,9 @@
+module RmDecl6 where
+
+foo a = baz
+ where
+ x = 1
+
+ y :: Int -> Int -> Int
+ y a b = undefined
+
diff --git a/utils/check-exact/cases/RmDecl6.hs b/utils/check-exact/cases/RmDecl6.hs
new file mode 100644
index 0000000000..cab5093ce8
--- /dev/null
+++ b/utils/check-exact/cases/RmDecl6.hs
@@ -0,0 +1,12 @@
+module RmDecl6 where
+
+foo a = baz
+ where
+ baz :: Int
+ baz = x + a
+
+ x = 1
+
+ y :: Int -> Int -> Int
+ y a b = undefined
+
diff --git a/utils/check-exact/cases/RmDecl7.expected.hs b/utils/check-exact/cases/RmDecl7.expected.hs
new file mode 100644
index 0000000000..9d7b8b9a69
--- /dev/null
+++ b/utils/check-exact/cases/RmDecl7.expected.hs
@@ -0,0 +1,7 @@
+module RmDecl7 where
+
+toplevel :: Integer -> Integer
+toplevel x = c * x
+
+d = 9
+
diff --git a/utils/check-exact/cases/RmDecl7.hs b/utils/check-exact/cases/RmDecl7.hs
new file mode 100644
index 0000000000..62cefe2154
--- /dev/null
+++ b/utils/check-exact/cases/RmDecl7.hs
@@ -0,0 +1,9 @@
+module RmDecl7 where
+
+toplevel :: Integer -> Integer
+toplevel x = c * x
+
+-- c,d :: Integer
+c = 7
+d = 9
+
diff --git a/utils/check-exact/cases/RmTypeSig1.expected.hs b/utils/check-exact/cases/RmTypeSig1.expected.hs
new file mode 100644
index 0000000000..46f7b13399
--- /dev/null
+++ b/utils/check-exact/cases/RmTypeSig1.expected.hs
@@ -0,0 +1,8 @@
+module RmTypeSig1 where
+
+anotherFun :: Int -> Int
+sq 0 = 0
+sq z = z^2
+
+anotherFun x = x^2
+
diff --git a/utils/check-exact/cases/RmTypeSig1.hs b/utils/check-exact/cases/RmTypeSig1.hs
new file mode 100644
index 0000000000..498892d791
--- /dev/null
+++ b/utils/check-exact/cases/RmTypeSig1.hs
@@ -0,0 +1,8 @@
+module RmTypeSig1 where
+
+sq,anotherFun :: Int -> Int
+sq 0 = 0
+sq z = z^2
+
+anotherFun x = x^2
+
diff --git a/utils/check-exact/cases/RmTypeSig2.expected.hs b/utils/check-exact/cases/RmTypeSig2.expected.hs
new file mode 100644
index 0000000000..c30e201bd0
--- /dev/null
+++ b/utils/check-exact/cases/RmTypeSig2.expected.hs
@@ -0,0 +1,7 @@
+module RmTypeSig2 where
+
+-- Pattern bind
+tup@(h,t) = (1,ff)
+ where
+ ff = 15
+
diff --git a/utils/check-exact/cases/RmTypeSig2.hs b/utils/check-exact/cases/RmTypeSig2.hs
new file mode 100644
index 0000000000..e8771f99dd
--- /dev/null
+++ b/utils/check-exact/cases/RmTypeSig2.hs
@@ -0,0 +1,8 @@
+module RmTypeSig2 where
+
+-- Pattern bind
+tup@(h,t) = (1,ff)
+ where
+ ff :: Int
+ ff = 15
+
diff --git a/utils/check-exact/cases/WhereIn3a.expected.hs b/utils/check-exact/cases/WhereIn3a.expected.hs
new file mode 100644
index 0000000000..acc94d3621
--- /dev/null
+++ b/utils/check-exact/cases/WhereIn3a.expected.hs
@@ -0,0 +1,20 @@
+module WhereIn3a where
+
+--A definition can be demoted to the local 'where' binding of a friend declaration,
+--if it is only used by this friend declaration.
+
+--Demoting a definition narrows down the scope of the definition.
+--In this example, demote the top level 'sq' to 'sumSquares'
+--In this case (there are multi matches), the parameters are not folded after demoting.
+
+sumSquares x y = sq p x + sq p y
+ where p=2 {-There is a comment-}
+
+sq :: Int -> Int -> Int
+sq pow 0 = 0 -- prior comment
+sq pow z = z^pow --there is a comment
+
+-- A leading comment
+anotherFun 0 y = sq y
+ where sq x = x^2
+
diff --git a/utils/check-exact/cases/WhereIn3a.hs b/utils/check-exact/cases/WhereIn3a.hs
new file mode 100644
index 0000000000..acc94d3621
--- /dev/null
+++ b/utils/check-exact/cases/WhereIn3a.hs
@@ -0,0 +1,20 @@
+module WhereIn3a where
+
+--A definition can be demoted to the local 'where' binding of a friend declaration,
+--if it is only used by this friend declaration.
+
+--Demoting a definition narrows down the scope of the definition.
+--In this example, demote the top level 'sq' to 'sumSquares'
+--In this case (there are multi matches), the parameters are not folded after demoting.
+
+sumSquares x y = sq p x + sq p y
+ where p=2 {-There is a comment-}
+
+sq :: Int -> Int -> Int
+sq pow 0 = 0 -- prior comment
+sq pow z = z^pow --there is a comment
+
+-- A leading comment
+anotherFun 0 y = sq y
+ where sq x = x^2
+
diff --git a/utils/check-exact/cases/WhereIn3b.expected.hs b/utils/check-exact/cases/WhereIn3b.expected.hs
new file mode 100644
index 0000000000..80ddc04825
--- /dev/null
+++ b/utils/check-exact/cases/WhereIn3b.expected.hs
@@ -0,0 +1,27 @@
+module WhereIn3a where
+
+--A definition can be demoted to the local 'where' binding of a friend declaration,
+--if it is only used by this friend declaration.
+
+--Demoting a definition narrows down the scope of the definition.
+--In this example, demote the top level 'sq' to 'sumSquares'
+--In this case (there are multi matches), the parameters are not folded after demoting.
+
+-- A leading comment
+anotherFun 0 y = sq y
+ where sq x = x^2
+
+sq pow 0 = 0 -- prior comment
+sq pow z = z^pow --there is a comment
+
+sumSquares x y = sq p x + sq p y
+ where p=2 {-There is a comment-}
+
+sq :: Int -> Int -> Int
+sq pow 0 = 0 -- prior comment
+sq pow z = z^pow --there is a comment
+
+-- A leading comment
+anotherFun 0 y = sq y
+ where sq x = x^2
+
diff --git a/utils/check-exact/cases/WhereIn3b.hs b/utils/check-exact/cases/WhereIn3b.hs
new file mode 100644
index 0000000000..acc94d3621
--- /dev/null
+++ b/utils/check-exact/cases/WhereIn3b.hs
@@ -0,0 +1,20 @@
+module WhereIn3a where
+
+--A definition can be demoted to the local 'where' binding of a friend declaration,
+--if it is only used by this friend declaration.
+
+--Demoting a definition narrows down the scope of the definition.
+--In this example, demote the top level 'sq' to 'sumSquares'
+--In this case (there are multi matches), the parameters are not folded after demoting.
+
+sumSquares x y = sq p x + sq p y
+ where p=2 {-There is a comment-}
+
+sq :: Int -> Int -> Int
+sq pow 0 = 0 -- prior comment
+sq pow z = z^pow --there is a comment
+
+-- A leading comment
+anotherFun 0 y = sq y
+ where sq x = x^2
+
diff --git a/utils/check-exact/cases/WhereIn4.expected.hs b/utils/check-exact/cases/WhereIn4.expected.hs
new file mode 100644
index 0000000000..4357bfdac7
--- /dev/null
+++ b/utils/check-exact/cases/WhereIn4.expected.hs
@@ -0,0 +1,19 @@
+module WhereIn4 where
+
+--A definition can be demoted to the local 'where' binding of a friend declaration,
+--if it is only used by this friend declaration.
+
+--Demoting a definition narrows down the scope of the definition.
+--In this example, demote the top level 'sq' to 'sumSquares'
+--In this case (there is single matches), if possible,
+--the parameters will be folded after demoting and type sigature will be removed.
+
+sumSquares x y = sq p x + sq p y
+ where p_2=2 {-There is a comment-}
+
+sq::Int->Int->Int
+sq pow z = z^pow --there is a comment
+
+anotherFun 0 y = sq y
+ where sq x = x^2
+
diff --git a/utils/check-exact/cases/WhereIn4.hs b/utils/check-exact/cases/WhereIn4.hs
new file mode 100644
index 0000000000..8b941fff4a
--- /dev/null
+++ b/utils/check-exact/cases/WhereIn4.hs
@@ -0,0 +1,19 @@
+module WhereIn4 where
+
+--A definition can be demoted to the local 'where' binding of a friend declaration,
+--if it is only used by this friend declaration.
+
+--Demoting a definition narrows down the scope of the definition.
+--In this example, demote the top level 'sq' to 'sumSquares'
+--In this case (there is single matches), if possible,
+--the parameters will be folded after demoting and type sigature will be removed.
+
+sumSquares x y = sq p x + sq p y
+ where p=2 {-There is a comment-}
+
+sq::Int->Int->Int
+sq pow z = z^pow --there is a comment
+
+anotherFun 0 y = sq y
+ where sq x = x^2
+
diff --git a/utils/check-exact/cases/Windows.hs b/utils/check-exact/cases/Windows.hs
new file mode 100644
index 0000000000..ad8ae692b6
--- /dev/null
+++ b/utils/check-exact/cases/Windows.hs
@@ -0,0 +1,10 @@
+module Windows where
+
+{-
+ This file has windows-style line endings, to check that trailing
+ \r's get stripped in comments.
+-}
+baz = 2
+
+-- Another comment
+foo = 1
diff --git a/utils/check-exact/check-exact.cabal b/utils/check-exact/check-exact.cabal
new file mode 100644
index 0000000000..40188c094f
--- /dev/null
+++ b/utils/check-exact/check-exact.cabal
@@ -0,0 +1,38 @@
+Name: check-exact
+Version: 0.1
+Copyright: XXX
+License: BSD3
+-- XXX License-File: LICENSE
+Author: XXX
+Maintainer: XXX
+Synopsis: A utilities for checking the consistency of GHC's exact printer
+Description:
+ This utility is used to check the consistency of the GHC exact
+ printer, by parsing a file, exact printing it, and then comparing
+ it to the original version. version. See
+ @utils/check-exact/README@ in GHC's source distribution for
+ details.
+Category: Development
+build-type: Simple
+cabal-version: >=1.10
+
+Executable check-exact
+ Default-Language: Haskell2010
+ Main-Is: Main.hs
+ Ghc-Options: -Wall
+ other-modules: ExactPrint
+ Lookup
+ Parsers
+ Preprocess
+ Transform
+ Types
+ Utils
+ Build-Depends: base >= 4 && < 5,
+ bytestring,
+ containers,
+ Cabal >= 3.2 && < 3.6,
+ directory,
+ filepath,
+ ghc,
+ ghc-boot,
+ mtl
diff --git a/utils/check-exact/ghc.mk b/utils/check-exact/ghc.mk
new file mode 100644
index 0000000000..f8ad02948b
--- /dev/null
+++ b/utils/check-exact/ghc.mk
@@ -0,0 +1,18 @@
+# -----------------------------------------------------------------------------
+#
+# (c) 2009 The University of Glasgow
+#
+# This file is part of the GHC build system.
+#
+# To understand how the build system works and how to modify it, see
+# https://gitlab.haskell.org/ghc/ghc/wikis/building/architecture
+# https://gitlab.haskell.org/ghc/ghc/wikis/building/modifying
+#
+# -----------------------------------------------------------------------------
+
+utils/check-exact_USES_CABAL = YES
+utils/check-exact_PACKAGE = check-exact
+utils/check-exact_dist-install_PROGNAME = check-exact
+utils/check-exact_dist-install_INSTALL = NO
+utils/check-exact_dist-install_INSTALL_INPLACE = YES
+$(eval $(call build-prog,utils/check-exact,dist-install,2))
diff --git a/utils/check-exact/run.sh b/utils/check-exact/run.sh
new file mode 100755
index 0000000000..a4f0858128
--- /dev/null
+++ b/utils/check-exact/run.sh
@@ -0,0 +1,3 @@
+#!/bin/sh
+
+../../_build/stage1/bin/ghc --interactive