module Language.Haskell.Refact.Refactoring.MoveDef
( liftToTopLevel
, compLiftToTopLevel
, liftOneLevel
, compLiftOneLevel
, demote
, compDemote
) where
import qualified Data.Generics as SYB
import qualified GHC.SYB.Utils as SYB
import qualified Data.Generics.Zipper as Z
import qualified Exception as GHC
import qualified GHC as GHC
import qualified Name as GHC
import qualified RdrName as GHC
import Control.Exception
import Control.Monad.State
import Data.Foldable
import Data.List
import Data.Maybe
import qualified Language.Haskell.GhcMod as GM
import Language.Haskell.Refact.API
import Language.Haskell.GHC.ExactPrint.Types
import Language.Haskell.GHC.ExactPrint
import Data.Generics.Strafunski.StrategyLib.StrategyLib
import System.Directory
liftToTopLevel :: RefactSettings -> GM.Options -> FilePath -> SimpPos -> IO [FilePath]
liftToTopLevel settings opts fileName (row,col) = do
absFileName <- canonicalizePath fileName
runRefacSession settings opts (compLiftToTopLevel absFileName (row,col))
compLiftToTopLevel :: FilePath -> SimpPos
-> RefactGhc [ApplyRefacResult]
compLiftToTopLevel fileName (row,col) = do
parseSourceFileGhc fileName
parsed <- getRefactParsed
nm <- getRefactNameMap
let (Just (modName,_)) = getModuleName parsed
let maybePn = locToRdrName (row, col) parsed
case maybePn of
Just ln@(GHC.L l _) -> liftToTopLevel' modName (GHC.L l (rdrName2NamePure nm ln))
_ -> error "\nInvalid cursor position!\n"
liftToTopLevel' :: GHC.ModuleName
-> GHC.Located GHC.Name
-> RefactGhc [ApplyRefacResult]
liftToTopLevel' modName pn@(GHC.L _ n) = do
renamed <- getRefactRenamed
parsed <- getRefactParsed
nm <- getRefactNameMap
targetModule <- getRefactTargetModule
logm $ "liftToTopLevel':pn=" ++ (showGhc pn)
if isLocalFunOrPatName nm n parsed
then do
(refactoredMod,declPns) <- applyRefac liftToMod RSAlreadyLoaded
logm $ "liftToTopLevel' applyRefac done "
if modIsExported modName renamed
then do clients <- clientModsAndFiles targetModule
logm $ "liftToTopLevel':(clients,declPns)=" ++ (showGhc (clients,declPns))
refactoredClients <- mapM (liftingInClientMod modName declPns) clients
return (refactoredMod:(concat refactoredClients))
else do return [refactoredMod]
else error "\nThe identifier is not a local function/pattern name!"
where
liftToMod = do
parsed' <- getRefactParsed
parsed <- liftT $ balanceAllComments parsed'
nm <- getRefactNameMap
logDataWithAnns "parsed after balanceAllComments" parsed
declsp <- liftT $ hsDecls parsed
(before,parent,after) <- divideDecls declsp pn
nameMap <- getRefactNameMap
let liftedDecls = definingDeclsRdrNames nameMap [n] parent True True
declaredPns = nub $ concatMap (definedNamesRdr nameMap) liftedDecls
liftedSigs = definingSigsRdrNames nameMap declaredPns parent
mLiftedSigs = liftedSigs
pns <- pnsNeedRenaming parsed parent liftedDecls declaredPns
logm $ "liftToMod:(pns needing renaming)=" ++ (showGhc pns)
decls <- liftT $ hsDecls parsed'
let dd = getDeclaredVarsRdr nm decls
logm $ "liftToMod:(ddd)=" ++ (showGhc dd)
if pns == []
then do
(parent',liftedDecls',mLiftedSigs') <- addParamsToParentAndLiftedDecl n dd parent liftedDecls mLiftedSigs
let defName = (ghead "liftToMod" (definedNamesRdr nameMap (ghead "liftToMod2" parent')))
parsed1 <- liftT $ replaceDecls parsed (before++parent'++after)
parsed2 <- moveDecl1 parsed1 (Just defName) [GHC.unLoc pn] liftedDecls'
declaredPns mLiftedSigs'
putRefactParsed parsed2 emptyAnns
return declaredPns
else askRenamingMsg pns "lifting"
liftOneLevel :: RefactSettings -> GM.Options -> FilePath -> SimpPos -> IO [FilePath]
liftOneLevel settings opts fileName (row,col) = do
absFileName <- canonicalizePath fileName
runRefacSession settings opts (compLiftOneLevel absFileName (row,col))
compLiftOneLevel :: FilePath -> SimpPos
-> RefactGhc [ApplyRefacResult]
compLiftOneLevel fileName (row,col) = do
parseSourceFileGhc fileName
parsed <- getRefactParsed
nm <- getRefactNameMap
let (Just (modName,_)) = getModuleName parsed
let maybePn = locToRdrName (row, col) parsed
case maybePn of
Just ln@(GHC.L l _) -> do
rs <- liftOneLevel' modName (GHC.L l (rdrName2NamePure nm ln))
logm $ "compLiftOneLevel:rs=" ++ (show $ (refactDone rs,map (\((_,d),_) -> d) rs))
if (refactDone rs)
then return rs
else error ( "Lifting this definition failed. "++
" This might be because that the definition to be "++
"lifted is defined in a class/instance declaration.")
_ -> error "\nInvalid cursor position!\n"
liftOneLevel' :: GHC.ModuleName
-> GHC.Located GHC.Name
-> RefactGhc [ApplyRefacResult]
liftOneLevel' modName pn@(GHC.L _ n) = do
renamed <- getRefactRenamed
parsed <- getRefactParsed
nm <- getRefactNameMap
targetModule <- getRefactTargetModule
if isLocalFunOrPatName nm n parsed
then do
(refactoredMod,(b,pns)) <- applyRefac doLiftOneLevel RSAlreadyLoaded
logm $ "liftOneLevel':main refactoring done:(p,pns)=" ++ showGhc (b,pns)
if b && modIsExported modName renamed
then do
logm $ "liftOneLevel':looking for clients"
clients <- clientModsAndFiles targetModule
logm $ "liftOneLevel':(clients,pns)=" ++ (showGhc (clients,pns))
refactoredClients <- mapM (liftingInClientMod modName pns) clients
return (refactoredMod:concat refactoredClients)
else do return [refactoredMod]
else error "\nThe identifer is not a function/pattern name!"
where
doLiftOneLevel = do
logm $ "in doLiftOneLevel"
parsed <- getRefactParsed
logDataWithAnns "doLiftOneLevel:parsed" parsed
nm <- getRefactNameMap
ans <- liftT getAnnsT
zp <- ztransformStagedM SYB.Parser
(Nothing
`SYB.mkQ` (liftToModQ nm ans)
`SYB.extQ` (liftToMatchQ nm ans)
`SYB.extQ` (liftToLetQ nm ans)
) (Z.toZipper parsed)
let parsed' = Z.fromZipper zp
putRefactParsed parsed' emptyAnns
liftedToTopLevel pn parsed'
where
isMatch :: GHC.LMatch GHC.RdrName (GHC.LHsExpr GHC.RdrName) -> Bool
isMatch _ = True
isHsLet :: GHC.LHsExpr GHC.RdrName -> Bool
isHsLet (GHC.L _ (GHC.HsLet _ _)) = True
isHsLet _ = False
liftToModQ ::
NameMap -> Anns
-> GHC.ParsedSource
-> Maybe (SYB.Stage
-> Z.Zipper GHC.ParsedSource
-> RefactGhc (Z.Zipper GHC.ParsedSource))
liftToModQ nm ans (p :: GHC.ParsedSource)
| nonEmptyList candidateBinds
= Just (doLiftZ p declsp)
| otherwise = Nothing
where
(declsp ,_,_) = runTransform ans (hsDecls p)
doOne bs = (definingDeclsRdrNames nm [n] declsbs False False,bs)
where
(declsbs,_,_) = runTransform ans (hsDeclsGeneric bs)
candidateBinds = map snd
$ filter (\(l,_bs) -> nonEmptyList l)
$ map doOne
$ declsp
getHsDecls ans t = decls
where (decls,_,_) = runTransform ans (hsDeclsGeneric t)
liftToMatchQ :: (SYB.Data a)
=> NameMap -> Anns
-> GHC.LMatch GHC.RdrName (GHC.LHsExpr GHC.RdrName)
-> Maybe (SYB.Stage -> Z.Zipper a -> RefactGhc (Z.Zipper a))
liftToMatchQ nm ans (m@(GHC.L _ (GHC.Match _ _pats _mtyp (GHC.GRHSs _rhs ds)))::GHC.LMatch GHC.RdrName (GHC.LHsExpr GHC.RdrName))
| (nonEmptyList (definingDeclsRdrNames nm [n] (getHsDecls ans ds) False False))
= Just (doLiftZ m (getHsDecls ans ds))
| otherwise = Nothing
liftToLetQ :: SYB.Data a
=> NameMap -> Anns
-> GHC.LHsExpr GHC.RdrName -> Maybe (SYB.Stage -> Z.Zipper a -> RefactGhc (Z.Zipper a))
liftToLetQ nm ans ll@(GHC.L _ (GHC.HsLet ds _e))
| nonEmptyList (definingDeclsRdrNames nm [n] (getHsDecls ans ds) False False)
= Just (doLiftZ ll (getHsDecls ans ll))
| otherwise = Nothing
liftToLetQ _ _ _ = Nothing
doLiftZ :: (SYB.Data t,SYB.Data a)
=> t
-> [GHC.LHsDecl GHC.RdrName]
-> SYB.Stage
-> Z.Zipper a
-> RefactGhc (Z.Zipper a)
doLiftZ ds decls _stage z =
do
logm $ "doLiftZ entered"
logDataWithAnns "doLiftZ:ds" ds
logDataWithAnns "doLiftZ:decls" decls
let zu = case (Z.up z) of
Just zz -> fromMaybe (error $ "MoveDef.liftToLet.1" ++ SYB.showData SYB.Parser 0 decls)
$ upUntil (False `SYB.mkQ` isMatch
`SYB.extQ` isHsLet
)
zz
Nothing -> z
let
wtop (ren::GHC.ParsedSource) = do
logm $ "wtop entered"
nm <- getRefactNameMap
let (_,DN dd) = (hsFreeAndDeclaredRdr nm ren)
worker ren decls pn dd
wmatch :: GHC.LMatch GHC.RdrName (GHC.LHsExpr GHC.RdrName)
-> RefactGhc (GHC.LMatch GHC.RdrName (GHC.LHsExpr GHC.RdrName))
wmatch (m@(GHC.L _ (GHC.Match _mln _pats _typ grhss))) = do
logm $ "wmatch entered:" ++ SYB.showData SYB.Parser 0 m
nm <- getRefactNameMap
let (_,DN dd) = hsFreeAndDeclaredRdr nm grhss
decls' <- liftT $ hsDecls m
workerTop m decls' dd
wlet :: GHC.LHsExpr GHC.RdrName -> RefactGhc (GHC.LHsExpr GHC.RdrName)
wlet l@(GHC.L _ (GHC.HsLet dsl _e)) = do
logm $ "wlet entered "
nm <- getRefactNameMap
let (_,DN dd) = hsFreeAndDeclaredRdr nm dsl
dsl' <- workerTop l decls dd
return dsl'
wlet x = return x
ds' <- Z.transM ( SYB.mkM wtop
`SYB.extM` wmatch
`SYB.extM` wlet
) zu
return ds'
workerTop :: (HasDecls t)
=> t
-> [GHC.LHsDecl GHC.RdrName]
-> [GHC.Name]
-> RefactGhc t
workerTop dest ds dd
=do
logm $ "MoveDef.worker: dest" ++ SYB.showData SYB.Parser 0 dest
logm $ "MoveDef.workerTop: ds=" ++ (showGhc ds)
done <- getRefactDone
if done then return dest
else do
setRefactDone
let parent = dest
nm <- getRefactNameMap
let liftedDecls = definingDeclsRdrNames' nm [n] parent
declaredPns = nub $ concatMap (definedNamesRdr nm) liftedDecls
logm $ "MoveDef.workerTop: n=" ++ (showGhc n)
logm $ "MoveDef.workerTop: liftedDecls=" ++ (showGhc liftedDecls)
pns <- pnsNeedRenaming dest parent liftedDecls declaredPns
logm $ "MoveDef.workerTop: pns=" ++ (showGhc pns)
if pns==[]
then do
(parent',liftedDecls',mLiftedSigs')<-addParamsToParentAndLiftedDecl n dd
parent liftedDecls []
logm $ "MoveDef.workerTop: liftedDecls'=" ++ (showGhc liftedDecls')
let toMove = parent'
pdecls <- liftT $ hsDecls toMove
let mAfter = case pdecls of
[] -> Nothing
_ -> (Just (ghead "worker" (definedNamesRdr nm (glast "workerTop" ds))))
dest' <- moveDecl1 toMove
mAfter
[n] liftedDecls' declaredPns mLiftedSigs'
return dest'
else askRenamingMsg pns "lifting"
worker :: (HasDecls t)
=> t
-> [GHC.LHsDecl GHC.RdrName]
-> GHC.Located GHC.Name
-> [GHC.Name]
-> RefactGhc t
worker dest ds pnn dd
=do
logm $ "MoveDef.worker: ds=" ++ (showGhc ds)
done <- getRefactDone
if done then return dest
else do
setRefactDone
(before,parent,after) <- divideDecls ds pnn
logm $ "MoveDef.worker:(before,parent,after)" ++ showGhc (before,parent,after)
nm <- getRefactNameMap
let liftedDecls = definingDeclsRdrNames nm [n] parent True True
declaredPns = nub $ concatMap (definedNamesRdr nm) liftedDecls
pns <- pnsNeedRenaming dest parent liftedDecls declaredPns
logm $ "MoveDef.worker: pns=" ++ (showGhc pns)
if pns==[]
then do
(parent',liftedDecls',mLiftedSigs')<-addParamsToParentAndLiftedDecl n dd
parent liftedDecls []
toMove <- liftT $ replaceDecls dest (before++parent'++after)
dest' <- moveDecl1 toMove
(Just (ghead "worker" (definedNamesRdr nm (ghead "worker" parent'))))
[n] liftedDecls' declaredPns mLiftedSigs'
return dest'
else askRenamingMsg pns "lifting"
demote :: RefactSettings -> GM.Options -> FilePath -> SimpPos -> IO [FilePath]
demote settings opts fileName (row,col) = do
absFileName <- canonicalizePath fileName
runRefacSession settings opts (compDemote absFileName (row,col))
compDemote ::FilePath -> SimpPos
-> RefactGhc [ApplyRefacResult]
compDemote fileName (row,col) = do
parseSourceFileGhc fileName
parsed <- getRefactParsed
nm <- getRefactNameMap
let (Just (modName,_)) = getModuleName parsed
let maybePn = locToRdrName (row, col) parsed
case maybePn of
Just pn@(GHC.L l _) -> demote' modName (GHC.L l (rdrName2NamePure nm pn))
_ -> error "\nInvalid cursor position!\n"
moveDecl1 :: (SYB.Data t)
=> t
-> Maybe GHC.Name
-> [GHC.Name]
-> [GHC.LHsDecl GHC.RdrName]
-> [GHC.Name]
-> [GHC.LSig GHC.RdrName]
-> RefactGhc t
moveDecl1 t defName ns mliftedDecls sigNames mliftedSigs = do
logm $ "moveDecl1:(defName,ns,sigNames,mliftedDecls)=" ++ showGhc (defName,ns,sigNames,mliftedDecls)
(t'',_sigsRemoved) <- rmTypeSigs sigNames t
logm $ "moveDecl1:mliftedSigs=" ++ showGhc mliftedSigs
(t',_declRemoved,_sigRemoved) <- rmDecl (ghead "moveDecl3.1" ns) False t''
let sigs = map wrapSig mliftedSigs
r <- addDecl t' defName (sigs++mliftedDecls,Nothing)
return r
askRenamingMsg :: [GHC.Name] -> String -> t
askRenamingMsg pns str
= error ("The identifier(s): " ++ (intercalate "," $ map showPN pns) ++
" will cause name clash/capture or ambiguity occurrence problem after "
++ str ++", please do renaming first!")
where
showPN pn = showGhc (pn,GHC.nameSrcLoc pn)
pnsNeedRenaming :: (SYB.Data t1,SYB.Data t2) =>
t1 -> t2 -> t3 -> [GHC.Name]
-> RefactGhc [GHC.Name]
pnsNeedRenaming dest parent _liftedDecls pns
= do
logm $ "MoveDef.pnsNeedRenaming entered:pns=" ++ showGhc pns
r <- mapM pnsNeedRenaming' pns
return (concat r)
where
pnsNeedRenaming' pn
= do
logm $ "MoveDef.pnsNeedRenaming' entered"
nm <- getRefactNameMap
let (FN f,DN d) = hsFDsFromInsideRdr nm dest
logm $ "MoveDef.pnsNeedRenaming':(f,d)=" ++ showGhc (f,d)
DN vs <- hsVisibleDsRdr nm pn parent
logm $ "MoveDef.pnsNeedRenaming':vs=" ++ showGhc vs
let vars = map pNtoName (nub (f `union` d `union` vs) \\ [pn])
isInScope <- isInScopeAndUnqualifiedGhc (pNtoName pn) Nothing
logm $ "MoveDef.pnsNeedRenaming:(f,d,vs,vars,isInScope)=" ++ (showGhc (f,d,vs,vars,isInScope))
if elem (pNtoName pn) vars || isInScope && findNameInRdr nm pn dest
then return [pn]
else return []
pNtoName = showGhc
addParamsToParent :: (SYB.Data t) => GHC.Name -> [GHC.RdrName] -> t -> RefactGhc t
addParamsToParent _pn [] t = return t
addParamsToParent pn params t = do
logm $ "addParamsToParent:(pn,params)" ++ (showGhc (pn,params))
nm <- getRefactNameMap
applyTP (full_buTP (idTP `adhocTP` (inExp nm))) t
where
#if __GLASGOW_HASKELL__ <= 710
inExp nm (e@(GHC.L l (GHC.HsVar n)) :: GHC.LHsExpr GHC.RdrName) = do
#else
inExp nm (e@(GHC.L l (GHC.HsVar (GHC.L _ n))) :: GHC.LHsExpr GHC.RdrName) = do
#endif
let ne = rdrName2NamePure nm (GHC.L l n)
if GHC.nameUnique ne == GHC.nameUnique pn
then addActualParamsToRhs pn params e
else return e
inExp _ e = return e
liftingInClientMod :: GHC.ModuleName -> [GHC.Name] -> TargetModule
-> RefactGhc [ApplyRefacResult]
liftingInClientMod serverModName pns targetModule = do
logm $ "liftingInClientMod:targetModule=" ++ (show targetModule)
getTargetGhc targetModule
parsed <- getRefactParsed
clientModule <- getRefactModule
logm $ "liftingInClientMod:clientModule=" ++ (showGhc clientModule)
modNames <- willBeUnQualImportedBy serverModName
logm $ "liftingInClientMod:modNames=" ++ (showGhc modNames)
if isJust modNames
then do
pns' <- namesNeedToBeHided clientModule (gfromJust "liftingInClientMod" modNames) pns
let pnsRdr' = map GHC.nameRdrName pns'
logm $ "liftingInClientMod:pns'=" ++ (showGhc pns')
if (nonEmptyList pns')
then do (refactoredMod,_) <- applyRefac (addHiding serverModName parsed pnsRdr') RSAlreadyLoaded
return [refactoredMod]
else return []
else return []
willBeExportedByClientMod :: [GHC.ModuleName] -> GHC.RenamedSource -> Bool
willBeExportedByClientMod names renamed =
let (_,_,exps,_) = renamed
in if isNothing exps
then False
else any isJust $ map (\y-> (find (\x-> (simpModule x==Just y)) (gfromJust "willBeExportedByClientMod" exps))) names
where simpModule (GHC.L _ (GHC.IEModuleContents (GHC.L _ m))) = Just m
simpModule _ = Nothing
willBeUnQualImportedBy :: GHC.ModuleName -> RefactGhc (Maybe [GHC.ModuleName])
willBeUnQualImportedBy modName = do
(_,imps,_,_) <- getRefactRenamed
let ms = filter (\(GHC.L _ (GHC.ImportDecl _ (GHC.L _ modName1) _qualify _source _safe isQualified _isImplicit _as h))
-> modName == modName1 && (not isQualified) && (isNothing h || (isJust h && ((fst (fromJust h)) == True))))
imps
res = if (emptyList ms) then Nothing
else Just $ nub $ map getModName ms
getModName (GHC.L _ (GHC.ImportDecl _ (GHC.L _ modName2) _qualify _source _safe _isQualified _isImplicit as _h))
= if isJust as then simpModName (fromJust as)
else modName2
simpModName m = m
logm $ "willBeUnQualImportedBy:(ms,res)=" ++ (showGhc (ms,res))
return res
namesNeedToBeHided :: GHC.Module -> [GHC.ModuleName] -> [GHC.Name]
-> RefactGhc [GHC.Name]
namesNeedToBeHided clientModule modNames pns = do
renamed <- getRefactRenamed
parsed <- getRefactParsed
logm $ "namesNeedToBeHided:willBeExportedByClientMod=" ++ (show $ willBeExportedByClientMod modNames renamed)
gnames <- GHC.getNamesInScope
let clientInscopes = filter (\n -> clientModule == GHC.nameModule n) gnames
logm $ "namesNeedToBeHided:(clientInscopes)=" ++ (showGhc (clientInscopes))
pnsMapped <- mapM getLocalEquiv pns
logm $ "namesNeedToBeHided:pnsMapped=" ++ (showGhc pnsMapped)
let pnsMapped' = filter (\(_,_,ns) -> not $ emptyList ns) pnsMapped
if willBeExportedByClientMod modNames renamed
then return pns
else do
ff <- mapM (needToBeHided parsed) pnsMapped'
return $ concat ff
where
getLocalEquiv :: GHC.Name -> RefactGhc (GHC.Name,String,[GHC.Name])
getLocalEquiv pn = do
let pnStr = stripPackage $ showGhc pn
logm $ "MoveDef getLocalEquiv: about to parseName:" ++ (show pnStr)
ecns <- GHC.gtry $ GHC.parseName pnStr
let cns = case ecns of
Left (_e::SomeException) -> []
Right v -> v
logm $ "MoveDef getLocalEquiv: cns:" ++ (showGhc cns)
return (pn,pnStr,cns)
stripPackage :: String -> String
stripPackage str = reverse s
where
(s,_) = break (== '.') $ reverse str
needToBeHided :: GHC.ParsedSource -> (GHC.Name,String,[GHC.Name]) -> RefactGhc [GHC.Name]
needToBeHided parsed (pn,_pnStr,pnsLocal) = do
let uwoq = map (\n -> usedWithoutQualR n parsed) pnsLocal
logm $ "needToBeHided:(pn,uwoq)=" ++ (showGhc (pn,uwoq))
if (any (== True) uwoq
|| False)
then return [pn]
else return []
liftedToTopLevel :: GHC.Located GHC.Name -> GHC.ParsedSource -> RefactGhc (Bool,[GHC.Name])
liftedToTopLevel (GHC.L _ pn) parsed = do
logm $ "liftedToTopLevel entered:pn=" ++ showGhc pn
nm <- getRefactNameMap
decls <- liftT $ hsDecls parsed
let topDecs = definingDeclsRdrNames nm [pn] decls False False
if nonEmptyList topDecs
then do
let liftedDecls = definingDeclsRdrNames nm [pn] topDecs False False
declaredPns = nub $ concatMap (definedNamesRdr nm) liftedDecls
return (True, declaredPns)
else return (False, [])
addParamsToParentAndLiftedDecl :: (SYB.Data t) =>
GHC.Name
-> [GHC.Name]
-> t
-> [GHC.LHsDecl GHC.RdrName]
-> [GHC.LSig GHC.RdrName]
-> RefactGhc (t, [GHC.LHsDecl GHC.RdrName], [GHC.LSig GHC.RdrName])
addParamsToParentAndLiftedDecl pn dd parent liftedDecls mLiftedSigs
=do
logm $ "addParamsToParentAndLiftedDecl:liftedDecls=" ++ (showGhc liftedDecls)
nm <- getRefactNameMap
let (FN ef,_) = hsFreeAndDeclaredRdr nm parent
let (FN lf,_) = hsFreeAndDeclaredRdr nm liftedDecls
logm $ "addParamsToParentAndLiftedDecl:(ef,lf)=" ++ showGhc (ef,lf)
let newParamsNames = ((nub lf) \\ (nub ef)) \\ dd
newParams = map GHC.nameRdrName newParamsNames
logm $ "addParamsToParentAndLiftedDecl:(newParams,ef,lf,dd)=" ++ (showGhc (newParams,ef,lf,dd))
if newParams /= []
then if (any isComplexPatDecl liftedDecls)
then error "This pattern binding cannot be lifted, as it uses some other local bindings!"
else do
(parent'',liftedDecls'',_msig) <- rmDecl pn False parent
parent' <- addParamsToParent pn newParams parent''
liftedDecls' <- addParamsToDecls [liftedDecls''] pn newParams
mLiftedSigs' <- mapM (addParamsToSigs newParamsNames) mLiftedSigs
logm $ "addParamsToParentAndLiftedDecl:mLiftedSigs'=" ++ showGhc mLiftedSigs'
return (parent',liftedDecls', mLiftedSigs')
else return (parent,liftedDecls,mLiftedSigs)
demote' ::
GHC.ModuleName
-> GHC.Located GHC.Name
-> RefactGhc [ApplyRefacResult]
demote' modName (GHC.L _ pn) = do
renamed <- getRefactRenamed
parsed <- getRefactParsed
nm <- getRefactNameMap
targetModule <- getRefactTargetModule
if isFunOrPatName nm pn parsed
then do
isTl <- isTopLevelPN pn
if isTl && isExplicitlyExported nm pn parsed
then error "This definition can not be demoted, as it is explicitly exported by the current module!"
else do
(refactoredMod,declaredPns) <- applyRefac (doDemoting pn) RSAlreadyLoaded
if isTl && modIsExported modName renamed
then do
logm $ "demote':isTl && isExported"
clients <- clientModsAndFiles targetModule
logm $ "demote':clients=" ++ (showGhc clients)
refactoredClients <-mapM (demotingInClientMod declaredPns) clients
return (refactoredMod:refactoredClients)
else do return [refactoredMod]
else error "\nInvalid cursor position!"
demotingInClientMod ::
[GHC.Name] -> TargetModule
-> RefactGhc ApplyRefacResult
demotingInClientMod pns targetModule = do
logm $ "demotingInClientMod:(pns,targetModule)=" ++ showGhc (pns,targetModule)
getTargetGhc targetModule
modu <- getRefactModule
(refactoredMod,_) <- applyRefac (doDemotingInClientMod pns modu) RSAlreadyLoaded
return refactoredMod
doDemotingInClientMod :: [GHC.Name] -> GHC.Module -> RefactGhc ()
doDemotingInClientMod pns modName = do
logm $ "doDemotingInClientMod:(pns,modName)=" ++ showGhc (pns,modName)
(GHC.L _ p) <- getRefactParsed
nm <- getRefactNameMap
if any (\pn -> findNameInRdr nm pn (GHC.hsmodDecls p) || findNameInRdr nm pn (GHC.hsmodExports p)) pns
then error $ "This definition can not be demoted, as it is used in the client module '"++(showGhc modName)++"'!"
else if any (\pn->findNameInRdr nm pn (GHC.hsmodImports p)) pns
then do
return ()
else return ()
doDemoting :: GHC.Name -> RefactGhc [GHC.Name]
doDemoting pn = do
clearRefactDone
parsed <- getRefactParsed
parsed' <- everywhereMStaged' SYB.Parser (SYB.mkM demoteInMod
`SYB.extM` demoteInMatch
`SYB.extM` demoteInPat
`SYB.extM` demoteInLet
`SYB.extM` demoteInStmt
) parsed
putRefactParsed parsed' emptyAnns
nm <- getRefactNameMap
decls <- liftT $ hsDecls parsed
let demotedDecls'= definingDeclsRdrNames nm [pn] decls True False
declaredPnsRdr = nub $ concatMap definedPNsRdr demotedDecls'
declaredPns = map (rdrName2NamePure nm) declaredPnsRdr
return declaredPns
where
demoteInMod x@(parsed :: GHC.ParsedSource) = do
decls <- liftT $ hsDecls parsed
nm <- getRefactNameMap
if not $ emptyList (definingDeclsRdrNames nm [pn] decls False False)
then do
logm "MoveDef:demoteInMod"
demoted <- doDemoting' parsed pn
return demoted
else return x
demoteInMatch match@(GHC.L _ (GHC.Match _ _pats _mt (GHC.GRHSs _ _ds))::GHC.LMatch GHC.RdrName (GHC.LHsExpr GHC.RdrName)) = do
decls <- liftT $ hsDecls match
nm <- getRefactNameMap
if not $ emptyList (definingDeclsRdrNames nm [pn] decls False False)
then do
logm "MoveDef:demoteInMatch"
done <- getRefactDone
match' <- if (not done)
then doDemoting' match pn
else return match
return match'
else return match
demoteInPat x@(pat@(GHC.L _ (GHC.ValD (GHC.PatBind _p (GHC.GRHSs _grhs _lb) _ _ _)))::GHC.LHsDecl GHC.RdrName) = do
decls <- liftT $ hsDeclsPatBindD x
nm <- getRefactNameMap
if not $ emptyList (definingDeclsRdrNames nm [pn] decls False False)
then do
logm "MoveDef:demoteInPat"
done <- getRefactDone
pat' <- if (not done)
then doDemoting' pat pn
else return pat
return pat'
else return x
demoteInPat x = return x
demoteInLet x@(letExp@(GHC.L _ (GHC.HsLet _ds _e))::GHC.LHsExpr GHC.RdrName) = do
decls <- liftT $ hsDecls x
nm <- getRefactNameMap
if not $ emptyList (definingDeclsRdrNames nm [pn] decls False False)
then do
logm "MoveDef:demoteInLet"
done <- getRefactDone
letExp' <- if (not done)
then doDemoting' letExp pn
else return letExp
return letExp'
else return x
demoteInLet x = return x
demoteInStmt (letStmt@(GHC.L _ (GHC.LetStmt _binds))::GHC.LStmt GHC.RdrName (GHC.LHsExpr GHC.RdrName)) = do
decls <- liftT $ hsDecls letStmt
nm <- getRefactNameMap
if not $ emptyList (definingDeclsRdrNames nm [pn] decls False False)
then do
logm "MoveDef:demoteInStmt"
done <- getRefactDone
letStmt' <- if (not done)
then doDemoting' letStmt pn
else return letStmt
return letStmt'
else return letStmt
demoteInStmt x = return x
doDemoting' :: (UsedByRhs t) => t -> GHC.Name -> RefactGhc t
doDemoting' t pn = do
nm <- getRefactNameMap
origDecls <- liftT $ hsDeclsGeneric t
let
demotedDecls'= definingDeclsRdrNames nm [pn] origDecls True False
declaredPns = nub $ concatMap (definedNamesRdr nm) demotedDecls'
pnsUsed = usedByRhsRdr nm t declaredPns
logm $ "doDemoting':(pn,declaredPns)=" ++ showGhc (pn,declaredPns)
logm $ "doDemoting':(declaredPns,pnsUsed)=" ++ showGhc (declaredPns,pnsUsed)
r <- if not pnsUsed
then do
logm $ "doDemoting' no pnsUsed"
let dt = origDecls
let demotedDecls = definingDeclsRdrNames nm [pn] dt True True
otherBinds = (deleteFirstsBy (sameBindRdr nm) dt demotedDecls)
xx = map (\b -> (b,uses nm declaredPns [b])) otherBinds
useCount = sum $ concatMap snd xx
logm $ "doDemoting': declaredPns=" ++ (showGhc declaredPns)
logm $ "doDemoting': uses xx=" ++ (showGhc xx)
logm $ "doDemoting': uses useCount=" ++ (show useCount)
case useCount of
0 ->do error "\n Nowhere to demote this function!\n"
1 ->
do
logm "MoveDef.doDemoting':target location found"
let (FN f,_d) = hsFreeAndDeclaredRdr nm demotedDecls
(ds,removedDecl,_sigRemoved) <- rmDecl pn False t
(t',demotedSigs) <- rmTypeSigs declaredPns ds
logDataWithAnns "MoveDef.doDemoting':after rmTypeSigs:demotedSigs=" demotedSigs
logm $ "MoveDef:declaredPns=" ++ (showGhc declaredPns)
dl <- mapM (flip (declaredNamesInTargetPlace nm) ds) declaredPns
logm $ "mapM declaredNamesInTargetPlace done"
let clashedNames=filter (\x-> elem (id x) (map id f)) $ (nub.concat) dl
if clashedNames/=[]
then error ("The identifier(s):" ++ showGhc clashedNames ++
", declared in where the definition will be demoted to, will cause name clash/capture"
++" after demoting, please do renaming first!")
else
do
duplicateDecls declaredPns removedDecl demotedSigs t'
_ ->error "\nThis function/pattern binding is used by more than one friend bindings\n"
else error "This function can not be demoted as it is used in current level!\n"
return r
where
uses :: NameMap -> [GHC.Name] -> [GHC.LHsDecl GHC.RdrName] -> [Int]
uses nm pns t2
= concatMap used t2
where
used :: GHC.LHsDecl GHC.RdrName -> [Int]
#if __GLASGOW_HASKELL__ <= 710
used (GHC.L _ (GHC.ValD (GHC.FunBind _n _ (GHC.MG matches _ _ _) _ _ _)))
#else
used (GHC.L _ (GHC.ValD (GHC.FunBind _n (GHC.MG (GHC.L _ matches) _ _ _) _ _ _)))
#endif
= concatMap (usedInMatch pns) matches
used (GHC.L _ (GHC.ValD (GHC.PatBind pat rhs _ _ _)))
| (not $ findNamesRdr nm pns pat) && findNamesRdr nm pns rhs
= [1::Int]
used _ = []
usedInMatch pns' (GHC.L _ (GHC.Match _ pats _ rhs))
| (not $ findNamesRdr nm pns' pats) && findNamesRdr nm pns' rhs
= [1::Int]
usedInMatch _ _ = []
duplicateDecls :: (SYB.Data t,SYB.Typeable t)
=> [GHC.Name]
-> GHC.LHsDecl GHC.RdrName
-> [GHC.LSig GHC.RdrName]
-> t
-> RefactGhc t
duplicateDecls pns demoted dsig o = do
logm $ "duplicateDecls:t=" ++ SYB.showData SYB.Parser 0 o
hasDeclsSybTransform workerHsDecls workerBind o
where
workerHsDecls :: forall t. HasDecls t => t -> RefactGhc t
workerHsDecls t' = do
dds <- liftT $ hsDecls t'
ds'' <- duplicateDecls' pns demoted dsig dds
liftT $ replaceDecls t' ds''
workerBind :: (GHC.LHsBind GHC.RdrName -> RefactGhc (GHC.LHsBind GHC.RdrName))
workerBind t'@(GHC.L _ (GHC.PatBind{})) = do
dds <- liftT $ hsDeclsPatBind t'
ds'' <- duplicateDecls' pns demoted dsig dds
liftT $ replaceDeclsPatBind t' ds''
workerBind x = error $ "MoveDef.duplicateDecls.workerBind:unmatched LHsBind:" ++ showGhc x
duplicateDecls' :: [GHC.Name]
-> GHC.LHsDecl GHC.RdrName
-> [GHC.LSig GHC.RdrName]
-> [GHC.LHsDecl GHC.RdrName]
-> RefactGhc [GHC.LHsDecl GHC.RdrName]
duplicateDecls' pns demoted dsig decls
= do
nm <- getRefactNameMap
everywhereMStaged' SYB.Parser (SYB.mkM (dupInMatch nm)
`SYB.extM` (dupInPat nm)) decls
where
dupInMatch nm (match@(GHC.L _ (GHC.Match _ pats _mt rhs)) :: GHC.LMatch GHC.RdrName (GHC.LHsExpr GHC.RdrName))
| (not $ findNamesRdr nm pns pats) && findNamesRdr nm pns rhs
= do
done <- getRefactDone
logm $ "duplicateDecls.dupInMatch:value of done=" ++ (show done)
if done
then return match
else do
logm "duplicateDecls:setting done"
setRefactDone
match' <- foldParams pns match decls demoted dsig
return match'
dupInMatch _ x = return x
dupInPat nm ((GHC.PatBind pat rhs@(GHC.GRHSs grhs lb) ty fvs ticks) :: GHC.HsBind GHC.RdrName)
| (not $ findNamesRdr nm pns pat) && findNamesRdr nm pns rhs
= do
logm $ "duplicateDecls.dupInPat"
let declsToLift = definingDeclsRdrNames' nm pns t
lb' <- moveDecl1 lb Nothing pns declsToLift pns []
return (GHC.PatBind pat (GHC.GRHSs grhs lb') ty fvs ticks)
dupInPat _ x = return x
declaredNamesInTargetPlace :: (SYB.Data t)
=> NameMap -> GHC.Name -> t
-> RefactGhc [GHC.Name]
declaredNamesInTargetPlace nm pn' t' = do
logm $ "declaredNamesInTargetPlace:pn=" ++ (showGhc pn')
res <- applyTU (stop_tdTU (failTU
`adhocTU` inMatch
`adhocTU` inPat)) t'
logm $ "declaredNamesInTargetPlace:res=" ++ (showGhc res)
return res
where
inMatch ((GHC.Match _ _pats _ rhs) :: GHC.Match GHC.RdrName (GHC.LHsExpr GHC.RdrName))
| findNameInRdr nm pn' rhs = do
logm $ "declaredNamesInTargetPlace:inMatch"
let (_,DN ds) = hsFDsFromInsideRdr nm rhs
return ds
inMatch _ = return mzero
inPat ((GHC.PatBind pat rhs _ _ _) :: GHC.HsBind GHC.RdrName)
|findNameInRdr nm pn' rhs = do
logm $ "declaredNamesInTargetPlace:inPat"
let (_,DN ds) = hsFDsFromInsideRdr nm pat
return ds
inPat _= return mzero
foldParams :: [GHC.Name]
-> GHC.LMatch GHC.RdrName (GHC.LHsExpr GHC.RdrName)
-> [GHC.LHsDecl GHC.RdrName]
-> GHC.LHsDecl GHC.RdrName
-> [GHC.LSig GHC.RdrName]
-> RefactGhc (GHC.LMatch GHC.RdrName (GHC.LHsExpr GHC.RdrName))
foldParams pns match@(GHC.L l (GHC.Match _mfn _pats _mt rhs)) _decls demotedDecls dsig
=do
logm $ "MoveDef.foldParams entered"
nm <- getRefactNameMap
let matches = concatMap matchesInDecls [demotedDecls]
pn = ghead "foldParams" pns
logm $ "MoveDef.foldParams before allParams"
params <- allParams pn rhs []
logm $ "foldParams:params=" ++ showGhc params
if (length.nub.map length) params==1
&& ((length matches)==1)
then do
let patsInDemotedDecls=(patsInMatch.(ghead "foldParams")) matches
subst = mkSubst nm patsInDemotedDecls params
fstSubst = map fst subst
sndSubst = map snd subst
rhs' <- rmParamsInParent pn sndSubst rhs
let ls = map (hsFreeAndDeclaredRdr nm) sndSubst
let newNames = ((concatMap (fn . fst) ls)) \\ (fstSubst)
clashedNames <- getClashedNames nm fstSubst newNames (ghead "foldParams" matches)
logm $ "MoveDef.foldParams about to foldInDemotedDecls"
demotedDecls''' <- foldInDemotedDecls pns clashedNames subst [demotedDecls]
logm $ "MoveDef.foldParams foldInDemotedDecls done"
let match' = GHC.L l ((GHC.unLoc match) {GHC.m_grhss = rhs' })
match'' <- addDecl match' Nothing (demotedDecls''',Nothing)
logm $ "MoveDef.foldParams addDecl done 1"
return match''
else do
logm $ "foldParams:no params"
let sigs = map wrapSig dsig
match' <- addDecl match Nothing (sigs++[demotedDecls],Nothing)
logm "MoveDef.foldParams addDecl done 2"
return match'
where
matchesInDecls :: GHC.LHsDecl GHC.RdrName -> [GHC.LMatch GHC.RdrName (GHC.LHsExpr GHC.RdrName)]
#if __GLASGOW_HASKELL__ <= 710
matchesInDecls (GHC.L _ (GHC.ValD (GHC.FunBind _ _ (GHC.MG matches _ _ _) _ _ _))) = matches
#else
matchesInDecls (GHC.L _ (GHC.ValD (GHC.FunBind _ (GHC.MG (GHC.L _ matches) _ _ _) _ _ _))) = matches
#endif
matchesInDecls _x = []
patsInMatch (GHC.L _ (GHC.Match _ pats' _ _)) = pats'
foldInDemotedDecls :: [GHC.Name]
-> [GHC.Name]
-> [(GHC.Name, GHC.HsExpr GHC.RdrName)]
-> [GHC.LHsDecl GHC.RdrName]
-> RefactGhc [GHC.LHsDecl GHC.RdrName]
foldInDemotedDecls pns' clashedNames subst decls = do
logm $ "foldInDemotedDecls:(pns',clashedNames,subst)=" ++ showGhc (pns',clashedNames,subst)
logm $ "foldInDemotedDecls:decls=" ++ SYB.showData SYB.Parser 0 decls
nm <- getRefactNameMap
SYB.everywhereMStaged SYB.Parser (SYB.mkM (worker nm) `SYB.extM` (workerBind nm)) decls
where
#if __GLASGOW_HASKELL__ <= 710
worker nm (match'@(GHC.L _ (GHC.FunBind ln _ (GHC.MG _matches _ _ _) _ _ _)) :: GHC.LHsBind GHC.RdrName)
#else
worker nm (match'@(GHC.L _ (GHC.FunBind ln (GHC.MG _matches _ _ _) _ _ _)) :: GHC.LHsBind GHC.RdrName)
#endif
= do
logm $ "foldInDemotedDecls:rdrName2NamePure nm ln=" ++ show (rdrName2NamePure nm ln)
if isJust (find (== rdrName2NamePure nm ln) pns')
then do
logm $ "foldInDemotedDecls:found match'"
match'' <- foldM (flip autoRenameLocalVar) match' clashedNames
match''' <- foldM replaceExpWithUpdToks match'' subst
rmParamsInDemotedDecls (map fst subst) match'''
else return match'
worker _ x = return x
workerBind nm ((GHC.L ll (GHC.ValD d)) :: GHC.LHsDecl GHC.RdrName)
= do
(GHC.L _ d') <- worker nm (GHC.L ll d)
return (GHC.L ll (GHC.ValD d'))
workerBind _ x = return x
allParams :: GHC.Name -> GHC.GRHSs GHC.RdrName (GHC.LHsExpr GHC.RdrName)
-> [[GHC.HsExpr GHC.RdrName]]
-> RefactGhc [[GHC.HsExpr GHC.RdrName]]
allParams pn rhs1 initial
=do
nm <- getRefactNameMap
let p = getOneParam nm pn rhs1
logm $ "allParams:p=" ++ showGhc p
if (nonEmptyList p)
then do rhs' <- rmOneParam pn rhs1
logDataWithAnns "allParams:rhs'=" rhs'
allParams pn rhs' (initial++[p])
else return initial
where
getOneParam :: (SYB.Data t) => NameMap -> GHC.Name -> t -> [GHC.HsExpr GHC.RdrName]
getOneParam nm pn1
= SYB.everythingStaged SYB.Renamer (++) []
([] `SYB.mkQ` worker)
where
worker :: GHC.HsExpr GHC.RdrName -> [GHC.HsExpr GHC.RdrName]
worker (GHC.HsApp e1 e2)
|(expToNameRdr nm e1 == Just pn1) = [GHC.unLoc e2]
worker _ = []
rmOneParam :: (SYB.Data t) => GHC.Name -> t -> RefactGhc t
rmOneParam pn1 t
= do
nm <- getRefactNameMap
everywhereMStaged' SYB.Parser (SYB.mkM (worker nm)) t
where
worker nm (GHC.L _ (GHC.HsApp e1 _e2 ))
|expToNameRdr nm e1 == Just pn1 = return e1
worker _ x = return x
rmParamsInDemotedDecls :: [GHC.Name] -> GHC.LHsBind GHC.RdrName
-> RefactGhc (GHC.LHsBind GHC.RdrName)
rmParamsInDemotedDecls ps bind
= SYB.everywhereMStaged SYB.Parser (SYB.mkM worker) bind
where worker :: GHC.Match GHC.RdrName (GHC.LHsExpr GHC.RdrName) -> RefactGhc (GHC.Match GHC.RdrName (GHC.LHsExpr GHC.RdrName))
worker (GHC.Match mfn' pats2 typ rhs1)
= do
nm <- getRefactNameMap
let pats'=filter (\x->not ((patToNameRdr nm x /= Nothing) &&
elem (gfromJust "rmParamsInDemotedDecls" $ patToNameRdr nm x) ps)) pats2
return (GHC.Match mfn' pats' typ rhs1)
rmParamsInParent :: GHC.Name -> [GHC.HsExpr GHC.RdrName]
-> GHC.GRHSs GHC.RdrName (GHC.LHsExpr GHC.RdrName)
-> RefactGhc (GHC.GRHSs GHC.RdrName (GHC.LHsExpr GHC.RdrName))
rmParamsInParent pn es grhss = do
nm <- getRefactNameMap
SYB.everywhereMStaged SYB.Renamer (SYB.mkM (worker nm)) grhss
where worker nm expr@(GHC.L _ (GHC.HsApp e1 e2))
| findNamesRdr nm [pn] e1 && (elem (showGhc (GHC.unLoc e2)) (map (showGhc) es))
= do
liftT $ transferEntryDPT expr e1
return e1
worker nm (expr@(GHC.L _ (GHC.HsPar e1)))
|Just pn==expToNameRdr nm e1
= do
liftT $ transferEntryDPT expr e1
return e1
worker _ x =return x
getClashedNames :: NameMap -> [GHC.Name] -> [GHC.Name]
-> GHC.LMatch GHC.RdrName (GHC.LHsExpr GHC.RdrName)
-> RefactGhc [GHC.Name]
getClashedNames nm oldNames newNames match'
= do let (_f,DN d) = hsFDsFromInsideRdr nm match'
ds'' <- mapM (flip (hsVisibleDsRdr nm) match') oldNames
let ds' = map (\(DN ds) -> ds) ds''
return (filter (\x->elem ( x) newNames)
( nub (d `union` (nub.concat) ds')))
mkSubst :: NameMap
-> [GHC.LPat GHC.RdrName] -> [[GHC.HsExpr GHC.RdrName]]
-> [(GHC.Name,GHC.HsExpr GHC.RdrName)]
mkSubst nm pats1 params
= catMaybes (zipWith (\x y -> if (patToNameRdr nm x/=Nothing) && (length (nub $ map showGhc y)==1)
then Just (gfromJust "mkSubst" $ patToNameRdr nm x,(ghead "mkSubst") y)
else Nothing) pats1 params)
replaceExpWithUpdToks :: GHC.LHsBind GHC.RdrName -> (GHC.Name, GHC.HsExpr GHC.RdrName)
-> RefactGhc (GHC.LHsBind GHC.RdrName)
replaceExpWithUpdToks decls subst = do
nm <- getRefactNameMap
let
worker (e@(GHC.L l _)::GHC.LHsExpr GHC.RdrName)
|(expToNameRdr nm e) == Just (fst subst)
= return (GHC.L l (snd subst))
worker x=return x
everywhereMStaged' SYB.Parser (SYB.mkM worker) decls
isLocalFunOrPatName :: SYB.Data t => NameMap -> GHC.Name -> t -> Bool
isLocalFunOrPatName nm pn scope
= isLocalPN pn && isFunOrPatName nm pn scope