packages feed

smuggler2 0.3.3.2 → 0.3.4.1

raw patch · 12 files changed

+130/−76 lines, 12 filesdep +ghc-bootPVP ok

version bump matches the API change (PVP)

Dependencies added: ghc-boot

API changes (from Hackage documentation)

Files

CHANGELOG.md view
@@ -5,6 +5,9 @@ `smuggler2` uses [PVP Versioning][1]. The change log is available [on GitHub][2]. +##  [0.3.4.1]: --  9 June 2020+- Place output from CPP and literate Haskell sources into new files+ ##  [0.3.3.2]: --  8 June 2020 - pattern synonyms and type families are handled - ghc-smuggler2 wrapper for ghc
Makefile view
@@ -27,6 +27,8 @@  doc: 	cabal haddock+	cabal v2-haddock --haddock-for-hackage --enable-doc+#	cabal upload -d --publish dist-newstyle/smuggler2-0.3.x.y-docs.tar.gz  hlint: 	hlint src test/Test.hs
README.md view
@@ -42,7 +42,7 @@ common smuggler-options   if flag(smuggler2)     ghc-options: -fplugin=Smuggler2.Plugin-    build-depends: smuggler2 >= 0.3+    build-depends: smuggler2 >= 0.3 && < 0.4 ```  and then `import: smuggler-options` in the appropriate `library` or `executable`@@ -76,12 +76,14 @@ ```bash $ cabal build -with-compiler=ghc-smuggler2 ```+ or just+ ```bash $ cabal build -w ghc-smuggler2 ``` -### Options+## Options  `Smuggler2` has several (case-insensitive) options, which can be set by adding `-fplugin-opt=Smuggler2.Plugin:` flags to your `ghc-options`@@ -123,16 +125,15 @@ $ ghcid --command='cabal repl' ``` - ## Caveats -- Because `cabal` and `ghc` don't have full support for distinguishing dependent-  packages from plug-ins you will probably want to ensure that the build-  dependencies for your project are installed into your local package db first,-  before enabling sumuggler, otherwise they will all be processed by it too,-  as your project builds, which should do no harm, but will increase your build time.+Because `cabal` and `ghc` don't have full support for distinguishing dependent+packages from plug-ins you will probably want to ensure that the build+dependencies for your project are installed into your local package db first,+before enabling sumuggler, otherwise they will all be processed by it too,+as your project builds, which should do no harm, but will increase your build time. -`Smugggler2` is robust -- it can chew through the+`Smuggler2` is robust -- it can chew through the [Agda](https://github.com/agda/agda) codebase of over 370 modules with complex interdependencies and be tripped over by only @@ -141,16 +142,20 @@ - and a couple of imports where both qualifed and unqualifed version of the   module are imported and there are references to both qualified and unqualifed   version of the same names+- some qualified record fields are overlooked  But there are some caveats, most of which are either easy enough to work around (and still offer the benefit of a great reduction in keyboard work):  - `Smuggler2` rewrites the existing imports, rather than attempting to prune   them. (This is a more aggressive approach than `smuggler` which focuses on-  removing redundant imports.) It has advantages and disadvantages. The-  advantage is that a minimal set of imports is generated in a reproducable-  format. The disdvantage is that imports may be reordered, comments and-  blankdropped, external imports mixed with external, etc.+  removing redundant imports.) It has advantages and disadvantages.+  The advantage is that a minimal set of imports is generated in a reproducable+  format. So you can just import a library without specifying any specific+  imports and `Smuggler2` will add an explict list of things that are+  used from it. This can be a useful check and better document your modules.+  The disdvantage is that imports may be reordered, comments and+  blank lines dropped, external imports mixed with external, etc.  - By default `Smuggler2` does not remove imports completely because an import   may be being used to only import instances of typeclasses, So it will leave@@ -167,7 +172,8 @@ - CPP files will not be processed correctly: the imports will be generated for   current CPP settings and any CPP annotations in the import block will be   discarded. This may be a particular problem if you are writing code for-  several generations of `ghc` and `base` for example.+  several generations of `ghc` and `base` for example. Nevetheless, `Smuggler2`+  will generate a new CPP preprocessed output file with a `-cpp` suffix.   [retrie](https://github.com/facebookincubator/retrie/blob/master/Retrie/CPP.hs)   solves this problem generating all possible versions of the module   (exponential in the number of `#if` directives), operating on each version@@ -183,15 +189,16 @@ - Multiple separate import lines referring to the same library are not   consolidated -- Literate Haskell `lhs` files are not supported+- Literate Haskell `.lhs` files will procssed into ordinary haskell files a+  `-lhs` suffix. -- `hiding` clauses may not be properly analysed. So hiding things that are not+* `hiding` clauses may not be properly analysed. So hiding things that are not   used may not be spotted. -- The test suite does not seem to run reliably on Windows. This is probably more+* The test suite does not seem to run reliably on Windows. This is probably more   of an issue with the way that the tests are run, than `Smuggler2` itself. -- Currently `cabal` does not have a particular way of specifying plugins. (See,+* Currently `cabal` does not have a particular way of specifying plugins. (See,   eg, https://gitlab.haskell.org/ghc/ghc/issues/11244 and   https://github.com/haskell/cabal/issues/2965) which would allow cleaner   separation of user code and plugin-code
TODO.md view
@@ -1,12 +1,13 @@ # Things to be done -- [ ] does hiding work?+- [ ] Does hiding work? No, as ghc doesn't check them -- [ ] does CPP work? (should do)+- [X] Does CPP work? Yes, but the mark is dropped, so put output into a new+  file.  Similarly for literate Haskell. -- [ ] Script to generate new test (from template?)+- [x] Script to generate new test (from template?) Not worth the effort -- [ ] get tasty-golden to delete successful results and update tests where+- [X] get tasty-golden to delete successful results and update tests where       smuggler makes no changes  - [ ] refactor the much more sophisticated https://github.com/ddssff/refact-global-hse@@ -17,16 +18,19 @@       it creates race conditions because both cases will generate the same minimum       imprts dump file -- [ ] Do a better job of preserving imported pattern annotation.+- [X] Do a better job of preserving imported pattern annotation. -- [ ] Check that type imports / exports work, Type Operators, +- [X] Check that type imports / exports work, Type Operators,  - [X] Running test suite on both an imported module and the module that imports       it creates race conditions because both cases will generate the same minimum       imports dump file -- [ ] User ghc environment files instead of `cabal exec` to launch tests+- [x] User ghc environment files instead of `cabal exec` to launch tests.  But+  these are invisible and so likely to trip you up  - [ ] Use NamedFieldPuns / RecordWildCards for Options  - [ ] Check that an export does not need to be qualified++- [ ]- Figure out fhy github workflow uses ghc 8.10.1 when it should be running 8.8.3
smuggler2.cabal view
@@ -1,6 +1,6 @@ cabal-version:      3.0 name:               smuggler2-version:            0.3.3.2+version:            0.3.4.1 synopsis:   GHC Source Plugin that helps to minimise imports and generate explicit exports @@ -109,6 +109,7 @@     , directory       ^>=1.3.3     , filepath        ^>=1.4.2     , ghc             >=8.6.5 && <8.11+    , ghc-boot        >=8.6.5 && <8.11     , ghc-exactprint  ^>=0.6.3     , syb             ^>=0.7.1 
src/Smuggler2/Anns.hs view
@@ -15,8 +15,12 @@ import Data.Maybe (fromMaybe) import GHC (AnnKeywordId (AnnCloseP, AnnOpenP)) import GhcPlugins (GenLocated (L), Located)-import Language.Haskell.GHC.ExactPrint (Annotation (annEntryDelta, annPriorComments, annsDP),-                                        TransformT, modifyAnnsT, uniqueSrcSpanT)+import Language.Haskell.GHC.ExactPrint+  ( Annotation (annEntryDelta, annPriorComments, annsDP),+    TransformT,+    modifyAnnsT,+    uniqueSrcSpanT,+  ) import Language.Haskell.GHC.ExactPrint.Types (DeltaPos (..), KeywordId (G), annNone, mkAnnKey)  -- Inspired by retrie
src/Smuggler2/Exports.hs view
@@ -1,8 +1,9 @@ -- | -- Description: Utility functions for generating an export list ast and--- associated Anns.  It's a bit fiddlier than it could be because ghc's--- functions for producing exportable things generates @AvailInfo@ from which we--- need to reconstitue @IEWrappedName@ and then @IE@+--              associated Anns.  It's a bit fiddlier than it could be because ghc's+--              functions for producing exportable things generates @AvailInfo@ from which we+--              need to reconstitue @IEWrappedName@ and then @IE@+ module Smuggler2.Exports   ( mkExportAnnT   )
src/Smuggler2/Imports.hs view
@@ -1,7 +1,8 @@ {-# LANGUAGE CPP #-}--- | Description:  A replacement for 'RnNames.getMinimalImports' that attempts--- to handle patterns and types, which is not done correctly in GHC 8.10.1 and--- earlier.+-- |+-- Description:  A replacement for 'RnNames.getMinimalImports' that attempts+--               to handle patterns and types, which is not done correctly in GHC 8.10.1 and+--               earlier. module Smuggler2.Imports (getMinimalImports) where  import Avail ( AvailInfo(..) )@@ -20,7 +21,9 @@ import LoadIface ( loadSrcInterface ) import Name ( HasOccName(..), isDataOcc, isSymOcc, isTcOcc ) import Outputable ( Outputable(ppr), text, (<+>) )+#if MIN_VERSION_GLASGOW_HASKELL(8,8,0,0) import RdrName ( gresToAvailInfo )+#endif import RnNames ( ImportDeclUsage ) import SrcLoc ( GenLocated(L), Located, noLoc ) import TcRnMonad ( RnM )
src/Smuggler2/Options.hs view
@@ -1,7 +1,6 @@-{- |- Description: handling of command line options+-- |+-- Description: handling of command line options - -} module Smuggler2.Options   ( Options (..),     parseCommandLineOptions,
src/Smuggler2/Parser.hs view
@@ -1,7 +1,6 @@-{-|- Description: provides a wrapper around the 'ghc-exactprint' parser- -} {-# LANGUAGE CPP #-}+-- |+-- Description: provides a wrapper around the 'ghc-exactprint' parser  module Smuggler2.Parser   ( runParser,@@ -14,7 +13,7 @@ import Language.Haskell.GHC.ExactPrint (Anns) import Language.Haskell.GHC.ExactPrint.Parsers (parseModuleFromStringInternal) #if MIN_VERSION_GLASGOW_HASKELL(8,10,1,0)-import ErrUtils (fatalErrorMsg, printBagOfErrors)+import ErrUtils (fatalErrorMsg, printBagOfErrors ) import Outputable (text) #else import ErrUtils (fatalErrorMsg)
src/Smuggler2/Plugin.hs view
@@ -9,9 +9,11 @@  import Avail ( AvailInfo, Avails ) import Control.Monad ( unless )+import Data.Bool ( bool ) import Data.Maybe ( fromMaybe, isNothing ) import Data.Version ( showVersion )-import DynFlags ( DynFlags(dumpDir), HasDynFlags(getDynFlags) )+import DynFlags+    ( DynFlags(dumpDir), HasDynFlags(getDynFlags), xopt ) import ErrUtils ( compilationProgressMsg, fatalErrorMsg ) import GHC     ( GenLocated(L),@@ -21,11 +23,14 @@       LIE,       LImportDecl,       Located,-      ModSummary(ms_hspp_buf, ms_hspp_file, ms_mod),+      ms_location,+      ml_hs_file,+      ModSummary(ms_hspp_buf, ms_mod),       Module(moduleName),       ParsedSource,       moduleNameString,       unLoc )+import GHC.LanguageExtensions ( Extension(Cpp) ) import GHC.IO.Encoding ( setLocaleEncoding, utf8 ) import IOEnv ( MonadIO(liftIO), readMutVar ) import Language.Haskell.GHC.ExactPrint@@ -45,11 +50,10 @@       Plugin(pluginRecompile, typeCheckResultAction),       defaultPlugin,       purePlugin )-import RnNames-    ( ImportDeclUsage, findImportUsage )-import Smuggler2.Anns-import Smuggler2.Imports-import Smuggler2.Exports+import RnNames ( ImportDeclUsage, findImportUsage )+import Smuggler2.Anns ( mkLoc, mkParenT )+import Smuggler2.Imports ( getMinimalImports )+import Smuggler2.Exports ( mkExportAnnT ) import Smuggler2.Options     ( ExportAction(AddExplicitExports, NoExportProcessing,                    ReplaceExports),@@ -59,7 +63,7 @@ import Smuggler2.Parser ( runParser ) import StringBuffer ( StringBuffer(StringBuffer), lexemeToString ) import System.Directory ( removeFile )-import System.FilePath ( (-<.>), (</>) )+import System.FilePath ( (-<.>), (</>), isExtensionOf, takeExtension ) import System.IO ( IOMode(WriteMode), withFile ) import TcRnExports ( exports_from_avail ) import TcRnTypes@@ -92,19 +96,20 @@     -- This ensures that the source file is not touched if there are no unused     -- imports, or exports already exist and we are not replacing them     let noUnusedImports = all (\(_decl, used, unused) -> not (null used) && null unused) usage+     let hasExplicitExports = case tcg_rn_exports tcEnv of-          Nothing   -> False -- There is not even a module header+          Nothing -> False -- There is not even a module header           (Just []) -> False-          (Just _)  -> True+          (Just _) -> True     -- ... so short circuit if:     -- - we are skipping import processing or there are no unused imports, and     -- - we are skipping export processing or there are explict exports and we are not replacing them     -- (This is not a complete check; ideally, that the new imp/exports are     -- different from the existing ones, etc)-    if (importAction options == NoImportProcessing || noUnusedImports) &&-       (     exportAction options == NoExportProcessing-         || (hasExplicitExports && exportAction options /= ReplaceExports)-       )+    if (importAction options == NoImportProcessing || noUnusedImports)+      && ( exportAction options == NoExportProcessing+             || (hasExplicitExports && exportAction options /= ReplaceExports)+         )       then return tcEnv       else do         dflags <- getDynFlags@@ -123,14 +128,14 @@          -- Run smuggling only for its side effects         tcEnv <$ smuggling dflags minImpFilePath-   where-     -- Does all the work     smuggling :: DynFlags -> FilePath -> RnM ()     smuggling dflags minImpFilePath = do--      let modulePath = ms_hspp_file modSummary+      -- The preprocessed source+      let modulePath = case ml_hs_file $ ms_location modSummary of+            Nothing -> error "smuggler2: missing source file location"+            Just loc -> loc        -- Read files as UTF-8 strings (GHC accepts only ASCII or UTF-8)       liftIO $ setLocaleEncoding utf8@@ -138,7 +143,7 @@       -- Get the pre-processed module source code       let modFileContents = case ms_hspp_buf modSummary of             -- Not clear under what circumstances this could happen-            Nothing       -> error $ "smuggler: missing source file: " ++ modulePath+            Nothing -> error $ "smuggler2: missing source file: " ++ modulePath             Just contents -> strBufToStr contents        -- Parse the whole module@@ -154,7 +159,6 @@               liftIO $                 fatalErrorMsg dflags (text $ "smuggler: failed to parse minimal imports from " ++ minImpFilePath)             Right (annsImpMod, L _ impMod) -> do-               -- The actual minimal imports themselves, as generated by GHC               let minImports = hsmodImports impMod @@ -170,19 +174,27 @@                       replaceImports annsImpMod minImports astHsMod                         >>= addExplicitExports exports +              -- Generate new file extension+              -- Was the CPP used?+              let usedCpp = bool "" "-cpp" (xopt Cpp dflags)+              -- Was the source literate Haskell+              let wasLhs = bool "" "-lhs" (isExtensionOf "lhs" modulePath)+              -- prefix any user-supplied options+              let ext =+                    fromMaybe (takeExtension modulePath) (newExtension options)+                      ++ usedCpp+                      ++ wasLhs+               -- Print the result               let newContent = exactPrint astHsMod' annsHsMod'-              liftIO $ case newExtension options of-                Nothing  -> writeFile modulePath newContent-                Just ext -> writeFile (modulePath -<.> ext) newContent+              liftIO $ writeFile (modulePath -<.> ext) newContent -            -- Clean up: delete the GHC-generated imports file+              -- Clean up: delete the GHC-generated imports file               liftIO $ removeFile minImpFilePath-           where              -- Generates the things that would be exportabe if there were no-            -- explict export header, so suitable for replacing one+            -- explicit export header, so suitable for replacing one             exportable :: RnM [AvailInfo]             exportable = do               let rdr_env = tcg_rdr_env tcEnv@@ -191,7 +203,7 @@               exports <- exports_from_avail Nothing rdr_env imports this_mod               return (snd exports) -            --  | Replace a target module's imports+            --  Replace a target module's imports             --  See <https://github.com/facebookincubator/retrie/blob/master/Retrie/CPP.hs>             replaceImports ::               Monad m =>@@ -214,7 +226,7 @@                   unless (null imps) $ setEntryDPT (head imps) (DP (2, 0))                   return $ L l m {hsmodImports = imps} -            -- | Add explict exports to the target module+            -- Add explict exports to the target module             addExplicitExports ::               Monad m =>               -- | The list of exports to be added@@ -235,14 +247,13 @@               where                 currentExplicitExports :: Maybe (Located [LIE GhcPs])                 currentExplicitExports = hsmodExports hsMod-                 -- This does all the export replacement work                 result :: Monad m => TransformT m ParsedSource                 result                   | null exports = return t -- there is nothing exportable                   | otherwise = do                     -- Generate the exports list-                    exportsList <- mapM mkExportAnnT  exports+                    exportsList <- mapM mkExportAnnT exports                     -- add commas in between and parens around                     mapM_ addTrailingCommaT (init exportsList)                     lExportsList <- mkLoc exportsList >>= mkParenT unLoc@@ -251,7 +262,7 @@                     -- annotations in the current transformation                     return $ L astLoc hsMod {hsmodExports = Just lExportsList} -    -- | This version of the GHC function ignores implicit imports, as they+    -- This version of the GHC function ignores implicit imports, as they     -- cannot be parsed back in.  (There is an extraneous (implicit))     -- It also provides for leaving out instance-only imports (eg, Data.List() )     printMinimalImports' :: DynFlags -> FilePath -> [ImportDeclUsage] -> RnM ()@@ -273,15 +284,12 @@       where         notImplicit :: ImportDecl pass -> Bool         notImplicit = not . ideclImplicit-         notInstancesOnly :: ImportDecl pass -> Bool         notInstancesOnly i = case ideclHiding i of           Just (False, L _ []) -> False-          _                    -> True-+          _ -> True         keepInstanceOnlyImports :: Bool         keepInstanceOnlyImports = importAction options /= MinimiseImports-         letThrough :: ImportDecl pass -> Bool         letThrough i = notImplicit i && (keepInstanceOnlyImports || notInstancesOnly i) 
+ test/tests/CPP.hs view
@@ -0,0 +1,23 @@+{-# LANGUAGE CPP #-}+module CPP where+#if !(MIN_VERSION_base(4,8,0))+import Data.Monoid+#endif+#if !(MIN_VERSION_base(4,11,0))+import Data.Semigroup+#endif++#if !(MIN_VERSION_base(4,5,0)) && !(MIN_VERSION_base(4,9,0))++infixr 6 <>++-- | An infix synonym for 'mappend'.+--+-- /Since: 4.5.0.0/+(<>) :: Monoid m => m -> m -> m+(<>) = mappend+{-# INLINE (<>) #-}++#endif++str = "abc" <> "def"