module Splint
  ( plugin
  )
where

import qualified Bag as GHC
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
import qualified Splint.Parser as Splint
import qualified System.IO.Unsafe as Unsafe

plugin :: GHC.Plugin
plugin :: Plugin
plugin = Plugin
GHC.defaultPlugin
  { parsedResultAction :: [CommandLineOption]
-> ModSummary -> HsParsedModule -> Hsc HsParsedModule
GHC.parsedResultAction = [CommandLineOption]
-> ModSummary -> HsParsedModule -> Hsc HsParsedModule
action
  , pluginRecompile :: [CommandLineOption] -> IO PluginRecompile
GHC.pluginRecompile = [CommandLineOption] -> IO PluginRecompile
GHC.purePlugin
  }

action
  :: [GHC.CommandLineOption]
  -> GHC.ModSummary
  -> GHC.HsParsedModule
  -> GHC.Hsc GHC.HsParsedModule
action :: [CommandLineOption]
-> ModSummary -> HsParsedModule -> Hsc HsParsedModule
action [CommandLineOption]
commandLineOptions ModSummary
modSummary HsParsedModule
hsParsedModule = do
  (ParseFlags
parseFlags, [Classify]
classifies, Hint
hint) <- [CommandLineOption] -> Hsc (ParseFlags, [Classify], Hint)
getSettings [CommandLineOption]
commandLineOptions
  ModuleEx
moduleEx <- ParseFlags -> ModSummary -> HsParsedModule -> Hsc ModuleEx
Splint.parse ParseFlags
parseFlags ModSummary
modSummary HsParsedModule
hsParsedModule
  DynFlags
dynFlags <- Hsc DynFlags
forall (m :: * -> *). HasDynFlags m => m DynFlags
GHC.getDynFlags
  IO () -> Hsc ()
forall (m :: * -> *) a. MonadIO m => IO a -> m a
io
    (IO () -> Hsc ()) -> ([Idea] -> IO ()) -> [Idea] -> Hsc ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. DynFlags -> Bag WarnMsg -> IO ()
GHC.printOrThrowWarnings DynFlags
dynFlags
    (Bag WarnMsg -> IO ())
-> ([Idea] -> Bag WarnMsg) -> [Idea] -> IO ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [WarnMsg] -> Bag WarnMsg
forall a. [a] -> Bag a
GHC.listToBag
    ([WarnMsg] -> Bag WarnMsg)
-> ([Idea] -> [WarnMsg]) -> [Idea] -> Bag WarnMsg
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Idea -> WarnMsg) -> [Idea] -> [WarnMsg]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap (DynFlags -> Idea -> WarnMsg
ideaToWarnMsg DynFlags
dynFlags)
    ([Idea] -> [WarnMsg]) -> ([Idea] -> [Idea]) -> [Idea] -> [WarnMsg]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Idea -> Bool) -> [Idea] -> [Idea]
forall a. (a -> Bool) -> [a] -> [a]
filter ((Severity -> Severity -> Bool
forall a. Eq a => a -> a -> Bool
/= Severity
HLint.Ignore) (Severity -> Bool) -> (Idea -> Severity) -> Idea -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Idea -> Severity
HLint.ideaSeverity)
    ([Idea] -> Hsc ()) -> [Idea] -> Hsc ()
forall a b. (a -> b) -> a -> b
$ [Classify] -> Hint -> [ModuleEx] -> [Idea]
HLint.applyHints [Classify]
classifies Hint
hint [ModuleEx
moduleEx]
  HsParsedModule -> Hsc HsParsedModule
forall (f :: * -> *) a. Applicative f => a -> f a
pure HsParsedModule
hsParsedModule

type Settings = (HLint.ParseFlags, [HLint.Classify], HLint.Hint)

getSettings :: [String] -> GHC.Hsc Settings
getSettings :: [CommandLineOption] -> Hsc (ParseFlags, [Classify], Hint)
getSettings [CommandLineOption]
options = do
  let insert :: RemoteData IOException (ParseFlags, [Classify], Hint) -> STM ()
insert = TVar
  (Map
     [CommandLineOption]
     (RemoteData IOException (ParseFlags, [Classify], Hint)))
