packages feed

splint 1.0.2.1 → 2.0.0.0

raw patch · 13 files changed

+212/−311 lines, 13 filesdep ~basedep ~containersdep ~ghc

Dependency ranges changed: base, containers, ghc, hlint, stm

Files

− LICENSE.markdown
@@ -1,13 +0,0 @@-Copyright 2022 Taylor Fausak--Permission to use, copy, modify, and/or distribute this software for any-purpose with or without fee is hereby granted, provided that the above-copyright notice and this permission notice appear in all copies.--THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH-REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND-FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,-INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM-LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR-OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR-PERFORMANCE OF THIS SOFTWARE.
+ LICENSE.txt view
@@ -0,0 +1,13 @@+Copyright 2024 Taylor Fausak++Permission to use, copy, modify, and/or distribute this software for any+purpose with or without fee is hereby granted, provided that the above+copyright notice and this permission notice appear in all copies.++THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH+REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND+FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,+INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM+LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR+OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR+PERFORMANCE OF THIS SOFTWARE.
README.markdown view
@@ -1,10 +1,7 @@ # Splint -[![CI](https://github.com/tfausak/splint/workflows/CI/badge.svg)](https://github.com/tfausak/splint/actions/new)-[![Hackage](https://img.shields.io/hackage/v/splint)](https://hackage.haskell.org/package/splint)-[![Stackage](https://www.stackage.org/package/splint/badge/nightly?label=stackage)](https://www.stackage.org/package/splint)--:warning: This package is not maintained anymore.+[![CI](https://github.com/tfausak/splint/actions/workflows/ci.yml/badge.svg)](https://github.com/tfausak/splint/actions/workflows/ci.yml)+[![Hackage](https://badgen.net/hackage/v/splint)](https://hackage.haskell.org/package/splint)  Splint makes [HLint 3][] available as a [GHC source plugin][]. It is similar to [hlint-source-plugin][] by Ollie Charles, except that it doesn't have to@@ -31,7 +28,7 @@ ``` Main.hs:1:8: warning:     Use concatMap-    Perhaps: print (concatMap pure ['a' .. 'z'])+    Suggested fix: print (concatMap pure ['a' .. 'z'])   | 1 | main = print . concat $ map pure [ 'a' .. 'z' ]   |        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ source/library/Splint.hs view
@@ -0,0 +1,73 @@+module Splint where++import qualified Data.Maybe as Maybe+import qualified GHC.Data.Bag+import qualified GHC.Driver.Config.Diagnostic+import qualified GHC.Driver.Errors+import qualified GHC.Driver.Errors.Types+import qualified GHC.Hs+import qualified GHC.Plugins+import qualified GHC.Types.Error+import qualified GHC.Utils.Error+import qualified GHC.Utils.Logger+import qualified Language.Haskell.HLint as HLint+import qualified Splint.Replacement as Replacement+import qualified Splint.Settings as Settings++plugin :: GHC.Plugins.Plugin+plugin =+  GHC.Plugins.defaultPlugin+    { GHC.Plugins.parsedResultAction = parsedResultAction,+      GHC.Plugins.pluginRecompile = GHC.Plugins.purePlugin+    }++parsedResultAction ::+  [GHC.Plugins.CommandLineOption] ->+  modSummary ->+  GHC.Plugins.ParsedResult ->+  GHC.Plugins.Hsc GHC.Plugins.ParsedResult+parsedResultAction commandLineOptions _modSummary parsedResult = do+  logger <- GHC.Utils.Logger.getLogger+  dynFlags <- GHC.Plugins.getDynFlags+  let ghcMessageOpts = GHC.Driver.Config.Diagnostic.initPrintConfig dynFlags+      diagOpts = GHC.Driver.Config.Diagnostic.initDiagOpts dynFlags+  GHC.Plugins.liftIO $ do+    settings <- Settings.load commandLineOptions+    GHC.Driver.Errors.printOrThrowDiagnostics logger ghcMessageOpts diagOpts+      . GHC.Types.Error.mkMessages+      . GHC.Data.Bag.listToBag+      . fmap (ideaToWarnMsg diagOpts)+      . uncurry HLint.applyHints settings+      . pure+      . HLint.createModuleEx+      . GHC.Hs.hpm_module+      $ GHC.Plugins.parsedResultModule parsedResult+  pure parsedResult++ideaToWarnMsg :: GHC.Utils.Error.DiagOpts -> HLint.Idea -> GHC.Driver.Errors.Types.WarnMsg+ideaToWarnMsg diagOpts idea =+  let srcSpan = HLint.ideaSpan idea+      ghcHints =+        fmap (GHC.Types.Error.UnknownHint . Replacement.fromString)+          . Maybe.maybeToList+          $ HLint.ideaTo idea+      decoratedSDoc =+        GHC.Types.Error.mkDecorated $+          GHC.Plugins.text+            (HLint.ideaHint idea)+            : fmap+              (GHC.Plugins.text . mappend "Note: " . show)+              (HLint.ideaNote idea)+      diagnosticReason = case HLint.ideaSeverity idea of+        HLint.Ignore -> GHC.Types.Error.WarningWithoutFlag+        HLint.Suggestion -> GHC.Types.Error.WarningWithoutFlag+        HLint.Warning -> GHC.Types.Error.WarningWithoutFlag+        HLint.Error -> GHC.Types.Error.ErrorWithoutFlag+      diagnosticMessage =+        GHC.Types.Error.DiagnosticMessage+          { GHC.Types.Error.diagHints = ghcHints,+            GHC.Types.Error.diagMessage = decoratedSDoc,+            GHC.Types.Error.diagReason = diagnosticReason+          }+      ghcMessage = GHC.Driver.Errors.Types.ghcUnknownMessage diagnosticMessage+   in GHC.Utils.Error.mkPlainMsgEnvelope diagOpts srcSpan ghcMessage
+ source/library/Splint/RemoteData.hs view
@@ -0,0 +1,8 @@+module Splint.RemoteData where++data RemoteData e a+  = NotAsked+  | Loading+  | Failure e+  | Success a+  deriving (Eq, Show)
+ source/library/Splint/Replacement.hs view
@@ -0,0 +1,16 @@+module Splint.Replacement where++import qualified GHC.Plugins as Plugin++newtype Replacement+  = Replacement String+  deriving (Eq, Show)++instance Plugin.Outputable Replacement where+  ppr = Plugin.text . toString++fromString :: String -> Replacement+fromString = Replacement++toString :: Replacement -> String+toString (Replacement x) = x
+ source/library/Splint/Settings.hs view
@@ -0,0 +1,71 @@+module Splint.Settings where++import qualified Control.Concurrent.STM as Stm+import qualified Control.Exception as Exception+import qualified Data.Map as Map+import qualified Language.Haskell.HLint as HLint+import qualified Splint.RemoteData as RemoteData+import qualified System.IO.Unsafe as Unsafe++type Settings = ([HLint.Classify], HLint.Hint)++-- | Getting settings is not instantaneous. Since settings are usually reused+-- between modules, it makes sense to cache them. However each module can+-- potentially customize its settings using the @OPTIONS_GHC@ pragma. This+-- variable is used as a cache of settings keyed on the command line options.+cache ::+  Stm.TVar+    (Map.Map [String] (RemoteData.RemoteData Exception.IOException Settings))+cache = Unsafe.unsafePerformIO $ Stm.newTVarIO Map.empty+{-# NOINLINE cache #-}++-- | Even though we cache settings based on command line options, we only want+-- to load settings one at a time. Practically this is to work around a bug in+-- GHC. But aside from that, loading settings calls @withArgs@ and doing that+-- simultaneously in separate threads is dubious.+-- <https://gitlab.haskell.org/ghc/ghc/issues/18261>+semaphore :: Stm.TMVar ()+semaphore = Unsafe.unsafePerformIO $ Stm.newTMVarIO ()+{-# NOINLINE semaphore #-}++withTMVar :: Stm.TMVar a -> (a -> IO b) -> IO b+withTMVar x =+  Exception.bracket+    (Stm.atomically $ Stm.takeTMVar x)+    (Stm.atomically . Stm.putTMVar x)++load :: [String] -> IO Settings+load commandLineOptions = do+  remoteData <- Stm.atomically $ do+    settings <- Stm.readTVar cache+    let remoteData =+          Map.findWithDefault RemoteData.NotAsked commandLineOptions settings+    case remoteData of+      RemoteData.NotAsked ->+        Stm.modifyTVar cache $ Map.insert commandLineOptions RemoteData.Loading+      RemoteData.Loading -> Stm.retry+      _ -> pure ()+    pure remoteData+  case remoteData of+    RemoteData.NotAsked -> do+      result <-+        withTMVar semaphore+          . const+          . Exception.try+          $ HLint.argsSettings commandLineOptions+      case result of+        Left ioException -> do+          Stm.atomically+            . Stm.modifyTVar cache+            . Map.insert commandLineOptions+            $ RemoteData.Failure ioException+          Exception.throwIO ioException+        Right (_, classifies, hint) -> do+          Stm.atomically+            . Stm.modifyTVar cache+            . Map.insert commandLineOptions+            $ RemoteData.Success (classifies, hint)+          pure (classifies, hint)+    RemoteData.Loading -> load commandLineOptions+    RemoteData.Failure ioException -> Exception.throwIO ioException+    RemoteData.Success settings -> pure settings
splint.cabal view
@@ -1,12 +1,8 @@-cabal-version: >= 1.10-+cabal-version: 2.2 name: splint-version: 1.0.2.1-+version: 2.0.0.0 synopsis: HLint as a GHC source plugin. description:-  Warning: This package is not maintained anymore.-  .   Splint makes HLint available as a GHC source plugin. To use it, pass   @-fplugin=Splint@ to GHC. Any options passed to Splint are passed through to   HLint. For example you can use @-fplugin-opt=Splint:'--ignore=Use concatMap'@@@ -14,8 +10,8 @@  build-type: Simple category: Development-extra-source-files: README.markdown-license-file: LICENSE.markdown+extra-doc-files: README.markdown+license-file: LICENSE.txt license: ISC maintainer: Taylor Fausak @@ -23,39 +19,46 @@   location: https://github.com/tfausak/splint   type: git +flag pedantic+  default: False+  manual: True+ library   build-depends:-    base >= 4.14 && < 4.17-    , containers >= 0.6 && < 0.7-    , ghc >= 8.10 && < 8.11 || >= 9.0 && < 9.3-    , hlint-    , stm >= 2.5 && < 2.6+    base ^>=4.18.0.0 || ^>=4.19.0.0,+    containers ^>=0.6.7,+    ghc ^>=9.6.1 || ^>=9.8.1,+    stm ^>=2.5.1.0,+   default-language: Haskell2010+  -- cabal-gild: discover source/library   exposed-modules:     Splint     Splint.RemoteData+    Splint.Replacement     Splint.Settings+   ghc-options:     -Weverything+    -Wno-all-missed-specialisations     -Wno-implicit-prelude     -Wno-missing-deriving-strategies     -Wno-missing-export-lists     -Wno-missing-exported-signatures     -Wno-missing-import-lists+    -Wno-missing-kind-signatures     -Wno-missing-safe-haskell-mode     -Wno-prepositive-qualified-module     -Wno-safe     -Wno-unsafe-  hs-source-dirs: src/lib -  if impl(ghc >= 9.2)-    build-depends: hlint >= 3.4 && < 3.5-    ghc-options: -Wno-missing-kind-signatures-    hs-source-dirs: src/ghc-9.2-  else-    if impl(ghc >= 9.0)-      build-depends: hlint >= 3.3 && < 3.4-      hs-source-dirs: src/ghc-9.0-    else-      build-depends: hlint >= 3.2 && < 3.3-      hs-source-dirs: src/ghc-8.10+  hs-source-dirs: source/library++  if flag(pedantic)+    ghc-options: -Werror++  if impl(ghc ^>=9.6.1)+    build-depends: hlint ^>=3.6.1+  elif impl(ghc ^>=9.8.1)+    build-depends: hlint ^>=3.8+    ghc-options: -Wno-missing-role-annotations
− src/ghc-8.10/Splint.hs
@@ -1,65 +0,0 @@-module Splint where--import qualified Bag as GHC-import qualified ErrUtils as GHC-import qualified GhcPlugins as GHC-import qualified Language.Haskell.HLint as HLint-import qualified Splint.Settings as Settings--plugin :: GHC.Plugin-plugin = GHC.defaultPlugin-  { GHC.parsedResultAction = action-  , GHC.pluginRecompile = GHC.purePlugin-  }--action-  :: [GHC.CommandLineOption]-  -> GHC.ModSummary-  -> GHC.HsParsedModule-  -> GHC.Hsc GHC.HsParsedModule-action commandLineOptions modSummary hsParsedModule = do-  (parseFlags, classifies, hint) <- GHC.liftIO $ Settings.load commandLineOptions-  moduleEx <- parse parseFlags modSummary hsParsedModule-  dynFlags <- GHC.getDynFlags-  GHC.liftIO-    . GHC.printOrThrowWarnings dynFlags-    . GHC.listToBag-    . fmap (ideaToWarnMsg dynFlags)-    . filter ((/= HLint.Ignore) . HLint.ideaSeverity)-    $ HLint.applyHints classifies hint [moduleEx]-  pure hsParsedModule--ideaToWarnMsg :: GHC.DynFlags -> HLint.Idea -> GHC.WarnMsg-ideaToWarnMsg dynFlags idea =-  let-    mkErrMsg = case HLint.ideaSeverity idea of-      HLint.Error -> GHC.mkPlainErrMsg-      _ -> GHC.mkPlainWarnMsg-    srcSpan = case HLint.unpackSrcSpan $ HLint.ideaSpan idea of-      Nothing -> GHC.noSrcSpan-      Just (file, (startLine, startColumn), (endLine, endColumn)) ->-        GHC.mkSrcSpan-          (GHC.mkSrcLoc (GHC.mkFastString file) startLine startColumn)-          (GHC.mkSrcLoc (GHC.mkFastString file) endLine endColumn)-    msgDoc = ideaToMsgDoc idea-  in mkErrMsg dynFlags srcSpan msgDoc--ideaToMsgDoc :: HLint.Idea -> GHC.MsgDoc-ideaToMsgDoc idea = GHC.vcat-  [ GHC.text $ HLint.ideaHint idea-  , case HLint.ideaTo idea of-    Just to | not $ null to -> GHC.text $ "Perhaps: " <> to-    _ -> GHC.empty-  , GHC.vcat . fmap (GHC.text . mappend "Note: " . show) $ HLint.ideaNote idea-  ]--parse-  :: HLint.ParseFlags-  -> GHC.ModSummary-  -> GHC.HsParsedModule-  -> GHC.Hsc HLint.ModuleEx-parse _ _ hsParsedModule = do-  let-    apiAnns = GHC.hpm_annotations hsParsedModule-    hsModule = GHC.hpm_module hsParsedModule-  pure $ HLint.createModuleEx apiAnns hsModule
− src/ghc-9.0/Splint.hs
@@ -1,65 +0,0 @@-module Splint where--import qualified GHC.Data.Bag as GHC-import qualified GHC.Plugins as GHC-import qualified GHC.Utils.Error as GHC-import qualified Language.Haskell.HLint as HLint-import qualified Splint.Settings as Settings--plugin :: GHC.Plugin-plugin = GHC.defaultPlugin-  { GHC.parsedResultAction = action-  , GHC.pluginRecompile = GHC.purePlugin-  }--action-  :: [GHC.CommandLineOption]-  -> GHC.ModSummary-  -> GHC.HsParsedModule-  -> GHC.Hsc GHC.HsParsedModule-action commandLineOptions modSummary hsParsedModule = do-  (parseFlags, classifies, hint) <- GHC.liftIO $ Settings.load commandLineOptions-  moduleEx <- parse parseFlags modSummary hsParsedModule-  dynFlags <- GHC.getDynFlags-  GHC.liftIO-    . GHC.printOrThrowWarnings dynFlags-    . GHC.listToBag-    . fmap (ideaToWarnMsg dynFlags)-    . filter ((/= HLint.Ignore) . HLint.ideaSeverity)-    $ HLint.applyHints classifies hint [moduleEx]-  pure hsParsedModule--ideaToWarnMsg :: GHC.DynFlags -> HLint.Idea -> GHC.WarnMsg-ideaToWarnMsg dynFlags idea =-  let-    mkErrMsg = case HLint.ideaSeverity idea of-      HLint.Error -> GHC.mkPlainErrMsg-      _ -> GHC.mkPlainWarnMsg-    srcSpan = case HLint.unpackSrcSpan $ HLint.ideaSpan idea of-      Nothing -> GHC.noSrcSpan-      Just (file, (startLine, startColumn), (endLine, endColumn)) ->-        GHC.mkSrcSpan-          (GHC.mkSrcLoc (GHC.mkFastString file) startLine startColumn)-          (GHC.mkSrcLoc (GHC.mkFastString file) endLine endColumn)-    msgDoc = ideaToMsgDoc idea-  in mkErrMsg dynFlags srcSpan msgDoc--ideaToMsgDoc :: HLint.Idea -> GHC.MsgDoc-ideaToMsgDoc idea = GHC.vcat-  [ GHC.text $ HLint.ideaHint idea-  , case HLint.ideaTo idea of-    Just to | not $ null to -> GHC.text $ "Perhaps: " <> to-    _ -> GHC.empty-  , GHC.vcat . fmap (GHC.text . mappend "Note: " . show) $ HLint.ideaNote idea-  ]--parse-  :: HLint.ParseFlags-  -> GHC.ModSummary-  -> GHC.HsParsedModule-  -> GHC.Hsc HLint.ModuleEx-parse _ _ hsParsedModule = do-  let-    apiAnns = GHC.hpm_annotations hsParsedModule-    hsModule = GHC.hpm_module hsParsedModule-  pure $ HLint.createModuleEx apiAnns hsModule
− src/ghc-9.2/Splint.hs
@@ -1,62 +0,0 @@-module Splint where--import qualified GHC.Data.Bag as Bag-import qualified GHC.Driver.Errors as Errors-import qualified GHC.Hs as Hs-import qualified GHC.Plugins as Plugins-import qualified GHC.Types.Error as Error-import qualified GHC.Utils.Logger as Logger-import qualified Language.Haskell.HLint as HLint-import qualified Splint.Settings as Settings--plugin :: Plugins.Plugin-plugin = Plugins.defaultPlugin-  { Plugins.parsedResultAction = parsedResultAction-  , Plugins.pluginRecompile = Plugins.purePlugin-  }--parsedResultAction-  :: [Plugins.CommandLineOption]-  -> Plugins.ModSummary-  -> Hs.HsParsedModule-  -> Plugins.Hsc Hs.HsParsedModule-parsedResultAction commandLineOptions _modSummary hsParsedModule = do-  (_parseFlags, classifies, hint) <- Plugins.liftIO-    $ Settings.load commandLineOptions-  logger <- Logger.getLogger-  dynFlags <- Plugins.getDynFlags-  Plugins.liftIO-    . Errors.printOrThrowWarnings logger dynFlags-    . Bag.listToBag-    . fmap ideaToWarnMsg-    . filter ((/=) HLint.Ignore . HLint.ideaSeverity)-    . HLint.applyHints classifies hint-    . pure-    . HLint.createModuleEx-    $ Hs.hpm_module hsParsedModule-  pure hsParsedModule--ideaToWarnMsg :: HLint.Idea -> Error.WarnMsg-ideaToWarnMsg idea =-  Error.mkPlainWarnMsg (ideaToSrcSpan idea) (ideaToSDoc idea)--ideaToSrcSpan :: HLint.Idea -> Plugins.SrcSpan-ideaToSrcSpan idea = case HLint.unpackSrcSpan $ HLint.ideaSpan idea of-  Nothing -> Plugins.noSrcSpan-  Just (filePath, (startLine, startColumn), (endLine, endColumn)) ->-    let fastString = Plugins.mkFastString filePath-    in-      Plugins.mkSrcSpan-        (Plugins.mkSrcLoc fastString startLine startColumn)-        (Plugins.mkSrcLoc fastString endLine endColumn)--ideaToSDoc :: HLint.Idea -> Error.SDoc-ideaToSDoc idea = Plugins.vcat-  [ Plugins.text $ HLint.ideaHint idea-  , case HLint.ideaTo idea of-    Just to | not $ null to -> Plugins.text $ "Perhaps: " <> to-    _ -> Plugins.empty-  , Plugins.vcat-  . fmap (Plugins.text . mappend "Note: " . show)-  $ HLint.ideaNote idea-  ]
− src/lib/Splint/RemoteData.hs
@@ -1,8 +0,0 @@-module Splint.RemoteData where--data RemoteData e a-  = NotAsked-  | Loading-  | Failure e-  | Success a-  deriving (Eq, Show)
− src/lib/Splint/Settings.hs
@@ -1,67 +0,0 @@-module Splint.Settings where--import qualified Control.Concurrent.STM as Stm-import qualified Control.Exception as Exception-import qualified Data.Map as Map-import qualified Language.Haskell.HLint as HLint-import qualified Splint.RemoteData as RemoteData-import qualified System.IO.Unsafe as Unsafe--type Settings = (HLint.ParseFlags, [HLint.Classify], HLint.Hint)---- | Getting settings is not instantaneous. Since settings are usually reused--- between modules, it makes sense to cache them. However each module can--- potentially customize its settings using the @OPTIONS_GHC@ pragma. This--- variable is used as a cache of settings keyed on the command line options.-cache-  :: Stm.TVar-       (Map.Map [String] (RemoteData.RemoteData Exception.IOException Settings))-cache = Unsafe.unsafePerformIO $ Stm.newTVarIO Map.empty-{-# NOINLINE cache #-}---- | Even though we cache settings based on command line options, we only want--- to load settings one at a time. Practically this is to work around a bug in--- GHC. But aside from that, loading settings calls @withArgs@ and doing that--- simultaneously in separate threads is dubious.--- <https://gitlab.haskell.org/ghc/ghc/issues/18261>-semaphore :: Stm.TMVar ()-semaphore = Unsafe.unsafePerformIO $ Stm.newTMVarIO ()-{-# NOINLINE semaphore #-}--withTMVar :: Stm.TMVar a -> (a -> IO b) -> IO b-withTMVar x = Exception.bracket-  (Stm.atomically $ Stm.takeTMVar x)-  (Stm.atomically . Stm.putTMVar x)--load :: [String] -> IO Settings-load commandLineOptions = do-  remoteData <- Stm.atomically $ do-    settings <- Stm.readTVar cache-    let-      remoteData =-        Map.findWithDefault RemoteData.NotAsked commandLineOptions settings-    case remoteData of-      RemoteData.NotAsked ->-        Stm.modifyTVar cache $ Map.insert commandLineOptions RemoteData.Loading-      RemoteData.Loading -> Stm.retry-      _ -> pure ()-    pure remoteData-  case remoteData of-    RemoteData.NotAsked -> withTMVar semaphore . const $ do-      result <- Exception.try $ HLint.argsSettings commandLineOptions-      case result of-        Left ioException -> do-          Stm.atomically-            . Stm.modifyTVar cache-            . Map.insert commandLineOptions-            $ RemoteData.Failure ioException-          Exception.throwIO ioException-        Right settings -> do-          Stm.atomically-            . Stm.modifyTVar cache-            . Map.insert commandLineOptions-            $ RemoteData.Success settings-          pure settings-    RemoteData.Loading -> load commandLineOptions-    RemoteData.Failure ioException -> Exception.throwIO ioException-    RemoteData.Success settings -> pure settings