haskell-tools-experimental-refactorings (empty) → 1.0.0.0
raw patch · 27 files changed
+466/−0 lines, 27 filesdep +Cabaldep +basedep +containerssetup-changed
Dependencies added: Cabal, base, containers, directory, either, filepath, ghc, ghc-paths, haskell-tools-ast, haskell-tools-backend-ghc, haskell-tools-experimental-refactorings, haskell-tools-prettyprint, haskell-tools-refactor, haskell-tools-rewrite, mtl, references, split, tasty, tasty-hunit, template-haskell, time, transformers, uniplate
Files
- LICENSE +29/−0
- Language/Haskell/Tools/Refactor/Builtin/DataToNewtype.hs +15/−0
- Language/Haskell/Tools/Refactor/Builtin/DollarApp.hs +49/−0
- Language/Haskell/Tools/Refactor/Builtin/DollarApp1.hs +17/−0
- Language/Haskell/Tools/Refactor/Builtin/DollarApp2.hs +23/−0
- Language/Haskell/Tools/Refactor/Builtin/DollarApp3.hs +43/−0
- Language/Haskell/Tools/Refactor/Builtin/HelloRefactor.hs +18/−0
- Language/Haskell/Tools/Refactor/Builtin/IfToGuards.hs +28/−0
- Setup.hs +2/−0
- examples/Refactor/DataToNewtype/Cases.hs +8/−0
- examples/Refactor/DataToNewtype/Cases_res.hs +8/−0
- examples/Refactor/DollarApp/AnotherOperator.hs +5/−0
- examples/Refactor/DollarApp/AnotherOperator_res.hs +5/−0
- examples/Refactor/DollarApp/Defs.hs +9/−0
- examples/Refactor/DollarApp/Final.hs +16/−0
- examples/Refactor/DollarApp/FirstMulti.hs +5/−0
- examples/Refactor/DollarApp/FirstMulti_res.hs +5/−0
- examples/Refactor/DollarApp/FirstSingle.hs +5/−0
- examples/Refactor/DollarApp/FirstSingle_res.hs +5/−0
- examples/Refactor/DollarApp/ImportDollar.hs +6/−0
- examples/Refactor/DollarApp/ImportDollar_res.hs +7/−0
- examples/Refactor/DollarApp/InfixOperator.hs +5/−0
- examples/Refactor/DollarApp/InfixOperator_res.hs +5/−0
- examples/Refactor/IfToGuards/Simple.hs +3/−0
- examples/Refactor/IfToGuards/Simple_res.hs +4/−0
- haskell-tools-experimental-refactorings.cabal +75/−0
- test/Main.hs +66/−0
+ LICENSE view
@@ -0,0 +1,29 @@+Haskell-Tools is Copyright (c) Boldizsár Németh, 2016, all rights reserved, +and is distributed as free software under the following license. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + +- Redistributions of source code must retain the above copyright +notice, this list of conditions, and the following disclaimer. + +- Redistributions in binary form must reproduce the above copyright +notice, this list of conditions, and the following disclaimer in the +documentation and/or other materials provided with the distribution. + +- The names of the copyright holders may not be used to endorse or +promote products derived from this software without specific prior +written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS "AS IS" AND ANY +EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR +BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN +IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ Language/Haskell/Tools/Refactor/Builtin/DataToNewtype.hs view
@@ -0,0 +1,15 @@+module Language.Haskell.Tools.Refactor.Builtin.DataToNewtype (dataToNewtype, tryItOut) where + +import Control.Reference ((.=), (.-), (&)) +import Language.Haskell.Tools.Refactor + +tryItOut :: String -> IO () +tryItOut moduleName = tryRefactor (\_ -> localRefactoring dataToNewtype) moduleName "" + +dataToNewtype :: LocalRefactoring dom +dataToNewtype = return . (modDecl & annList .- changeDeclaration) + +changeDeclaration :: Decl dom -> Decl dom +changeDeclaration dd@(DataDecl DataKeyword _ _ (AnnList [ConDecl _ (AnnList [_])]) _) + = declNewtype .= mkNewtypeKeyword $ dd +changeDeclaration decl = decl
+ Language/Haskell/Tools/Refactor/Builtin/DollarApp.hs view
@@ -0,0 +1,49 @@+{-# LANGUAGE FlexibleContexts, ConstraintKinds, TypeFamilies #-} +module Language.Haskell.Tools.Refactor.Builtin.DollarApp (dollarApp, DollarDomain, tryItOut) where + +import Language.Haskell.Tools.Refactor + +import BasicTypes (Fixity(..)) +import Id (idName) +import qualified Name as GHC (Name) +import PrelInfo (wiredInIds) +import PrelNames (dollarIdKey) +import SrcLoc (RealSrcSpan, SrcSpan) +import Unique (getUnique) + +import Control.Monad.State +import Control.Reference ((^.), (!~), biplateRef) + +tryItOut :: String -> String -> IO () +tryItOut = tryRefactor (localRefactoring . dollarApp) + +type DollarMonad dom = StateT [SrcSpan] (LocalRefactor dom) +type DollarDomain dom = (HasImportInfo dom, HasModuleInfo dom, HasFixityInfo dom, HasNameInfo dom) + +dollarApp :: DollarDomain dom => RealSrcSpan -> LocalRefactoring dom +dollarApp sp = flip evalStateT [] . ((nodesContained sp !~ (\e -> get >>= replaceExpr e)) + >=> (biplateRef !~ parenExpr)) + +replaceExpr :: DollarDomain dom => Expr dom -> [SrcSpan] -> DollarMonad dom (Expr dom) +replaceExpr expr@(App _ (Paren (InfixApp _ op arg))) replacedRanges + | not (getRange arg `elem` replacedRanges) + , semanticsName (op ^. operatorName) /= Just dollarName + , case semanticsFixity (op ^. operatorName) of Just (Fixity _ p _) | p > 0 -> False; _ -> True + = return expr +replaceExpr (App fun (Paren arg)) _ = do modify $ (getRange arg :) + mkInfixApp fun <$> lift (referenceOperator dollarName) <*> pure arg +replaceExpr e _ = return e + +parenExpr :: Expr dom -> DollarMonad dom (Expr dom) +parenExpr e = (exprLhs !~ parenDollar True) =<< (exprRhs !~ parenDollar False $ e) + +parenDollar :: Bool -> Expr dom -> DollarMonad dom (Expr dom) +parenDollar lhs expr@(InfixApp _ _ arg) + = do replacedRanges <- get + if getRange arg `elem` replacedRanges && (lhs || getRange expr `notElem` replacedRanges) + then return $ mkParen expr + else return expr +parenDollar _ e = return e + +dollarName :: GHC.Name +[dollarName] = map idName $ filter ((dollarIdKey==) . getUnique) wiredInIds
+ Language/Haskell/Tools/Refactor/Builtin/DollarApp1.hs view
@@ -0,0 +1,17 @@+{-# LANGUAGE TypeFamilies, FlexibleContexts #-} +module Language.Haskell.Tools.Refactor.Builtin.DollarApp1 where + +import Language.Haskell.Tools.Refactor + +import Control.Reference ((.-)) +import SrcLoc (RealSrcSpan) + +tryItOut :: String -> String -> IO () +tryItOut = tryRefactor (localRefactoring . dollarApp) + +dollarApp :: Domain dom => RealSrcSpan -> LocalRefactoring dom +dollarApp sp = return . (nodesContained sp .- replaceExpr) + +replaceExpr :: Expr dom -> Expr dom +replaceExpr (App fun (Paren arg)) = mkInfixApp fun (mkUnqualOp "$") arg +replaceExpr e = e
+ Language/Haskell/Tools/Refactor/Builtin/DollarApp2.hs view
@@ -0,0 +1,23 @@+{-# LANGUAGE FlexibleContexts, TypeFamilies #-} +module Language.Haskell.Tools.Refactor.Builtin.DollarApp2 where + +import Language.Haskell.Tools.Refactor + +import Control.Reference ((!~)) +import Id (idName) +import PrelInfo (wiredInIds) +import PrelNames (dollarIdKey) +import SrcLoc (RealSrcSpan) +import Unique (getUnique) + +tryItOut :: String -> String -> IO () +tryItOut = tryRefactor (localRefactoring . dollarApp) + +dollarApp :: (HasImportInfo dom, HasModuleInfo dom) => RealSrcSpan -> LocalRefactoring dom +dollarApp sp = nodesContained sp !~ replaceExpr + +replaceExpr :: (HasImportInfo dom, HasModuleInfo dom) => Expr dom -> LocalRefactor dom (Expr dom) +replaceExpr (App fun (Paren arg)) = mkInfixApp fun <$> referenceOperator dollarName <*> pure arg +replaceExpr e = pure e + +[dollarName] = map idName $ filter ((dollarIdKey==) . getUnique) wiredInIds
+ Language/Haskell/Tools/Refactor/Builtin/DollarApp3.hs view
@@ -0,0 +1,43 @@+{-# LANGUAGE FlexibleContexts, ConstraintKinds, TypeFamilies #-} +module Language.Haskell.Tools.Refactor.Builtin.DollarApp3 where + +import Language.Haskell.Tools.Refactor + +import Id (idName) +import qualified Name as GHC (Name) +import PrelInfo (wiredInIds) +import PrelNames (dollarIdKey) +import SrcLoc (RealSrcSpan, SrcSpan) +import Unique (getUnique) + +import Control.Monad.State +import Control.Reference ((!~), biplateRef) + +tryItOut :: String -> String -> IO () +tryItOut = tryRefactor (localRefactoring . dollarApp) + +type DollarMonad dom = StateT [SrcSpan] (LocalRefactor dom) +type DollarDomain dom = (HasImportInfo dom, HasModuleInfo dom, HasFixityInfo dom, HasNameInfo dom) + +dollarApp :: DollarDomain dom => RealSrcSpan -> LocalRefactoring dom +dollarApp sp = flip evalStateT [] . ((nodesContained sp !~ (\e -> get >>= replaceExpr e)) + >=> (biplateRef !~ parenExpr)) + +replaceExpr :: DollarDomain dom => Expr dom -> [SrcSpan] -> DollarMonad dom (Expr dom) +replaceExpr (App fun (Paren arg)) _ = do modify $ (getRange arg :) + mkInfixApp fun <$> lift (referenceOperator dollarName) <*> pure arg +replaceExpr e _ = return e + +parenExpr :: Expr dom -> DollarMonad dom (Expr dom) +parenExpr e = (exprLhs !~ parenDollar True) =<< (exprRhs !~ parenDollar False $ e) + +parenDollar :: Bool -> Expr dom -> DollarMonad dom (Expr dom) +parenDollar lhs expr@(InfixApp _ _ arg) + = do replacedRanges <- get + if getRange arg `elem` replacedRanges && (lhs || getRange expr `notElem` replacedRanges) + then return $ mkParen expr + else return expr +parenDollar _ e = return e + +dollarName :: GHC.Name +[dollarName] = map idName $ filter ((dollarIdKey==) . getUnique) wiredInIds
+ Language/Haskell/Tools/Refactor/Builtin/HelloRefactor.hs view
@@ -0,0 +1,18 @@+{-# LANGUAGE TypeFamilies, FlexibleContexts #-} +module Language.Haskell.Tools.Refactor.Builtin.HelloRefactor where + +import Language.Haskell.Tools.PrettyPrint (prettyPrint) +import Language.Haskell.Tools.Refactor + +import Control.Reference ((.-)) +import Debug.Trace (trace) +import SrcLoc (RealSrcSpan) + +tryItOut :: String -> String -> IO () +tryItOut = tryRefactor (localRefactoring . helloRefactor) + +helloRefactor :: Domain dom => RealSrcSpan -> LocalRefactoring dom +helloRefactor sp = return . (nodesContained sp .- helloExpr) + +helloExpr :: Expr dom -> Expr dom +helloExpr e = trace ("\n### Hello: " ++ prettyPrint e) $ e
+ Language/Haskell/Tools/Refactor/Builtin/IfToGuards.hs view
@@ -0,0 +1,28 @@+{-# LANGUAGE RankNTypes, FlexibleContexts, TypeFamilies #-} +module Language.Haskell.Tools.Refactor.Builtin.IfToGuards (ifToGuards, tryItOut) where + +import Control.Reference ((^.), (.-), (&)) +import Data.Generics.Uniplate.Data () +import Language.Haskell.Tools.Refactor +import SrcLoc (RealSrcSpan) + +tryItOut :: String -> String -> IO () +tryItOut = tryRefactor (localRefactoring . ifToGuards) + +ifToGuards :: Domain dom => RealSrcSpan -> LocalRefactoring dom +ifToGuards sp = return . (nodesContaining sp .- changeBindings) + +changeBindings :: ValueBind dom -> ValueBind dom +changeBindings (SimpleBind (VarPat name) (UnguardedRhs (If pred thenE elseE)) locals) + = mkFunctionBind [mkMatch (mkMatchLhs name []) (createSimpleIfRhss pred thenE elseE) (locals ^. annMaybe) ] +changeBindings fbs@(FunctionBind {}) + = funBindMatches&annList&matchRhs .- trfRhs $ fbs + where trfRhs :: Rhs dom -> Rhs dom + trfRhs (UnguardedRhs (If pred thenE elseE)) = createSimpleIfRhss pred thenE elseE + trfRhs e = e -- don't transform already guarded right-hand sides to avoid multiple evaluation of the same condition +changeBindings b = b + +createSimpleIfRhss :: Expr dom -> Expr dom -> Expr dom -> Rhs dom +createSimpleIfRhss pred thenE elseE = mkGuardedRhss [ mkGuardedRhs [mkGuardCheck pred] thenE + , mkGuardedRhs [mkGuardCheck (mkVar (mkName "otherwise"))] elseE + ]
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple +main = defaultMain
+ examples/Refactor/DataToNewtype/Cases.hs view
@@ -0,0 +1,8 @@+module Refactor.DataToNewtype.Cases where + +data A = A Int + +newtype B = B Int +data C = C +data D = D Int Int +data E
+ examples/Refactor/DataToNewtype/Cases_res.hs view
@@ -0,0 +1,8 @@+module Refactor.DataToNewtype.Cases where + +newtype A = A Int + +newtype B = B Int +data C = C +data D = D Int Int +data E
+ examples/Refactor/DollarApp/AnotherOperator.hs view
@@ -0,0 +1,5 @@+module Refactor.DollarApp.AnotherOperator where + +import Refactor.DollarApp.Defs + +x = f (g $$ 3)
+ examples/Refactor/DollarApp/AnotherOperator_res.hs view
@@ -0,0 +1,5 @@+module Refactor.DollarApp.AnotherOperator where + +import Refactor.DollarApp.Defs + +x = f (g $$ 3)
+ examples/Refactor/DollarApp/Defs.hs view
@@ -0,0 +1,9 @@+module Refactor.DollarApp.Defs where + +f = id +g = id + +($$) :: (a -> b) -> a -> a +f $$ a = a + +infixl 0 $$
+ examples/Refactor/DollarApp/Final.hs view
@@ -0,0 +1,16 @@+{-# LANGUAGE NoImplicitPrelude #-} +module Refactor.DollarApp.Final where + +import Prelude ((+)) + +import A + +x = f (g 1) +x2 = f (f (g 2)) + +x3 = f (g 3) + 3 +x4 = 3 + f (g 4) + +x5 = f (g $$ 1) +x6 = f (1 + 1) +
+ examples/Refactor/DollarApp/FirstMulti.hs view
@@ -0,0 +1,5 @@+module Refactor.DollarApp.FirstMulti where + +import Refactor.DollarApp.Defs + +x = f (f (g 2))
+ examples/Refactor/DollarApp/FirstMulti_res.hs view
@@ -0,0 +1,5 @@+module Refactor.DollarApp.FirstMulti where + +import Refactor.DollarApp.Defs + +x = f $ f $ g 2
+ examples/Refactor/DollarApp/FirstSingle.hs view
@@ -0,0 +1,5 @@+module Refactor.DollarApp.FirstSingle where + +import Refactor.DollarApp.Defs + +x = f (g 1)
+ examples/Refactor/DollarApp/FirstSingle_res.hs view
@@ -0,0 +1,5 @@+module Refactor.DollarApp.FirstSingle where + +import Refactor.DollarApp.Defs + +x = f $ g 1
+ examples/Refactor/DollarApp/ImportDollar.hs view
@@ -0,0 +1,6 @@+{-# LANGUAGE NoImplicitPrelude #-} +module Refactor.DollarApp.ImportDollar where + +import Refactor.DollarApp.Defs + +x = f (g x)
+ examples/Refactor/DollarApp/ImportDollar_res.hs view
@@ -0,0 +1,7 @@+{-# LANGUAGE NoImplicitPrelude #-} +module Refactor.DollarApp.ImportDollar where + +import Refactor.DollarApp.Defs +import qualified GHC.Base(($)) + +x = f GHC.Base.$ g x
+ examples/Refactor/DollarApp/InfixOperator.hs view
@@ -0,0 +1,5 @@+module Refactor.DollarApp.InfixOperator where + +import Refactor.DollarApp.Defs + +x = f (g 3) + 3
+ examples/Refactor/DollarApp/InfixOperator_res.hs view
@@ -0,0 +1,5 @@+module Refactor.DollarApp.InfixOperator where + +import Refactor.DollarApp.Defs + +x = (f $ g 3) + 3
+ examples/Refactor/IfToGuards/Simple.hs view
@@ -0,0 +1,3 @@+module Refactor.IfToGuards.Simple where + +max a b = if a > b then a else b
+ examples/Refactor/IfToGuards/Simple_res.hs view
@@ -0,0 +1,4 @@+module Refactor.IfToGuards.Simple where + +max a b | a > b = a + | otherwise = b
+ haskell-tools-experimental-refactorings.cabal view
@@ -0,0 +1,75 @@+name: haskell-tools-experimental-refactorings +version: 1.0.0.0 +synopsis: Refactoring Tool for Haskell +description: Contains experimental refactorings implemented in the Haskell-tools framework for tutorial purposes, or to be added later to the set of mature refactorings. +homepage: https://github.com/haskell-tools/haskell-tools +license: BSD3 +license-file: LICENSE +author: Boldizsar Nemeth +maintainer: nboldi@elte.hu +category: Language +build-type: Simple +cabal-version: >=1.10 + +extra-source-files: examples/Refactor/DataToNewtype/*.hs + , examples/Refactor/DollarApp/*.hs + , examples/Refactor/IfToGuards/*.hs + +library + exposed-modules: Language.Haskell.Tools.Refactor.Builtin.DataToNewtype + , Language.Haskell.Tools.Refactor.Builtin.IfToGuards + , Language.Haskell.Tools.Refactor.Builtin.DollarApp + , Language.Haskell.Tools.Refactor.Builtin.HelloRefactor + , Language.Haskell.Tools.Refactor.Builtin.DollarApp1 + , Language.Haskell.Tools.Refactor.Builtin.DollarApp2 + , Language.Haskell.Tools.Refactor.Builtin.DollarApp3 + + build-depends: base >= 4.10 && < 4.11 + , mtl >= 2.2 && < 2.3 + , uniplate >= 1.6 && < 1.7 + , ghc-paths >= 0.1 && < 0.2 + , containers >= 0.5 && < 0.6 + , directory >= 1.2 && < 1.4 + , transformers >= 0.5 && < 0.6 + , references >= 0.3 && < 0.4 + , split >= 0.2 && < 0.3 + , filepath >= 1.4 && < 1.5 + , template-haskell >= 2.12 && < 2.13 + , ghc >= 8.2 && < 8.3 + , Cabal >= 2.0 && < 2.1 + , haskell-tools-ast >= 1.0 && < 1.1 + , haskell-tools-backend-ghc >= 1.0 && < 1.1 + , haskell-tools-rewrite >= 1.0 && < 1.1 + , haskell-tools-prettyprint >= 1.0 && < 1.1 + , haskell-tools-refactor >= 1.0 && < 1.1 + default-language: Haskell2010 + +test-suite haskell-tools-experimental-refactorings-test + type: exitcode-stdio-1.0 + ghc-options: -with-rtsopts=-M2g + hs-source-dirs: test + main-is: Main.hs + build-depends: base >= 4.10 && < 4.11 + , tasty >= 0.11 && < 0.12 + , tasty-hunit >= 0.9 && < 0.10 + , transformers >= 0.5 && < 0.6 + , either >= 4.4 && < 4.5 + , filepath >= 1.4 && < 1.5 + , mtl >= 2.2 && < 2.3 + , uniplate >= 1.6 && < 1.7 + , containers >= 0.5 && < 0.6 + , directory >= 1.2 && < 1.4 + , references >= 0.3 && < 0.4 + , split >= 0.2 && < 0.3 + , time >= 1.6 && < 1.9 + , template-haskell >= 2.12 && < 2.13 + , ghc >= 8.2 && < 8.3 + , ghc-paths >= 0.1 && < 0.2 + , Cabal >= 2.0 && < 2.1 + , haskell-tools-ast >= 1.0 && < 1.1 + , haskell-tools-backend-ghc >= 1.0 && < 1.1 + , haskell-tools-rewrite >= 1.0 && < 1.1 + , haskell-tools-prettyprint >= 1.0 && < 1.1 + , haskell-tools-refactor >= 1.0 && < 1.1 + , haskell-tools-experimental-refactorings + default-language: Haskell2010
+ test/Main.hs view
@@ -0,0 +1,66 @@+{-# LANGUAGE LambdaCase + , TypeFamilies + #-} +module Main where + +import Test.Tasty (TestTree, testGroup, defaultMain) +import Test.Tasty.HUnit (assertEqual, testCase) + +import GHC hiding (loadModule, ParsedModule) +import GHC.Paths ( libdir ) + +import Control.Monad (Monad(..)) +import Data.Either.Combinators (mapRight) +import System.FilePath (pathSeparator, (</>)) +import System.IO + +import Language.Haskell.Tools.AST as AST (IdDom) +import Language.Haskell.Tools.PrettyPrint (prettyPrint) +import Language.Haskell.Tools.Refactor +import Language.Haskell.Tools.Refactor.Builtin.DataToNewtype (dataToNewtype) +import Language.Haskell.Tools.Refactor.Builtin.DollarApp (dollarApp) +import Language.Haskell.Tools.Refactor.Builtin.IfToGuards (ifToGuards) + +main :: IO () +main = defaultMain $ testGroup "experimental refactor tests" functionalTests + +functionalTests :: [TestTree] +functionalTests = map makeMiscRefactorTest miscRefactorTests + +rootDir = "examples" + +miscRefactorTests = + [ ("Refactor.DataToNewtype.Cases", \_ -> dataToNewtype) + , ("Refactor.IfToGuards.Simple", \m -> ifToGuards (correctRefactorSpan m $ readSrcSpan "3:11-3:33")) + , ("Refactor.DollarApp.FirstSingle", \m -> dollarApp (correctRefactorSpan m $ readSrcSpan "5:5-5:12")) + , ("Refactor.DollarApp.FirstMulti", \m -> dollarApp (correctRefactorSpan m $ readSrcSpan "5:5-5:16")) + , ("Refactor.DollarApp.InfixOperator", \m -> dollarApp (correctRefactorSpan m $ readSrcSpan "5:5-5:16")) + , ("Refactor.DollarApp.AnotherOperator", \m -> dollarApp (correctRefactorSpan m $ readSrcSpan "5:5-5:15")) + , ("Refactor.DollarApp.ImportDollar", \m -> dollarApp (correctRefactorSpan m $ readSrcSpan "6:5-6:12")) + ] + +makeMiscRefactorTest :: (String, UnnamedModule IdDom -> LocalRefactoring IdDom) -> TestTree +makeMiscRefactorTest (moduleName, refact) + = testCase moduleName $ + do expected <- loadExpected True rootDir moduleName + res <- testRefactor refact moduleName + assertEqual "The transformed result is not what is expected" (Right (standardizeLineEndings expected)) + (mapRight standardizeLineEndings res) + +testRefactor :: (UnnamedModule IdDom -> LocalRefactoring IdDom) -> String -> IO (Either String String) +testRefactor refact moduleName + = runGhc (Just libdir) $ do + initGhcFlags + useDirs [rootDir] + mod <- loadModule rootDir moduleName >>= parseTyped + res <- runRefactor (SourceFileKey (rootDir </> moduleSourceFile moduleName) moduleName, mod) [] (localRefactoring $ refact mod) + case res of Right r -> return $ Right $ prettyPrint $ snd $ fromContentChanged $ head r + Left err -> return $ Left err + +loadExpected :: Bool -> String -> String -> IO String +loadExpected resSuffix workingDir moduleName = + do -- need to use binary or line endings will be translated + expectedHandle <- openBinaryFile (workingDir </> map (\case '.' -> pathSeparator; c -> c) moduleName ++ (if resSuffix then "_res" else "") ++ ".hs") ReadMode + hGetContents expectedHandle + +standardizeLineEndings = filter (/= '\r')