packages feed

splint 1.0.1.0 → 1.0.1.1

raw patch · 2 files changed

+68/−15 lines, 2 filesdep +containersdep +stmPVP: minor bump suggested

API additions: PVP suggests at least a minor version bump

Dependencies added: containers, stm

API changes (from Hackage documentation)

+ Splint: instance (GHC.Classes.Eq e, GHC.Classes.Eq a) => GHC.Classes.Eq (Splint.RemoteData e a)
+ Splint: instance (GHC.Show.Show e, GHC.Show.Show a) => GHC.Show.Show (Splint.RemoteData e a)

Files

splint.cabal view
@@ -1,7 +1,7 @@ cabal-version: 2.2  name: splint-version: 1.0.1.0+version: 1.0.1.1  synopsis: HLint as a GHC source plugin. description:@@ -46,8 +46,10 @@    build-depends:     base >= 4.12.0 && < 4.15+    , containers >= 0.6.0 && < 0.7     , ghc >= 8.6.1 && < 8.11     , hlint >= 3.0 && < 3.2+    , stm >= 2.5.0 && < 2.6   exposed-modules: Splint   hs-source-dirs: src/lib   other-modules: Splint.Parser
src/lib/Splint.hs view
@@ -4,7 +4,11 @@ where  import qualified Bag as GHC-import qualified Data.IORef as IORef+import qualified Control.Concurrent as Concurrent+import qualified Control.Concurrent.STM as Stm+import qualified Control.Exception as Exception+import qualified Control.Monad.IO.Class as IO+import qualified Data.Map as Map import qualified ErrUtils as GHC import qualified GhcPlugins as GHC import qualified Language.Haskell.HLint as HLint@@ -26,7 +30,7 @@   (parseFlags, classifies, hint) <- getSettings commandLineOptions   moduleEx <- Splint.parse parseFlags modSummary hsParsedModule   dynFlags <- GHC.getDynFlags-  GHC.liftIO+  io     . GHC.printOrThrowWarnings dynFlags     . GHC.listToBag     . fmap (ideaToWarnMsg dynFlags)@@ -36,19 +40,66 @@  type Settings = (HLint.ParseFlags, [HLint.Classify], HLint.Hint) -getSettings :: [GHC.CommandLineOption] -> GHC.Hsc Settings-getSettings commandLineOptions = GHC.liftIO $ do-  maybeSettings <- IORef.readIORef settingsRef-  case maybeSettings of-    Just settings -> pure settings-    Nothing -> do-      settings <- HLint.argsSettings commandLineOptions-      IORef.writeIORef settingsRef $ Just settings-      pure settings+getSettings :: [String] -> GHC.Hsc Settings+getSettings options = do+  let insert = Stm.modifyTVar settingsTVar . Map.insert options+  remoteData <- io . stm $ do+    settings <- Stm.readTVar settingsTVar+    let remoteData = Map.findWithDefault NotAsked options settings+    case remoteData of+      NotAsked -> insert Loading+      _ -> pure ()+    pure remoteData+  case remoteData of+    NotAsked -> io . withTMVar settingsTMVar . const $ do+      result <- Exception.try $ HLint.argsSettings options+      case result of+        Left ioException -> do+          stm . insert $ Failure ioException+          Exception.throwIO ioException+        Right settings -> do+          stm . insert $ Success settings+          pure settings+    Loading -> do+      io $ Concurrent.threadDelay 1000+      getSettings options+    Failure ioException -> io $ Exception.throwIO ioException+    Success settings -> pure settings -{-# NOINLINE settingsRef #-}-settingsRef :: IORef.IORef (Maybe Settings)-settingsRef = Unsafe.unsafePerformIO $ IORef.newIORef Nothing+io :: IO.MonadIO m => IO a -> m a+io = GHC.liftIO++stm :: Stm.STM a -> IO a+stm = Stm.atomically++withTMVar :: Stm.TMVar a -> (a -> IO b) -> IO b+withTMVar var =+  Exception.bracket (stm $ Stm.takeTMVar var) (stm . Stm.putTMVar var)++-- | 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.+settingsTVar+  :: Stm.TVar (Map.Map [String] (RemoteData Exception.IOException Settings))+settingsTVar = Unsafe.unsafePerformIO $ Stm.newTVarIO Map.empty+{-# NOINLINE settingsTVar #-}++-- | 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>+settingsTMVar :: Stm.TMVar ()+settingsTMVar = Unsafe.unsafePerformIO $ Stm.newTMVarIO ()+{-# NOINLINE settingsTMVar #-}++data RemoteData e a+  = NotAsked+  | Loading+  | Failure e+  | Success a+  deriving (Eq, Show)  ideaToWarnMsg :: GHC.DynFlags -> HLint.Idea -> GHC.WarnMsg ideaToWarnMsg dynFlags idea =