mirror of
https://github.com/tfausak/splint.git
synced 2024-10-05 13:37:37 +03:00
Upgrade to GHC 9.6
This commit is contained in:
parent
9028a8b631
commit
edb872c6ac
6
.github/dependabot.yml
vendored
Normal file
6
.github/dependabot.yml
vendored
Normal file
@ -0,0 +1,6 @@
|
||||
updates:
|
||||
- directory: /
|
||||
package-ecosystem: github-actions
|
||||
schedule:
|
||||
interval: weekly
|
||||
version: 2
|
38
.github/workflows/ci.yaml
vendored
38
.github/workflows/ci.yaml
vendored
@ -1,38 +0,0 @@
|
||||
name: CI
|
||||
on:
|
||||
push: null
|
||||
release:
|
||||
types:
|
||||
- created
|
||||
jobs:
|
||||
build:
|
||||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
- { os: ubuntu, ghc: 9.2.3 }
|
||||
- { os: ubuntu, ghc: 9.0.2 }
|
||||
- { os: ubuntu, ghc: 8.10.7 }
|
||||
- { os: macos, ghc: 8.10.7 }
|
||||
- { os: windows, ghc: 8.10.7 }
|
||||
runs-on: ${{ matrix.os }}-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- id: setup-haskell
|
||||
uses: haskell/actions/setup@v1
|
||||
with:
|
||||
ghc-version: ${{ matrix.ghc }}
|
||||
- run: cabal freeze && cat cabal.project.freeze
|
||||
- uses: actions/cache@v2
|
||||
with:
|
||||
path: ${{ steps.setup-haskell.outputs.cabal-store }}
|
||||
key: ${{ matrix.os }}-${{ matrix.ghc }}-${{ hashFiles('cabal.project.freeze') }}
|
||||
restore-keys: ${{ matrix.os }}-${{ matrix.ghc }}-
|
||||
- run: cabal build
|
||||
- run: cabal sdist
|
||||
- uses: actions/upload-artifact@v2
|
||||
with:
|
||||
path: dist-newstyle/sdist/splint-*.tar.gz
|
||||
name: splint-${{ github.sha }}.tar.gz
|
||||
- run: cabal check
|
||||
- if: github.event_name == 'release' && matrix.os == 'ubuntu' && matrix.ghc == '9.0.2'
|
||||
run: cabal upload --publish --username '${{ secrets.HACKAGE_USERNAME }}' --password '${{ secrets.HACKAGE_PASSWORD }}' dist-newstyle/sdist/splint-*.tar.gz
|
97
.github/workflows/ci.yml
vendored
Normal file
97
.github/workflows/ci.yml
vendored
Normal file
@ -0,0 +1,97 @@
|
||||
jobs:
|
||||
build:
|
||||
name: GHC ${{ matrix.ghc }} on ${{ matrix.os }}
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- run: mkdir artifact
|
||||
- id: haskell
|
||||
uses: haskell-actions/setup@v2
|
||||
with:
|
||||
ghc-version: ${{ matrix.ghc }}
|
||||
- run: ghc-pkg list
|
||||
- run: cabal sdist --output-dir artifact
|
||||
- run: cabal configure --flags=pedantic --jobs
|
||||
- run: cat cabal.project.local
|
||||
- run: cp cabal.project.local artifact
|
||||
- run: cabal freeze
|
||||
- run: cat cabal.project.freeze
|
||||
- run: cp cabal.project.freeze artifact
|
||||
- run: cabal outdated --v2-freeze-file
|
||||
- uses: actions/cache@v4
|
||||
with:
|
||||
key: ${{ matrix.os }}-${{ matrix.ghc }}-${{ hashFiles('cabal.project.freeze') }}
|
||||
path: ${{ steps.haskell.outputs.cabal-store }}
|
||||
restore-keys: ${{ matrix.os }}-${{ matrix.ghc }}-
|
||||
- run: cabal build --only-download
|
||||
- run: cabal build --only-dependencies
|
||||
- run: cabal build
|
||||
- run: tar --create --file artifact.tar --verbose artifact
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: splint-${{ github.sha }}-ghc-${{ matrix.ghc }}-${{ matrix.os }}
|
||||
path: artifact.tar
|
||||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
- ghc: 9.6
|
||||
os: macos-latest
|
||||
- ghc: 9.6
|
||||
os: ubuntu-latest
|
||||
- ghc: 9.6
|
||||
os: windows-latest
|
||||
cabal:
|
||||
name: Cabal
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- run: cabal check
|
||||
gild:
|
||||
name: Gild
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: tfausak/cabal-gild-setup-action@v2
|
||||
- run: cabal-gild --input splint.cabal --mode check
|
||||
hlint:
|
||||
name: HLint
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: haskell-actions/hlint-setup@v2
|
||||
- uses: haskell-actions/hlint-run@v2
|
||||
with:
|
||||
fail-on: status
|
||||
ormolu:
|
||||
name: Ormolu
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: haskell-actions/run-ormolu@v16
|
||||
release:
|
||||
if: ${{ github.event_name == 'release' }}
|
||||
name: Release
|
||||
needs: build
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: splint-${{ github.sha }}-ghc-9.6-ubuntu-latest
|
||||
- run: tar --extract --file artifact.tar --verbose
|
||||
- uses: softprops/action-gh-release@v2
|
||||
with:
|
||||
files: artifact/splint-${{ github.event.release.tag_name }}.tar.gz
|
||||
- run: cabal upload --publish --username '${{ secrets.HACKAGE_USERNAME }}' --password '${{ secrets.HACKAGE_PASSWORD }}' artifact/splint-${{ github.event.release.tag_name }}.tar.gz
|
||||
name: CI
|
||||
on:
|
||||
pull_request:
|
||||
branches:
|
||||
- main
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
release:
|
||||
types:
|
||||
- created
|
||||
schedule:
|
||||
- cron: 0 0 * * 1
|
4
.gitignore
vendored
4
.gitignore
vendored
@ -1,2 +1,2 @@
|
||||
/.stack-work/
|
||||
/stack.yaml.lock
|
||||
/cabal.project.*
|
||||
/dist-newstyle/
|
||||
|
@ -1,4 +1,4 @@
|
||||
Copyright 2022 Taylor Fausak
|
||||
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
|
@ -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 @@ Among all the usual output from GHC, you should see this new warning:
|
||||
```
|
||||
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' ]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
8
cabal.project
Normal file
8
cabal.project
Normal file
@ -0,0 +1,8 @@
|
||||
packages: .
|
||||
with-compiler: ghc-9.6
|
||||
|
||||
package ghc-lib-parser-ex
|
||||
flags: +no-ghc-lib
|
||||
|
||||
package hlint
|
||||
flags: -ghc-lib
|
77
source/library/Splint.hs
Normal file
77
source/library/Splint.hs
Normal file
@ -0,0 +1,77 @@
|
||||
module Splint where
|
||||
|
||||
import qualified Data.Maybe as Maybe
|
||||
import qualified GHC.Data.Bag as Bag
|
||||
import qualified GHC.Driver.Config.Diagnostic as Config
|
||||
import qualified GHC.Driver.Errors
|
||||
import qualified GHC.Driver.Errors.Types
|
||||
import qualified GHC.Hs as Hs
|
||||
import qualified GHC.Plugins as Plugin
|
||||
import qualified GHC.Types.Error as Error
|
||||
import qualified GHC.Utils.Logger as Logger
|
||||
import qualified Language.Haskell.HLint as HLint
|
||||
import qualified Splint.Replacement as Replacement
|
||||
import qualified Splint.Settings as Settings
|
||||
|
||||
plugin :: Plugin.Plugin
|
||||
plugin =
|
||||
Plugin.defaultPlugin
|
||||
{ Plugin.parsedResultAction = parsedResultAction,
|
||||
Plugin.pluginRecompile = Plugin.purePlugin
|
||||
}
|
||||
|
||||
parsedResultAction ::
|
||||
[Plugin.CommandLineOption] ->
|
||||
modSummary ->
|
||||
Plugin.ParsedResult ->
|
||||
Plugin.Hsc Plugin.ParsedResult
|
||||
parsedResultAction commandLineOptions _modSummary parsedResult = do
|
||||
logger <- Logger.getLogger
|
||||
dynFlags <- Plugin.getDynFlags
|
||||
Plugin.liftIO $ do
|
||||
(_parseFlags, classifies, hint) <- Settings.load commandLineOptions
|
||||
GHC.Driver.Errors.printOrThrowDiagnostics
|
||||
logger
|
||||
(Config.initPrintConfig dynFlags)
|
||||
(Config.initDiagOpts dynFlags)
|
||||
. Error.mkMessages
|
||||
. Bag.listToBag
|
||||
. fmap ideaToWarnMsg
|
||||
. HLint.applyHints classifies hint
|
||||
. pure
|
||||
. HLint.createModuleEx
|
||||
. Hs.hpm_module
|
||||
$ Plugin.parsedResultModule parsedResult
|
||||
pure parsedResult
|
||||
|
||||
ideaToWarnMsg :: HLint.Idea -> GHC.Driver.Errors.Types.WarnMsg
|
||||
ideaToWarnMsg idea =
|
||||
Error.MsgEnvelope
|
||||
{ Error.errMsgContext = Plugin.neverQualify,
|
||||
Error.errMsgDiagnostic =
|
||||
GHC.Driver.Errors.Types.ghcUnknownMessage
|
||||
Error.DiagnosticMessage
|
||||
{ Error.diagHints =
|
||||
fmap (Error.UnknownHint . Replacement.fromString)
|
||||
. Maybe.maybeToList
|
||||
$ HLint.ideaTo idea,
|
||||
Error.diagMessage =
|
||||
Error.mkDecorated $
|
||||
Plugin.text
|
||||
(HLint.ideaHint idea)
|
||||
: fmap
|
||||
(Plugin.text . mappend "Note: " . show)
|
||||
(HLint.ideaNote idea),
|
||||
Error.diagReason = case HLint.ideaSeverity idea of
|
||||
HLint.Ignore -> Error.WarningWithoutFlag
|
||||
HLint.Suggestion -> Error.WarningWithoutFlag
|
||||
HLint.Warning -> Error.WarningWithoutFlag
|
||||
HLint.Error -> Error.ErrorWithoutFlag
|
||||
},
|
||||
Error.errMsgSeverity = case HLint.ideaSeverity idea of
|
||||
HLint.Ignore -> Error.SevIgnore
|
||||
HLint.Suggestion -> Error.SevWarning
|
||||
HLint.Warning -> Error.SevWarning
|
||||
HLint.Error -> Error.SevError,
|
||||
Error.errMsgSpan = HLint.ideaSpan idea
|
||||
}
|
16
source/library/Splint/Replacement.hs
Normal file
16
source/library/Splint/Replacement.hs
Normal file
@ -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
|
@ -13,9 +13,9 @@ type Settings = (HLint.ParseFlags, [HLint.Classify], HLint.Hint)
|
||||
-- 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 ::
|
||||
Stm.TVar
|
||||
(Map.Map [String] (RemoteData.RemoteData Exception.IOException Settings))
|
||||
cache = Unsafe.unsafePerformIO $ Stm.newTVarIO Map.empty
|
||||
{-# NOINLINE cache #-}
|
||||
|
||||
@ -29,17 +29,17 @@ 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)
|
||||
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
|
||||
let remoteData =
|
||||
Map.findWithDefault RemoteData.NotAsked commandLineOptions settings
|
||||
case remoteData of
|
||||
RemoteData.NotAsked ->
|
||||
Stm.modifyTVar cache $ Map.insert commandLineOptions RemoteData.Loading
|
||||
@ -47,8 +47,12 @@ load commandLineOptions = do
|
||||
_ -> pure ()
|
||||
pure remoteData
|
||||
case remoteData of
|
||||
RemoteData.NotAsked -> withTMVar semaphore . const $ do
|
||||
result <- Exception.try $ HLint.argsSettings commandLineOptions
|
||||
RemoteData.NotAsked -> do
|
||||
result <-
|
||||
withTMVar semaphore
|
||||
. const
|
||||
. Exception.try
|
||||
$ HLint.argsSettings commandLineOptions
|
||||
case result of
|
||||
Left ioException -> do
|
||||
Stm.atomically
|
46
splint.cabal
46
splint.cabal
@ -1,12 +1,8 @@
|
||||
cabal-version: >= 1.10
|
||||
|
||||
cabal-version: 2.0
|
||||
name: splint
|
||||
version: 1.0.2.1
|
||||
|
||||
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 @@ description:
|
||||
|
||||
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,41 @@ source-repository head
|
||||
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,
|
||||
containers ^>=0.6.7,
|
||||
ghc ^>=9.6.1,
|
||||
hlint ^>=3.6.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
|
||||
|
@ -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
|
@ -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
|
@ -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
|
||||
]
|
Loading…
Reference in New Issue
Block a user