git.iko.soy/rowtype-yoga/purescript-native
on golang help sign in
Infer types in psc-{docs,publish}

Fixes #1834 Instead of taking types from the partially-desugared AST, we now insert wildcard types for value declarations without explicit type signatures in the first pass, and then we typecheck if necessary in order to insert the real types for all value declarations later on, using the Environment obtained during typechecking. This involved: * Changing the module name field in the Docs.Types.Module type to a real P.ModuleName; to be frank, it really ought to have been that type to begin with. * Refactoring ParseAndDesugar so that it no longer does any desugaring. With this new pipeline, it is easier to pass modules that are not desugared at all into Docs.Convert. Also renamed ParseAndDesugar appropriately. * Re-reversing the modules in Sugar.Names; desugarImports was previously (unintentionally) reversing the list of modules. This new code depends on those modules remaining in a topologically sorted order, which is why this change was necessary.

commit7d936d3b64257fe4ad203f2514ab8192eae5b142 authorHarry Garrood <harry@garrood.me> dateThu 4 Feb 2016, 14:55 (11 years ago) parent2380c68
12 files changed+247−115
examples/docs/src/ExplicitTypeSignatures.purs+16 psc-docs/Main.hs+6−6 purescript.cabal+1−1 src/Language/PureScript/Docs.hs+1−1 src/Language/PureScript/Docs/AsMarkdown.hs+7−9 src/Language/PureScript/Docs/Convert.hs+139−30 src/Language/PureScript/Docs/Convert/Single.hs+7−3 src/Language/PureScript/Docs/ParseAndDesugar.hs → src/Language/PureScript/Docs/ParseAndBookmark.hs+16−53 src/Language/PureScript/Docs/Types.hs+3−3 src/Language/PureScript/Publish.hs+4−4 src/Language/PureScript/Sugar/Names.hs+2−1 tests/TestDocs.hs+45−4
examples/docs/src/ExplicitTypeSignatures.pursnew file+16
@@ -0,0 +1,16 @@
1+
2+module ExplicitTypeSignatures where
3+
4+-- This should use the explicit type signature so that the type variable name
5+-- is preserved.
6+explicit :: forall something. something -> something
7+explicit x
8+ | true = x
9+ | false = x
10+
11+-- This should use the inferred type.
12+anInt :: _
13+anInt = 0
14+
15+-- This should infer a type.
16+aNumber = 1.0
psc-docs/Main.hs+6−6
@@ -69,19 +69,19 @@ docgen (PSCDocsOptions fmt inputGlob output) = do
6969 Etags -> dumpTags input dumpEtags
7070 Ctags -> dumpTags input dumpCtags
7171 Markdown -> do
72− ms <- runExceptT (D.parseAndDesugar input []
73− >>= ((\(ms, _, env) -> D.convertModulesInPackage env ms)))
72+ ms <- runExceptT (D.parseAndBookmark input []
73+ >>= (fst >>> D.convertModulesInPackage))
7474 >>= successOrExit
7575
7676 case output of
7777 EverythingToStdOut ->
7878 putStrLn (D.runDocs (D.modulesAsMarkdown ms))
7979 ToStdOut names -> do
80− let (ms', missing) = takeByName ms (map P.runModuleName names)
80+ let (ms', missing) = takeByName ms names
8181 guardMissing missing
8282 putStrLn (D.runDocs (D.modulesAsMarkdown ms'))
8383 ToFiles names -> do
84− let (ms', missing) = takeByName' ms (map (first P.runModuleName) names)
84+ let (ms', missing) = takeByName' ms names
8585 guardMissing missing
8686
8787 let ms'' = groupBy ((==) `on` fst) . sortBy (compare `on` fst) $ map swap ms'
@@ -93,12 +93,12 @@ docgen (PSCDocsOptions fmt inputGlob output) = do
9393 where
9494 guardMissing [] = return ()
9595 guardMissing [mn] = do
96− hPutStrLn stderr ("psc-docs: error: unknown module \"" ++ mn ++ "\"")
96+ hPutStrLn stderr ("psc-docs: error: unknown module \"" ++ P.runModuleName mn ++ "\"")
9797 exitFailure
9898 guardMissing mns = do
9999 hPutStrLn stderr "psc-docs: error: unknown modules:"
100100 forM_ mns $ \mn ->
101− hPutStrLn stderr (" * " ++ mn)
101+ hPutStrLn stderr (" * " ++ P.runModuleName mn)
102102 exitFailure
103103
104104 successOrExit :: Either P.MultipleErrors a -> IO a
purescript.cabal+1−1
@@ -170,7 +170,7 @@ library
170170 Language.PureScript.Docs.RenderedCode.Types
171171 Language.PureScript.Docs.RenderedCode.Render
172172 Language.PureScript.Docs.AsMarkdown
173− Language.PureScript.Docs.ParseAndDesugar
173+ Language.PureScript.Docs.ParseAndBookmark
174174 Language.PureScript.Docs.Utils.MonoidExtras
175175
176176 Language.PureScript.Publish
src/Language/PureScript/Docs.hs+1−1
@@ -11,4 +11,4 @@ import Language.PureScript.Docs.RenderedCode.Types as Docs
1111import Language.PureScript.Docs.RenderedCode.Render as Docs
1212import Language.PureScript.Docs.Convert as Docs
1313import Language.PureScript.Docs.Render as Docs
14−import Language.PureScript.Docs.ParseAndDesugar as Docs
14+import Language.PureScript.Docs.ParseAndBookmark as Docs
src/Language/PureScript/Docs/AsMarkdown.hs+7−9
@@ -31,31 +31,29 @@ import qualified Language.PureScript.Docs.Render as Render
3131renderModulesAsMarkdown ::
3232 (Functor m, Applicative m,
3333 MonadError P.MultipleErrors m) =>
34− P.Env ->
3534 [P.Module] ->
3635 m String
37−renderModulesAsMarkdown env =
38− fmap (runDocs . modulesAsMarkdown) . Convert.convertModules env
36+renderModulesAsMarkdown =
37+ fmap (runDocs . modulesAsMarkdown) . Convert.convertModules
3938
4039modulesAsMarkdown :: [Module] -> Docs
4140modulesAsMarkdown = mapM_ moduleAsMarkdown
4241
4342moduleAsMarkdown :: Module -> Docs
4443moduleAsMarkdown Module{..} = do
45− headerLevel 2 $ "Module " ++ modName
44+ headerLevel 2 $ "Module " ++ P.runModuleName modName
4645 spacer
4746 for_ modComments tell'
4847 mapM_ (declAsMarkdown modName) modDeclarations
4948 spacer
5049 for_ modReExports $ \(mn, decls) -> do
51− let modName' = P.runModuleName mn
52− headerLevel 3 $ "Re-exported from " ++ modName' ++ ":"
50+ headerLevel 3 $ "Re-exported from " ++ P.runModuleName mn ++ ":"
5351 spacer
54− mapM_ (declAsMarkdown modName') decls
52+ mapM_ (declAsMarkdown mn) decls
5553
56−declAsMarkdown :: String -> Declaration -> Docs
54+declAsMarkdown :: P.ModuleName -> Declaration -> Docs
5755declAsMarkdown mn decl@Declaration{..} = do
58− let options = defaultRenderTypeOptions { currentModule = Just (P.moduleNameFromString mn) }
56+ let options = defaultRenderTypeOptions { currentModule = Just mn }
5957 headerLevel 4 (ticks declTitle)
6058 spacer
6159
src/Language/PureScript/Docs/Convert.hs+139−30
@@ -16,12 +16,17 @@ module Language.PureScript.Docs.Convert
1616import Prelude ()
1717import Prelude.Compat
1818
19−import Control.Monad.Error.Class (MonadError)
20−import Control.Arrow ((&&&))
19+import Control.Arrow ((&&&), second)
2120import Control.Category ((>>>))
21+import Control.Monad
22+import Control.Monad.State (runStateT)
23+import Control.Monad.Writer.Strict (runWriterT)
24+import Control.Monad.Error.Class (MonadError)
2225import qualified Data.Map as Map
2326
27+import Text.Parsec (eof)
2428import qualified Language.PureScript as P
29+import qualified Language.PureScript.Constants as C
2530
2631import Language.PureScript.Docs.Types
2732import Language.PureScript.Docs.Convert.Single (convertSingleModule, collectBookmarks)
@@ -33,18 +38,17 @@ import Language.PureScript.Docs.Convert.ReExports (updateReExports)
3338-- documentation.
3439--
3540convertModulesInPackage ::
36− (Functor m, MonadError P.MultipleErrors m) =>
37− P.Env ->
41+ (Functor m, Applicative m, MonadError P.MultipleErrors m) =>
3842 [InPackage P.Module] ->
3943 m [Module]
40−convertModulesInPackage env modules =
44+convertModulesInPackage modules =
4145 go modules
4246 where
4347 localNames =
44− map (P.runModuleName . P.getModuleName) (takeLocals modules)
48+ map P.getModuleName (takeLocals modules)
4549 go =
4650 map ignorePackage
47− >>> convertModules env
51+ >>> convertModules
4852 >>> fmap (filter ((`elem` localNames) . modName))
4953
5054-- |
@@ -53,35 +57,140 @@ convertModulesInPackage env modules =
5357-- imports/exports information about the list of modules, which is needed for
5458-- documenting re-exports.
5559--
56−-- Preconditions:
60+-- Note that the whole module dependency graph must be included in the list; if
61+-- some modules import things from other modules, then those modules must also
62+-- be included.
5763--
58−-- * If any module in the list re-exports documentation from other
59−-- modules, those modules must also be included in the list.
60−-- * The modules passed must have had names desugared and re-exports
61−-- elaborated first.
62−--
63−-- If either of these are not satisfied, an internal error will be thrown. To
64−-- avoid this, it is recommended to use
65−-- Language.PureScript.Docs.ParseAndDesugar to construct the inputs to this
66−-- function.
64+-- For value declarations, if explicit type signatures are omitted, or a
65+-- wildcard type is used, then we typecheck the modules and use the inferred
66+-- types.
6767--
6868convertModules ::
69− (Functor m, MonadError P.MultipleErrors m) =>
70− P.Env ->
69+ (Functor m, Applicative m, MonadError P.MultipleErrors m) =>
7170 [P.Module] ->
7271 m [Module]
73−convertModules env =
74− P.sortModules >>> fmap (convertSorted env . fst)
72+convertModules =
73+ P.sortModules
74+ >>> fmap (fst >>> map importPrim)
75+ >=> convertSorted
76+
77+importPrim :: P.Module -> P.Module
78+importPrim = P.addDefaultImport (P.ModuleName [P.ProperName C.prim])
7579
7680-- |
7781-- Convert a sorted list of modules.
7882--
79−convertSorted :: P.Env -> [P.Module] -> [Module]
80−convertSorted env modules =
81− let
82− traversalOrder =
83− map P.getModuleName modules
84− moduleMap =
85− Map.fromList $ map (P.getModuleName &&& convertSingleModule) modules
86− in
87− Map.elems (updateReExports env traversalOrder moduleMap)
83+convertSorted ::
84+ (Functor m, Applicative m, MonadError P.MultipleErrors m) =>
85+ [P.Module] ->
86+ m [Module]
87+convertSorted modules = do
88+ (env, convertedModules) <- second (map convertSingleModule) <$> partiallyDesugar modules
89+
90+ modulesWithTypes <- typeCheckIfNecessary modules convertedModules
91+ let moduleMap = Map.fromList (map (modName &&& id) modulesWithTypes)
92+
93+ let traversalOrder = map P.getModuleName modules
94+ pure (Map.elems (updateReExports env traversalOrder moduleMap))
95+
96+-- |
97+-- If any exported value declarations have either wildcard type signatures, or
98+-- none at all, then typecheck in order to fill them in with the inferred
99+-- types.
100+--
101+typeCheckIfNecessary ::
102+ (Functor m, Applicative m, MonadError P.MultipleErrors m) =>
103+ [P.Module] ->
104+ [Module] ->
105+ m [Module]
106+typeCheckIfNecessary modules convertedModules =
107+ if any hasWildcards convertedModules
108+ then go
109+ else pure convertedModules
110+
111+ where
112+ hasWildcards =
113+ any ((==) (ValueDeclaration P.TypeWildcard) . declInfo) . modDeclarations
114+
115+ go = do
116+ checkEnv <- snd <$> typeCheck modules
117+ pure (map (insertValueTypes checkEnv) convertedModules)
118+
119+-- |
120+-- Typechecks all the modules together. Also returns the final 'P.Environment',
121+-- which is useful for adding in inferred types where explicit declarations
122+-- were not provided.
123+--
124+typeCheck ::
125+ (Functor m, MonadError P.MultipleErrors m) =>
126+ [P.Module] ->
127+ m ([P.Module], P.Environment)
128+typeCheck =
129+ (P.desugar [] >=> check)
130+ >>> fmap (second P.checkEnv)
131+ >>> P.evalSupplyT 0
132+ >>> ignoreWarnings
133+
134+ where
135+ check ms =
136+ runStateT
137+ (traverse P.typeCheckModule ms)
138+ (P.emptyCheckState P.initEnvironment)
139+
140+ ignoreWarnings =
141+ fmap fst . runWriterT
142+
143+-- |
144+-- Updates all the types of the ValueDeclarations inside the module based on
145+-- their types inside the given Environment.
146+--
147+insertValueTypes ::
148+ P.Environment -> Module -> Module
149+insertValueTypes env m =
150+ m { modDeclarations = map go (modDeclarations m) }
151+ where
152+ go (d@Declaration { declInfo = ValueDeclaration P.TypeWildcard }) =
153+ let
154+ ident = parseIdent (declTitle d)
155+ ty = lookupName ident
156+ in
157+ d { declInfo = ValueDeclaration ty }
158+ go other =
159+ other
160+
161+ parseIdent =
162+ either (err . ("failed to parse Ident: " ++)) id . runParser P.parseIdent
163+
164+ lookupName name =
165+ let key = (modName m, name)
166+ in case Map.lookup key (P.names env) of
167+ Just (ty, _, _) ->
168+ ty
169+ Nothing ->
170+ err ("name not found: " ++ show key)
171+
172+ err msg =
173+ P.internalError ("Docs.Convert.insertValueTypes: " ++ msg)
174+
175+runParser :: P.TokenParser a -> String -> Either String a
176+runParser p s = either (Left . show) Right $ do
177+ ts <- P.lex "" s
178+ P.runTokenParser "" (p <* eof) ts
179+
180+-- |
181+-- Partially desugar modules so that they are suitable for extracting
182+-- documentation information from.
183+--
184+partiallyDesugar ::
185+ (Functor m, Applicative m, MonadError P.MultipleErrors m) =>
186+ [P.Module]
187+ -> m (P.Env, [P.Module])
188+partiallyDesugar = P.evalSupplyT 0 . desugar'
189+ where
190+ desugar' =
191+ traverse P.desugarDoModule
192+ >=> P.desugarCasesModule
193+ >=> P.desugarTypeDeclarationsModule
194+ >=> ignoreWarnings . P.desugarImportsWithEnv []
195+
196+ ignoreWarnings = fmap fst . runWriterT
src/Language/PureScript/Docs/Convert/Single.hs+7−3
@@ -29,7 +29,7 @@ import Language.PureScript.Docs.Types
2929--
3030convertSingleModule :: P.Module -> Module
3131convertSingleModule m@(P.Module _ coms moduleName _ _) =
32− Module (P.runModuleName moduleName) comments (declarations m) []
32+ Module moduleName comments (declarations m) []
3333 where
3434 comments = convertComments coms
3535 declarations =
@@ -109,7 +109,7 @@ addDefaultFixity decl@Declaration{..}
109109 defaultFixity = P.Fixity P.Infixl (-1)
110110
111111getDeclarationTitle :: P.Declaration -> Maybe String
112−getDeclarationTitle (P.TypeDeclaration name _) = Just (P.showIdent name)
112+getDeclarationTitle (P.ValueDeclaration name _ _ _) = Just (P.showIdent name)
113113getDeclarationTitle (P.ExternDeclaration name _) = Just (P.showIdent name)
114114getDeclarationTitle (P.DataDeclaration _ name _ _) = Just (P.runProperName name)
115115getDeclarationTitle (P.ExternDataDeclaration name _) = Just (P.runProperName name)
@@ -135,8 +135,12 @@ basicDeclaration :: String -> DeclarationInfo -> Maybe IntermediateDeclaration
135135basicDeclaration title info = Just $ Right $ mkDeclaration title info
136136
137137convertDeclaration :: P.Declaration -> String -> Maybe IntermediateDeclaration
138−convertDeclaration (P.TypeDeclaration _ ty) title =
138+convertDeclaration (P.ValueDeclaration _ _ _ (Right (P.TypedValue _ _ ty))) title =
139139 basicDeclaration title (ValueDeclaration ty)
140+convertDeclaration (P.ValueDeclaration _ _ _ _) title =
141+ -- If no explicit type declaration was provided, insert a wildcard, so that
142+ -- the actual type will be added during type checking.
143+ basicDeclaration title (ValueDeclaration P.TypeWildcard)
140144convertDeclaration (P.ExternDeclaration _ ty) title =
141145 basicDeclaration title (ValueDeclaration ty)
142146convertDeclaration (P.DataDeclaration dtype _ args ctors) title =
src/Language/PureScript/Docs/ParseAndDesugar.hs → src/Language/PureScript/Docs/ParseAndBookmark.hsrenamed+16−53
@@ -1,8 +1,8 @@
11{-# LANGUAGE TupleSections #-}
22{-# LANGUAGE FlexibleContexts #-}
33
4−module Language.PureScript.Docs.ParseAndDesugar
5− ( parseAndDesugar
4+module Language.PureScript.Docs.ParseAndBookmark
5+ ( parseAndBookmark
66 ) where
77
88import Prelude ()
@@ -10,16 +10,13 @@ import Prelude.Compat
1010
1111import qualified Data.Map as M
1212import Control.Arrow (first)
13−import Control.Monad
1413
15−import Control.Monad.Writer.Strict (runWriterT)
1614import Control.Monad.Error.Class (MonadError(..))
1715import Control.Monad.IO.Class (MonadIO(..))
1816
1917import Web.Bower.PackageMeta (PackageName)
2018
2119import qualified Language.PureScript as P
22−import qualified Language.PureScript.Constants as C
2320import Language.PureScript.Docs.Types
2421import Language.PureScript.Docs.Convert (collectBookmarks)
2522
@@ -35,23 +32,18 @@ import Language.PureScript.Docs.Convert (collectBookmarks)
3532-- * Parse all of the input and dependency source files
3633-- * Associate each dependency module with its package name, thereby
3734-- distinguishing these from local modules
38−-- * Partially desugar all of the resulting modules (just enough for
39−-- producing documentation from them)
4035-- * Collect a list of bookmarks from the whole set of source files
41−-- * Return the desugared modules, the bookmarks, and the imports/exports
42−-- Env (which is needed for producing documentation).
43−parseAndDesugar ::
36+-- * Return the parsed modules and the bookmarks
37+parseAndBookmark ::
4438 (Functor m, Applicative m, MonadError P.MultipleErrors m, MonadIO m) =>
4539 [FilePath]
4640 -> [(PackageName, FilePath)]
47− -> m ([InPackage P.Module], [Bookmark], P.Env)
48−parseAndDesugar inputFiles depsFiles = do
41+ -> m ([InPackage P.Module], [Bookmark])
42+parseAndBookmark inputFiles depsFiles = do
4943 inputFiles' <- traverse (parseAs Local) inputFiles
5044 depsFiles' <- traverse (\(pkgName, f) -> parseAs (FromDep pkgName) f) depsFiles
5145
52− ms <- parseFiles (inputFiles' ++ depsFiles')
53− ms' <- sortModules (map snd ms)
54− desugarWithBookmarks ms ms'
46+ addBookmarks <$> parseFiles (inputFiles' ++ depsFiles')
5547
5648parseFiles ::
5749 (MonadError P.MultipleErrors m, MonadIO m) =>
@@ -60,29 +52,16 @@ parseFiles ::
6052parseFiles =
6153 throwLeft . P.parseModulesFromFiles fileInfoToString
6254
63−sortModules ::
64− (Functor m, MonadError P.MultipleErrors m, MonadIO m) =>
65− [P.Module]
66− -> m [P.Module]
67−sortModules =
68− fmap fst . throwLeft . sortModules' . map importPrim
69− where
70− sortModules' :: [P.Module] -> Either P.MultipleErrors ([P.Module], P.ModuleGraph)
71− sortModules' = P.sortModules
72−
73−desugarWithBookmarks ::
74− (MonadError P.MultipleErrors m, MonadIO m) =>
55+addBookmarks ::
7556 [(FileInfo, P.Module)]
76− -> [P.Module]
77− -> m ([InPackage P.Module], [Bookmark], P.Env)
78−desugarWithBookmarks msInfo msSorted = do
79− (env, msDesugared) <- throwLeft (desugar msSorted)
80−
81− let msDeps = getDepsModuleNames (map (\(fp, m) -> (,m) <$> fp) msInfo)
82− msPackages = map (addPackage msDeps) msDesugared
83− bookmarks = concatMap collectBookmarks msPackages
84−
85− return (msPackages, bookmarks, env)
57+ -> ([InPackage P.Module], [Bookmark])
58+addBookmarks msInfo =
59+ let
60+ msDeps = getDepsModuleNames (map (\(fp, m) -> (,m) <$> fp) msInfo)
61+ msPackages = map (addPackage msDeps . snd) msInfo
62+ bookmarks = concatMap collectBookmarks msPackages
63+ in
64+ (msPackages, bookmarks)
8665
8766throwLeft :: (MonadError l m) => Either l r -> m r
8867throwLeft = either throwError return
@@ -100,22 +79,6 @@ fileInfoToString :: FileInfo -> FilePath
10079fileInfoToString (Local fn) = fn
10180fileInfoToString (FromDep _ fn) = fn
10281
103−importPrim :: P.Module -> P.Module
104−importPrim = P.addDefaultImport (P.ModuleName [P.ProperName C.prim])
105−
106−desugar ::
107− (Functor m, Applicative m, MonadError P.MultipleErrors m) =>
108− [P.Module]
109− -> m (P.Env, [P.Module])
110−desugar = P.evalSupplyT 0 . desugar'
111− where
112− desugar' =
113− traverse P.desugarDoModule
114− >=> P.desugarCasesModule
115− >=> ignoreWarnings . P.desugarImportsWithEnv []
116−
117− ignoreWarnings m = liftM fst (runWriterT m)
118−
11982parseFile :: FilePath -> IO (FilePath, String)
12083parseFile input' = (,) input' <$> readFile input'
12184
src/Language/PureScript/Docs/Types.hs+3−3
@@ -73,7 +73,7 @@ packageName :: Package a -> PackageName
7373packageName = bowerName . pkgMeta
7474
7575data Module = Module
76− { modName :: String
76+ { modName :: P.ModuleName
7777 , modComments :: Maybe String
7878 , modDeclarations :: [Declaration]
7979 -- Re-exported values from other modules
@@ -346,7 +346,7 @@ parseVersion' str =
346346
347347asModule :: Parse PackageError Module
348348asModule =
349− Module <$> key "name" asString
349+ Module <$> key "name" (P.moduleNameFromString <$> asString)
350350 <*> key "comments" (perhaps asString)
351351 <*> key "declarations" (eachInArray asDeclaration)
352352 <*> key "reExports" (eachInArray asReExport)
@@ -512,7 +512,7 @@ instance A.ToJSON NotYetKnown where
512512
513513instance A.ToJSON Module where
514514 toJSON Module{..} =
515− A.object [ "name" .= modName
515+ A.object [ "name" .= P.runModuleName modName
516516 , "comments" .= modComments
517517 , "declarations" .= modDeclarations
518518 , "reExports" .= map toObj modReExports
src/Language/PureScript/Publish.hs+4−4
@@ -146,15 +146,15 @@ preparePackage' opts = do
146146getModulesAndBookmarks :: PrepareM ([D.Bookmark], [D.Module])
147147getModulesAndBookmarks = do
148148 (inputFiles, depsFiles) <- liftIO getInputAndDepsFiles
149− (modules', bookmarks, env) <- parseAndDesugar inputFiles depsFiles
149+ (modules', bookmarks) <- parseAndBookmark inputFiles depsFiles
150150
151− case runExcept (D.convertModulesInPackage env modules') of
151+ case runExcept (D.convertModulesInPackage modules') of
152152 Right modules -> return (bookmarks, modules)
153153 Left err -> userError (CompileError err)
154154
155155 where
156− parseAndDesugar inputFiles depsFiles = do
157− r <- liftIO . runExceptT $ D.parseAndDesugar inputFiles depsFiles
156+ parseAndBookmark inputFiles depsFiles = do
157+ r <- liftIO . runExceptT $ D.parseAndBookmark inputFiles depsFiles
158158 case r of
159159 Right r' ->
160160 return r'
src/Language/PureScript/Sugar/Names.hs+2−1
@@ -17,6 +17,7 @@ import Prelude.Compat
1717import Data.List (find, nub)
1818import Data.Maybe (fromMaybe, mapMaybe)
1919
20+import Control.Arrow (first)
2021import Control.Monad
2122import Control.Monad.Error.Class (MonadError(..))
2223import Control.Monad.Writer (MonadWriter(..), censor)
@@ -54,7 +55,7 @@ desugarImportsWithEnv
5455desugarImportsWithEnv externs modules = do
5556 env <- silence $ foldM externsEnv primEnv externs
5657 modules' <- traverse updateExportRefs modules
57− (modules'', env') <- foldM updateEnv ([], env) modules'
58+ (modules'', env') <- first reverse <$> foldM updateEnv ([], env) modules'
5859 (env',) <$> traverse (renameInModule' env') modules''
5960 where
6061 silence :: m a -> m a
tests/TestDocs.hs+45−4
@@ -14,6 +14,7 @@ import Control.Applicative
1414import Control.Arrow
1515import Data.Maybe (fromMaybe)
1616import Data.List ((\\))
17+import Data.Monoid
1718import Data.Foldable
1819import Data.Traversable
1920import System.Exit
@@ -36,8 +37,8 @@ main :: IO ()
3637main = do
3738 TestPscPublish.pushd "examples/docs" $ do
3839 Docs.Package{..} <- Publish.preparePackage publishOpts
39− forM_ testCases $ \(mn, pragmas) ->
40− let mdl = takeJust ("module not found in docs: " ++ mn)
40+ forM_ testCases $ \(P.moduleNameFromString -> mn, pragmas) ->
41+ let mdl = takeJust ("module not found in docs: " ++ P.runModuleName mn)
4142 (find ((==) mn . Docs.modName) pkgModules)
4243 in forM_ pragmas (flip runAssertionIO mdl)
4344
@@ -56,8 +57,16 @@ data Assertion
5657 -- | Assert that a particular declaration has a particular type class
5758 -- constraint.
5859 | ShouldBeConstrained P.ModuleName String String
60+ -- | Assert that a particular value declaration exists, and its type
61+ -- satisfies the given predicate.
62+ | ValueShouldHaveTypeSignature P.ModuleName String (ShowFn (P.Type -> Bool))
5963 deriving (Show)
6064
65+newtype ShowFn a = ShowFn a
66+
67+instance Show (ShowFn a) where
68+ show _ = "<function>"
69+
6170data AssertionFailure
6271 -- | A declaration was not documented, but should have been
6372 = NotDocumented P.ModuleName String
@@ -72,6 +81,11 @@ data AssertionFailure
7281 -- | A declaration had the wrong "type" (ie, value, type, type class)
7382 -- Fields: declaration title, expected "type", actual "type".
7483 | WrongDeclarationType P.ModuleName String String String
84+ -- | A value declaration had the wrong type (in the sense of "type
85+ -- checking"), eg, because the inferred type was used when the explicit type
86+ -- should have been.
87+ -- Fields: module name, declaration name, actual type.
88+ | ValueDeclarationWrongType P.ModuleName String P.Type
7589 deriving (Show)
7690
7791data AssertionResult
@@ -121,9 +135,24 @@ runAssertion assertion Docs.Module{..} =
121135 Fail (WrongDeclarationType mn decl "value"
122136 (Docs.declInfoToString declInfo))
123137
138+ ValueShouldHaveTypeSignature mn decl (ShowFn tyPredicate) ->
139+ case find ((==) decl . Docs.declTitle) (declarationsFor mn) of
140+ Nothing ->
141+ Fail (NotDocumented mn decl)
142+ Just Docs.Declaration{..} ->
143+ case declInfo of
144+ Docs.ValueDeclaration ty ->
145+ if tyPredicate ty
146+ then Pass
147+ else Fail
148+ (ValueDeclarationWrongType mn decl ty)
149+ _ ->
150+ Fail (WrongDeclarationType mn decl "value"
151+ (Docs.declInfoToString declInfo))
152+
124153 where
125154 declarationsFor mn =
126− if P.runModuleName mn == modName
155+ if mn == modName
127156 then modDeclarations
128157 else fromMaybe [] (lookup mn modReExports)
129158
@@ -149,7 +178,7 @@ checkConstrained ty tyClass =
149178
150179runAssertionIO :: Assertion -> Docs.Module -> IO ()
151180runAssertionIO assertion mdl = do
152− putStrLn ("In " ++ Docs.modName mdl ++ ": " ++ show assertion)
181+ putStrLn ("In " ++ P.runModuleName (Docs.modName mdl) ++ ": " ++ show assertion)
153182 case runAssertion assertion mdl of
154183 Pass -> pure ()
155184 fail -> do
@@ -226,7 +255,19 @@ testCases =
226255 , ("NewOperators",
227256 [ ShouldBeDocumented (n "NewOperators2") "(>>>)" []
228257 ])
258+
259+ , ("ExplicitTypeSignatures",
260+ [ ValueShouldHaveTypeSignature (n "ExplicitTypeSignatures") "explicit" (ShowFn (hasTypeVar "something"))
261+ , ValueShouldHaveTypeSignature (n "ExplicitTypeSignatures") "anInt" (ShowFn ((==) P.tyInt))
262+ , ValueShouldHaveTypeSignature (n "ExplicitTypeSignatures") "aNumber" (ShowFn ((==) P.tyNumber))
263+ ])
229264 ]
230265
231266 where
232267 n = P.moduleNameFromString
268+
269+ hasTypeVar varName =
270+ getAny . P.everythingOnTypes (<>) (Any . isVar varName)
271+
272+ isVar varName (P.TypeVar name) | varName == name = True
273+ isVar _ _ = False