git.iko.soy/rowtype-yoga/purescript-native
on golang help sign in
Add tests for warnings & fix some warnings
commit8a6bf0168b838a30a623957f106d7023564819ce authorGary Burgess <gary.burgess@gmail.com> dateTue 17 May 2016, 03:03 (10 years ago) parent4852b5d
29 files changed+324−70
examples/warning/DuplicateExportRef.purs+30 examples/warning/DuplicateImport.purs+10 examples/warning/DuplicateImportRef.purs+18 examples/warning/DuplicateSelectiveImport.purs+10 examples/warning/HidingImport.purs+9 examples/warning/ImplicitImport.purs+9 examples/warning/ImplicitQualifiedImport.purs+11 examples/warning/MissingTypeDeclaration.purs+4 examples/warning/OverlappingInstances.purs+17 examples/warning/OverlappingPattern.purs+15 examples/warning/ScopeShadowing.purs+13 examples/warning/ShadowedTypeVar.purs+5 examples/warning/UnnecessaryFFIModule.js+1 examples/warning/UnnecessaryFFIModule.purs+5 examples/warning/UnusedDctorExplicitImport.purs+8 examples/warning/UnusedDctorImportAll.purs+7 examples/warning/UnusedDctorImportExplicit.purs+7 examples/warning/UnusedExplicitImport.purs+8 examples/warning/UnusedFFIImplementations.js+2 examples/warning/UnusedFFIImplementations.purs+4 examples/warning/UnusedImport.purs+14 examples/warning/UnusedTypeVar.purs+5 examples/warning/WildcardInferredType.purs+23 purescript.cabal+2 src/Language/PureScript/Errors.hs−6 src/Language/PureScript/Linter/Imports.hs+18−32 src/Language/PureScript/Sugar/Names.hs+1−1 src/Language/PureScript/Sugar/Names/Common.hs+3−1 tests/TestCompiler.hs+65−30
examples/warning/DuplicateExportRef.pursnew file+30
@@ -0,0 +1,30 @@
1+-- @shouldWarnWith DuplicateExportRef
2+-- @shouldWarnWith DuplicateExportRef
3+-- @shouldWarnWith DuplicateExportRef
4+-- @shouldWarnWith DuplicateExportRef
5+-- @shouldWarnWith DuplicateExportRef
6+-- @shouldWarnWith DuplicateExportRef
7+-- @shouldWarnWith DuplicateExportRef
8+module Main
9+ ( X(X, X), X
10+ , fn, fn
11+ , (!), (!)
12+ , class Y, class Y
13+ , Natural, type (~>), type (~>)
14+ , module Prelude, module Prelude
15+ ) where
16+
17+import Prelude (Unit)
18+
19+data X = X
20+
21+fn :: X -> X -> X
22+fn _ _ = X
23+
24+infix 2 fn as !
25+
26+class Y a
27+
28+type Natural f g = forall a. f a -> g a
29+
30+infixl 1 type Natural as ~>
examples/warning/DuplicateImport.pursnew file+10
@@ -0,0 +1,10 @@
1+-- @shouldWarnWith DuplicateImport
2+module Main where
3+
4+import Prelude (Unit, unit, pure)
5+import Prelude (Unit, unit, pure)
6+
7+import Control.Monad.Eff (Eff)
8+
9+main :: Eff () Unit
10+main = pure unit
examples/warning/DuplicateImportRef.pursnew file+18
@@ -0,0 +1,18 @@
1+-- @shouldWarnWith DuplicateImportRef
2+-- @shouldWarnWith DuplicateImportRef
3+-- @shouldWarnWith DuplicateImportRef
4+-- @shouldWarnWith DuplicateImportRef
5+module Main where
6+
7+import Prelude
8+ ( Unit, Unit
9+ , unit, unit
10+ , class Functor, class Functor
11+ , (<>), (<>)
12+ )
13+
14+u :: Unit
15+u = unit <> unit
16+
17+fid :: forall f a. Functor f => f a -> f a
18+fid fa = fa
examples/warning/DuplicateSelectiveImport.pursnew file+10
@@ -0,0 +1,10 @@
1+-- @shouldWarnWith DuplicateSelectiveImport
2+module Main where
3+
4+import Prelude (Unit, unit)
5+import Prelude (pure)
6+
7+import Control.Monad.Eff (Eff)
8+
9+main :: Eff () Unit
10+main = pure unit
examples/warning/HidingImport.pursnew file+9
@@ -0,0 +1,9 @@
1+-- @shouldWarnWith HidingImport
2+-- @shouldWarnWith HidingImport
3+module Main where
4+
5+import Prelude hiding (one)
6+import Control.Monad.Eff hiding (runPure)
7+
8+main :: Eff () Unit
9+main = pure unit
examples/warning/ImplicitImport.pursnew file+9
@@ -0,0 +1,9 @@
1+-- @shouldWarnWith ImplicitImport
2+-- @shouldWarnWith ImplicitImport
3+module Main where
4+
5+import Prelude
6+import Control.Monad.Eff
7+
8+main :: Eff () Unit
9+main = pure unit
examples/warning/ImplicitQualifiedImport.pursnew file+11
@@ -0,0 +1,11 @@
1+-- @shouldWarnWith ImplicitQualifiedImport
2+-- @shouldWarnWith ImplicitQualifiedImport
3+module Main where
4+
5+import Data.Unit
6+
7+import Control.Monad.Eff as E
8+import Control.Monad.Eff.Console as E
9+
10+main :: E.Eff (console :: E.CONSOLE) Unit
11+main = E.log "test"
examples/warning/MissingTypeDeclaration.pursnew file+4
@@ -0,0 +1,4 @@
1+-- @shouldWarnWith MissingTypeDeclaration
2+module Main where
3+
4+x = 0
examples/warning/OverlappingInstances.pursnew file+17
@@ -0,0 +1,17 @@
1+-- @shouldWarnWith OverlappingInstances
2+module Main where
3+
4+class Test a where
5+ test :: a -> a
6+
7+instance testRefl :: Test a where
8+ test x = x
9+
10+instance testInt :: Test Int where
11+ test _ = 0
12+
13+-- The OverlappingInstances instances warning only arises when there are two
14+-- choices for a dictionary, not when the instances are defined. So without
15+-- `value` this module would not raise a warning.
16+value :: Int
17+value = test 1
examples/warning/OverlappingPattern.pursnew file+15
@@ -0,0 +1,15 @@
1+-- @shouldWarnWith OverlappingPattern
2+-- @shouldWarnWith OverlappingPattern
3+module Main where
4+
5+data X = A | B
6+
7+pat1 :: X -> Boolean
8+pat1 A = true
9+pat1 A = true
10+pat1 B = false
11+
12+pat2 :: X -> Boolean
13+pat2 A = true
14+pat2 _ = false
15+pat2 B = false
examples/warning/ScopeShadowing.pursnew file+13
@@ -0,0 +1,13 @@
1+-- @shouldWarnWith ScopeShadowing
2+module Main where
3+
4+import Prelude
5+
6+-- No warning at the definition, only when the name is later resolved
7+data Unit = Unit
8+
9+-- This is only a warning as the `Prelude` import is implicit. If `Unit` was
10+-- named explicitly in an import list, then this refernce to `Unit`
11+-- would be a `ScopeConflict` error instead.
12+test :: Unit
13+test = const Unit unit
examples/warning/ShadowedTypeVar.pursnew file+5
@@ -0,0 +1,5 @@
1+-- @shouldWarnWith ShadowedTypeVar
2+module Main where
3+
4+f :: forall a. (forall a. a -> a) -> a -> a
5+f g x = g x
examples/warning/UnnecessaryFFIModule.jsnew file+1
@@ -0,0 +1 @@
1+exports.out = null;
examples/warning/UnnecessaryFFIModule.pursnew file+5
@@ -0,0 +1,5 @@
1+-- @shouldWarnWith UnnecessaryFFIModule
2+module Main where
3+
4+t :: Boolean
5+t = true
examples/warning/UnusedDctorExplicitImport.pursnew file+8
@@ -0,0 +1,8 @@
1+-- @shouldWarnWith UnusedDctorExplicitImport
2+module Main where
3+
4+import Data.Ordering (Ordering(EQ, LT))
5+
6+f :: Ordering -> Ordering
7+f EQ = EQ
8+f x = x
examples/warning/UnusedDctorImportAll.pursnew file+7
@@ -0,0 +1,7 @@
1+-- @shouldWarnWith UnusedDctorImport
2+module Main where
3+
4+import Data.Ordering (Ordering(..))
5+
6+f :: Ordering -> Ordering
7+f x = x
examples/warning/UnusedDctorImportExplicit.pursnew file+7
@@ -0,0 +1,7 @@
1+-- @shouldWarnWith UnusedDctorImport
2+module Main where
3+
4+import Data.Ordering (Ordering(EQ))
5+
6+f :: Ordering -> Ordering
7+f x = x
examples/warning/UnusedExplicitImport.pursnew file+8
@@ -0,0 +1,8 @@
1+-- @shouldWarnWith UnusedExplicitImport
2+module Main where
3+
4+import Prelude (Unit, unit, pure, bind)
5+import Control.Monad.Eff (Eff)
6+
7+main :: Eff () Unit
8+main = pure unit
examples/warning/UnusedFFIImplementations.jsnew file+2
@@ -0,0 +1,2 @@
1+exports.yes = true;
2+exports.no = false;
examples/warning/UnusedFFIImplementations.pursnew file+4
@@ -0,0 +1,4 @@
1+-- @shouldWarnWith UnusedFFIImplementations
2+module Main where
3+
4+foreign import yes :: Boolean
examples/warning/UnusedImport.pursnew file+14
@@ -0,0 +1,14 @@
1+-- @shouldWarnWith UnusedImport
2+-- @shouldWarnWith UnusedImport
3+-- @shouldWarnWith UnusedImport
4+module Main where
5+
6+import Data.Unit (Unit, unit)
7+
8+-- All of the below are unused
9+import Control.Monad.Eff
10+import Control.Monad.Eff.Console as Console
11+import Test.Assert ()
12+
13+main :: Unit
14+main = unit
examples/warning/UnusedTypeVar.pursnew file+5
@@ -0,0 +1,5 @@
1+-- @shouldWarnWith UnusedTypeVar
2+module Main where
3+
4+f :: forall a b. a -> a
5+f x = x
examples/warning/WildcardInferredType.pursnew file+23
@@ -0,0 +1,23 @@
1+-- @shouldWarnWith WildcardInferredType
2+-- @shouldWarnWith WildcardInferredType
3+-- @shouldWarnWith WildcardInferredType
4+-- @shouldWarnWith WildcardInferredType
5+module Main where
6+
7+x :: Int
8+x = 0 :: _
9+
10+y :: _
11+y = 0
12+
13+z :: Int
14+z =
15+ let n :: _
16+ n = 0
17+ in n
18+
19+w :: Int
20+w = n
21+ where
22+ n :: _
23+ n = 0
purescript.cabal+2
@@ -66,6 +66,8 @@ extra-source-files: examples/passing/*.purs
6666 , examples/failing/InstanceExport/*.purs
6767 , examples/failing/OrphanInstance/*.purs
6868 , examples/failing/OverlappingReExport/*.purs
69+ , examples/warning/*.purs
70+ , examples/warning/*.js
6971 , examples/docs/bower_components/purescript-prelude/src/*.purs
7072 , examples/docs/bower.json
7173 , examples/docs/src/*.purs
src/Language/PureScript/Errors.hs−6
@@ -127,7 +127,6 @@ data SimpleErrorMessage
127127 | DuplicateImportRef Name
128128 | DuplicateExportRef Name
129129 | IntOutOfRange Integer String Integer Integer
130− | RedundantEmptyHidingImport ModuleName
131130 | ImplicitQualifiedImport ModuleName ModuleName [DeclarationRef]
132131 | ImplicitImport ModuleName [DeclarationRef]
133132 | HidingImport ModuleName [DeclarationRef]
@@ -297,7 +296,6 @@ errorCode em = case unwrapErrorMessage em of
297296 DuplicateImportRef{} -> "DuplicateImportRef"
298297 DuplicateExportRef{} -> "DuplicateExportRef"
299298 IntOutOfRange{} -> "IntOutOfRange"
300− RedundantEmptyHidingImport{} -> "RedundantEmptyHidingImport"
301299 ImplicitQualifiedImport{} -> "ImplicitQualifiedImport"
302300 ImplicitImport{} -> "ImplicitImport"
303301 HidingImport{} -> "HidingImport"
@@ -418,7 +416,6 @@ wikiUri e = "https://github.com/purescript/purescript/wiki/Error-Code-" ++ error
418416errorSuggestion :: SimpleErrorMessage -> Maybe ErrorSuggestion
419417errorSuggestion err = case err of
420418 UnusedImport{} -> emptySuggestion
421− RedundantEmptyHidingImport{} -> emptySuggestion
422419 DuplicateImport{} -> emptySuggestion
423420 UnusedExplicitImport mn _ qual refs -> suggest $ importSuggestion mn refs qual
424421 ImplicitImport mn refs -> suggest $ importSuggestion mn refs Nothing
@@ -868,9 +865,6 @@ prettyPrintSingleError full level showWiki e = flip evalState defaultUnknownMap
868865 paras [ line $ "Integer value " ++ show value ++ " is out of range for the " ++ backend ++ " backend."
869866 , line $ "Acceptable values fall within the range " ++ show lo ++ " to " ++ show hi ++ " (inclusive)." ]
870867
871− renderSimpleErrorMessage (RedundantEmptyHidingImport mn) =
872− line $ "The import for module " ++ runModuleName mn ++ " is redundant as all members have been explicitly hidden."
873−
874868 renderSimpleErrorMessage msg@(ImplicitQualifiedImport importedModule asModule _) =
875869 paras [ line $ "Module " ++ runModuleName importedModule ++ " was imported as " ++ runModuleName asModule ++ " with unspecified imports."
876870 , line $ "As there are multiple modules being imported as " ++ runModuleName asModule ++ ", consider using the explicit form:"
src/Language/PureScript/Linter/Imports.hs+18−32
@@ -6,7 +6,7 @@ module Language.PureScript.Linter.Imports
66
77import Prelude.Compat
88
9−import Control.Monad (join, unless, when, foldM, (<=<))
9+import Control.Monad (join, unless, foldM, (<=<))
1010import Control.Monad.Writer.Class
1111
1212import Data.Function (on)
@@ -54,7 +54,8 @@ lintImports
5454 -> Env
5555 -> UsedImports
5656 -> m ()
57−lintImports (Module _ _ _ _ Nothing) _ _ = return ()
57+lintImports (Module _ _ _ _ Nothing) _ _ =
58+ internalError "lintImports needs desugared exports"
5859lintImports (Module ss _ mn mdecls (Just mexports)) env usedImps = do
5960
6061 -- TODO: this needs some work to be easier to understand
@@ -100,13 +101,11 @@ lintImports (Module ss _ mn mdecls (Just mexports)) env usedImps = do
100101 maybe id warnWithPosition pos $
101102 tell $ errorMessage $ DuplicateSelectiveImport mnq
102103
103− for_ (imps \\ (warned ++ duplicates)) $ \(pos, typ, _) -> do
104+ for_ (imps \\ (warned ++ duplicates)) $ \(pos, typ, _) ->
104105 warnDuplicateRefs (fromMaybe ss pos) DuplicateImportRef $ case typ of
105106 Explicit refs -> refs
106107 Hiding refs -> refs
107108 _ -> []
108− for_ (M.lookup mn env) $ \(_, imported, _) ->
109− checkEmptyImport mnq imported typ
110109
111110 where
112111
@@ -120,9 +119,9 @@ lintImports (Module ss _ mn mdecls (Just mexports)) env usedImps = do
120119 countOpenImports :: Declaration -> Int
121120 countOpenImports (PositionedDeclaration _ _ d) = countOpenImports d
122121 countOpenImports (ImportDeclaration mn' Implicit Nothing)
123− | not (isPrim mn') = 1
122+ | not (isPrim mn' || mn == mn') = 1
124123 countOpenImports (ImportDeclaration mn' (Hiding _) Nothing)
125− | not (isPrim mn') = 1
124+ | not (isPrim mn' || mn == mn') = 1
126125 countOpenImports _ = 0
127126
128127 -- Checks whether a module is the Prim module - used to suppress any checks
@@ -199,7 +198,10 @@ lintImportDecl
199198lintImportDecl env mni qualifierName names declType allowImplicit =
200199 case declType of
201200 Implicit -> case qualifierName of
202− Nothing -> unless' allowImplicit (checkImplicit ImplicitImport)
201+ Nothing ->
202+ if null allRefs
203+ then unused
204+ else unless' allowImplicit (checkImplicit ImplicitImport)
203205 Just q -> unless' (q `elem` mapMaybe getQual names) unused
204206 Hiding _ -> unless' allowImplicit (checkImplicit HidingImport)
205207 Explicit [] -> unused
@@ -248,9 +250,15 @@ lintImportDecl env mni qualifierName names declType allowImplicit =
248250 warn :: SimpleErrorMessage -> m Bool
249251 warn err = tell (errorMessage err) >> return True
250252
253+ -- Unless the boolean is true, run the action. Return false when the action is
254+ -- not run, otherwise return whatever the action does.
255+ --
256+ -- The return value is intended for cases where we want to track whether some
257+ -- work was done, as there may be further conditions in the action that mean
258+ -- it ends up doing nothing.
251259 unless' :: Bool -> m Bool -> m Bool
252− unless' True m = m
253− unless' False _ = return False
260+ unless' False m = m
261+ unless' True _ = return False
254262
255263 allRefs :: [DeclarationRef]
256264 allRefs = findUsedRefs env mni qualifierName names
@@ -344,25 +352,3 @@ checkDuplicateImports mn xs ((_, t1, q1), (pos, t2, q2)) =
344352 tell $ errorMessage $ DuplicateImport mn t2 q2
345353 return $ (pos, t2, q2) : xs
346354 else return xs
347−
348−-- |
349−-- Checks that an import with a hiding reference is not hiding all possible
350−-- imports.
351−--
352−checkEmptyImport
353− :: MonadWriter MultipleErrors m
354− => ModuleName
355− -> Imports
356− -> ImportDeclarationType
357− -> m ()
358−checkEmptyImport importModule imps (Hiding _) = do
359− let isEmptyImport
360− = M.null (importedTypes imps)
361− && M.null (importedTypeOps imps)
362− && M.null (importedDataConstructors imps)
363− && M.null (importedTypeClasses imps)
364− && M.null (importedValues imps)
365− && M.null (importedValueOps imps)
366− when isEmptyImport . tell . errorMessage $
367− RedundantEmptyHidingImport importModule
368−checkEmptyImport _ _ _ = return ()
src/Language/PureScript/Sugar/Names.hs+1−1
@@ -111,7 +111,7 @@ desugarImportsWithEnv externs modules = do
111111 warnAndRethrow (addHint (ErrorInModule mn)) $ do
112112 let (_, imps, exps) = fromMaybe (internalError "Module is missing in renameInModule'") $ M.lookup mn env
113113 (m', used) <- flip runStateT M.empty $ renameInModule env imps (elaborateExports exps m)
114− lintImports m env used
114+ lintImports m' env used
115115 return m'
116116
117117-- |
src/Language/PureScript/Sugar/Names/Common.hs+3−1
@@ -43,15 +43,17 @@ warnDuplicateRefs pos toError refs = do
4343 -- referenced type is used in the duplicate check - constructors are handled
4444 -- separately
4545 deleteCtors :: DeclarationRef -> DeclarationRef
46+ deleteCtors (PositionedDeclarationRef ss com ref) =
47+ PositionedDeclarationRef ss com (deleteCtors ref)
4648 deleteCtors (TypeRef pn _) = TypeRef pn Nothing
4749 deleteCtors other = other
4850
4951 -- Extracts the names of duplicate constructor references from TypeRefs.
5052 extractCtors :: SourceSpan -> DeclarationRef -> Maybe [(SourceSpan, Name)]
53+ extractCtors _ (PositionedDeclarationRef pos' _ ref) = extractCtors pos' ref
5154 extractCtors pos' (TypeRef _ (Just dctors)) =
5255 let dupes = dctors \\ nub dctors
5356 in if null dupes then Nothing else Just $ ((pos',) . DctorName) <$> dupes
54− extractCtors _ (PositionedDeclarationRef pos' _ ref) = extractCtors pos' ref
5557 extractCtors _ _ = Nothing
5658
5759 -- Converts a DeclarationRef into a name for an error message.
tests/TestCompiler.hs+65−30
@@ -27,7 +27,7 @@ import qualified Language.PureScript as P
2727
2828import Data.Char (isSpace)
2929import Data.Function (on)
30−import Data.List (sort, stripPrefix, intercalate, groupBy, sortBy)
30+import Data.List (sort, stripPrefix, intercalate, groupBy, sortBy, minimumBy)
3131import Data.Maybe (mapMaybe)
3232import Data.Time.Clock (UTCTime())
3333import Data.Tuple (swap)
@@ -58,31 +58,40 @@ main = hspec spec
5858spec :: Spec
5959spec = do
6060
61− (supportExterns, passingTestCases, failingTestCases) <- runIO $ do
61+ (supportExterns, passingTestCases, warningTestCases, failingTestCases) <- runIO $ do
6262 cwd <- getCurrentDirectory
6363 let passing = cwd </> "examples" </> "passing"
64+ let warning = cwd </> "examples" </> "warning"
6465 let failing = cwd </> "examples" </> "failing"
6566 let supportDir = cwd </> "tests" </> "support" </> "bower_components"
6667 let supportFiles ext = Glob.globDir1 (Glob.compile ("purescript-*/**/*." ++ ext)) supportDir
6768 passingFiles <- getTestFiles passing <$> testGlob passing
69+ warningFiles <- getTestFiles warning <$> testGlob warning
6870 failingFiles <- getTestFiles failing <$> testGlob failing
6971 supportPurs <- supportFiles "purs"
7072 supportPursFiles <- readInput supportPurs
7173 supportExterns <- runExceptT $ do
7274 modules <- ExceptT . return $ P.parseModulesFromFiles id supportPursFiles
7375 foreigns <- inferForeignModules modules
74− externs <- ExceptT . runTest $ P.make (makeActions foreigns) (map snd modules)
76+ externs <- ExceptT . fmap fst . runTest $ P.make (makeActions foreigns) (map snd modules)
7577 return (zip (map snd modules) externs)
7678 case supportExterns of
7779 Left errs -> fail (P.prettyPrintMultipleErrors False errs)
78− Right externs -> return (externs, passingFiles, failingFiles)
80+ Right externs -> return (externs, passingFiles, warningFiles, failingFiles)
7981
80− context ("Passing examples") $ do
82+ context "Passing examples" $
8183 forM_ passingTestCases $ \testPurs ->
8284 it ("'" <> takeFileName (getTestMain testPurs) <> "' should compile and run without error") $
8385 assertCompiles supportExterns testPurs
8486
85− context ("Failing examples") $ do
87+ context "Warning examples" $
88+ forM_ warningTestCases $ \testPurs -> do
89+ let mainPath = getTestMain testPurs
90+ expectedWarnings <- runIO $ getShouldWarnWith mainPath
91+ it ("'" <> takeFileName mainPath <> "' should compile with warning(s) '" <> intercalate "', '" expectedWarnings <> "'") $
92+ assertCompilesWithWarnings supportExterns testPurs expectedWarnings
93+
94+ context "Failing examples" $
8695 forM_ failingTestCases $ \testPurs -> do
8796 let mainPath = getTestMain testPurs
8897 expectedFailures <- runIO $ getShouldFailWith mainPath
@@ -100,8 +109,7 @@ spec = do
100109 -- .purs files and the .js files for the test case.
101110 getTestFiles :: FilePath -> [FilePath] -> [[FilePath]]
102111 getTestFiles baseDir
103− = map (filter ((== ".purs") . takeExtensions))
104− . map (map (baseDir </>))
112+ = map (filter ((== ".purs") . takeExtensions) . map (baseDir </>))
105113 . groupBy ((==) `on` extractPrefix)
106114 . sortBy (compare `on` extractPrefix)
107115 . map (makeRelative baseDir)
@@ -110,7 +118,7 @@ spec = do
110118 -- by the file with the shortest path name, as everything but the main file
111119 -- will be under a subdirectory.
112120 getTestMain :: [FilePath] -> FilePath
113− getTestMain = head . sortBy (compare `on` length)
121+ getTestMain = minimumBy (compare `on` length)
114122
115123 -- Extracts the filename part of a .purs file, or if the file is in a
116124 -- subdirectory, the first part of that directory path.
@@ -125,9 +133,17 @@ spec = do
125133 -- Scans a file for @shouldFailWith directives in the comments, used to
126134 -- determine expected failures
127135 getShouldFailWith :: FilePath -> IO [String]
128− getShouldFailWith = fmap extractFailWiths . readUTF8File
136+ getShouldFailWith = extractPragma "shouldFailWith"
137+
138+ -- Scans a file for @shouldWarnWith directives in the comments, used to
139+ -- determine expected warnings
140+ getShouldWarnWith :: FilePath -> IO [String]
141+ getShouldWarnWith = extractPragma "shouldWarnWith"
142+
143+ extractPragma :: String -> FilePath -> IO [String]
144+ extractPragma pragma = fmap go . readUTF8File
129145 where
130− extractFailWiths = lines >>> mapMaybe (stripPrefix "-- @shouldFailWith ") >>> map trim
146+ go = lines >>> mapMaybe (stripPrefix ("-- @" ++ pragma ++ " ")) >>> map trim
131147
132148inferForeignModules
133149 :: MonadIO m
@@ -168,16 +184,14 @@ readInput inputFiles = forM inputFiles $ \inputFile -> do
168184 text <- readUTF8File inputFile
169185 return (inputFile, text)
170186
171−type TestM = WriterT [(FilePath, String)] IO
172−
173−runTest :: P.Make a -> IO (Either P.MultipleErrors a)
174−runTest = fmap fst . P.runMake P.defaultOptions
187+runTest :: P.Make a -> IO (Either P.MultipleErrors a, P.MultipleErrors)
188+runTest = P.runMake P.defaultOptions
175189
176190compile
177191 :: [(P.Module, P.ExternsFile)]
178192 -> [FilePath]
179193 -> ([P.Module] -> IO ())
180− -> IO (Either P.MultipleErrors [P.ExternsFile])
194+ -> IO (Either P.MultipleErrors [P.ExternsFile], P.MultipleErrors)
181195compile supportExterns inputFiles check = silence $ runTest $ do
182196 fs <- liftIO $ readInput inputFiles
183197 ms <- P.parseModulesFromFiles id fs
@@ -192,18 +206,30 @@ assert
192206 :: [(P.Module, P.ExternsFile)]
193207 -> [FilePath]
194208 -> ([P.Module] -> IO ())
195− -> (Either P.MultipleErrors [P.ExternsFile] -> IO (Maybe String))
209+ -> (Either P.MultipleErrors P.MultipleErrors -> IO (Maybe String))
196210 -> Expectation
197211assert supportExterns inputFiles check f = do
198− e <- compile supportExterns inputFiles check
199− maybeErr <- f e
212+ (e, w) <- compile supportExterns inputFiles check
213+ maybeErr <- f (const w <$> e)
200214 maybe (return ()) expectationFailure maybeErr
201215
216+checkMain :: [P.Module] -> IO ()
217+checkMain ms =
218+ unless (any ((== P.moduleNameFromString "Main") . P.getModuleName) ms)
219+ (fail "Main module missing")
220+
221+checkShouldFailWith :: [String] -> P.MultipleErrors -> Maybe String
222+checkShouldFailWith expected errs =
223+ let actual = map P.errorCode $ P.runMultipleErrors errs
224+ in if sort expected == sort actual
225+ then Nothing
226+ else Just $ "Expected these errors: " ++ show expected ++ ", but got these: " ++ show actual
227+
202228assertCompiles
203229 :: [(P.Module, P.ExternsFile)]
204230 -> [FilePath]
205231 -> Expectation
206−assertCompiles supportExterns inputFiles = do
232+assertCompiles supportExterns inputFiles =
207233 assert supportExterns inputFiles checkMain $ \e ->
208234 case e of
209235 Left errs -> return . Just . P.prettyPrintMultipleErrors False $ errs
@@ -219,17 +245,32 @@ assertCompiles supportExterns inputFiles = do
219245 | otherwise -> return $ Just $ "Test did not finish with 'Done':\n\n" <> out
220246 Just (ExitFailure _, _, err) -> return $ Just err
221247 Nothing -> return $ Just "Couldn't find node.js executable"
248+
249+assertCompilesWithWarnings
250+ :: [(P.Module, P.ExternsFile)]
251+ -> [FilePath]
252+ -> [String]
253+ -> Expectation
254+assertCompilesWithWarnings supportExterns inputFiles shouldWarnWith =
255+ assert supportExterns inputFiles checkMain $ \e ->
256+ case e of
257+ Left errs ->
258+ return . Just . P.prettyPrintMultipleErrors False $ errs
259+ Right warnings ->
260+ return
261+ . fmap (printAllWarnings warnings)
262+ $ checkShouldFailWith shouldWarnWith warnings
263+
222264 where
223− checkMain ms =
224− unless (any ((== P.moduleNameFromString "Main") . P.getModuleName) ms)
225− (fail "Main module missing")
265+ printAllWarnings warnings =
266+ (<> "\n\n" <> P.prettyPrintMultipleErrors False warnings)
226267
227268assertDoesNotCompile
228269 :: [(P.Module, P.ExternsFile)]
229270 -> [FilePath]
230271 -> [String]
231272 -> Expectation
232−assertDoesNotCompile supportExterns inputFiles shouldFailWith = do
273+assertDoesNotCompile supportExterns inputFiles shouldFailWith =
233274 assert supportExterns inputFiles noPreCheck $ \e ->
234275 case e of
235276 Left errs ->
@@ -243,9 +284,3 @@ assertDoesNotCompile supportExterns inputFiles shouldFailWith = do
243284
244285 where
245286 noPreCheck = const (return ())
246−
247− checkShouldFailWith expected errs =
248− let actual = map P.errorCode $ P.runMultipleErrors errs
249− in if sort expected == sort actual
250− then Nothing
251− else Just $ "Expected these errors: " ++ show expected ++ ", but got these: " ++ show actual