git.iko.soy/rowtype-yoga/purescript-native
on 7cf48f0 help sign in
90 lines 3.3 kB history raw
1-----------------------------------------------------------------------------
2--
3-- Module : Language.PureScript.Linter
4-- Copyright : (c) 2013-15 Phil Freeman, (c) 2014-15 Gary Burgess
5-- License : MIT (http://opensource.org/licenses/MIT)
6--
7-- Maintainer : Phil Freeman <paf31@cantab.net>
8-- Stability : experimental
9-- Portability :
10--
11-- | This module implements a simple linting pass on the PureScript AST.
12--
13-----------------------------------------------------------------------------
15{-# LANGUAGE GeneralizedNewtypeDeriving #-}
16{-# LANGUAGE FlexibleContexts #-}
17{-# LANGUAGE ScopedTypeVariables #-}
18{-# LANGUAGE CPP #-}
20module Language.PureScript.Linter (lint, module L) where
22import Data.List (mapAccumL, nub)
23import Data.Maybe (mapMaybe)
24import Data.Monoid
26import qualified Data.Set as S
28#if __GLASGOW_HASKELL__ < 710
29import Control.Applicative
30#endif
31import Control.Monad.Writer.Class
33import Language.PureScript.AST
34import Language.PureScript.Names
35import Language.PureScript.Errors
36import Language.PureScript.Linter.Exhaustive as L
38-- | Lint the PureScript AST.
39-- |
40-- | Right now, this pass only performs a shadowing check.
41lint :: forall m. (Applicative m, MonadWriter MultipleErrors m) => Module -> m ()
42lint (Module _ _ mn ds _) = censor (onErrorMessages (ErrorInModule mn)) $ mapM_ lintDeclaration ds
43 where
44 moduleNames :: S.Set Ident
45 moduleNames = S.fromList (nub (mapMaybe getDeclIdent ds))
47 getDeclIdent :: Declaration -> Maybe Ident
48 getDeclIdent (PositionedDeclaration _ _ d) = getDeclIdent d
49 getDeclIdent (ValueDeclaration ident _ _ _) = Just ident
50 getDeclIdent (ExternDeclaration ident _) = Just ident
51 getDeclIdent (TypeInstanceDeclaration ident _ _ _ _) = Just ident
52 getDeclIdent (BindingGroupDeclaration _) = error "lint: binding groups should not be desugared yet."
53 getDeclIdent _ = Nothing
55 lintDeclaration :: Declaration -> m ()
56 lintDeclaration d =
57 let (f, _, _, _, _) = everythingWithContextOnValues moduleNames mempty mappend stepD stepE stepB def def
59 f' :: Declaration -> MultipleErrors
60 f' (PositionedDeclaration pos _ dec) = onErrorMessages (PositionedError pos) (f' dec)
61 f' dec = f dec
63 in tell (f' d)
64 where
65 def s _ = (s, mempty)
67 stepD :: S.Set Ident -> Declaration -> (S.Set Ident, MultipleErrors)
68 stepD s (TypeClassDeclaration name _ _ decls) = (s, foldr go mempty decls)
69 where
70 go :: Declaration -> MultipleErrors -> MultipleErrors
71 go (PositionedDeclaration _ _ d') errs = go d' errs
72 go (TypeDeclaration op@(Op _) _) errs = errorMessage (ClassOperator name op) <> errs
73 go _ errs = errs
74 stepD s _ = (s, mempty)
76 stepE :: S.Set Ident -> Expr -> (S.Set Ident, MultipleErrors)
77 stepE s (Abs (Left name) _) = bind s name
78 stepE s (Let ds' _) =
79 case mapAccumL bind s (nub (mapMaybe getDeclIdent ds')) of
80 (s', es) -> (s', mconcat es)
81 stepE s _ = (s, mempty)
83 stepB :: S.Set Ident -> Binder -> (S.Set Ident, MultipleErrors)
84 stepB s (VarBinder name) = bind s name
85 stepB s (NamedBinder name _) = bind s name
86 stepB s _ = (s, mempty)
88 bind :: S.Set Ident -> Ident -> (S.Set Ident, MultipleErrors)
89 bind s name | name `S.member` s = (s, errorMessage (ShadowedName name))
90 | otherwise = (S.insert name s, mempty)