-- | A test program to check that ghc has got all of its extensions registered
--
module Main where

import Language.Haskell.Extension
import Distribution.Text
import Distribution.Simple.Utils
import Distribution.Verbosity

import Data.List ((\\))
import Data.Maybe
import Control.Applicative
import Control.Monad
import System.Environment
import System.Exit

-- | A list of GHC extensions that are deliberately not registered,
-- e.g. due to being experimental and not ready for public consumption
--
exceptions = map readExtension []

checkProblems :: [Extension] -> [String]
checkProblems implemented =

  let unregistered  =
        [ ext | ext <- implemented          -- extensions that ghc knows about
              , not (registered ext)        -- but that are not registered
              , ext `notElem` exceptions ]  -- except for the exceptions

      -- check if someone has forgotten to update the exceptions list...

      -- exceptions that are not implemented
      badExceptions  = exceptions \\ implemented

      -- exceptions that are now registered
      badExceptions' = filter registered exceptions

   in catMaybes
      [ check unregistered $ unlines
          [ "The following extensions are known to GHC but are not in the "
          , "extension registry in Language.Haskell.Extension."
          , "  " ++ intercalate "\n  " (map display unregistered)
          , "If these extensions are ready for public consumption then they "
          , "should be registered. If they are still experimental and you "
          , "think they are not ready to be registered then please add them "
          , "to the exceptions list in this test program along with an "
          , "explanation."
          ]
      , check badExceptions $ unlines
          [ "Error in the extension exception list. The following extensions"
          , "are listed as exceptions but are not even implemented by GHC:"
          , "  " ++ intercalate "\n  " (map display badExceptions)
          , "Please fix this test program by correcting the list of"
          , "exceptions."
          ]
      , check badExceptions' $ unlines
          [ "Error in the extension exception list. The following extensions"
          , "are listed as exceptions to registration but they are in fact"
          , "now registered in Language.Haskell.Extension:"
          , "  " ++ intercalate "\n  " (map display badExceptions')
          , "Please fix this test program by correcting the list of"
          , "exceptions."
          ]
      ]
  where
   registered (UnknownExtension _) = False
   registered _                    = True

   check [] _ = Nothing
   check _  i = Just i


main = topHandler $ do
  [ghcPath] <- getArgs
  exts      <- getExtensions ghcPath
  let problems = checkProblems exts
  putStrLn (intercalate "\n" problems)
  if null problems
    then exitSuccess
    else exitFailure

getExtensions :: FilePath -> IO [Extension]
getExtensions ghcPath =
        map readExtension . lines
    <$> rawSystemStdout normal ghcPath ["--supported-languages"]

readExtension :: String -> Extension
readExtension str = handleNoParse $ do
    -- GHC defines extensions in a positive way, Cabal defines them
    -- relative to H98 so we try parsing ("No" ++ extName) first
    ext <- simpleParse ("No" ++ str)
    case ext of
      UnknownExtension _ -> simpleParse str
      _                  -> return ext
  where
    handleNoParse :: Maybe Extension -> Extension
    handleNoParse = fromMaybe (error $ "unparsable extension " ++ show str)