-> (Map
      [CommandLineOption]
      (RemoteData IOException (ParseFlags, [Classify], Hint))
    -> Map
         [CommandLineOption]
         (RemoteData IOException (ParseFlags, [Classify], Hint)))
-> STM ()
forall a. TVar a -> (a -> a) -> STM ()
Stm.modifyTVar TVar
  (Map
     [CommandLineOption]
     (RemoteData IOException (ParseFlags, [Classify], Hint)))
settingsTVar ((Map
    [CommandLineOption]
    (RemoteData IOException (ParseFlags, [Classify], Hint))
  -> Map
       [CommandLineOption]
       (RemoteData IOException (ParseFlags, [Classify], Hint)))
 -> STM ())
-> (RemoteData IOException (ParseFlags, [Classify], Hint)
    -> Map
         [CommandLineOption]
         (RemoteData IOException (ParseFlags, [Classify], Hint))
    -> Map
         [CommandLineOption]
         (RemoteData IOException (ParseFlags, [Classify], Hint)))
-> RemoteData IOException (ParseFlags, [Classify], Hint)
-> STM ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [CommandLineOption]
-> RemoteData IOException (ParseFlags, [Classify], Hint)
-> Map
     [CommandLineOption]
     (RemoteData IOException (ParseFlags, [Classify], Hint))
-> Map
     [CommandLineOption]
     (RemoteData IOException (ParseFlags, [Classify], Hint))
forall k a. Ord k => k -> a -> Map k a -> Map k a
Map.insert [CommandLineOption]
options
  RemoteData IOException (ParseFlags, [Classify], Hint)
remoteData <- IO (RemoteData IOException (ParseFlags, [Classify], Hint))
-> Hsc (RemoteData IOException (ParseFlags, [Classify], Hint))
forall (m :: * -> *) a. MonadIO m => IO a -> m a
io (IO (RemoteData IOException (ParseFlags, [Classify], Hint))
 -> Hsc (RemoteData IOException (ParseFlags, [Classify], Hint)))
-> (STM (RemoteData IOException (ParseFlags, [Classify], Hint))
    -> IO (RemoteData IOException (ParseFlags, [Classify], Hint)))
-> STM (RemoteData IOException (ParseFlags, [Classify], Hint))
-> Hsc (RemoteData IOException (ParseFlags, [Classify], Hint))
forall b c a. (b -> c) -> (a -> b) -> a -> c
. STM (RemoteData IOException (ParseFlags, [Classify], Hint))
-> IO (RemoteData IOException (ParseFlags, [Classify], Hint))
forall a. STM a -> IO a
stm (STM (RemoteData IOException (ParseFlags, [Classify], Hint))
 -> Hsc (RemoteData IOException (ParseFlags, [Classify], Hint)))
-> STM (RemoteData IOException (ParseFlags, [Classify], Hint))
-> Hsc (RemoteData IOException (ParseFlags, [Classify], Hint))
forall a b. (a -> b) -> a -> b
$ do
    Map
  [CommandLineOption]
  (RemoteData IOException (ParseFlags, [Classify], Hint))
settings <- TVar
  (Map
     [CommandLineOption]
     (RemoteData IOException (ParseFlags, [Classify], Hint)))
-> STM
     (Map
        [CommandLineOption]
        (RemoteData IOException (ParseFlags, [Classify], Hint)))
forall a. TVar a -> STM a
Stm.readTVar TVar
  (Map
     [CommandLineOption]
     (RemoteData IOException (ParseFlags, [Classify], Hint)))
settingsTVar
    let remoteData :: RemoteData IOException (ParseFlags, [Classify], Hint)
remoteData = RemoteData IOException (ParseFlags, [Classify], Hint)
-> [CommandLineOption]
-> Map
     [CommandLineOption]
     (RemoteData IOException (ParseFlags, [Classify], Hint))
-> RemoteData IOException (ParseFlags, [Classify], Hint)
forall k a. Ord k => a -> k -> Map k a -> a
Map.findWithDefault RemoteData IOException (ParseFlags, [Classify], Hint)
forall e a. RemoteData e a
NotAsked [CommandLineOption]
options Map
  [CommandLineOption]
  (RemoteData IOException (ParseFlags, [Classify], Hint))
