mirror of
https://github.com/github/semantic.git
synced 2024-12-19 21:01:35 +03:00
Merge pull request #1574 from github/ruby-imports
Eval Ruby require and load
This commit is contained in:
commit
e40a5e7c71
@ -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
|
||||
|
@ -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
|
||||
= 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.SideEffectImport where
|
||||
customToDeclaration Blob{..} _ (Declaration.SideEffectImport (Term (In fromAnn _), _) _)
|
||||
= Just $ ImportDeclaration ((stripQuotes . getSource) fromAnn) "" [] blobLanguage
|
||||
where
|
||||
stripQuotes = T.dropAround (`elem` ['"', '\''])
|
||||
getSource = toText . flip Source.slice blobSource . getField
|
||||
= 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), _) _ _)
|
||||
@ -185,6 +191,7 @@ type family DeclarationStrategy syntax where
|
||||
DeclarationStrategy Declaration.Import = 'Custom
|
||||
DeclarationStrategy Declaration.QualifiedImport = 'Custom
|
||||
DeclarationStrategy Declaration.SideEffectImport = 'Custom
|
||||
DeclarationStrategy Ruby.Syntax.Require = 'Custom
|
||||
DeclarationStrategy Declaration.Method = 'Custom
|
||||
DeclarationStrategy Markdown.Heading = 'Custom
|
||||
DeclarationStrategy Expression.Call = 'Custom
|
||||
|
@ -56,7 +56,7 @@ class (Monad m, Show value) => MonadValue value m where
|
||||
-- necessary to satisfy implementation details of Haskell left/right shift,
|
||||
-- but it's fine, since these are only ever operating on integral values.
|
||||
liftBitwise2 :: (forall a . (Integral a, Bits a) => a -> a -> a)
|
||||
-> (value -> value -> m value)
|
||||
-> (value -> value -> m value)
|
||||
|
||||
-- | Construct an abstract boolean value.
|
||||
boolean :: Bool -> m value
|
||||
@ -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
|
||||
|
||||
@ -143,9 +146,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)
|
||||
|
@ -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)
|
||||
|
||||
|
24
src/Data/Abstract/Path.hs
Normal file
24
src/Data/Abstract/Path.hs
Normal file
@ -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)
|
@ -107,7 +107,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
|
||||
|
@ -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
|
||||
@ -395,7 +393,7 @@ importDeclaration = makeTerm'' <$> symbol ImportDeclaration <*> children (manyTe
|
||||
namedImport = inj <$> (flip Declaration.QualifiedImport <$> packageIdentifier <*> importFromPath <*> pure [])
|
||||
-- `import "lib/Math"`
|
||||
plainImport = inj <$> (symbol InterpretedStringLiteral >>= \loc -> do
|
||||
names <- pathToNames <$> source
|
||||
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 [])
|
||||
@ -406,9 +404,6 @@ importDeclaration = makeTerm'' <$> symbol ImportDeclaration <*> children (manyTe
|
||||
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)
|
||||
|
@ -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 [] <*> emptyTerm)
|
||||
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))
|
||||
|
54
src/Language/Ruby/Syntax.hs
Normal file
54
src/Language/Ruby/Syntax.hs
Normal file
@ -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
|
@ -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
|
||||
@ -676,12 +673,6 @@ importStatement = makeImportTerm <$> symbol Grammar.ImportStatement <*> childr
|
||||
|
||||
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)
|
||||
|
@ -1,4 +1,3 @@
|
||||
{-# LANGUAGE TypeApplications #-}
|
||||
module Analysis.Go.Spec (spec) where
|
||||
|
||||
import Data.Abstract.Value
|
||||
|
@ -1,4 +1,3 @@
|
||||
{-# LANGUAGE TypeApplications #-}
|
||||
module Analysis.Python.Spec (spec) where
|
||||
|
||||
import Data.Abstract.Value
|
||||
|
35
test/Analysis/Ruby/Spec.hs
Normal file
35
test/Analysis/Ruby/Spec.hs
Normal file
@ -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"
|
||||
]
|
@ -1,4 +1,3 @@
|
||||
{-# LANGUAGE TypeApplications #-}
|
||||
module Analysis.TypeScript.Spec (spec) where
|
||||
|
||||
import Data.Abstract.Value
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
3
test/fixtures/ruby/analysis/foo.rb
vendored
Normal file
3
test/fixtures/ruby/analysis/foo.rb
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
def foo(x)
|
||||
return x
|
||||
end
|
3
test/fixtures/ruby/analysis/load-wrap.rb
vendored
Normal file
3
test/fixtures/ruby/analysis/load-wrap.rb
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
load "./foo.rb", true
|
||||
|
||||
foo(1)
|
3
test/fixtures/ruby/analysis/load.rb
vendored
Normal file
3
test/fixtures/ruby/analysis/load.rb
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
load "./foo.rb"
|
||||
|
||||
foo(1)
|
3
test/fixtures/ruby/analysis/main.rb
vendored
Normal file
3
test/fixtures/ruby/analysis/main.rb
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
require_relative "foo"
|
||||
|
||||
foo(1)
|
19
test/fixtures/ruby/require.diffA-B.txt
vendored
19
test/fixtures/ruby/require.diffA-B.txt
vendored
@ -1,14 +1,11 @@
|
||||
(Program
|
||||
(Import
|
||||
(Require
|
||||
{ (TextElement)
|
||||
->(TextElement) }
|
||||
(Empty))
|
||||
{+(Import
|
||||
{+(
|
||||
{+(Symbol)+}
|
||||
{+(TextElement)+})+}
|
||||
{+(Empty)+})+}
|
||||
{-(Call
|
||||
->(TextElement) })
|
||||
(Call
|
||||
{ (Identifier)
|
||||
->(Identifier) }
|
||||
{+(Symbol)+}
|
||||
{+(TextElement)+}
|
||||
{-(Identifier)-}
|
||||
{-(Identifier)-}
|
||||
{-(Empty)-})-})
|
||||
(Empty)))
|
||||
|
19
test/fixtures/ruby/require.diffB-A.txt
vendored
19
test/fixtures/ruby/require.diffB-A.txt
vendored
@ -1,14 +1,11 @@
|
||||
(Program
|
||||
(Import
|
||||
(Require
|
||||
{ (TextElement)
|
||||
->(TextElement) }
|
||||
(Empty))
|
||||
{+(Call
|
||||
->(TextElement) })
|
||||
(Call
|
||||
{ (Identifier)
|
||||
->(Identifier) }
|
||||
{+(Identifier)+}
|
||||
{+(Identifier)+}
|
||||
{+(Empty)+})+}
|
||||
{-(Import
|
||||
{-(
|
||||
{-(Symbol)-}
|
||||
{-(TextElement)-})-}
|
||||
{-(Empty)-})-})
|
||||
{-(Symbol)-}
|
||||
{-(TextElement)-}
|
||||
(Empty)))
|
||||
|
5
test/fixtures/ruby/require.parseA.txt
vendored
5
test/fixtures/ruby/require.parseA.txt
vendored
@ -1,7 +1,6 @@
|
||||
(Program
|
||||
(Import
|
||||
(TextElement)
|
||||
(Empty))
|
||||
(Require
|
||||
(TextElement))
|
||||
(Call
|
||||
(Identifier)
|
||||
(Identifier)
|
||||
|
11
test/fixtures/ruby/require.parseB.txt
vendored
11
test/fixtures/ruby/require.parseB.txt
vendored
@ -1,9 +1,8 @@
|
||||
(Program
|
||||
(Import
|
||||
(Require
|
||||
(TextElement))
|
||||
(Call
|
||||
(Identifier)
|
||||
(Symbol)
|
||||
(TextElement)
|
||||
(Empty))
|
||||
(Import
|
||||
(
|
||||
(Symbol)
|
||||
(TextElement))
|
||||
(Empty)))
|
||||
|
Loading…
Reference in New Issue
Block a user