Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Mark extensions implied by language (such as GHC2021) as unused #1464

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 5 additions & 4 deletions src/GHC/All.hs
Original file line number Diff line number Diff line change
Expand Up @@ -84,8 +84,9 @@ data ParseError = ParseError
}

-- | Result of 'parseModuleEx', representing a parsed module.
newtype ModuleEx = ModuleEx {
data ModuleEx = ModuleEx {
ghcModule :: Located HsModule
, ghcLanguage :: Maybe Language
}

-- | Extract a complete list of all the comments in a module.
Expand Down Expand Up @@ -147,10 +148,10 @@ parseDeclGhcWithMode parseMode s =
-- | Create a 'ModuleEx' from a GHC module. It is assumed the incoming
-- parsed module has not been adjusted to account for operator
-- fixities (it uses the HLint default fixities).
createModuleEx :: Located HsModule -> ModuleEx
createModuleEx :: Located HsModule -> Maybe Language -> ModuleEx
createModuleEx = createModuleExWithFixities (map toFixity defaultFixities)

createModuleExWithFixities :: [(String, Fixity)] -> Located HsModule -> ModuleEx
createModuleExWithFixities :: [(String, Fixity)] -> Located HsModule -> Maybe Language -> ModuleEx
createModuleExWithFixities fixities ast =
ModuleEx (applyFixities (fixitiesFromModule ast ++ fixities) ast)

Expand Down Expand Up @@ -188,7 +189,7 @@ parseModuleEx flags file str = timedIO "Parse" file $ runExceptT $ do
ExceptT $ parseFailureErr dynFlags str file str errs
else do
let fixes = fixitiesFromModule a ++ ghcFixitiesFromParseFlags flags
pure $ ModuleEx (applyFixities fixes a)
pure $ ModuleEx (applyFixities fixes a) (language dynFlags)
PFailed s ->
ExceptT $ parseFailureErr dynFlags str file str $ bagToList . getMessages $ GhcPsMessage <$> snd (getPsMessages s)
where
Expand Down
2 changes: 1 addition & 1 deletion src/Hint/Duplicate.hs
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ duplicateHint ms =
]
where
ds = [(modName m, fromMaybe "" (declName d), unLoc d)
| ModuleEx m <- map snd ms
| ModuleEx {ghcModule = m} <- map snd ms
, d <- hsmodDecls (unLoc m)]

dupes :: (Outputable e, Data e) => [(String, String, [LocatedA e])] -> [Idea]
Expand Down
2 changes: 1 addition & 1 deletion src/Hint/Export.hs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import GHC.Types.Name.Occurrence
import GHC.Types.Name.Reader

exportHint :: ModuHint
exportHint _ (ModuleEx (L s m@HsModule {hsmodName = Just name, hsmodExports = exports}) )
exportHint _ ModuleEx {ghcModule = L s m@HsModule {hsmodName = Just name, hsmodExports = exports}}
| Nothing <- exports =
let r = o{ hsmodExports = Just (noLocA [noLocA (IEModuleContents EpAnnNotUsed name)] )} in
[(ignore "Use module export list" (L s o) (noLoc r) []){ideaNote = [Note "an explicit list is usually better"]}]
Expand Down
30 changes: 22 additions & 8 deletions src/Hint/Extensions.hs
Original file line number Diff line number Diff line change
Expand Up @@ -254,7 +254,7 @@ foo = $bar

module Hint.Extensions(extensionsHint) where

import Hint.Type(ModuHint,rawIdea,Severity(Warning),Note(..),toSSAnc,ghcModule,modComments)
import Hint.Type(ModuHint,rawIdea,Severity(Warning),Note(..),toSSAnc,ghcModule,modComments,ModuleEx (..))
import Extension

import Data.Generics.Uniplate.DataOnly
Expand All @@ -266,6 +266,7 @@ import Refact.Types
import qualified Data.Set as Set
import qualified Data.Map as Map

