splint 1.0.0.1 → 1.0.1.0
raw patch · 5 files changed
+140/−26 lines, 5 filesdep ~basedep ~ghc
Dependency ranges changed: base, ghc
Files
- splint.cabal +32/−10
- src/lib/Splint.hs +22/−16
- src/lib/Splint/Parser.hs +33/−0
- src/lib/Splint/Parser/Fallback.hs +35/−0
- src/lib/Splint/Parser/Native.hs +18/−0
splint.cabal view
@@ -1,5 +1,7 @@+cabal-version: 2.2+ name: splint-version: 1.0.0.1+version: 1.0.1.0 synopsis: HLint as a GHC source plugin. description:@@ -9,7 +11,6 @@ to ignore the "Use @concatMap@" suggestion. build-type: Simple-cabal-version: >= 1.10 category: Development extra-source-files: README.markdown license-file: LICENSE.markdown@@ -20,17 +21,38 @@ location: https://github.com/tfausak/splint type: git -library- build-depends:- base >= 4.14.0 && < 4.15- , ghc >= 8.10.1 && < 8.11- , hlint >= 3.0 && < 3.2+common basics default-language: Haskell2010- exposed-modules: Splint ghc-options: -Weverything+ -Wno-all-missed-specialisations -Wno-implicit-prelude- -Wno-missing-safe-haskell-mode- -Wno-prepositive-qualified-module+ -Wno-missing-exported-signatures+ -Wno-missing-import-lists+ -Wno-safe -Wno-unsafe++ if impl(ghc >= 8.8)+ ghc-options:+ -Wno-missing-deriving-strategies++ if impl(ghc >= 8.10)+ ghc-options:+ -Wno-missing-safe-haskell-mode+ -Wno-prepositive-qualified-module++library+ import: basics++ build-depends:+ base >= 4.12.0 && < 4.15+ , ghc >= 8.6.1 && < 8.11+ , hlint >= 3.0 && < 3.2+ exposed-modules: Splint hs-source-dirs: src/lib+ other-modules: Splint.Parser++ if impl(ghc == 8.10.*)+ other-modules: Splint.Parser.Native+ else+ other-modules: Splint.Parser.Fallback
src/lib/Splint.hs view
@@ -1,10 +1,14 @@-module Splint ( plugin ) where+module Splint+ ( plugin+ )+where import qualified Bag as GHC import qualified Data.IORef as IORef 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@@ -18,25 +22,22 @@ -> GHC.ModSummary -> GHC.HsParsedModule -> GHC.Hsc GHC.HsParsedModule-action commandLineOptions _modSummary hsParsedModule = do+action commandLineOptions modSummary hsParsedModule = do+ (parseFlags, classifies, hint) <- getSettings commandLineOptions+ moduleEx <- Splint.parse parseFlags modSummary hsParsedModule dynFlags <- GHC.getDynFlags- GHC.liftIO $ do- (_parseFlags, classifies, hint) <- getSettings commandLineOptions- let- apiAnns = GHC.hpm_annotations hsParsedModule- hsModule = GHC.hpm_module hsParsedModule- moduleEx = HLint.createModuleEx apiAnns hsModule- ideas = HLint.applyHints classifies hint [moduleEx]- GHC.printOrThrowWarnings dynFlags- . GHC.listToBag- . fmap (ideaToWarnMsg dynFlags)- $ filter ((/= HLint.Ignore) . HLint.ideaSeverity) ideas+ GHC.liftIO+ . GHC.printOrThrowWarnings dynFlags+ . GHC.listToBag+ . fmap (ideaToWarnMsg dynFlags)+ . filter ((/= HLint.Ignore) . HLint.ideaSeverity)+ $ HLint.applyHints classifies hint [moduleEx] pure hsParsedModule type Settings = (HLint.ParseFlags, [HLint.Classify], HLint.Hint) -getSettings :: [GHC.CommandLineOption] -> IO Settings-getSettings commandLineOptions = do+getSettings :: [GHC.CommandLineOption] -> GHC.Hsc Settings+getSettings commandLineOptions = GHC.liftIO $ do maybeSettings <- IORef.readIORef settingsRef case maybeSettings of Just settings -> pure settings@@ -55,7 +56,12 @@ mkErrMsg = case HLint.ideaSeverity idea of HLint.Error -> GHC.mkPlainErrMsg _ -> GHC.mkPlainWarnMsg- srcSpan = HLint.ideaSpan idea+ 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
+ src/lib/Splint/Parser.hs view
@@ -0,0 +1,33 @@+{-# LANGUAGE CPP #-}++-- | Starting with HLint 3, it's possible to re-use the parsed module that GHC+-- produces to avoid re-parsing the module. Unfortunately this is only possible+-- when the GHC version matches the version that HLint expects. Otherwise we+-- have to let HLint re-parse the module to produce it's expected AST.+--+-- This module is responsible for picking that behavior. The so-called "native"+-- behavior is the one where GHC's parse module is re-used. The "fallback"+-- parser is the one that re-parses the module.+--+-- Doing this requires a build-time decision, which means using CPP. Therefore+-- this module is kept as small as possible in order to make it easier to+-- reason about.+--+-- It is expected that both the "native" and "fallback" parsers expose the+-- exact same interface.+--+-- You could in theory always use the "fallback" parser, but that would mean+-- doing a lot of unnecessary work by re-parsing modules. Parsing is typically+-- fast and this is hard to quantify, so if you want to go that route you+-- should consider using Ollie Charles's hlint-source-plugin instead.+-- <https://github.com/ocharles/hlint-source-plugin>+module Splint.Parser+ ( parse+ )+where++#if (__GLASGOW_HASKELL__ == 810)+import Splint.Parser.Native (parse)+#else+import Splint.Parser.Fallback (parse)+#endif
+ src/lib/Splint/Parser/Fallback.hs view
@@ -0,0 +1,35 @@+module Splint.Parser.Fallback+ ( parse+ )+where++import qualified Control.Exception as Exception+import qualified GhcPlugins as GHC+import qualified Language.Haskell.HLint as HLint++parse+ :: HLint.ParseFlags+ -> GHC.ModSummary+ -> GHC.HsParsedModule+ -> GHC.Hsc HLint.ModuleEx+parse parseFlags modSummary _ = GHC.liftIO $ do+ let filePath = GHC.msHsFilePath modSummary+ result <- HLint.parseModuleEx parseFlags filePath Nothing+ case result of+ Left parseError -> Exception.throwIO $ ParseError parseError+ Right moduleEx -> pure moduleEx++newtype ParseError = ParseError HLint.ParseError++instance Exception.Exception ParseError++instance Show ParseError where+ show (ParseError parseError) = mconcat+ [ "ParseError { parseErrorLocation = "+ , show $ HLint.parseErrorLocation parseError+ , ", parseErrorMessage = "+ , show $ HLint.parseErrorMessage parseError+ , ", parseErrorContents = "+ , show $ HLint.parseErrorContents parseError+ , " }"+ ]
+ src/lib/Splint/Parser/Native.hs view
@@ -0,0 +1,18 @@+module Splint.Parser.Native+ ( parse+ )+where++import qualified GhcPlugins as GHC+import qualified Language.Haskell.HLint as HLint++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