Skip to content

Commit 1cd28e3

Browse files
authored
Merge of #11634
2 parents df5520c + 82823b4 commit 1cd28e3

4 files changed

Lines changed: 150 additions & 98 deletions

File tree

Cabal-tests/Cabal-tests.cabal

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -200,3 +200,13 @@ test-suite no-thunks-test
200200
if impl(ghc >=8.6)
201201
build-depends:
202202
, nothunks >=0.1.1.0 && <0.3
203+
204+
executable ghc-supported-extensions
205+
hs-source-dirs: exes
206+
main-is: GhcSupportedExtensions.hs
207+
build-depends:
208+
, base
209+
, Cabal
210+
211+
ghc-options: -Wall
212+
default-language: Haskell2010
Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
{-# LANGUAGE LambdaCase #-}
2+
3+
-- | A test program to check that ghc has got all of its extensions registered
4+
-- with `KnownExtension` of Cabal-syntax.
5+
module Main where
6+
7+
import Distribution.Compat.Prelude
8+
import Distribution.Pretty (prettyShow)
9+
import Distribution.Simple.Utils (rawSystemStdout)
10+
import Distribution.Text (display, simpleParse)
11+
import Distribution.Verbosity (Verbosity (..), defaultVerbosityHandles, normal)
12+
import Language.Haskell.Extension (Extension (..), knownLanguages)
13+
14+
import Data.List ((\\))
15+
import System.Environment (getArgs, getProgName)
16+
17+
-- | Language editions as Extensions.
18+
--
19+
-- >>> langsAsExts
20+
-- [UnknownExtension "Haskell98",UnknownExtension "Haskell2010",UnknownExtension "GHC2021",UnknownExtension "GHC2024"]
21+
--
22+
-- Both of the following calls to @ghc@ return the same set of results but we
23+
-- want to separate `Language` editions from other extensions (both enabled and
24+
-- disabled) so we need a list of `knownLanguages` as unknown extensions that we
25+
-- can then use to filter out those languages.
26+
--
27+
-- @
28+
29+
-- $ ghc --supported-languages
30+
-- Haskell98
31+
-- Haskell2010
32+
-- GHC2021
33+
-- GHC2024
34+
-- Unsafe
35+
-- Trustworthy
36+
-- Safe
37+
-- CPP
38+
-- NoCPP
39+
-- ...
40+
41+
-- $ ghc --supported-extensions
42+
-- Haskell98
43+
-- Haskell2010
44+
-- GHC2021
45+
-- GHC2024
46+
-- Unsafe
47+
-- Trustworthy
48+
-- Safe
49+
-- CPP
50+
-- NoCPP
51+
-- ...
52+
-- @
53+
--
54+
-- If we're missing a language edition from `knownLanguages` then we'll notice
55+
-- this omission as it will appear in the unregistered list.
56+
langsAsExts :: [Extension]
57+
langsAsExts = map (readExtension . prettyShow) knownLanguages
58+
59+
checkProblems :: [Extension] -> [String]
60+
checkProblems implemented =
61+
-- Extensions that ghc knows about but that are not registered except for the known languages.
62+
let unregistered = [ext | ext <- implemented, not (registered ext), ext `notElem` langsAsExts]
63+
64+
-- check if someone has forgotten to update the `langsAsExts` exceptions list...
65+
badExceptions = langsAsExts \\ implemented
66+
67+
-- exceptions that are now registered
68+
badExceptions' = filter registered langsAsExts
69+
in catMaybes
70+
[ check unregistered $
71+
unlines
72+
[ "The following extensions are known to GHC but are not in the "
73+
, "extension registry in Language.Haskell.Extension."
74+
, " " ++ intercalate "\n " (map display unregistered)
75+
, "All extensions should be registered, even experimental extensions."
76+
]
77+
, check badExceptions $
78+
unlines
79+
[ "Error in the extension exception list. The following extensions"
80+
, "are listed as exceptions but are not even implemented by GHC:"
81+
, " " ++ intercalate "\n " (map display badExceptions)
82+
, "Please fix this test program by correcting the list of"
83+
, "exceptions."
84+
]
85+
, check badExceptions' $
86+
unlines
87+
[ "Error in the extension exception list. The following extensions"
88+
, "are listed as exceptions to registration but they are in fact"
89+
, "now registered in Language.Haskell.Extension:"
90+
, " " ++ intercalate "\n " (map display badExceptions')
91+
, "Please fix this test program by correcting the list of"
92+
, "exceptions."
93+
]
94+
]
95+
where
96+
registered UnknownExtension{} = False
97+
registered EnableExtension{} = True
98+
registered DisableExtension{} = True
99+
100+
check [] _ = Nothing
101+
check _ i = Just i
102+
103+
main :: IO a
104+
main = do
105+
getArgs >>= \case
106+
[ghcPath] -> do
107+
exts <- getExtensions ghcPath
108+
let problems = checkProblems exts
109+
putStrLn (intercalate "\n" problems)
110+
if null problems
111+
then exitSuccess
112+
else exitFailure
113+
args -> do
114+
n <- getProgName
115+
putStrLn $ "Error: Got " ++ show (length args) ++ " arguments" ++ if null args then "." else ": " ++ show args ++ "."
116+
putStrLn $ "Usage: Supply the path to ghc as a single argument to " ++ n ++ "."
117+
exitFailure
118+
119+
getExtensions :: FilePath -> IO [Extension]
120+
getExtensions ghcPath =
121+
map readExtension . lines
122+
<$> rawSystemStdout (Verbosity normal defaultVerbosityHandles) ghcPath ["--supported-languages"]
123+
124+
-- | Reads extensions. Anything unknown becomes an `UnknownExtension`.
125+
--
126+
-- >>> readExtension "Haskell98"
127+
-- UnknownExtension "Haskell98"
128+
readExtension :: String -> Extension
129+
readExtension str = handleNoParse $ do
130+
-- GHC defines extensions in a positive way, Cabal defines them
131+
-- relative to H98 so we try parsing ("No" ++ extName) first
132+
ext <- simpleParse ("No" ++ str)
133+
case ext of
134+
UnknownExtension _ -> simpleParse str
135+
_ -> return ext
136+
where
137+
handleNoParse :: Maybe Extension -> Extension
138+
handleNoParse = fromMaybe (error $ "unparsable extension " ++ show str)

Cabal-tests/tests/misc/ghc-supported-languages.hs

Lines changed: 0 additions & 97 deletions
This file was deleted.

Makefile

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,8 @@ FORMAT_DIRS := \
3131
Cabal-syntax \
3232
cabal-install \
3333
cabal-testsuite/src \
34-
cabal-validate
34+
cabal-validate \
35+
Cabal-tests/exes
3536

3637
FORMAT_DIRS_TODO := \
3738
Cabal-QuickCheck \

0 commit comments

Comments
 (0)