settings
    case RemoteData IOException (ParseFlags, [Classify], Hint)
remoteData of
      RemoteData IOException (ParseFlags, [Classify], Hint)
NotAsked -> RemoteData IOException (ParseFlags, [Classify], Hint) -> STM ()
insert RemoteData IOException (ParseFlags, [Classify], Hint)
forall e a. RemoteData e a
Loading
      RemoteData IOException (ParseFlags, [Classify], Hint)
_ -> () -> STM ()
forall (f :: * -> *) a. Applicative f => a -> f a
pure ()
    RemoteData IOException (ParseFlags, [Classify], Hint)
-> STM (RemoteData IOException (ParseFlags, [Classify], Hint))
forall (f :: * -> *) a. Applicative f => a -> f a
pure RemoteData IOException (ParseFlags, [Classify], Hint)
remoteData
  case RemoteData IOException (ParseFlags, [Classify], Hint)
remoteData of
    RemoteData IOException (ParseFlags, [Classify], Hint)
NotAsked -> IO (ParseFlags, [Classify], Hint)
-> Hsc (ParseFlags, [Classify], Hint)
forall (m :: * -> *) a. MonadIO m => IO a -> m a
io (IO (ParseFlags, [Classify], Hint)
 -> Hsc (ParseFlags, [Classify], Hint))
-> (IO (ParseFlags, [Classify], Hint)
    -> IO (ParseFlags, [Classify], Hint))
-> IO (ParseFlags, [Classify], Hint)
-> Hsc (ParseFlags, [Classify], Hint)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. TMVar ()
-> (() -> IO (ParseFlags, [Classify], Hint))
-> IO (ParseFlags, [Classify], Hint)
forall a b. TMVar a -> (a -> IO b) -> IO b
withTMVar TMVar ()
settingsTMVar ((() -> IO (ParseFlags, [Classify], Hint))
 -> IO (ParseFlags, [Classify], Hint))
-> (IO (ParseFlags, [Classify], Hint)
    -> () -> IO (ParseFlags, [Classify], Hint))
-> IO (ParseFlags, [Classify], Hint)
-> IO (ParseFlags, [Classify], Hint)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. IO (ParseFlags, [Classify], Hint)
-> () -> IO (ParseFlags, [Classify], Hint)
forall a b. a -> b -> a
const (IO (ParseFlags, [Classify], Hint)
 -> Hsc (ParseFlags, [Classify], Hint))
-> IO (ParseFlags, [Classify], Hint)
-> Hsc (ParseFlags, [Classify], Hint)
forall a b. (a -> b) -> a -> b
$ do
      Either IOException (ParseFlags, [Classify], Hint)
result <- IO (ParseFlags, [Classify], Hint)
-> IO (Either IOException (ParseFlags, [Classify], Hint))
forall e a. Exception e => IO a -> IO (Either e a)
Exception.try (IO (ParseFlags, [Classify], Hint)
 -> IO (Either IOException (ParseFlags, [Classify], Hint)))
-> IO (ParseFlags, [Classify], Hint)
-> IO (Either IOException (ParseFlags, [Classify], Hint))
forall a b. (a -> b) -> a -> b
$ [CommandLineOption] -> IO (ParseFlags, [Classify], Hint)
HLint.argsSettings [CommandLineOption]
options
      case Either IOException (ParseFlags, [Classify], Hint)
result of
        Left IOException
ioException -> do
          STM () -> IO ()
forall a. STM a -> IO a
stm (STM () -> IO ())
-> (RemoteData IOException (ParseFlags, [Classify], Hint)
    -> STM ())
-> RemoteData IOException (ParseFlags, [Classify], Hint)
-> IO ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. RemoteData IOException (ParseFlags, [Classify], Hint) -> STM ()
insert (RemoteData IOException (ParseFlags, [Classify], Hint) -> IO ())
-> RemoteData IOException (ParseFlags, [Classify], Hint) -> IO ()
forall a b. (a -> b) -> a -> b
$ IOException
-> RemoteData IOException (ParseFlags, [Classify], Hint)
forall e a. e -> RemoteData e a
Failure IOException
ioException
          IOException -> IO (ParseFlags, [Classify], Hint)
