diff --git a/semantic.cabal b/semantic.cabal index 3ba54b0d0..d2b02b695 100644 --- a/semantic.cabal +++ b/semantic.cabal @@ -51,6 +51,7 @@ library , Data.Abstract.Live , Data.Abstract.ModuleTable , Data.Abstract.Number + , Data.Abstract.Path , Data.Abstract.Type , Data.Abstract.Value -- General datatype definitions & generic algorithms @@ -99,6 +100,7 @@ library , Language.JSON.Assignment , Language.Ruby.Grammar , Language.Ruby.Assignment + , Language.Ruby.Syntax , Language.TypeScript.Assignment , Language.TypeScript.Grammar , Language.TypeScript.Syntax @@ -208,6 +210,7 @@ test-suite test other-modules: Assigning.Assignment.Spec , Analysis.Go.Spec , Analysis.Python.Spec + , Analysis.Ruby.Spec , Analysis.TypeScript.Spec , Data.Diff.Spec , Data.Functor.Classes.Generic.Spec diff --git a/src/Analysis/Abstract/Evaluating.hs b/src/Analysis/Abstract/Evaluating.hs index 8f131fffd..8accb44d6 100644 --- a/src/Analysis/Abstract/Evaluating.hs +++ b/src/Analysis/Abstract/Evaluating.hs @@ -14,7 +14,6 @@ import Control.Monad.Effect.Reader import Control.Monad.Effect.State import Data.Abstract.Configuration import Data.Abstract.Evaluatable -import Data.Abstract.Address import Data.Abstract.ModuleTable import Data.Abstract.Value import Data.Blob @@ -81,12 +80,11 @@ type EvaluatingEffects term value = '[ Fail -- Failure with an error message , Reader (EnvironmentFor value) -- Local environment (e.g. binding over a closure) , State (EnvironmentFor value) -- Global (imperative) environment - , State (HeapFor value) -- The heap + , State (HeapFor value) -- The heap , Reader (ModuleTable [term]) -- Cache of unevaluated modules , State (ModuleTable (EnvironmentFor value)) -- Cache of evaluated modules - - , State (Map Name (Name, Maybe (Address (LocationFor value) value))) -- Set of exports - , State (IntMap.IntMap term) -- For jumps + , State (ExportsFor value) -- Exports (used to filter environments when they are imported) + , State (IntMap.IntMap term) -- For jumps ] instance Members '[Fail, State (IntMap.IntMap term)] effects => MonadControl term (Evaluating term value effects) where @@ -98,13 +96,13 @@ instance Members '[Fail, State (IntMap.IntMap term)] effects => MonadControl ter goto label = IntMap.lookup label <$> raise get >>= maybe (fail ("unknown label: " <> show label)) pure -instance Members '[State (Map Name (Name, Maybe (Address (LocationFor value) value))), Reader (EnvironmentFor value), State (EnvironmentFor value)] effects => MonadEnvironment value (Evaluating term value effects) where +instance Members '[State (ExportsFor value), Reader (EnvironmentFor value), State (EnvironmentFor value)] effects => MonadEnvironment value (Evaluating term value effects) where getGlobalEnv = raise get putGlobalEnv = raise . put withGlobalEnv s = raise . localState s . lower - addExport key = raise . modify . Map.insert key getExports = raise get + putExports = raise . put withExports s = raise . localState s . lower askLocalEnv = raise ask diff --git a/src/Analysis/Declaration.hs b/src/Analysis/Declaration.hs index 826837f86..c83a0d6a1 100644 --- a/src/Analysis/Declaration.hs +++ b/src/Analysis/Declaration.hs @@ -18,6 +18,7 @@ import Data.Abstract.FreeVariables import qualified Data.Syntax as Syntax import qualified Data.Syntax.Declaration as Declaration import qualified Data.Syntax.Expression as Expression +import qualified Language.Ruby.Syntax as Ruby.Syntax import qualified Data.Text as T import qualified Data.Text.Encoding as T import qualified Language.Markdown.Syntax as Markdown @@ -98,54 +99,59 @@ instance CustomHasDeclaration whole Declaration.Function where -- Do not summarize anonymous functions | isEmpty identifierAnn = Nothing -- Named functions - | otherwise = Just $ FunctionDeclaration (getSource identifierAnn) (getFunctionSource blob (In ann decl)) blobLanguage - where getSource = toText . flip Source.slice blobSource . getField - isEmpty = (== 0) . rangeLength . getField + | otherwise = Just $ FunctionDeclaration (getSource blobSource identifierAnn) (getFunctionSource blob (In ann decl)) blobLanguage + where isEmpty = (== 0) . rangeLength . getField -- | Produce a 'MethodDeclaration' for 'Declaration.Method' nodes. If the method’s receiver is non-empty (defined as having a non-empty 'Range'), the 'declarationIdentifier' will be formatted as 'receiver.method_name'; otherwise it will be simply 'method_name'. instance CustomHasDeclaration whole Declaration.Method where customToDeclaration blob@Blob{..} ann decl@(Declaration.Method _ (Term (In receiverAnn receiverF), _) (Term (In identifierAnn _), _) _ _) -- Methods without a receiver - | isEmpty receiverAnn = Just $ MethodDeclaration (getSource identifierAnn) (getMethodSource blob (In ann decl)) blobLanguage Nothing + | isEmpty receiverAnn = Just $ MethodDeclaration (getSource blobSource identifierAnn) (getMethodSource blob (In ann decl)) blobLanguage Nothing -- Methods with a receiver type and an identifier (e.g. (a *Type) in Go). | blobLanguage == Just Go - , [ _, Term (In receiverType _) ] <- toList receiverF = Just $ MethodDeclaration (getSource identifierAnn) (getMethodSource blob (In ann decl)) blobLanguage (Just (getSource receiverType)) + , [ _, Term (In receiverType _) ] <- toList receiverF = Just $ MethodDeclaration (getSource blobSource identifierAnn) (getMethodSource blob (In ann decl)) blobLanguage (Just (getSource blobSource receiverType)) -- Methods with a receiver (class methods) are formatted like `receiver.method_name` - | otherwise = Just $ MethodDeclaration (getSource identifierAnn) (getMethodSource blob (In ann decl)) blobLanguage (Just (getSource receiverAnn)) - where getSource = toText . flip Source.slice blobSource . getField - isEmpty = (== 0) . rangeLength . getField + | otherwise = Just $ MethodDeclaration (getSource blobSource identifierAnn) (getMethodSource blob (In ann decl)) blobLanguage (Just (getSource blobSource receiverAnn)) + where isEmpty = (== 0) . rangeLength . getField -- | Produce a 'ClassDeclaration' for 'Declaration.Class' nodes. instance CustomHasDeclaration whole Declaration.Class where customToDeclaration blob@Blob{..} ann decl@(Declaration.Class _ (Term (In identifierAnn _), _) _ _) - -- Classes - = Just $ ClassDeclaration (getSource identifierAnn) (getClassSource blob (In ann decl)) blobLanguage - where getSource = toText . flip Source.slice blobSource . getField + = Just $ ClassDeclaration (getSource blobSource identifierAnn) (getClassSource blob (In ann decl)) blobLanguage instance CustomHasDeclaration (Union fs) Declaration.Import where - customToDeclaration Blob{..} _ (Declaration.Import (Term (In fromAnn _), _) symbols) - = Just $ ImportDeclaration ((stripQuotes . getSource) fromAnn) "" (fmap getSymbol symbols) blobLanguage + customToDeclaration Blob{..} _ (Declaration.Import (Term (In fromAnn _), _) symbols _) + = Just $ ImportDeclaration ((stripQuotes . getSource blobSource) fromAnn) "" (fmap getSymbol symbols) blobLanguage where - stripQuotes = T.dropAround (`elem` ['"', '\'']) - getSource = toText . flip Source.slice blobSource . getField getSymbol = let f = (T.decodeUtf8 . friendlyName) in bimap f f instance (Syntax.Identifier :< fs) => CustomHasDeclaration (Union fs) Declaration.QualifiedImport where customToDeclaration Blob{..} _ (Declaration.QualifiedImport (Term (In fromAnn _), _) (Term (In aliasAnn aliasF), _) symbols) - | Just (Syntax.Identifier alias) <- prj aliasF = Just $ ImportDeclaration ((stripQuotes . getSource) fromAnn) (toName alias) (fmap getSymbol symbols) blobLanguage - | otherwise = Just $ ImportDeclaration ((stripQuotes . getSource) fromAnn) (getSource aliasAnn) (fmap getSymbol symbols) blobLanguage + | Just (Syntax.Identifier alias) <- prj aliasF = Just $ ImportDeclaration ((stripQuotes . getSource blobSource) fromAnn) (toName alias) (fmap getSymbol symbols) blobLanguage + | otherwise = Just $ ImportDeclaration ((stripQuotes . getSource blobSource) fromAnn) (getSource blobSource aliasAnn) (fmap getSymbol symbols) blobLanguage where - stripQuotes = T.dropAround (`elem` ['"', '\'']) - getSource = toText . flip Source.slice blobSource . getField getSymbol = bimap toName toName toName = T.decodeUtf8 . friendlyName -instance CustomHasDeclaration (Union fs) Declaration.WildcardImport where - customToDeclaration Blob{..} _ (Declaration.WildcardImport (Term (In fromAnn _), _) _) - = Just $ ImportDeclaration ((stripQuotes . getSource) fromAnn) "" [] blobLanguage - where - stripQuotes = T.dropAround (`elem` ['"', '\'']) - getSource = toText . flip Source.slice blobSource . getField +instance CustomHasDeclaration (Union fs) Declaration.SideEffectImport where + customToDeclaration Blob{..} _ (Declaration.SideEffectImport (Term (In fromAnn _), _) _) + = Just $ ImportDeclaration ((stripQuotes . getSource blobSource) fromAnn) "" [] blobLanguage + +instance CustomHasDeclaration (Union fs) Ruby.Syntax.Require where + customToDeclaration Blob{..} _ (Ruby.Syntax.Require _ (Term (In fromAnn _), _)) + = Just $ ImportDeclaration ((stripQuotes . getSource blobSource) fromAnn) "" [] blobLanguage + +instance CustomHasDeclaration (Union fs) Ruby.Syntax.Load where + customToDeclaration Blob{..} _ (Ruby.Syntax.Load ((Term (In fromArgs _), _):_)) + = Just $ ImportDeclaration ((stripQuotes . getSource blobSource) fromArgs) "" [] blobLanguage + customToDeclaration Blob{..} _ (Ruby.Syntax.Load _) + = Nothing + +getSource :: HasField fields Range => Source -> Record fields -> Text +getSource blobSource = toText . flip Source.slice blobSource . getField + +stripQuotes :: Text -> Text +stripQuotes = T.dropAround (`elem` ['"', '\'']) instance (Syntax.Identifier :< fs, Expression.MemberAccess :< fs) => CustomHasDeclaration (Union fs) Expression.Call where customToDeclaration Blob{..} _ (Expression.Call _ (Term (In fromAnn fromF), _) _ _) @@ -184,7 +190,8 @@ type family DeclarationStrategy syntax where DeclarationStrategy Declaration.Function = 'Custom DeclarationStrategy Declaration.Import = 'Custom DeclarationStrategy Declaration.QualifiedImport = 'Custom - DeclarationStrategy Declaration.WildcardImport = 'Custom + DeclarationStrategy Declaration.SideEffectImport = 'Custom + DeclarationStrategy Ruby.Syntax.Require = 'Custom DeclarationStrategy Declaration.Method = 'Custom DeclarationStrategy Markdown.Heading = 'Custom DeclarationStrategy Expression.Call = 'Custom diff --git a/src/Control/Abstract/Analysis.hs b/src/Control/Abstract/Analysis.hs index 9d812875d..d24cb2562 100644 --- a/src/Control/Abstract/Analysis.hs +++ b/src/Control/Abstract/Analysis.hs @@ -42,6 +42,10 @@ class (MonadEvaluator term value m, Recursive term) => MonadAnalysis term value evaluateModule :: term -> m value evaluateModule = evaluateTerm + -- | Isolate the given action with an empty global environment and exports. + isolate :: m a -> m a + isolate = withGlobalEnv mempty . withExports mempty + -- | Evaluate a term to a value using the semantics of the current analysis. -- -- This should always be called when e.g. evaluating the bodies of closures instead of explicitly folding either 'eval' or 'analyzeTerm' over subterms, except in 'MonadAnalysis' instances themselves. On the other hand, top-level evaluation should be performed using 'evaluateModule'. @@ -68,19 +72,25 @@ load :: ( MonadAnalysis term value m => ModuleName -> m (EnvironmentFor value) load name = askModuleTable >>= maybe notFound evalAndCache . moduleTableLookup name - where notFound = fail ("cannot load module: " <> show name) - evalAndCache :: (MonadAnalysis term value m, Ord (LocationFor value)) => [term] -> m (EnvironmentFor value) - evalAndCache [] = pure mempty - evalAndCache (x:xs) = do - void $ evaluateModule x - env <- getGlobalEnv - exports <- getExports - -- TODO: If the set of exports is empty because no exports have been - -- defined, do we export all terms, or no terms? This behavior varies across - -- languages. We need better semantics rather than doing it ad-hoc. - let env' = if Map.null exports then env else bindExports exports env - modifyModuleTable (moduleTableInsert name env') - (env' <>) <$> evalAndCache xs + where + notFound = fail ("cannot load module: " <> show name) + evalAndCache :: (MonadAnalysis term value m, Ord (LocationFor value)) => [term] -> m (EnvironmentFor value) + evalAndCache [] = pure mempty + evalAndCache (x:xs) = do + void $ evaluateModule x + env <- filterEnv <$> getExports <*> getGlobalEnv + modifyModuleTable (moduleTableInsert name env) + (env <>) <$> evalAndCache xs + + -- TODO: If the set of exports is empty because no exports have been + -- defined, do we export all terms, or no terms? This behavior varies across + -- languages. We need better semantics rather than doing it ad-hoc. + filterEnv :: (Ord l) => Exports l a -> Environment l a -> Environment l a + filterEnv Exports{..} env + | Map.null unExports = env + | otherwise = Environment $ Map.foldrWithKey maybeInsert mempty unExports + where maybeInsert name (alias, address) accum = + maybe accum (\v -> Map.insert alias v accum) (address <|> envLookup name env) -- | Lift a 'SubtermAlgebra' for an underlying analysis into a containing analysis. Use this when defining an analysis which can be composed onto other analyses to ensure that a call to 'analyzeTerm' occurs in the inner analysis and not the outer one. liftAnalyze :: ( Coercible ( m term value (effects :: [* -> *]) value) (t m term value effects value) diff --git a/src/Control/Abstract/Evaluator.hs b/src/Control/Abstract/Evaluator.hs index 6e707d22b..c113555d3 100644 --- a/src/Control/Abstract/Evaluator.hs +++ b/src/Control/Abstract/Evaluator.hs @@ -3,6 +3,8 @@ module Control.Abstract.Evaluator ( MonadEvaluator(..) , MonadEnvironment(..) , modifyGlobalEnv +, modifyExports +, addExport , MonadHeap(..) , modifyHeap , lookupHeap @@ -20,7 +22,7 @@ import Data.Abstract.Heap import Data.Abstract.ModuleTable import Data.Abstract.Value import Data.Semigroup.Reducer -import Prelude hiding (fail) +import Prelude import Prologue -- | A 'Monad' providing the basic essentials for evaluation. @@ -45,14 +47,15 @@ class Monad m => MonadEnvironment value m | m -> value where getGlobalEnv :: m (EnvironmentFor value) -- | Set the global environment putGlobalEnv :: EnvironmentFor value -> m () + -- | Sets the global environment for the lifetime of the given action. withGlobalEnv :: EnvironmentFor value -> m a -> m a - -- | Add an export to the global export state. - addExport :: Name -> (Name, Maybe (Address (LocationFor value) value)) -> m () -- | Get the global export state. - getExports :: m (Map Name (Name, Maybe (Address (LocationFor value) value))) - -- | Sets the exports state to the given map for the lifetime of the given action. - withExports :: Map Name (Name, Maybe (Address (LocationFor value) value)) -> m a -> m a + getExports :: m (ExportsFor value) + -- | Set the global export state. + putExports :: ExportsFor value -> m () + -- | Sets the global export state for the lifetime of the given action. + withExports :: ExportsFor value -> m a -> m a -- | Retrieve the local environment. askLocalEnv :: m (EnvironmentFor value) @@ -70,11 +73,20 @@ class Monad m => MonadEnvironment value m | m -> value where maybe (pure Nothing) (fmap Just . with) addr -- | Update the global environment. -modifyGlobalEnv :: MonadEnvironment value m => (EnvironmentFor value -> EnvironmentFor value) -> m () +modifyGlobalEnv :: MonadEnvironment value m => (EnvironmentFor value -> EnvironmentFor value) -> m () modifyGlobalEnv f = do env <- getGlobalEnv putGlobalEnv $! f env +-- | Update the global export state. +modifyExports :: MonadEnvironment value m => (ExportsFor value -> ExportsFor value) -> m () +modifyExports f = do + exports <- getExports + putExports $! f exports + +-- | Add an export to the global export state. +addExport :: MonadEnvironment value m => Name -> Name -> Maybe (Address (LocationFor value) value) -> m () +addExport name alias = modifyExports . exportInsert name alias -- | A 'Monad' abstracting a heap of values. class Monad m => MonadHeap value m | m -> value where diff --git a/src/Control/Abstract/Value.hs b/src/Control/Abstract/Value.hs index 72aab09f3..12bfe53a9 100644 --- a/src/Control/Abstract/Value.hs +++ b/src/Control/Abstract/Value.hs @@ -80,6 +80,9 @@ class (Monad m, Show value) => MonadValue value m where -- | Construct an array of zero or more values. array :: [value] -> m value +-- | Extract a 'ByteString' from a given value. + asString :: value -> m ByteString + -- | Eliminate boolean values. TODO: s/boolean/truthy ifthenelse :: value -> m a -> m a -> m a @@ -142,9 +145,12 @@ instance ( Monad m rational = pure . injValue . Value.Rational . Ratio multiple = pure . injValue . Value.Tuple - array = pure . injValue . Value.Array + asString v + | Just (Value.String n) <- prjValue v = pure n + | otherwise = fail ("expected " <> show v <> " to be a string") + ifthenelse cond if' else' | Just (Boolean b) <- prjValue cond = if b then if' else else' | otherwise = fail ("not defined for non-boolean conditions: " <> show cond) diff --git a/src/Data/Abstract/Environment.hs b/src/Data/Abstract/Environment.hs index 1b3b2f5f4..bde306095 100644 --- a/src/Data/Abstract/Environment.hs +++ b/src/Data/Abstract/Environment.hs @@ -1,12 +1,12 @@ {-# LANGUAGE GeneralizedNewtypeDeriving, MultiParamTypeClasses, StandaloneDeriving #-} module Data.Abstract.Environment where -import Prologue import Data.Abstract.Address import Data.Abstract.FreeVariables import Data.Abstract.Live -import qualified Data.Map as Map import Data.Semigroup.Reducer +import Prologue +import qualified Data.Map as Map import qualified Data.Set as Set -- | A map of names to addresses that represents the evaluation environment. @@ -15,6 +15,10 @@ newtype Environment l a = Environment { unEnvironment :: Map.Map Name (Address l deriving instance Reducer (Name, Address l a) (Environment l a) +-- | A map of export names to an alias & address tuple. +newtype Exports l a = Exports { unExports :: Map.Map Name (Name, Maybe (Address l a)) } + deriving (Eq, Foldable, Functor, Generic1, Monoid, Ord, Semigroup, Show, Traversable) + -- | Lookup a 'Name' in the environment. envLookup :: Name -> Environment l a -> Maybe (Address l a) envLookup k = Map.lookup k . unEnvironment @@ -30,11 +34,8 @@ bindEnv :: Foldable t => t Name -> Environment l a -> Environment l a bindEnv names env = foldMap envForName names where envForName name = maybe mempty (curry unit name) (envLookup name env) -bindExports :: Map Name (Name, Maybe (Address l a)) -> Environment l a -> Environment l a -bindExports aliases env = Environment pairs - where - pairs = Map.foldrWithKey (\name (alias, address) accum -> - maybe accum (\v -> Map.insert alias v accum) (address <|> envLookup name env)) mempty aliases +exportInsert :: Name -> Name -> Maybe (Address l a) -> Exports l a -> Exports l a +exportInsert name alias address = Exports . Map.insert name (alias, address) . unExports -- | Retrieve the 'Live' set of addresses to which the given free variable names are bound. -- @@ -45,8 +46,13 @@ envRoots env = foldMap (maybe mempty liveSingleton . flip envLookup env) envAll :: (Ord l) => Environment l a -> Live l a envAll (Environment env) = Live $ Set.fromList (Map.elems env) + -- Instances instance Eq l => Eq1 (Environment l) where liftEq = genericLiftEq instance Ord l => Ord1 (Environment l) where liftCompare = genericLiftCompare instance Show l => Show1 (Environment l) where liftShowsPrec = genericLiftShowsPrec + +instance Eq l => Eq1 (Exports l) where liftEq = genericLiftEq +instance Ord l => Ord1 (Exports l) where liftCompare = genericLiftCompare +instance Show l => Show1 (Exports l) where liftShowsPrec = genericLiftShowsPrec diff --git a/src/Data/Abstract/FreeVariables.hs b/src/Data/Abstract/FreeVariables.hs index 55138f9e9..fbfbfc079 100644 --- a/src/Data/Abstract/FreeVariables.hs +++ b/src/Data/Abstract/FreeVariables.hs @@ -5,16 +5,24 @@ import Prologue import Data.Term import Data.ByteString (intercalate) import qualified Data.List.NonEmpty as NonEmpty +import Data.Abstract.Path -- | The type of variable names. type Name = NonEmpty ByteString +-- | Construct a qualified name from a 'ByteString' name :: ByteString -> Name name x = x :| [] +-- | Construct a qualified name from a list of 'ByteString's qualifiedName :: [ByteString] -> Name qualifiedName = NonEmpty.fromList +-- | Construct a qualified 'Name' from a `/` delimited path. +pathToQualifiedName :: ByteString -> Name +pathToQualifiedName = qualifiedName . splitOnPathSeparator + +-- | User friendly 'ByteString' of a qualified 'Name'. friendlyName :: Name -> ByteString friendlyName xs = intercalate "." (NonEmpty.toList xs) diff --git a/src/Data/Abstract/Path.hs b/src/Data/Abstract/Path.hs new file mode 100644 index 000000000..ce29e69bd --- /dev/null +++ b/src/Data/Abstract/Path.hs @@ -0,0 +1,24 @@ +module Data.Abstract.Path where + +import Prologue +import qualified Data.ByteString.Char8 as BC +import qualified Data.ByteString as B +import Data.Char (ord) + +-- | Split a 'ByteString' path on `/`, stripping quotes and any `./` prefix. +splitOnPathSeparator :: ByteString -> [ByteString] +splitOnPathSeparator = splitOnPathSeparator' id + +splitOnPathSeparator' :: (ByteString -> ByteString) -> ByteString -> [ByteString] +splitOnPathSeparator' f = BC.split '/' . f . dropRelativePrefix . stripQuotes + +stripQuotes :: ByteString -> ByteString +stripQuotes = B.filter (/= fromIntegral (ord '\"')) + +dropRelativePrefix :: ByteString -> ByteString +dropRelativePrefix = BC.dropWhile (== '/') . BC.dropWhile (== '.') + +dropExtension :: ByteString -> ByteString +dropExtension path = case BC.split '.' path of + [] -> path + xs -> BC.intercalate "." (Prelude.init xs) diff --git a/src/Data/Abstract/Value.hs b/src/Data/Abstract/Value.hs index d89ba74e6..df533790a 100644 --- a/src/Data/Abstract/Value.hs +++ b/src/Data/Abstract/Value.hs @@ -134,6 +134,9 @@ instance Show1 Array where liftShowsPrec = genericLiftShowsPrec -- | The environment for an abstract value type. type EnvironmentFor v = Environment (LocationFor v) v +-- | The exports for an abstract value type. +type ExportsFor v = Exports (LocationFor v) v + -- | The 'Heap' for an abstract value type. type HeapFor value = Heap (LocationFor value) value diff --git a/src/Data/Syntax.hs b/src/Data/Syntax.hs index ccd48d63f..d9b3f3917 100644 --- a/src/Data/Syntax.hs +++ b/src/Data/Syntax.hs @@ -108,7 +108,7 @@ instance Ord1 Identifier where liftCompare = genericLiftCompare instance Show1 Identifier where liftShowsPrec = genericLiftShowsPrec instance Evaluatable Identifier where - eval (Identifier name) = lookupWith deref name >>= maybe (fail ("free variable: " <> show name)) pure + eval (Identifier name) = lookupWith deref name >>= maybe (fail ("free variable: " <> show (friendlyName name))) pure instance FreeVariables1 Identifier where liftFreeVariables _ (Identifier x) = Set.singleton x diff --git a/src/Data/Syntax/Declaration.hs b/src/Data/Syntax/Declaration.hs index a28b5618a..e0ee0e888 100644 --- a/src/Data/Syntax/Declaration.hs +++ b/src/Data/Syntax/Declaration.hs @@ -5,6 +5,7 @@ import Data.Abstract.Environment import Data.Abstract.Evaluatable import Diffing.Algorithm import qualified Data.Map as Map +import Prelude hiding (fail) import Prologue data Function a = Function { functionContext :: ![a], functionName :: !a, functionParameters :: ![a], functionBody :: !a } @@ -223,7 +224,7 @@ instance Evaluatable QualifiedExport where eval (QualifiedExport exportSymbols) = do -- Insert the aliases with no addresses. for_ exportSymbols $ \(name, alias) -> - addExport name (alias, Nothing) + addExport name alias Nothing unit @@ -238,12 +239,15 @@ instance Show1 QualifiedExportFrom where liftShowsPrec = genericLiftShowsPrec instance Evaluatable QualifiedExportFrom where eval (QualifiedExportFrom from exportSymbols) = do let moduleName = freeVariable (subterm from) - importedEnv <- withGlobalEnv mempty (require moduleName) + importedEnv <- isolate (require moduleName) -- Look up addresses in importedEnv and insert the aliases with addresses into the exports. for_ exportSymbols $ \(name, alias) -> do let address = Map.lookup name (unEnvironment importedEnv) - addExport name (alias, address) + maybe (cannotExport moduleName name) (addExport name alias . Just) address unit + where + cannotExport moduleName name = fail $ + "module " <> show (friendlyName moduleName) <> " does not export " <> show (friendlyName name) newtype DefaultExport a = DefaultExport { defaultExport :: a } @@ -257,6 +261,8 @@ instance Evaluatable DefaultExport where -- | Qualified Import declarations (symbols are qualified in calling environment). +-- +-- If the list of symbols is empty copy and qualify everything to the calling environment. data QualifiedImport a = QualifiedImport { qualifiedImportFrom :: !a, qualifiedImportAlias :: !a, qualifiedImportSymbols :: ![(Name, Name)]} deriving (Diffable, Eq, Foldable, Functor, GAlign, Generic1, Mergeable, Ord, Show, Traversable, FreeVariables1) @@ -267,21 +273,21 @@ instance Show1 QualifiedImport where liftShowsPrec = genericLiftShowsPrec instance Evaluatable QualifiedImport where eval (QualifiedImport from alias xs) = do let moduleName = freeVariable (subterm from) - importedEnv <- withGlobalEnv mempty (require moduleName) + importedEnv <- isolate (require moduleName) modifyGlobalEnv (flip (Map.foldrWithKey copy) (unEnvironment importedEnv)) unit where prefix = freeVariable (subterm alias) symbols = Map.fromList xs - copy = if Map.null symbols then qualifyInsert else directInsert - qualifyInsert k v rest = envInsert (prefix <> k) v rest - directInsert k v rest = maybe rest (\symAlias -> envInsert symAlias v rest) (Map.lookup k symbols) + copy = if Map.null symbols then insert else maybeInsert + insert sym = envInsert (prefix <> sym) + maybeInsert sym v env = maybe env (\symAlias -> insert symAlias v env) (Map.lookup sym symbols) --- | Import declarations (symbols are added directly to the calling env). +-- | Import declarations (symbols are added directly to the calling environment). -- --- If symbols is empty, just import the module for its side effects. -data Import a = Import { importFrom :: !a, importSymbols :: ![(Name, Name)] } +-- If the list of symbols is empty copy everything to the calling environment. +data Import a = Import { importFrom :: !a, importSymbols :: ![(Name, Name)], importWildcardToken :: !a } deriving (Diffable, Eq, Foldable, Functor, GAlign, Generic1, Mergeable, Ord, Show, Traversable, FreeVariables1) instance Eq1 Import where liftEq = genericLiftEq @@ -289,31 +295,28 @@ instance Ord1 Import where liftCompare = genericLiftCompare instance Show1 Import where liftShowsPrec = genericLiftShowsPrec instance Evaluatable Import where - eval (Import from xs) = do + eval (Import from xs _) = do let moduleName = freeVariable (subterm from) - importedEnv <- withGlobalEnv mempty (require moduleName) - modifyGlobalEnv (flip (Map.foldrWithKey directInsert) (unEnvironment importedEnv)) + importedEnv <- isolate (require moduleName) + modifyGlobalEnv (flip (Map.foldrWithKey copy) (unEnvironment importedEnv)) unit where symbols = Map.fromList xs - directInsert k v rest = maybe rest (\symAlias -> envInsert symAlias v rest) (Map.lookup k symbols) + copy = if Map.null symbols then envInsert else maybeInsert + maybeInsert k v env = maybe env (\symAlias -> envInsert symAlias v env) (Map.lookup k symbols) - --- | A wildcard import (all symbols are added directly to the calling env) --- --- Import a module updating the importing environments. -data WildcardImport a = WildcardImport { wildcardImportFrom :: !a, wildcardImportToken :: !a } +-- | Side effect only imports (no symbols made available to the calling environment). +data SideEffectImport a = SideEffectImport { sideEffectImportFrom :: !a, sideEffectImportToken :: !a } deriving (Diffable, Eq, Foldable, Functor, GAlign, Generic1, Mergeable, Ord, Show, Traversable, FreeVariables1) -instance Eq1 WildcardImport where liftEq = genericLiftEq -instance Ord1 WildcardImport where liftCompare = genericLiftCompare -instance Show1 WildcardImport where liftShowsPrec = genericLiftShowsPrec +instance Eq1 SideEffectImport where liftEq = genericLiftEq +instance Ord1 SideEffectImport where liftCompare = genericLiftCompare +instance Show1 SideEffectImport where liftShowsPrec = genericLiftShowsPrec -instance Evaluatable WildcardImport where - eval (WildcardImport from _) = do +instance Evaluatable SideEffectImport where + eval (SideEffectImport from _) = do let moduleName = freeVariable (subterm from) - importedEnv <- withGlobalEnv mempty (require moduleName) - modifyGlobalEnv (flip (Map.foldrWithKey envInsert) (unEnvironment importedEnv)) + void $ isolate (require moduleName) unit diff --git a/src/Language/Go/Assignment.hs b/src/Language/Go/Assignment.hs index f9b0454df..1bbd21dae 100644 --- a/src/Language/Go/Assignment.hs +++ b/src/Language/Go/Assignment.hs @@ -8,9 +8,7 @@ module Language.Go.Assignment import Assigning.Assignment hiding (Assignment, Error) import Data.Abstract.FreeVariables -import qualified Data.ByteString.Char8 as BC -import qualified Data.ByteString as B -import Data.Char (ord) +import Data.Abstract.Path import Data.Record import Data.Syntax (contextualize, emptyTerm, parseError, handleError, infixContext, makeTerm, makeTerm', makeTerm'', makeTerm1) import Language.Go.Grammar as Grammar @@ -33,7 +31,7 @@ type Syntax = , Declaration.Function , Declaration.Import , Declaration.QualifiedImport - , Declaration.WildcardImport + , Declaration.SideEffectImport , Declaration.Method , Declaration.MethodSignature , Declaration.Module @@ -386,27 +384,27 @@ functionDeclaration = makeTerm <$> (symbol FunctionDeclaration <|> symbol FuncL importDeclaration :: Assignment importDeclaration = makeTerm'' <$> symbol ImportDeclaration <*> children (manyTerm (importSpec <|> importSpecList)) where - importSpecList = makeTerm <$> symbol ImportSpecList <*> children (manyTerm (importSpec <|> comment)) - importSpec = makeTerm <$> symbol ImportSpec <*> children sideEffectImport - <|> makeTerm <$> symbol ImportSpec <*> children dotImport - <|> makeTerm <$> symbol ImportSpec <*> children namedImport - <|> makeTerm <$> symbol ImportSpec <*> children plainImport - - dotImport = flip Declaration.WildcardImport <$> (makeTerm <$> symbol Dot <*> (Syntax.Identifier <$> (name <$> source))) <*> importFromPath - sideEffectImport = symbol BlankIdentifier *> source *> (Declaration.Import <$> importFromPath <*> pure []) - namedImport = flip Declaration.QualifiedImport <$> packageIdentifier <*> importFromPath <*> pure [] - plainImport = symbol InterpretedStringLiteral >>= \loc -> do - names <- pathToNames <$> source + -- `import . "lib/Math"` + dotImport = inj <$> (makeImport <$> dot <*> importFromPath) + -- dotImport = inj <$> (flip Declaration.Import <$> (symbol Dot *> source *> pure []) <*> importFromPath) + -- `import _ "lib/Math"` + sideEffectImport = inj <$> (flip Declaration.SideEffectImport <$> underscore <*> importFromPath) + -- `import m "lib/Math"` + namedImport = inj <$> (flip Declaration.QualifiedImport <$> packageIdentifier <*> importFromPath <*> pure []) + -- `import "lib/Math"` + plainImport = inj <$> (symbol InterpretedStringLiteral >>= \loc -> do + names <- splitOnPathSeparator <$> source let from = makeTerm loc (Syntax.Identifier (qualifiedName names)) let alias = makeTerm loc (Syntax.Identifier (name (last names))) -- Go takes `import "lib/Math"` and uses `Math` as the qualified name (e.g. `Math.Sin()`) - Declaration.QualifiedImport <$> pure from <*> pure alias <*> pure [] + Declaration.QualifiedImport <$> pure from <*> pure alias <*> pure []) + makeImport dot path = Declaration.Import path [] dot + dot = makeTerm <$> symbol Dot <*> (Literal.TextElement <$> source) + underscore = makeTerm <$> symbol BlankIdentifier <*> (Literal.TextElement <$> source) + importSpec = makeTerm' <$> symbol ImportSpec <*> children (sideEffectImport <|> dotImport <|> namedImport <|> plainImport) + importSpecList = makeTerm <$> symbol ImportSpecList <*> children (manyTerm (importSpec <|> comment)) importFromPath = makeTerm <$> symbol InterpretedStringLiteral <*> (Syntax.Identifier <$> (pathToQualifiedName <$> source)) - pathToQualifiedName = qualifiedName . pathToNames - pathToNames = BC.split '/' . (BC.dropWhile (== '/')) . (BC.dropWhile (== '.')) . stripQuotes - stripQuotes = B.filter (/= (fromIntegral (ord '\"'))) - indexExpression :: Assignment indexExpression = makeTerm <$> symbol IndexExpression <*> children (Expression.Subscript <$> expression <*> manyTerm expression) diff --git a/src/Language/Python/Assignment.hs b/src/Language/Python/Assignment.hs index 521075df4..d2c5dcf0d 100644 --- a/src/Language/Python/Assignment.hs +++ b/src/Language/Python/Assignment.hs @@ -37,9 +37,9 @@ type Syntax = , Declaration.Comprehension , Declaration.Decorator , Declaration.Function + , Declaration.Import , Declaration.QualifiedImport , Declaration.Variable - , Declaration.WildcardImport , Expression.Arithmetic , Expression.Boolean , Expression.Bitwise @@ -386,22 +386,24 @@ comment = makeTerm <$> symbol Comment <*> (Comment.Comment <$> source) import' :: Assignment import' = makeTerm'' <$> symbol ImportStatement <*> children (manyTerm (aliasedImport <|> plainImport)) - <|> makeTerm <$> symbol ImportFromStatement <*> children (Declaration.QualifiedImport <$> (identifier <|> emptyTerm) <*> emptyTerm <*> some (aliasImportSymbol <|> importSymbol)) - <|> makeTerm <$> symbol ImportFromStatement <*> children (Declaration.WildcardImport <$> identifier <*> wildcard) + <|> makeTerm <$> symbol ImportFromStatement <*> children (Declaration.Import <$> (identifier <|> emptyTerm) <*> (wildcard <|> (some (aliasImportSymbol <|> importSymbol))) <*> emptyTerm) where + -- `import a as b` + aliasedImport = makeImport <$> symbol AliasedImport <*> children ((,) <$> expression <*> (Just <$> expression)) + -- `import a` + plainImport = makeImport <$> location <*> ((,) <$> identifier <*> pure Nothing) + -- `from a import foo ` + importSymbol = makeNameAliasPair <$> rawIdentifier <*> pure Nothing + -- `from a import foo as bar` + aliasImportSymbol = symbol AliasedImport *> children (makeNameAliasPair <$> rawIdentifier <*> (Just <$> rawIdentifier)) + -- `from a import *` + wildcard = symbol WildcardImport *> source *> pure [] + rawIdentifier = (name <$> identifier') <|> (qualifiedName <$> dottedName') dottedName' = symbol DottedName *> children (some identifier') identifier' = (symbol Identifier <|> symbol Identifier') *> source - makeNameAliasPair from (Just alias) = (from, alias) makeNameAliasPair from Nothing = (from, from) - importSymbol = makeNameAliasPair <$> rawIdentifier <*> pure Nothing - aliasImportSymbol = symbol AliasedImport *> children (makeNameAliasPair <$> rawIdentifier <*> (Just <$> rawIdentifier)) - - wildcard = makeTerm <$> symbol WildcardImport <*> (Syntax.Identifier <$> (name <$> source)) - - aliasedImport = makeImport <$> symbol AliasedImport <*> children ((,) <$> expression <*> (Just <$> expression)) - plainImport = makeImport <$> location <*> ((,) <$> identifier <*> pure Nothing) makeImport loc (from, Just alias) = makeTerm loc (Declaration.QualifiedImport from alias []) makeImport loc (from, Nothing) = makeTerm loc (Declaration.QualifiedImport from from []) diff --git a/src/Language/Ruby/Assignment.hs b/src/Language/Ruby/Assignment.hs index 1d66f0700..ae1c5ffab 100644 --- a/src/Language/Ruby/Assignment.hs +++ b/src/Language/Ruby/Assignment.hs @@ -21,13 +21,13 @@ import qualified Data.Syntax.Expression as Expression import qualified Data.Syntax.Literal as Literal import qualified Data.Syntax.Statement as Statement import qualified Data.Term as Term +import qualified Language.Ruby.Syntax as Ruby.Syntax -- | The type of Ruby syntax. type Syntax = '[ Comment.Comment , Declaration.Class , Declaration.Function - , Declaration.Import , Declaration.Method , Declaration.Module , Expression.Arithmetic @@ -74,6 +74,8 @@ type Syntax = '[ , Syntax.Error , Syntax.Identifier , Syntax.Program + , Ruby.Syntax.Require + , Ruby.Syntax.Load , [] ] @@ -295,15 +297,20 @@ pair :: Assignment pair = makeTerm <$> symbol Pair <*> children (Literal.KeyValue <$> expression <*> (expression <|> emptyTerm)) methodCall :: Assignment -methodCall = makeTerm' <$> symbol MethodCall <*> children (require <|> regularCall) +methodCall = makeTerm' <$> symbol MethodCall <*> children (require <|> load <|> regularCall) where regularCall = inj <$> (Expression.Call <$> pure [] <*> expression <*> args <*> (block <|> emptyTerm)) require = inj <$> (symbol Identifier *> do s <- source - guard (elem s ["autoload", "load", "require", "require_relative"]) - Declaration.Import <$> args' <*> pure []) + guard (elem s ["require", "require_relative"]) + Ruby.Syntax.Require (s == "require_relative") <$> nameExpression) + load = inj <$> (symbol Identifier *> do + s <- source + guard (elem s ["load"]) + Ruby.Syntax.Load <$> loadArgs) args = (symbol ArgumentList <|> symbol ArgumentListWithParens) *> children (many expression) <|> pure [] - args' = makeTerm'' <$> (symbol ArgumentList <|> symbol ArgumentListWithParens) <*> children (many expression) <|> emptyTerm + loadArgs = (symbol ArgumentList <|> symbol ArgumentListWithParens) *> children (some expression) + nameExpression = (symbol ArgumentList <|> symbol ArgumentListWithParens) *> children expression call :: Assignment call = makeTerm <$> symbol Call <*> children (Expression.MemberAccess <$> expression <*> (expression <|> args)) diff --git a/src/Language/Ruby/Syntax.hs b/src/Language/Ruby/Syntax.hs new file mode 100644 index 000000000..753f3273b --- /dev/null +++ b/src/Language/Ruby/Syntax.hs @@ -0,0 +1,54 @@ +{-# LANGUAGE DeriveAnyClass #-} +module Language.Ruby.Syntax where + +import Control.Monad (unless) +import Control.Abstract.Value (MonadValue) +import Data.Abstract.Environment +import Data.Abstract.Evaluatable +import Data.Abstract.Path +import Data.Abstract.Value (LocationFor) +import Diffing.Algorithm +import Prelude hiding (fail) +import Prologue +import qualified Data.Map as Map + +data Require a = Require { requireRelative :: Bool, requirePath :: !a } + deriving (Diffable, Eq, Foldable, Functor, GAlign, Generic1, Mergeable, Ord, Show, Traversable, FreeVariables1) + +instance Eq1 Require where liftEq = genericLiftEq +instance Ord1 Require where liftCompare = genericLiftCompare +instance Show1 Require where liftShowsPrec = genericLiftShowsPrec + +instance Evaluatable Require where + eval (Require _ x) = do + name <- pathToQualifiedName <$> (subtermValue x >>= asString) + importedEnv <- isolate (require name) + modifyGlobalEnv (flip (Map.foldrWithKey envInsert) (unEnvironment importedEnv)) + unit + +newtype Load a = Load { loadArgs :: [a] } + deriving (Diffable, Eq, Foldable, Functor, GAlign, Generic1, Mergeable, Ord, Show, Traversable, FreeVariables1) + +instance Eq1 Load where liftEq = genericLiftEq +instance Ord1 Load where liftCompare = genericLiftCompare +instance Show1 Load where liftShowsPrec = genericLiftShowsPrec + +instance Evaluatable Load where + eval (Load [x]) = do + path <- subtermValue x >>= asString + doLoad path False + eval (Load [x, wrap]) = do + path <- subtermValue x >>= asString + shouldWrap <- subtermValue wrap >>= toBool + doLoad path shouldWrap + eval (Load _) = fail "invalid argument supplied to load, path is required" + +doLoad :: (MonadAnalysis term value m, MonadValue value m, Ord (LocationFor value)) => ByteString -> Bool -> m value +doLoad path shouldWrap = do + let name = pathToQualifiedName path + importedEnv <- isolate (load name) + unless shouldWrap $ modifyGlobalEnv (flip (Map.foldrWithKey envInsert) (unEnvironment importedEnv)) + unit + where pathToQualifiedName = qualifiedName . splitOnPathSeparator' dropExtension + +-- TODO: autoload diff --git a/src/Language/TypeScript/Assignment.hs b/src/Language/TypeScript/Assignment.hs index 63759a9d4..ab0df4765 100644 --- a/src/Language/TypeScript/Assignment.hs +++ b/src/Language/TypeScript/Assignment.hs @@ -9,9 +9,6 @@ module Language.TypeScript.Assignment import Assigning.Assignment hiding (Assignment, Error) import qualified Assigning.Assignment as Assignment import Data.Abstract.FreeVariables -import qualified Data.ByteString as B (filter) -import qualified Data.ByteString.Char8 as BC -import Data.Char (ord) import Data.Record import Data.Syntax (emptyTerm, handleError, parseError, infixContext, makeTerm, makeTerm', makeTerm'', makeTerm1, contextualize, postContextualize) import qualified Data.Syntax as Syntax @@ -39,6 +36,7 @@ type Syntax = '[ , Declaration.TypeAlias , Declaration.Import , Declaration.QualifiedImport + , Declaration.SideEffectImport , Declaration.DefaultExport , Declaration.QualifiedExport , Declaration.QualifiedExportFrom @@ -642,47 +640,39 @@ statementIdentifier = makeTerm <$> symbol StatementIdentifier <*> (Syntax.Identi importStatement :: Assignment importStatement = makeImportTerm <$> symbol Grammar.ImportStatement <*> children ((,) <$> importClause <*> term fromClause) - <|> makeImport <$> symbol Grammar.ImportStatement <*> children requireImport - <|> makeImport <$> symbol Grammar.ImportStatement <*> children bareRequireImport + <|> makeTerm' <$> symbol Grammar.ImportStatement <*> children (requireImport <|> sideEffectImport) where - -- Straightforward imports - makeImport loc (Just alias, symbols, from) = makeTerm loc (Declaration.QualifiedImport from alias symbols) - makeImport loc (Nothing, symbols, from) = makeTerm loc (Declaration.Import from symbols) - -- Import a file giving it an alias (e.g. import foo = require "./foo") - requireImport = symbol Grammar.ImportRequireClause *> children ((,,) <$> (Just <$> (term identifier)) <*> pure [] <*> term fromClause) - -- Import a file just for it's side effects (e.g. import "./foo") - bareRequireImport = (,,) <$> (pure Nothing) <*> pure [] <*> term fromClause + -- `import foo = require "./foo"` + requireImport = inj <$> (symbol Grammar.ImportRequireClause *> children (flip Declaration.QualifiedImport <$> (term identifier) <*> term fromClause <*> pure [])) + -- `import "./foo"` + sideEffectImport = inj <$> (Declaration.SideEffectImport <$> term fromClause <*> emptyTerm) + -- `import { bar } from "./foo"` + namedImport = (,,,) <$> pure Prelude.False <*> pure Nothing <*> (symbol Grammar.NamedImports *> children (many importSymbol)) <*> emptyTerm + -- `import defaultMember from "./foo"` + defaultImport = (,,,) <$> pure Prelude.False <*> pure Nothing <*> (pure <$> (makeNameAliasPair <$> rawIdentifier <*> pure Nothing)) <*> emptyTerm + -- `import * as name from "./foo"` + namespaceImport = symbol Grammar.NamespaceImport *> children ((,,,) <$> pure Prelude.True <*> (Just <$> (term identifier)) <*> pure [] <*> emptyTerm) + -- Combinations of the above. + importClause = symbol Grammar.ImportClause *> + children ( + (pure <$> namedImport) + <|> (pure <$> namespaceImport) + <|> ((\a b -> [a, b]) <$> defaultImport <*> (namedImport <|> namespaceImport)) + <|> (pure <$> defaultImport)) - -- Imports with import clauses - makeImportTerm1 loc from (Prelude.True, Just alias, symbols) = makeTerm loc (Declaration.QualifiedImport from alias symbols) - makeImportTerm1 loc from (Prelude.True, Nothing, symbols) = makeTerm loc (Declaration.QualifiedImport from from symbols) - makeImportTerm1 loc from (_, _, symbols) = makeTerm loc (Declaration.Import from symbols) + makeImportTerm1 loc from (Prelude.True, Just alias, symbols, _) = makeTerm loc (Declaration.QualifiedImport from alias symbols) + makeImportTerm1 loc from (Prelude.True, Nothing, symbols, _) = makeTerm loc (Declaration.QualifiedImport from from symbols) + makeImportTerm1 loc from (_, _, symbols, extra) = makeTerm loc (Declaration.Import from symbols extra) makeImportTerm loc ([x], from) = makeImportTerm1 loc from x makeImportTerm loc (xs, from) = makeTerm loc $ fmap (makeImportTerm1 loc from) xs - importClause = symbol Grammar.ImportClause *> - children ( (pure <$> namedImport) - <|> (pure <$> namespaceImport) - <|> ((\a b -> [a, b]) <$> defaultImport <*> (namedImport <|> namespaceImport)) - <|> (pure <$> defaultImport) - ) - namedImport = (,,) <$> pure Prelude.False <*> pure Nothing <*> (symbol Grammar.NamedImports *> children (many importSymbol)) -- import { bar } from "./foo" - defaultImport = (,,) <$> pure Prelude.False <*> pure Nothing <*> (pure <$> (makeNameAliasPair <$> rawIdentifier <*> pure Nothing)) -- import defaultMember from "./foo" - namespaceImport = symbol Grammar.NamespaceImport *> children ((,,) <$> pure Prelude.True <*> (Just <$> (term identifier)) <*> pure []) -- import * as name from "./foo" - importSymbol = symbol Grammar.ImportSpecifier *> children (makeNameAliasPair <$> rawIdentifier <*> (Just <$> rawIdentifier)) - <|> symbol Grammar.ImportSpecifier *> children (makeNameAliasPair <$> rawIdentifier <*> (pure Nothing)) + importSymbol = symbol Grammar.ImportSpecifier *> children (makeNameAliasPair <$> rawIdentifier <*> ((Just <$> rawIdentifier) <|> (pure Nothing))) rawIdentifier = (symbol Identifier <|> symbol Identifier') *> (name <$> source) makeNameAliasPair from (Just alias) = (from, alias) makeNameAliasPair from Nothing = (from, from) fromClause :: Assignment fromClause = makeTerm <$> symbol Grammar.String <*> (Syntax.Identifier <$> (pathToQualifiedName <$> source)) - where - pathToQualifiedName :: ByteString -> Name - pathToQualifiedName = qualifiedName . BC.split '/' . (BC.dropWhile (== '/')) . (BC.dropWhile (== '.')) . stripQuotes - - stripQuotes :: ByteString -> ByteString - stripQuotes = B.filter (/= (fromIntegral (ord '\"'))) debuggerStatement :: Assignment debuggerStatement = makeTerm <$> symbol Grammar.DebuggerStatement <*> (TypeScript.Syntax.Debugger <$ source) diff --git a/test/Analysis/Go/Spec.hs b/test/Analysis/Go/Spec.hs index 1e2d044ac..7fedf7488 100644 --- a/test/Analysis/Go/Spec.hs +++ b/test/Analysis/Go/Spec.hs @@ -1,4 +1,3 @@ -{-# LANGUAGE TypeApplications #-} module Analysis.Go.Spec (spec) where import Data.Abstract.Value diff --git a/test/Analysis/Python/Spec.hs b/test/Analysis/Python/Spec.hs index 84df0f177..7129ef560 100644 --- a/test/Analysis/Python/Spec.hs +++ b/test/Analysis/Python/Spec.hs @@ -1,4 +1,3 @@ -{-# LANGUAGE TypeApplications #-} module Analysis.Python.Spec (spec) where import Data.Abstract.Value diff --git a/test/Analysis/Ruby/Spec.hs b/test/Analysis/Ruby/Spec.hs new file mode 100644 index 000000000..332ea2725 --- /dev/null +++ b/test/Analysis/Ruby/Spec.hs @@ -0,0 +1,35 @@ +module Analysis.Ruby.Spec (spec) where + +import Data.Abstract.Value +import Data.Map + +import SpecHelpers + + +spec :: Spec +spec = parallel $ do + describe "evalutes Ruby" $ do + it "require_relative" $ do + env <- evaluate "main.rb" + let expectedEnv = Environment $ fromList [ (qualifiedName ["foo"], addr 0) ] + env `shouldBe` expectedEnv + + it "load" $ do + env <- evaluate "load.rb" + let expectedEnv = Environment $ fromList [ (qualifiedName ["foo"], addr 0) ] + env `shouldBe` expectedEnv + + it "load wrap" $ do + res <- evaluate' "load-wrap.rb" + fst res `shouldBe` Left "free variable: \"foo\"" + snd res `shouldBe` Environment (fromList [ ]) + + where + addr = Address . Precise + fixtures = "test/fixtures/ruby/analysis/" + evaluate entry = snd <$> evaluate' entry + evaluate' entry = fst . fst . fst . fst <$> + evaluateFiles rubyParser + [ fixtures <> entry + , fixtures <> "foo.rb" + ] diff --git a/test/Analysis/TypeScript/Spec.hs b/test/Analysis/TypeScript/Spec.hs index 0deef4129..a32348ef8 100644 --- a/test/Analysis/TypeScript/Spec.hs +++ b/test/Analysis/TypeScript/Spec.hs @@ -1,4 +1,3 @@ -{-# LANGUAGE TypeApplications #-} module Analysis.TypeScript.Spec (spec) where import Data.Abstract.Value @@ -31,12 +30,18 @@ spec = parallel $ do env <- evaluate "main2.ts" env `shouldBe` Environment (fromList []) + it "fails exporting symbols not defined in the module" $ do + env <- fst <$> evaluate' "bad-export.ts" + env `shouldBe` Left "module \"foo\" does not export \"pip\"" + where addr = Address . Precise fixtures = "test/fixtures/typescript/analysis/" - evaluate entry = snd . fst . fst . fst . fst <$> + evaluate entry = snd <$> evaluate' entry + evaluate' entry = fst . fst . fst . fst <$> evaluateFiles typescriptParser [ fixtures <> entry , fixtures <> "a.ts" , fixtures <> "foo.ts" + , fixtures <> "pip.ts" ] diff --git a/test/Spec.hs b/test/Spec.hs index 3d606ae9a..1fd244248 100644 --- a/test/Spec.hs +++ b/test/Spec.hs @@ -2,6 +2,7 @@ module Main where import qualified Analysis.Go.Spec import qualified Analysis.Python.Spec +import qualified Analysis.Ruby.Spec import qualified Analysis.TypeScript.Spec import qualified Assigning.Assignment.Spec import qualified Data.Diff.Spec @@ -27,6 +28,7 @@ main = hspec $ do parallel $ do describe "Analysis.Go" Analysis.Go.Spec.spec describe "Analysis.Python" Analysis.Python.Spec.spec + describe "Analysis.Ruby" Analysis.Ruby.Spec.spec describe "Analysis.TypeScript" Analysis.TypeScript.Spec.spec describe "Assigning.Assignment" Assigning.Assignment.Spec.spec describe "Data.Diff" Data.Diff.Spec.spec diff --git a/test/SpecHelpers.hs b/test/SpecHelpers.hs index 568fb4276..7d21c30b4 100644 --- a/test/SpecHelpers.hs +++ b/test/SpecHelpers.hs @@ -11,7 +11,7 @@ module SpecHelpers ( import Data.Abstract.Address as X import Data.Abstract.Environment as X -import Data.Abstract.FreeVariables as X +import Data.Abstract.FreeVariables as X hiding (dropExtension) import Data.Abstract.Heap as X import Data.Abstract.ModuleTable as X import Data.Blob as X diff --git a/test/fixtures/go/grouped-import-declarations.diffA-B.txt b/test/fixtures/go/grouped-import-declarations.diffA-B.txt index 6bcf72719..b7dd5826e 100644 --- a/test/fixtures/go/grouped-import-declarations.diffA-B.txt +++ b/test/fixtures/go/grouped-import-declarations.diffA-B.txt @@ -7,14 +7,18 @@ ->(Identifier) } { (Identifier) ->(Identifier) }) - (WildcardImport - { (Identifier) - ->(Identifier) } - (Identifier)) - (QualifiedImport - { (Identifier) - ->(Identifier) } - (Identifier))) + {+(Import + {+(Identifier)+} + {+(TextElement)+})+} + {+(QualifiedImport + {+(Identifier)+} + {+(Identifier)+})+} + {-(Import + {-(Identifier)-} + {-(TextElement)-})-} + {-(QualifiedImport + {-(Identifier)-} + {-(Identifier)-})-}) (Function (Empty) (Identifier) diff --git a/test/fixtures/go/grouped-import-declarations.diffB-A.txt b/test/fixtures/go/grouped-import-declarations.diffB-A.txt index 6bcf72719..b7dd5826e 100644 --- a/test/fixtures/go/grouped-import-declarations.diffB-A.txt +++ b/test/fixtures/go/grouped-import-declarations.diffB-A.txt @@ -7,14 +7,18 @@ ->(Identifier) } { (Identifier) ->(Identifier) }) - (WildcardImport - { (Identifier) - ->(Identifier) } - (Identifier)) - (QualifiedImport - { (Identifier) - ->(Identifier) } - (Identifier))) + {+(Import + {+(Identifier)+} + {+(TextElement)+})+} + {+(QualifiedImport + {+(Identifier)+} + {+(Identifier)+})+} + {-(Import + {-(Identifier)-} + {-(TextElement)-})-} + {-(QualifiedImport + {-(Identifier)-} + {-(Identifier)-})-}) (Function (Empty) (Identifier) diff --git a/test/fixtures/go/grouped-import-declarations.parseA.txt b/test/fixtures/go/grouped-import-declarations.parseA.txt index c8cd467fd..1a876c61c 100644 --- a/test/fixtures/go/grouped-import-declarations.parseA.txt +++ b/test/fixtures/go/grouped-import-declarations.parseA.txt @@ -5,9 +5,9 @@ (QualifiedImport (Identifier) (Identifier)) - (WildcardImport + (Import (Identifier) - (Identifier)) + (TextElement)) (QualifiedImport (Identifier) (Identifier))) diff --git a/test/fixtures/go/grouped-import-declarations.parseB.txt b/test/fixtures/go/grouped-import-declarations.parseB.txt index c8cd467fd..1a876c61c 100644 --- a/test/fixtures/go/grouped-import-declarations.parseB.txt +++ b/test/fixtures/go/grouped-import-declarations.parseB.txt @@ -5,9 +5,9 @@ (QualifiedImport (Identifier) (Identifier)) - (WildcardImport + (Import (Identifier) - (Identifier)) + (TextElement)) (QualifiedImport (Identifier) (Identifier))) diff --git a/test/fixtures/go/single-import-declarations.diffA-B.txt b/test/fixtures/go/single-import-declarations.diffA-B.txt index 96e0c8a0d..7ec3d901f 100644 --- a/test/fixtures/go/single-import-declarations.diffA-B.txt +++ b/test/fixtures/go/single-import-declarations.diffA-B.txt @@ -4,17 +4,17 @@ {+(QualifiedImport {+(Identifier)+} {+(Identifier)+})+} -{+(WildcardImport +{+(Import {+(Identifier)+} - {+(Identifier)+})+} + {+(TextElement)+})+} (QualifiedImport { (Identifier) ->(Identifier) } { (Identifier) ->(Identifier) }) -{-(WildcardImport +{-(Import {-(Identifier)-} - {-(Identifier)-})-} + {-(TextElement)-})-} {-(QualifiedImport {-(Identifier)-} {-(Identifier)-})-} diff --git a/test/fixtures/go/single-import-declarations.diffB-A.txt b/test/fixtures/go/single-import-declarations.diffB-A.txt index 96e0c8a0d..7ec3d901f 100644 --- a/test/fixtures/go/single-import-declarations.diffB-A.txt +++ b/test/fixtures/go/single-import-declarations.diffB-A.txt @@ -4,17 +4,17 @@ {+(QualifiedImport {+(Identifier)+} {+(Identifier)+})+} -{+(WildcardImport +{+(Import {+(Identifier)+} - {+(Identifier)+})+} + {+(TextElement)+})+} (QualifiedImport { (Identifier) ->(Identifier) } { (Identifier) ->(Identifier) }) -{-(WildcardImport +{-(Import {-(Identifier)-} - {-(Identifier)-})-} + {-(TextElement)-})-} {-(QualifiedImport {-(Identifier)-} {-(Identifier)-})-} diff --git a/test/fixtures/go/single-import-declarations.parseA.txt b/test/fixtures/go/single-import-declarations.parseA.txt index 2d35be6ef..65e7cdd40 100644 --- a/test/fixtures/go/single-import-declarations.parseA.txt +++ b/test/fixtures/go/single-import-declarations.parseA.txt @@ -4,9 +4,9 @@ (QualifiedImport (Identifier) (Identifier)) - (WildcardImport + (Import (Identifier) - (Identifier)) + (TextElement)) (QualifiedImport (Identifier) (Identifier)) diff --git a/test/fixtures/go/single-import-declarations.parseB.txt b/test/fixtures/go/single-import-declarations.parseB.txt index 2d35be6ef..65e7cdd40 100644 --- a/test/fixtures/go/single-import-declarations.parseB.txt +++ b/test/fixtures/go/single-import-declarations.parseB.txt @@ -4,9 +4,9 @@ (QualifiedImport (Identifier) (Identifier)) - (WildcardImport + (Import (Identifier) - (Identifier)) + (TextElement)) (QualifiedImport (Identifier) (Identifier)) diff --git a/test/fixtures/javascript/import.diffA-B.txt b/test/fixtures/javascript/import.diffA-B.txt index 48e18361f..16e534292 100644 --- a/test/fixtures/javascript/import.diffA-B.txt +++ b/test/fixtures/javascript/import.diffA-B.txt @@ -1,41 +1,65 @@ (Program - (Import - { (Identifier) - ->(Identifier) }) - (QualifiedImport - { (Identifier) - ->(Identifier) } - { (Identifier) - ->(Identifier) }) -{ (Import - {-(Identifier)-}) -->(Import - {+(Identifier)+}) } -{ (Import - {-(Identifier)-}) -->(Import - {+(Identifier)+}) } -{ (Import - {-(Identifier)-}) -->(Import - {+(Identifier)+}) } - ( - (Import - { (Identifier) - ->(Identifier) }) - { (Import - {-(Identifier)-}) - ->(Import - {+(Identifier)+}) }) - ( - (Import - { (Identifier) - ->(Identifier) }) - (QualifiedImport - { (Identifier) - ->(Identifier) } - { (Identifier) - ->(Identifier) })) - (Import - { (Identifier) - ->(Identifier) })) +{+(Import + {+(Identifier)+} + {+(Empty)+})+} +{+(QualifiedImport + {+(Identifier)+} + {+(Identifier)+})+} +{+(Import + {+(Identifier)+} + {+(Empty)+})+} +{+(Import + {+(Identifier)+} + {+(Empty)+})+} +{+(Import + {+(Identifier)+} + {+(Empty)+})+} +{+( + {+(Import + {+(Identifier)+} + {+(Empty)+})+} + {+(Import + {+(Identifier)+} + {+(Empty)+})+})+} +{+( + {+(Import + {+(Identifier)+} + {+(Empty)+})+} + {+(QualifiedImport + {+(Identifier)+} + {+(Identifier)+})+})+} +{+(SideEffectImport + {+(Identifier)+} + {+(Empty)+})+} +{-(Import + {-(Identifier)-} + {-(Empty)-})-} +{-(QualifiedImport + {-(Identifier)-} + {-(Identifier)-})-} +{-(Import + {-(Identifier)-} + {-(Empty)-})-} +{-(Import + {-(Identifier)-} + {-(Empty)-})-} +{-(Import + {-(Identifier)-} + {-(Empty)-})-} +{-( + {-(Import + {-(Identifier)-} + {-(Empty)-})-} + {-(Import + {-(Identifier)-} + {-(Empty)-})-})-} +{-( + {-(Import + {-(Identifier)-} + {-(Empty)-})-} + {-(QualifiedImport + {-(Identifier)-} + {-(Identifier)-})-})-} +{-(SideEffectImport + {-(Identifier)-} + {-(Empty)-})-}) diff --git a/test/fixtures/javascript/import.diffB-A.txt b/test/fixtures/javascript/import.diffB-A.txt index 48e18361f..c5d3c09bb 100644 --- a/test/fixtures/javascript/import.diffB-A.txt +++ b/test/fixtures/javascript/import.diffB-A.txt @@ -1,41 +1,65 @@ (Program - (Import - { (Identifier) - ->(Identifier) }) - (QualifiedImport - { (Identifier) - ->(Identifier) } - { (Identifier) - ->(Identifier) }) +{+(Import + {+(Identifier)+} + {+(Empty)+})+} +{+(QualifiedImport + {+(Identifier)+} + {+(Identifier)+})+} { (Import - {-(Identifier)-}) + {-(Identifier)-} + {-(Empty)-}) ->(Import - {+(Identifier)+}) } -{ (Import - {-(Identifier)-}) -->(Import - {+(Identifier)+}) } -{ (Import - {-(Identifier)-}) -->(Import - {+(Identifier)+}) } - ( - (Import - { (Identifier) - ->(Identifier) }) - { (Import - {-(Identifier)-}) - ->(Import - {+(Identifier)+}) }) - ( - (Import - { (Identifier) - ->(Identifier) }) - (QualifiedImport - { (Identifier) - ->(Identifier) } - { (Identifier) - ->(Identifier) })) - (Import - { (Identifier) - ->(Identifier) })) + {+(Identifier)+} + {+(Empty)+}) } +{+(Import + {+(Identifier)+} + {+(Empty)+})+} +{+(Import + {+(Identifier)+} + {+(Empty)+})+} +{+( + {+(Import + {+(Identifier)+} + {+(Empty)+})+} + {+(Import + {+(Identifier)+} + {+(Empty)+})+})+} +{+( + {+(Import + {+(Identifier)+} + {+(Empty)+})+} + {+(QualifiedImport + {+(Identifier)+} + {+(Identifier)+})+})+} +{+(SideEffectImport + {+(Identifier)+} + {+(Empty)+})+} +{-(QualifiedImport + {-(Identifier)-} + {-(Identifier)-})-} +{-(Import + {-(Identifier)-} + {-(Empty)-})-} +{-(Import + {-(Identifier)-} + {-(Empty)-})-} +{-(Import + {-(Identifier)-} + {-(Empty)-})-} +{-( + {-(Import + {-(Identifier)-} + {-(Empty)-})-} + {-(Import + {-(Identifier)-} + {-(Empty)-})-})-} +{-( + {-(Import + {-(Identifier)-} + {-(Empty)-})-} + {-(QualifiedImport + {-(Identifier)-} + {-(Identifier)-})-})-} +{-(SideEffectImport + {-(Identifier)-} + {-(Empty)-})-}) diff --git a/test/fixtures/javascript/import.parseA.txt b/test/fixtures/javascript/import.parseA.txt index 6bd6755f3..ea05f8535 100644 --- a/test/fixtures/javascript/import.parseA.txt +++ b/test/fixtures/javascript/import.parseA.txt @@ -1,25 +1,33 @@ (Program (Import - (Identifier)) + (Identifier) + (Empty)) (QualifiedImport (Identifier) (Identifier)) (Import - (Identifier)) + (Identifier) + (Empty)) (Import - (Identifier)) + (Identifier) + (Empty)) (Import - (Identifier)) + (Identifier) + (Empty)) ( (Import - (Identifier)) + (Identifier) + (Empty)) (Import - (Identifier))) + (Identifier) + (Empty))) ( (Import - (Identifier)) + (Identifier) + (Empty)) (QualifiedImport (Identifier) (Identifier))) - (Import - (Identifier))) + (SideEffectImport + (Identifier) + (Empty))) diff --git a/test/fixtures/javascript/import.parseB.txt b/test/fixtures/javascript/import.parseB.txt index 6bd6755f3..ea05f8535 100644 --- a/test/fixtures/javascript/import.parseB.txt +++ b/test/fixtures/javascript/import.parseB.txt @@ -1,25 +1,33 @@ (Program (Import - (Identifier)) + (Identifier) + (Empty)) (QualifiedImport (Identifier) (Identifier)) (Import - (Identifier)) + (Identifier) + (Empty)) (Import - (Identifier)) + (Identifier) + (Empty)) (Import - (Identifier)) + (Identifier) + (Empty)) ( (Import - (Identifier)) + (Identifier) + (Empty)) (Import - (Identifier))) + (Identifier) + (Empty))) ( (Import - (Identifier)) + (Identifier) + (Empty)) (QualifiedImport (Identifier) (Identifier))) - (Import - (Identifier))) + (SideEffectImport + (Identifier) + (Empty))) diff --git a/test/fixtures/python/import-from-statement.diffA-B.txt b/test/fixtures/python/import-from-statement.diffA-B.txt index 0d182be1a..5aa00923e 100644 --- a/test/fixtures/python/import-from-statement.diffA-B.txt +++ b/test/fixtures/python/import-from-statement.diffA-B.txt @@ -1,34 +1,34 @@ (Program -{+(QualifiedImport +{+(Import {+(Identifier)+} {+(Empty)+})+} -{+(QualifiedImport +{+(Import {+(Identifier)+} {+(Empty)+})+} -{+(WildcardImport +{ (Import + {-(Identifier)-} + {-(Empty)-}) +->(Import {+(Identifier)+} - {+(Identifier)+})+} -{+(QualifiedImport + {+(Empty)+}) } +{+(Import {+(Identifier)+} {+(Empty)+})+} -{+(QualifiedImport +{+(Import {+(Identifier)+} {+(Empty)+})+} -{+(QualifiedImport +{ (Import + {-(Identifier)-} + {-(Empty)-}) +->(Import {+(Empty)+} - {+(Empty)+})+} -{-(QualifiedImport + {+(Empty)+}) } +{-(Import {-(Identifier)-} {-(Empty)-})-} -{-(QualifiedImport +{-(Import {-(Identifier)-} {-(Empty)-})-} -{-(WildcardImport - {-(Identifier)-} - {-(Identifier)-})-} -{-(QualifiedImport - {-(Identifier)-} - {-(Empty)-})-} -{-(QualifiedImport +{-(Import {-(Empty)-} {-(Empty)-})-}) diff --git a/test/fixtures/python/import-from-statement.diffB-A.txt b/test/fixtures/python/import-from-statement.diffB-A.txt index 2571e0d5f..53a5a0e48 100644 --- a/test/fixtures/python/import-from-statement.diffB-A.txt +++ b/test/fixtures/python/import-from-statement.diffB-A.txt @@ -1,34 +1,34 @@ (Program -{+(QualifiedImport +{+(Import {+(Identifier)+} {+(Empty)+})+} -{+(QualifiedImport +{+(Import {+(Identifier)+} {+(Empty)+})+} -{+(WildcardImport - {+(Identifier)+} - {+(Identifier)+})+} -{+(QualifiedImport +{+(Import {+(Identifier)+} {+(Empty)+})+} -{+(QualifiedImport +{+(Import + {+(Identifier)+} + {+(Empty)+})+} +{+(Import {+(Empty)+} {+(Empty)+})+} -{-(QualifiedImport +{-(Import {-(Identifier)-} {-(Empty)-})-} -{-(QualifiedImport +{-(Import {-(Identifier)-} {-(Empty)-})-} -{-(WildcardImport - {-(Identifier)-} - {-(Identifier)-})-} -{-(QualifiedImport +{-(Import {-(Identifier)-} {-(Empty)-})-} -{-(QualifiedImport +{-(Import {-(Identifier)-} {-(Empty)-})-} -{-(QualifiedImport +{-(Import + {-(Identifier)-} + {-(Empty)-})-} +{-(Import {-(Empty)-} {-(Empty)-})-}) diff --git a/test/fixtures/python/import-from-statement.parseA.txt b/test/fixtures/python/import-from-statement.parseA.txt index 8897d83a5..06ddf01b0 100644 --- a/test/fixtures/python/import-from-statement.parseA.txt +++ b/test/fixtures/python/import-from-statement.parseA.txt @@ -1,16 +1,16 @@ (Program - (QualifiedImport + (Import (Identifier) (Empty)) - (QualifiedImport + (Import (Identifier) (Empty)) - (WildcardImport - (Identifier) - (Identifier)) - (QualifiedImport + (Import (Identifier) (Empty)) - (QualifiedImport + (Import + (Identifier) + (Empty)) + (Import (Empty) (Empty))) diff --git a/test/fixtures/python/import-from-statement.parseB.txt b/test/fixtures/python/import-from-statement.parseB.txt index d409e0e2d..9b3febadc 100644 --- a/test/fixtures/python/import-from-statement.parseB.txt +++ b/test/fixtures/python/import-from-statement.parseB.txt @@ -1,19 +1,19 @@ (Program - (QualifiedImport + (Import (Identifier) (Empty)) - (QualifiedImport + (Import (Identifier) (Empty)) - (WildcardImport - (Identifier) - (Identifier)) - (QualifiedImport + (Import (Identifier) (Empty)) - (QualifiedImport + (Import (Identifier) (Empty)) - (QualifiedImport + (Import + (Identifier) + (Empty)) + (Import (Empty) (Empty))) diff --git a/test/fixtures/ruby/analysis/foo.rb b/test/fixtures/ruby/analysis/foo.rb new file mode 100644 index 000000000..8917b8d4e --- /dev/null +++ b/test/fixtures/ruby/analysis/foo.rb @@ -0,0 +1,3 @@ +def foo(x) + return x +end diff --git a/test/fixtures/ruby/analysis/load-wrap.rb b/test/fixtures/ruby/analysis/load-wrap.rb new file mode 100644 index 000000000..1db226541 --- /dev/null +++ b/test/fixtures/ruby/analysis/load-wrap.rb @@ -0,0 +1,3 @@ +load "./foo.rb", true + +foo(1) diff --git a/test/fixtures/ruby/analysis/load.rb b/test/fixtures/ruby/analysis/load.rb new file mode 100644 index 000000000..b112b1deb --- /dev/null +++ b/test/fixtures/ruby/analysis/load.rb @@ -0,0 +1,3 @@ +load "./foo.rb" + +foo(1) diff --git a/test/fixtures/ruby/analysis/main.rb b/test/fixtures/ruby/analysis/main.rb new file mode 100644 index 000000000..fcc0cc22a --- /dev/null +++ b/test/fixtures/ruby/analysis/main.rb @@ -0,0 +1,3 @@ +require_relative "foo" + +foo(1) diff --git a/test/fixtures/ruby/require.diffA-B.txt b/test/fixtures/ruby/require.diffA-B.txt index 45b1b787c..ec63bdae0 100644 --- a/test/fixtures/ruby/require.diffA-B.txt +++ b/test/fixtures/ruby/require.diffA-B.txt @@ -1,12 +1,11 @@ (Program - (Import + (Require { (TextElement) ->(TextElement) }) -{+(Import - {+( - {+(Symbol)+} - {+(TextElement)+})+})+} -{-(Call + (Call + { (Identifier) + ->(Identifier) } + {+(Symbol)+} + {+(TextElement)+} {-(Identifier)-} - {-(Identifier)-} - {-(Empty)-})-}) + (Empty))) diff --git a/test/fixtures/ruby/require.diffB-A.txt b/test/fixtures/ruby/require.diffB-A.txt index ff66aea10..890c906af 100644 --- a/test/fixtures/ruby/require.diffB-A.txt +++ b/test/fixtures/ruby/require.diffB-A.txt @@ -1,12 +1,11 @@ (Program - (Import + (Require { (TextElement) ->(TextElement) }) -{+(Call + (Call + { (Identifier) + ->(Identifier) } {+(Identifier)+} - {+(Identifier)+} - {+(Empty)+})+} -{-(Import - {-( - {-(Symbol)-} - {-(TextElement)-})-})-}) + {-(Symbol)-} + {-(TextElement)-} + (Empty))) diff --git a/test/fixtures/ruby/require.parseA.txt b/test/fixtures/ruby/require.parseA.txt index c566f858c..f33f28750 100644 --- a/test/fixtures/ruby/require.parseA.txt +++ b/test/fixtures/ruby/require.parseA.txt @@ -1,5 +1,5 @@ (Program - (Import + (Require (TextElement)) (Call (Identifier) diff --git a/test/fixtures/ruby/require.parseB.txt b/test/fixtures/ruby/require.parseB.txt index 48a0e219a..638ed1768 100644 --- a/test/fixtures/ruby/require.parseB.txt +++ b/test/fixtures/ruby/require.parseB.txt @@ -1,7 +1,8 @@ (Program - (Import + (Require (TextElement)) - (Import - ( - (Symbol) - (TextElement)))) + (Call + (Identifier) + (Symbol) + (TextElement) + (Empty))) diff --git a/test/fixtures/typescript/ambient-exports.diffA-B.txt b/test/fixtures/typescript/ambient-exports.diffA-B.txt index a7c5bad9f..fda31a882 100644 --- a/test/fixtures/typescript/ambient-exports.diffA-B.txt +++ b/test/fixtures/typescript/ambient-exports.diffA-B.txt @@ -1,24 +1,30 @@ (Program (DefaultExport - {(Class - {-(Identifier)-} - {-([])-}) - -> - (Function + { (Class + {-(Identifier)-} + {-([])-}) + ->(Function + {+(Empty)+} + {+(Empty)+} + {+(Identifier)+} + {+(RequiredParameter {+(Empty)+} {+(Empty)+} - {+(Identifier)+} - {+(RequiredParameter - {+(Empty)+} - {+(Empty)+} - {+(Annotation{+(PredefinedType)+})+} - {+(Assignment{+(Identifier)+}{+(Empty)+})+})+} - {+(RequiredParameter - {+(Empty)+} - {+(Empty)+} - {+(Annotation{+(PredefinedType)+})+} - {+(Assignment{+(Identifier)+}{+(Empty)+})+})+} - {+({+(Return + {+(Annotation + {+(PredefinedType)+})+} + {+(Assignment + {+(Identifier)+} + {+(Empty)+})+})+} + {+(RequiredParameter + {+(Empty)+} + {+(Empty)+} + {+(Annotation + {+(PredefinedType)+})+} + {+(Assignment + {+(Identifier)+} + {+(Empty)+})+})+} + {+( + {+(Return {+(Hash {+(ShorthandPropertyIdentifier)+} - {+(ShorthandPropertyIdentifier)+})+})+})+})})) \ No newline at end of file + {+(ShorthandPropertyIdentifier)+})+})+})+}) })) diff --git a/test/fixtures/typescript/ambient-exports.diffB-A.txt b/test/fixtures/typescript/ambient-exports.diffB-A.txt index 22561ea57..afbaad04f 100644 --- a/test/fixtures/typescript/ambient-exports.diffB-A.txt +++ b/test/fixtures/typescript/ambient-exports.diffB-A.txt @@ -1,10 +1,30 @@ (Program (DefaultExport - {(Function + { (Function + {-(Empty)-} + {-(Empty)-} + {-(Identifier)-} + {-(RequiredParameter {-(Empty)-} {-(Empty)-} - {-(Identifier)-} - {-(RequiredParameter{-(Empty)-}{-(Empty)-}{-(Annotation{-(PredefinedType)-})-}{-(Assignment{-(Identifier)-}{-(Empty)-})-})-} - {-(RequiredParameter{-(Empty)-}{-(Empty)-}{-(Annotation{-(PredefinedType)-})-}{-(Assignment{-(Identifier)-}{-(Empty)-})-})-} - {-({-(Return{-(Hash{-(ShorthandPropertyIdentifier)-}{-(ShorthandPropertyIdentifier)-})-})-})-}) - ->(Class{+(Identifier)+}{+([])+})})) \ No newline at end of file + {-(Annotation + {-(PredefinedType)-})-} + {-(Assignment + {-(Identifier)-} + {-(Empty)-})-})-} + {-(RequiredParameter + {-(Empty)-} + {-(Empty)-} + {-(Annotation + {-(PredefinedType)-})-} + {-(Assignment + {-(Identifier)-} + {-(Empty)-})-})-} + {-( + {-(Return + {-(Hash + {-(ShorthandPropertyIdentifier)-} + {-(ShorthandPropertyIdentifier)-})-})-})-}) + ->(Class + {+(Identifier)+} + {+([])+}) })) diff --git a/test/fixtures/typescript/analysis/bad-export.ts b/test/fixtures/typescript/analysis/bad-export.ts new file mode 100644 index 000000000..b8fac52e9 --- /dev/null +++ b/test/fixtures/typescript/analysis/bad-export.ts @@ -0,0 +1,2 @@ +export { pip } from "./pip" +export { pip } from "./foo" diff --git a/test/fixtures/typescript/analysis/pip.ts b/test/fixtures/typescript/analysis/pip.ts new file mode 100644 index 000000000..0203bfe04 --- /dev/null +++ b/test/fixtures/typescript/analysis/pip.ts @@ -0,0 +1,5 @@ +export { pip } + +function pip() { + return "this is the pip function" +} diff --git a/test/fixtures/typescript/export.diffA-B.txt b/test/fixtures/typescript/export.diffA-B.txt index eb927dac7..a046f61e7 100644 --- a/test/fixtures/typescript/export.diffA-B.txt +++ b/test/fixtures/typescript/export.diffA-B.txt @@ -1,37 +1,81 @@ (Program - {(QualifiedExport)->(QualifiedExport)} - {(QualifiedExport)->(QualifiedExport)} +{ (QualifiedExport) +->(QualifiedExport) } +{ (QualifiedExport) +->(QualifiedExport) } (DefaultExport (VariableDeclaration - (Assignment(Empty){(Identifier)->(Identifier)}(Empty)) - (Assignment(Empty){(Identifier)->(Identifier)}(Empty)) - (Assignment(Empty){(Identifier)->(Identifier)}(Empty)))) + (Assignment + (Empty) + { (Identifier) + ->(Identifier) } + (Empty)) + (Assignment + (Empty) + { (Identifier) + ->(Identifier) } + (Empty)) + (Assignment + (Empty) + { (Identifier) + ->(Identifier) } + (Empty)))) (DefaultExport (VariableDeclaration - {-(Assignment{-(Empty)-}{-(Identifier)-}{-(Identifier)-})-} - (Assignment(Empty)(Identifier)(Identifier)) - (Assignment(Empty)(Identifier){(Empty)->(Identifier)}) - {+(Assignment{+(Empty)+}{+(Identifier)+}{+(Empty)+})+} - (Assignment(Empty){(Identifier)->(Identifier)}(Empty)))) + {-(Assignment + {-(Empty)-} + {-(Identifier)-} + {-(Identifier)-})-} + (Assignment + (Empty) + (Identifier) + (Identifier)) + (Assignment + (Empty) + (Identifier) + { (Empty) + ->(Identifier) }) + {+(Assignment + {+(Empty)+} + {+(Identifier)+} + {+(Empty)+})+} + (Assignment + (Empty) + { (Identifier) + ->(Identifier) } + (Empty)))) (DefaultExport - {(Identifier)->(Identifier)}) - {+(DefaultExport - {+(Function{+(Empty)+}{+(Empty)+}{+(Identifier)+}{+([])+})+})+} + { (Identifier) + ->(Identifier) }) +{+(DefaultExport + {+(Function + {+(Empty)+} + {+(Empty)+} + {+(Identifier)+} + {+([])+})+})+} (DefaultExport - (Function(Empty)(Empty)(Empty)([]))) - {+(QualifiedExport)+} - {+(DefaultExport - {+(TextElement)+})+} - {+(QualifiedExportFrom - {+(Identifier)+})+} - {+(QualifiedExportFrom - {+(Identifier)+})+} - {-(DefaultExport - {-(Function{-(Empty)-}{-(Empty)-}{-(Identifier)-}{-([])-})-})-} - {-(QualifiedExport)-} - {-(DefaultExport - {-(TextElement)-})-} - {-(QualifiedExportFrom - {-(Identifier)-})-} - {-(QualifiedExportFrom - {-(Identifier)-})-}) \ No newline at end of file + (Function + (Empty) + (Empty) + (Empty) + ([]))) +{+(QualifiedExport)+} +{+(DefaultExport + {+(TextElement)+})+} +{+(QualifiedExportFrom + {+(Identifier)+})+} +{+(QualifiedExportFrom + {+(Identifier)+})+} +{-(DefaultExport + {-(Function + {-(Empty)-} + {-(Empty)-} + {-(Identifier)-} + {-([])-})-})-} +{-(QualifiedExport)-} +{-(DefaultExport + {-(TextElement)-})-} +{-(QualifiedExportFrom + {-(Identifier)-})-} +{-(QualifiedExportFrom + {-(Identifier)-})-}) diff --git a/test/fixtures/typescript/export.diffB-A.txt b/test/fixtures/typescript/export.diffB-A.txt index 03d231796..6b97b37d8 100644 --- a/test/fixtures/typescript/export.diffB-A.txt +++ b/test/fixtures/typescript/export.diffB-A.txt @@ -1,29 +1,83 @@ (Program - {(QualifiedExport)->(QualifiedExport)} - {(QualifiedExport)->(QualifiedExport)} +{ (QualifiedExport) +->(QualifiedExport) } +{ (QualifiedExport) +->(QualifiedExport) } (DefaultExport (VariableDeclaration - (Assignment(Empty){(Identifier)->(Identifier)}(Empty)) - (Assignment(Empty){(Identifier)->(Identifier)}(Empty)) - (Assignment(Empty){(Identifier)->(Identifier)}(Empty)))) + (Assignment + (Empty) + { (Identifier) + ->(Identifier) } + (Empty)) + (Assignment + (Empty) + { (Identifier) + ->(Identifier) } + (Empty)) + (Assignment + (Empty) + { (Identifier) + ->(Identifier) } + (Empty)))) (DefaultExport (VariableDeclaration - {+(Assignment{+(Empty)+}{+(Identifier)+}{+(Identifier)+})+} - (Assignment(Empty)(Identifier)(Identifier)) - (Assignment(Empty)(Identifier){(Identifier)->(Empty)}) - {+(Assignment{+(Empty)+}{+(Identifier)+}{+(Empty)+})+} - {-(Assignment{-(Empty)-}{-(Identifier)-}{-(Empty)-})-} - {-(Assignment{-(Empty)-}{-(Identifier)-}{-(Empty)-})-})) + {+(Assignment + {+(Empty)+} + {+(Identifier)+} + {+(Identifier)+})+} + (Assignment + (Empty) + (Identifier) + (Identifier)) + (Assignment + (Empty) + (Identifier) + { (Identifier) + ->(Empty) }) + {+(Assignment + {+(Empty)+} + {+(Identifier)+} + {+(Empty)+})+} + {-(Assignment + {-(Empty)-} + {-(Identifier)-} + {-(Empty)-})-} + {-(Assignment + {-(Empty)-} + {-(Identifier)-} + {-(Empty)-})-})) (DefaultExport - {(Identifier)->(Identifier)}) - {-(DefaultExport - {-(Function{-(Empty)-}{-(Empty)-}{-(Identifier)-}{-([])-})-})-} + { (Identifier) + ->(Identifier) }) +{-(DefaultExport + {-(Function + {-(Empty)-} + {-(Empty)-} + {-(Identifier)-} + {-([])-})-})-} (DefaultExport - (Function(Empty)(Empty)(Empty)([]))) - {+(DefaultExport - {+(Function{+(Empty)+}{+(Empty)+}{+(Identifier)+}{+([])+})+})+} - {(QualifiedExport)->(QualifiedExport)} + (Function + (Empty) + (Empty) + (Empty) + ([]))) +{+(DefaultExport + {+(Function + {+(Empty)+} + {+(Empty)+} + {+(Identifier)+} + {+([])+})+})+} +{ (QualifiedExport) +->(QualifiedExport) } (DefaultExport - {(TextElement)->(TextElement)}) - {(QualifiedExportFrom {-(Identifier)-})->(QualifiedExportFrom {+(Identifier)+})} - {(QualifiedExportFrom {-(Identifier)-})->(QualifiedExportFrom {+(Identifier)+})}) \ No newline at end of file + { (TextElement) + ->(TextElement) }) +{ (QualifiedExportFrom + {-(Identifier)-}) +->(QualifiedExportFrom + {+(Identifier)+}) } +{ (QualifiedExportFrom + {-(Identifier)-}) +->(QualifiedExportFrom + {+(Identifier)+}) }) diff --git a/test/fixtures/typescript/export.parseA.txt b/test/fixtures/typescript/export.parseA.txt index 2c57afc28..fa9cfc3dd 100644 --- a/test/fixtures/typescript/export.parseA.txt +++ b/test/fixtures/typescript/export.parseA.txt @@ -3,19 +3,54 @@ (QualifiedExport) (DefaultExport (VariableDeclaration - (Assignment(Empty)(Identifier)(Empty)) - (Assignment(Empty)(Identifier)(Empty)) - (Assignment(Empty)(Identifier)(Empty)))) + (Assignment + (Empty) + (Identifier) + (Empty)) + (Assignment + (Empty) + (Identifier) + (Empty)) + (Assignment + (Empty) + (Identifier) + (Empty)))) (DefaultExport (VariableDeclaration - (Assignment(Empty)(Identifier)(Identifier)) - (Assignment(Empty)(Identifier)(Identifier)) - (Assignment(Empty)(Identifier)(Empty)) - (Assignment(Empty)(Identifier)(Empty)))) - (DefaultExport (Identifier)) - (DefaultExport (Function(Empty)(Empty)(Empty)([]))) - (DefaultExport (Function(Empty)(Empty)(Identifier)([]))) + (Assignment + (Empty) + (Identifier) + (Identifier)) + (Assignment + (Empty) + (Identifier) + (Identifier)) + (Assignment + (Empty) + (Identifier) + (Empty)) + (Assignment + (Empty) + (Identifier) + (Empty)))) + (DefaultExport + (Identifier)) + (DefaultExport + (Function + (Empty) + (Empty) + (Empty) + ([]))) + (DefaultExport + (Function + (Empty) + (Empty) + (Identifier) + ([]))) (QualifiedExport) - (DefaultExport (TextElement)) - (QualifiedExportFrom (Identifier)) - (QualifiedExportFrom (Identifier))) \ No newline at end of file + (DefaultExport + (TextElement)) + (QualifiedExportFrom + (Identifier)) + (QualifiedExportFrom + (Identifier))) diff --git a/test/fixtures/typescript/export.parseB.txt b/test/fixtures/typescript/export.parseB.txt index 217fc2584..bf96a822f 100644 --- a/test/fixtures/typescript/export.parseB.txt +++ b/test/fixtures/typescript/export.parseB.txt @@ -3,19 +3,54 @@ (QualifiedExport) (DefaultExport (VariableDeclaration - (Assignment(Empty)(Identifier)(Empty)) - (Assignment(Empty)(Identifier)(Empty)) - (Assignment(Empty)(Identifier)(Empty)))) + (Assignment + (Empty) + (Identifier) + (Empty)) + (Assignment + (Empty) + (Identifier) + (Empty)) + (Assignment + (Empty) + (Identifier) + (Empty)))) (DefaultExport (VariableDeclaration - (Assignment(Empty)(Identifier)(Identifier)) - (Assignment(Empty)(Identifier)(Identifier)) - (Assignment(Empty)(Identifier)(Empty)) - (Assignment(Empty)(Identifier)(Empty)))) - (DefaultExport(Identifier)) - (DefaultExport(Function(Empty)(Empty)(Identifier)([]))) - (DefaultExport(Function(Empty)(Empty)(Empty)([]))) + (Assignment + (Empty) + (Identifier) + (Identifier)) + (Assignment + (Empty) + (Identifier) + (Identifier)) + (Assignment + (Empty) + (Identifier) + (Empty)) + (Assignment + (Empty) + (Identifier) + (Empty)))) + (DefaultExport + (Identifier)) + (DefaultExport + (Function + (Empty) + (Empty) + (Identifier) + ([]))) + (DefaultExport + (Function + (Empty) + (Empty) + (Empty) + ([]))) (QualifiedExport) - (DefaultExport (TextElement)) - (QualifiedExportFrom (Identifier)) - (QualifiedExportFrom (Identifier))) \ No newline at end of file + (DefaultExport + (TextElement)) + (QualifiedExportFrom + (Identifier)) + (QualifiedExportFrom + (Identifier))) diff --git a/test/fixtures/typescript/import.diffA-B.txt b/test/fixtures/typescript/import.diffA-B.txt index 987fcaecf..70af8adff 100644 --- a/test/fixtures/typescript/import.diffA-B.txt +++ b/test/fixtures/typescript/import.diffA-B.txt @@ -1,45 +1,68 @@ (Program - (Import - { (Identifier) - ->(Identifier) }) - (QualifiedImport - { (Identifier) - ->(Identifier) } - { (Identifier) - ->(Identifier) }) -{ (Import - {-(Identifier)-}) -->(Import - {+(Identifier)+}) } -{ (Import - {-(Identifier)-}) -->(Import - {+(Identifier)+}) } -{ (Import - {-(Identifier)-}) -->(Import - {+(Identifier)+}) } - ( - (Import - { (Identifier) - ->(Identifier) }) - { (Import - {-(Identifier)-}) - ->(Import - {+(Identifier)+}) }) - ( - (Import - { (Identifier) - ->(Identifier) }) - (QualifiedImport - { (Identifier) - ->(Identifier) } - { (Identifier) - ->(Identifier) })) {+(Import + {+(Identifier)+} + {+(Empty)+})+} +{+(QualifiedImport + {+(Identifier)+} {+(Identifier)+})+} +{+(Import + {+(Identifier)+} + {+(Empty)+})+} +{+(Import + {+(Identifier)+} + {+(Empty)+})+} +{+(Import + {+(Identifier)+} + {+(Empty)+})+} +{+( + {+(Import + {+(Identifier)+} + {+(Empty)+})+} + {+(Import + {+(Identifier)+} + {+(Empty)+})+})+} +{+( + {+(Import + {+(Identifier)+} + {+(Empty)+})+} + {+(QualifiedImport + {+(Identifier)+} + {+(Identifier)+})+})+} +{+(SideEffectImport + {+(Identifier)+} + {+(Empty)+})+} {-(Import + {-(Identifier)-} + {-(Empty)-})-} +{-(QualifiedImport + {-(Identifier)-} {-(Identifier)-})-} +{-(Import + {-(Identifier)-} + {-(Empty)-})-} +{-(Import + {-(Identifier)-} + {-(Empty)-})-} +{-(Import + {-(Identifier)-} + {-(Empty)-})-} +{-( + {-(Import + {-(Identifier)-} + {-(Empty)-})-} + {-(Import + {-(Identifier)-} + {-(Empty)-})-})-} +{-( + {-(Import + {-(Identifier)-} + {-(Empty)-})-} + {-(QualifiedImport + {-(Identifier)-} + {-(Identifier)-})-})-} +{-(SideEffectImport + {-(Identifier)-} + {-(Empty)-})-} {-(QualifiedImport {-(Identifier)-} {-(Identifier)-})-}) diff --git a/test/fixtures/typescript/import.diffB-A.txt b/test/fixtures/typescript/import.diffB-A.txt index 79a3b8e36..17359b2a2 100644 --- a/test/fixtures/typescript/import.diffB-A.txt +++ b/test/fixtures/typescript/import.diffB-A.txt @@ -1,45 +1,68 @@ (Program - (Import - { (Identifier) - ->(Identifier) }) - (QualifiedImport - { (Identifier) - ->(Identifier) } - { (Identifier) - ->(Identifier) }) -{ (Import - {-(Identifier)-}) -->(Import - {+(Identifier)+}) } -{ (Import - {-(Identifier)-}) -->(Import - {+(Identifier)+}) } -{ (Import - {-(Identifier)-}) -->(Import - {+(Identifier)+}) } - ( - (Import - { (Identifier) - ->(Identifier) }) - { (Import - {-(Identifier)-}) - ->(Import - {+(Identifier)+}) }) - ( - (Import - { (Identifier) - ->(Identifier) }) - (QualifiedImport - { (Identifier) - ->(Identifier) } - { (Identifier) - ->(Identifier) })) {+(Import - {+(Identifier)+})+} + {+(Identifier)+} + {+(Empty)+})+} {+(QualifiedImport {+(Identifier)+} {+(Identifier)+})+} +{ (Import + {-(Identifier)-} + {-(Empty)-}) +->(Import + {+(Identifier)+} + {+(Empty)+}) } +{+(Import + {+(Identifier)+} + {+(Empty)+})+} +{+(Import + {+(Identifier)+} + {+(Empty)+})+} +{+( + {+(Import + {+(Identifier)+} + {+(Empty)+})+} + {+(Import + {+(Identifier)+} + {+(Empty)+})+})+} +{+( + {+(Import + {+(Identifier)+} + {+(Empty)+})+} + {+(QualifiedImport + {+(Identifier)+} + {+(Identifier)+})+})+} +{+(SideEffectImport + {+(Identifier)+} + {+(Empty)+})+} +{+(QualifiedImport + {+(Identifier)+} + {+(Identifier)+})+} +{-(QualifiedImport + {-(Identifier)-} + {-(Identifier)-})-} {-(Import - {-(Identifier)-})-}) + {-(Identifier)-} + {-(Empty)-})-} +{-(Import + {-(Identifier)-} + {-(Empty)-})-} +{-(Import + {-(Identifier)-} + {-(Empty)-})-} +{-( + {-(Import + {-(Identifier)-} + {-(Empty)-})-} + {-(Import + {-(Identifier)-} + {-(Empty)-})-})-} +{-( + {-(Import + {-(Identifier)-} + {-(Empty)-})-} + {-(QualifiedImport + {-(Identifier)-} + {-(Identifier)-})-})-} +{-(SideEffectImport + {-(Identifier)-} + {-(Empty)-})-}) diff --git a/test/fixtures/typescript/import.parseA.txt b/test/fixtures/typescript/import.parseA.txt index 23ec906e5..b51ad3f68 100644 --- a/test/fixtures/typescript/import.parseA.txt +++ b/test/fixtures/typescript/import.parseA.txt @@ -1,28 +1,36 @@ (Program (Import - (Identifier)) + (Identifier) + (Empty)) (QualifiedImport (Identifier) (Identifier)) (Import - (Identifier)) + (Identifier) + (Empty)) (Import - (Identifier)) + (Identifier) + (Empty)) (Import - (Identifier)) + (Identifier) + (Empty)) ( (Import - (Identifier)) + (Identifier) + (Empty)) (Import - (Identifier))) + (Identifier) + (Empty))) ( (Import - (Identifier)) + (Identifier) + (Empty)) (QualifiedImport (Identifier) (Identifier))) - (Import - (Identifier)) + (SideEffectImport + (Identifier) + (Empty)) (QualifiedImport (Identifier) (Identifier))) diff --git a/test/fixtures/typescript/import.parseB.txt b/test/fixtures/typescript/import.parseB.txt index 6bd6755f3..ea05f8535 100644 --- a/test/fixtures/typescript/import.parseB.txt +++ b/test/fixtures/typescript/import.parseB.txt @@ -1,25 +1,33 @@ (Program (Import - (Identifier)) + (Identifier) + (Empty)) (QualifiedImport (Identifier) (Identifier)) (Import - (Identifier)) + (Identifier) + (Empty)) (Import - (Identifier)) + (Identifier) + (Empty)) (Import - (Identifier)) + (Identifier) + (Empty)) ( (Import - (Identifier)) + (Identifier) + (Empty)) (Import - (Identifier))) + (Identifier) + (Empty))) ( (Import - (Identifier)) + (Identifier) + (Empty)) (QualifiedImport (Identifier) (Identifier))) - (Import - (Identifier))) + (SideEffectImport + (Identifier) + (Empty)))