import GHC.Driver.Session (languageExtensions)
import GHC.Types.SrcLoc
import GHC.Types.SourceText
import GHC.Hs
Expand All @@ -289,7 +290,7 @@ import Language.Haskell.GhclibParserEx.GHC.Utils.Outputable
import Language.Haskell.GhclibParserEx.GHC.Types.Name.Reader

extensionsHint :: ModuHint
extensionsHint _ x =
extensionsHint _ x@ModuleEx{ghcLanguage} =
[
rawIdea Hint.Type.Warning "Unused LANGUAGE pragma"
(RealSrcSpan (anchor sl) GHC.Data.Strict.Nothing)
Expand All @@ -304,7 +305,7 @@ extensionsHint _ x =
, let after = filter (maybe True (`Set.member` keep) . snd) before
, before /= after
, let explainedRemovals
| null after && not (any (`Map.member` implied) $ mapMaybe snd before) = []
| null after && not (any (`Set.member` impliedExtensions) $ mapMaybe snd before) = []
| otherwise = before \\ after
, let newPragma =
if null after then "" else comment_ (mkLanguagePragmas sl $ map fst after)
Expand Down Expand Up @@ -336,9 +337,18 @@ extensionsHint _ x =
| e <- Set.toList useful
, a:_ <- [filter (`Set.member` useful) $ extensionImpliedEnabledBy e]
]
impliedByLanguage :: Set.Set Extension
impliedByLanguage = case ghcLanguage of
Just l -> Set.fromList $ languageExtensions (Just l)
-- If we pass 'Nothing' to 'languageExtensions', the latest language
-- (i.e. GHC2021) is used; which might be unexpected for users on older
-- GHC versions where GHC2021 doesn't even exist yet.
Nothing -> Set.empty
impliedExtensions :: Set.Set Extension
impliedExtensions = Map.keysSet implied `Set.union` impliedByLanguage
-- Those we should keep.
keep :: Set.Set Extension
keep = useful `Set.difference` Map.keysSet implied
keep = useful `Set.difference` impliedExtensions
-- The meaning of (a,b) is a used to imply b, but has gone, so
-- suggest enabling b.
disappear :: Map.Map Extension [Extension]
Expand All @@ -352,10 +362,14 @@ extensionsHint _ x =
, usedTH || usedExt a (ghcModule x)
]
reason :: Extension -> String
reason x =
case Map.lookup x implied of
Just a -> "implied by " ++ show a
Nothing -> "not used"
reason x
| Just a <- Map.lookup x implied
= "implied by " ++ show a
| x `Set.member` impliedByLanguage
, Just l <- ghcLanguage
= "implied by " ++ show l
| otherwise
= "not used"

deriveHaskell = ["Eq","Ord","Enum","Ix","Bounded","Read","Show"]
deriveGenerics = ["Data","Typeable","Generic","Generic1","Lift"]
Expand Down
2 changes: 1 addition & 1 deletion src/Hint/Unsafe.hs
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ import Language.Haskell.GhclibParserEx.GHC.Utils.Outputable
-- @
-- is. We advise that such constants should have a @NOINLINE@ pragma.
unsafeHint :: DeclHint
unsafeHint _ (ModuleEx (L _ m)) = \ld@(L loc d) ->
unsafeHint _ ModuleEx {ghcModule = L _ m} = \ld@(L loc d) ->
[rawIdea Hint.Type.Warning "Missing NOINLINE pragma" (locA loc)
(unsafePrettyPrint d)
(Just $ trimStart (unsafePrettyPrint $ gen x) ++ "\n" ++ unsafePrettyPrint d)
Expand Down
17 changes: 17 additions & 0 deletions tests/ghc2021.test
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
---------------------------------------------------------------------
RUN tests/ghc2021.hs
FILE tests/ghc2021.hs
{-# LANGUAGE FlexibleContexts #-}
OUTPUT
No hints

---------------------------------------------------------------------
RUN tests/ghc2021.hs -XGHC2021
OUTPUT
tests/ghc2021.hs:1:1-33: Warning: Unused LANGUAGE pragma
Found:
{-# LANGUAGE FlexibleContexts #-}
Perhaps you should remove it.
Note: Extension FlexibleContexts is implied by GHC2021

1 hint