forall e a. Exception e => e -> IO a
Exception.throwIO IOException
ioException
        Right (ParseFlags, [Classify], Hint)
settings -> do
          STM () -> IO ()
forall a. STM a -> IO a
stm (STM () -> IO ())
-> (RemoteData IOException (ParseFlags, [Classify], Hint)
    -> STM ())
-> RemoteData IOException (ParseFlags, [Classify], Hint)
-> IO ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. RemoteData IOException (ParseFlags, [Classify], Hint) -> STM ()
insert (RemoteData IOException (ParseFlags, [Classify], Hint) -> IO ())
-> RemoteData IOException (ParseFlags, [Classify], Hint) -> IO ()
forall a b. (a -> b) -> a -> b
$ (ParseFlags, [Classify], Hint)
-> RemoteData IOException (ParseFlags, [Classify], Hint)
forall e a. a -> RemoteData e a
Success (ParseFlags, [Classify], Hint)
settings
          (ParseFlags, [Classify], Hint) -> IO (ParseFlags, [Classify], Hint)
forall (f :: * -> *) a. Applicative f => a -> f a
pure (ParseFlags, [Classify], Hint)
settings
    RemoteData IOException (ParseFlags, [Classify], Hint)
Loading -> do
      IO () -> Hsc ()
forall (m :: * -> *) a. MonadIO m => IO a -> m a
io (IO () -> Hsc ()) -> IO () -> Hsc ()
forall a b. (a -> b) -> a -> b
$ Int -> IO ()
Concurrent.threadDelay Int
1000
      [CommandLineOption] -> Hsc (ParseFlags, [Classify], Hint)
getSettings [CommandLineOption]
options
    Failure IOException
ioException -> IO (ParseFlags, [Classify], Hint)
-> Hsc (ParseFlags, [Classify], Hint)
forall (m :: * -> *) a. MonadIO m => IO a -> m a
io (IO (ParseFlags, [Classify], Hint)
 -> Hsc (ParseFlags, [Classify], Hint))
-> IO (ParseFlags, [Classify], Hint)
-> Hsc (ParseFlags, [Classify], Hint)
forall a b. (a -> b) -> a -> b
$ IOException -> IO (ParseFlags, [Classify], Hint)
forall e a. Exception e => e -> IO a
Exception.throwIO IOException
ioException
    Success (ParseFlags, [Classify], Hint)
settings -> (ParseFlags, [Classify], Hint)
-> Hsc (ParseFlags, [Classify], Hint)
forall (f :: * -> *) a. Applicative f => a -> f a
pure (ParseFlags, [Classify], Hint)
settings

io :: IO.MonadIO m => IO a -> m a
io :: IO a -> m a
io = IO a -> m a
forall (m :: * -> *) a. MonadIO m => IO a -> m a
GHC.liftIO

stm :: Stm.STM a -> IO a
stm :: STM a -> IO a
stm = STM a -> IO a
forall a. STM a -> IO a
Stm.atomically

withTMVar :: Stm.TMVar a -> (a -> IO b) -> IO b
withTMVar :: TMVar a -> (a -> IO b) -> IO b
withTMVar TMVar a
var =
  IO a -> (a -> IO ()) -> (a -> IO b) -> IO b
forall a b c. IO a -> (a -> IO b) -> (a -> IO c) -> IO c
Exception.bracket (STM a -> IO a
forall a. STM a -> IO a
stm (STM a -> IO a) -> STM a -> IO a
forall a b. (a -> b) -> a -> b
$ TMVar a -> STM a
forall a. TMVar a -> STM a
Stm.takeTMVar TMVar a
var) (STM () -> IO ()
forall a. STM a -> IO a
stm (STM () -> IO ()) -> (a -> STM ()) -> a -> IO ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. TMVar a -> a -> STM ()
forall a. TMVar a -> a -> STM ()
Stm.putTMVar TMVar a
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 :: TVar
  (Map
     [CommandLineOption]
     (RemoteData IOException (ParseFlags, [Classify], Hint)))
settingsTVar = IO
  (TVar
     (Map
        [CommandLineOption]
        (RemoteData IOException (ParseFlags, [Classify], Hint))))
-> TVar
     (Map
        [CommandLineOption]
        (RemoteData IOException (ParseFlags, [Classify], Hint)))
forall a. IO a -> a
Unsafe.unsafePerformIO (IO
   (TVar
      (Map
         [CommandLineOption]
         (RemoteData IOException (ParseFlags, [Classify], Hint))))
 -> TVar
      (Map
         [CommandLineOption]
         (RemoteData IOException (ParseFlags, [Classify], Hint))))
-> IO
     (TVar
        (Map
           [CommandLineOption]
           (RemoteData IOException (ParseFlags, [Classify], Hint))))
-> TVar
     (Map
        [CommandLineOption]
        (RemoteData IOException (ParseFlags, [Classify], Hint)))
forall a b. (a -> b) -> a -> b
$ Map
  [CommandLineOption]
  (RemoteData IOException (ParseFlags, [Classify], Hint))
-> IO
     (TVar
        (Map
           [CommandLineOption]
           (RemoteData IOException (ParseFlags, [Classify], Hint))))
forall a. a -> IO (TVar a)
Stm.newTVarIO Map
  [CommandLineOption]
  (RemoteData IOException (ParseFlags, [Classify], Hint))
forall k a. Map k a
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 :: TMVar ()
settingsTMVar = IO (TMVar ()) -> TMVar ()
forall a. IO a -> a
Unsafe.unsafePerformIO (IO (TMVar ()) -> TMVar ()) -> IO (TMVar ()) -> TMVar ()
forall a b. (a -> b) -> a -> b
$ () -> IO (TMVar ())
forall a. a -> IO (TMVar a)
Stm.newTMVarIO ()
{-# NOINLINE settingsTMVar #-}

data RemoteData e a
  = NotAsked
  | Loading
  | Failure e
  | Success a
  deriving (RemoteData e a -> RemoteData e a -> Bool
(RemoteData e a -> RemoteData e a -> Bool)
-> (RemoteData e a -> RemoteData e a -> Bool)
-> Eq (RemoteData e a)
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
forall e a.
(Eq e, Eq a) =>
RemoteData e a -> RemoteData e a -> Bool
/= :: RemoteData e a -> RemoteData e a -> Bool
$c/= :: forall e a.
(Eq e, Eq a) =>
RemoteData e a -> RemoteData e a -> Bool
== :: RemoteData e a -> RemoteData e a -> Bool
$c== :: forall e a.
(Eq e, Eq a) =>
RemoteData e a -> RemoteData e a -> Bool
Eq, Int -> RemoteData e a -> ShowS
[RemoteData e a] -> ShowS
RemoteData e a -> CommandLineOption
(Int -> RemoteData e a -> ShowS)
-> (RemoteData e a -> CommandLineOption)
-> ([RemoteData e a] -> ShowS)
-> Show (RemoteData e a)
forall a.
(Int -> a -> ShowS)
-> (a -> CommandLineOption) -> ([a] -> ShowS) -> Show a
forall e a. (Show e, Show a) => Int -> RemoteData e a -> ShowS
forall e a. (Show e, Show a) => [RemoteData e a] -> ShowS
forall e a. (Show e, Show a) => RemoteData e a -> CommandLineOption
showList :: [RemoteData e a] -> ShowS
$cshowList :: forall e a. (Show e, Show a) => [RemoteData e a] -> ShowS
show :: RemoteData e a -> CommandLineOption
$cshow :: forall e a. (Show e, Show a) => RemoteData e a -> CommandLineOption
showsPrec :: Int -> RemoteData e a -> ShowS
$cshowsPrec :: forall e a. (Show e, Show a) => Int -> RemoteData e a -> ShowS
Show)

ideaToWarnMsg :: GHC.DynFlags -> HLint.Idea -> GHC.WarnMsg
ideaToWarnMsg :: DynFlags -> Idea -> WarnMsg
ideaToWarnMsg DynFlags
dynFlags Idea
idea =
  let
    mkErrMsg :: DynFlags -> SrcSpan -> MsgDoc -> WarnMsg
mkErrMsg = case Idea -> Severity
HLint.ideaSeverity Idea
idea of
      Severity
HLint.Error -> DynFlags -> SrcSpan -> MsgDoc -> WarnMsg
GHC.mkPlainErrMsg
      Severity
_ -> DynFlags -> SrcSpan -> MsgDoc -> WarnMsg
GHC.mkPlainWarnMsg
    srcSpan :: SrcSpan
srcSpan = case SrcSpan -> Maybe (CommandLineOption, (Int, Int), (Int, Int))
HLint.unpackSrcSpan (SrcSpan -> Maybe (CommandLineOption, (Int, Int), (Int, Int)))
-> SrcSpan -> Maybe (CommandLineOption, (Int, Int), (Int, Int))
forall a b. (a -> b) -> a -> b
$ Idea -> SrcSpan
HLint.ideaSpan Idea
idea of
      Maybe (CommandLineOption, (Int, Int), (Int, Int))
Nothing -> SrcSpan
GHC.noSrcSpan
      Just (CommandLineOption
file, (Int
startLine, Int
startColumn), (Int
endLine, Int
endColumn)) ->
        SrcLoc -> SrcLoc -> SrcSpan
GHC.mkSrcSpan
          (FastString -> Int -> Int -> SrcLoc
GHC.mkSrcLoc (CommandLineOption -> FastString
GHC.mkFastString CommandLineOption
file) Int
startLine Int
startColumn)
          (FastString -> Int -> Int -> SrcLoc
GHC.mkSrcLoc (CommandLineOption -> FastString
GHC.mkFastString CommandLineOption
file) Int
endLine Int
endColumn)
    msgDoc :: MsgDoc
msgDoc = Idea -> MsgDoc
ideaToMsgDoc Idea
idea
  in DynFlags -> SrcSpan -> MsgDoc -> WarnMsg
mkErrMsg DynFlags
dynFlags SrcSpan
srcSpan MsgDoc
msgDoc

ideaToMsgDoc :: HLint.Idea -> GHC.MsgDoc
ideaToMsgDoc :: Idea -> MsgDoc
ideaToMsgDoc Idea
idea = [MsgDoc] -> MsgDoc
GHC.vcat
  [ CommandLineOption -> MsgDoc
GHC.text (CommandLineOption -> MsgDoc) -> CommandLineOption -> MsgDoc
forall a b. (a -> b) -> a -> b
$ Idea -> CommandLineOption
HLint.ideaHint Idea
idea
  , case Idea -> Maybe CommandLineOption
HLint.ideaTo Idea
idea of
    Just CommandLineOption
to | Bool -> Bool
not (Bool -> Bool) -> Bool -> Bool
forall a b. (a -> b) -> a -> b
$ CommandLineOption -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null CommandLineOption
to -> CommandLineOption -> MsgDoc
GHC.text (CommandLineOption -> MsgDoc) -> CommandLineOption -> MsgDoc
forall a b. (a -> b) -> a -> b
$ CommandLineOption
"Perhaps: " CommandLineOption -> ShowS
forall a. Semigroup a => a -> a -> a
<> CommandLineOption
to
    Maybe CommandLineOption
_ -> MsgDoc
GHC.empty
  , [MsgDoc] -> MsgDoc
GHC.vcat ([MsgDoc] -> MsgDoc) -> ([Note] -> [MsgDoc]) -> [Note] -> MsgDoc
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Note -> MsgDoc) -> [Note] -> [MsgDoc]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap (CommandLineOption -> MsgDoc
GHC.text (CommandLineOption -> MsgDoc)
-> (Note -> CommandLineOption) -> Note -> MsgDoc
forall b c a. (b -> c) -> (a -> b) -> a -> c
. CommandLineOption -> ShowS
forall a. Monoid a => a -> a -> a
mappend CommandLineOption
"Note: " ShowS -> (Note -> CommandLineOption) -> Note -> CommandLineOption
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Note -> CommandLineOption
forall a. Show a => a -> CommandLineOption
show) ([Note] -> MsgDoc) -> [Note] -> MsgDoc
forall a b. (a -> b) -> a -> b
$ Idea -> [Note]
HLint.ideaNote Idea
idea
  ]