hic-0.0.0.1: test/Language/Cimple/Analysis/ScopeSpec.hs
{-# LANGUAGE OverloadedStrings #-}
module Language.Cimple.Analysis.ScopeSpec (spec) where
import Data.Text (Text)
import qualified Data.Text as Text
import qualified Language.Cimple as C
import Language.Cimple.Analysis.Scope
import Language.Cimple.Hic.InferenceSpec (mustParseNodes)
import Language.Cimple.Pretty (showNodePlain)
import qualified Language.Cimple.Program as Program
import Test.Hspec
spec :: Spec
spec = describe "Language.Cimple.Analysis.Scope" $ do
it "resolves a simple variable" $ do
ast <- mustParseNodes
[ "int main() {"
, " int x;"
, " return x;"
, "}"
]
let (transformedAst, _finalState) = runScopePass ast
let expected = Text.unlines
[ "int main_1() {"
, " int x_2;"
, ""
, " return x_2;"
, "}"
]
let actual = Text.unlines (map showNodePlain transformedAst)
Text.stripEnd actual `shouldBe` Text.stripEnd expected
it "handles name shadowing" $ do
ast <- mustParseNodes
[ "int main() {"
, " int x;"
, " if (true) {"
, " int x;"
, " return x;"
, " }"
, " return x;"
, "}"
]
let (transformedAst, _finalState) = runScopePass ast
let expected = Text.unlines
[ "int main_1() {"
, " int x_2;"
, ""
, " if (true) {"
, " int x_3;"
, ""
, " return x_3;"
, " }"
, ""
, " return x_2;"
, "}"
]
let actual = Text.unlines (map showNodePlain transformedAst)
Text.stripEnd actual `shouldBe` Text.stripEnd expected
it "handles function parameters" $ do
ast <- mustParseNodes
[ "int f(int x) {"
, " return x;"
, "}"
]
let (transformedAst, _finalState) = runScopePass ast
let expected = Text.unlines
[ "int f_1(int x_2) {"
, " return x_2;"
, "}"
]
let actual = Text.unlines (map showNodePlain transformedAst)
Text.stripEnd actual `shouldBe` Text.stripEnd expected
it "handles global variables" $ do
ast <- mustParseNodes
[ "const int x = 3;"
, "int main() {"
, " return x;"
, "}"
]
let (transformedAst, _finalState) = runScopePass ast
let expected = Text.unlines
[ "const int x_1 = 3;"
, "int main_2() {"
, " return x_1;"
, "}"
]
let actual = Text.unlines (map showNodePlain transformedAst)
Text.stripEnd actual `shouldBe` Text.stripEnd expected
it "handles static variables" $ do
ast <- mustParseNodes
[ "static const int x = 3;"
, "int main() {"
, " return x;"
, "}"
]
let (transformedAst, _finalState) = runScopePass ast
let expected = Text.unlines
[ "static const int x_1 = 3;"
, "int main_2() {"
, " return x_1;"
, "}"
]
let actual = Text.unlines (map showNodePlain transformedAst)
Text.stripEnd actual `shouldBe` Text.stripEnd expected
it "handles function declarations and definitions" $ do
ast <- mustParseNodes
[ "int f(int x);"
, "int main() {"
, " return f(0);"
, "}"
, "int f(int x) {"
, " return x;"
, "}"
]
let (transformedAst, _finalState) = runScopePass ast
let expected = Text.unlines
[ "int f_1(int x_2);"
, "int main_3() {"
, " return f_1(0);"
, "}"
, "int f_1(int x_4) {"
, " return x_4;"
, "}"
]
let actual = Text.unlines (map showNodePlain transformedAst)
Text.stripEnd actual `shouldBe` Text.stripEnd expected
it "handles for loop initializers" $ do
ast <- mustParseNodes
[ "int main() {"
, " for (int i = 0; i < 10; ++i) {"
, " int x;"
, " }"
, "}"
]
let (transformedAst, _finalState) = runScopePass ast
let expected = Text.unlines
[ "int main_1() {"
, " for (int i_2 = 0; i_2 < 10; ++i_2) {"
, " int x_3;"
, " }"
, "}"
]
let actual = Text.unlines (map showNodePlain transformedAst)
Text.stripEnd actual `shouldBe` Text.stripEnd expected
it "handles structs" $ do
ast <- mustParseNodes
[ "struct Struct {"
, " int x;"
, "};"
, "int main() {"
, " struct Struct s;"
, " s.x = 0;"
, " return s.x;"
, "}"
]
let (transformedAst, _finalState) = runScopePass ast
let expected = Text.unlines
[ "struct Struct {"
, " int x_1;"
, "};"
, "int main_2() {"
, " struct Struct s_3;"
, ""
, " s_3.x = 0;"
, ""
, " return s_3.x;"
, "}"
]
let actual = Text.unlines (map showNodePlain transformedAst)
Text.stripEnd actual `shouldBe` Text.stripEnd expected
it "handles unions" $ do
ast <- mustParseNodes
[ "union Union {"
, " int x;"
, " float y;"
, "};"
, "int main() {"
, " union Union u;"
, " u.x = 0;"
, " return u.x;"
, "}"
]
let (transformedAst, _finalState) = runScopePass ast
let expected = Text.unlines
[ "union Union {"
, " int x_1;"
, " float y_2;"
, "};"
, "int main_3() {"
, " union Union u_4;"
, ""
, " u_4.x = 0;"
, ""
, " return u_4.x;"
, "}"
]
let actual = Text.unlines (map showNodePlain transformedAst)
Text.stripEnd actual `shouldBe` Text.stripEnd expected
it "handles enums" $ do
ast <- mustParseNodes
[ "typedef enum Enum {"
, " ENUM_A,"
, " ENUM_B"
, "} Enum;"
, "int main() {"
, " Enum e = ENUM_A;"
, " return e;"
, "}"
]
let (transformedAst, _finalState) = runScopePass ast
let expected = Text.unlines
[ "typedef enum Enum {"
, " ENUM_A_1,"
, " ENUM_B_2,"
, "} Enum;"
, "int main_3() {"
, " Enum e_4 = ENUM_A_1;"
, ""
, " return e_4;"
, "}"
]
let actual = Text.unlines (map showNodePlain transformedAst)
Text.stripEnd actual `shouldBe` Text.stripEnd expected
it "handles typedefs" $ do
ast <- mustParseNodes
[ "typedef int My_Int;"
, "int main() {"
, " My_Int x;"
, " return x;"
, "}"
]
let (transformedAst, _finalState) = runScopePass ast
let expected = Text.unlines
[ "typedef int My_Int;"
, "int main_1() {"
, " My_Int x_2;"
, ""
, " return x_2;"
, "}"
]
let actual = Text.unlines (map showNodePlain transformedAst)
Text.stripEnd actual `shouldBe` Text.stripEnd expected
it "handles a complex scenario" $ do
ast <- mustParseNodes
[ "const int g = 3;"
, "static const int s = 4;"
, "int f(int p) {"
, " int l;"
, " return g + s + p + l;"
, "}"
, "int main() {"
, " return f(s);"
, "}"
]
let (transformedAst, _finalState) = runScopePass ast
let expected = Text.unlines
[ "const int g_1 = 3;"
, "static const int s_2 = 4;"
, "int f_3(int p_4) {"
, " int l_5;"
, ""
, " return g_1 + s_2 + p_4 + l_5;"
, "}"
, "int main_6() {"
, " return f_3(s_2);"
, "}"
]
let actual = Text.unlines (map showNodePlain transformedAst)
Text.stripEnd actual `shouldBe` Text.stripEnd expected
it "handles a very complex scoping scenario" $ do
ast <- mustParseNodes
[ "const int g = 1;"
, "typedef int math_op_cb(int a, int b);"
, "int add(int a, int b) { return a + b; }"
, "int complex_scope(int g) {"
, " int s = 0;"
, " s = s + 1;"
, " math_op_cb *operation = &add;"
, " if (s > 1) {"
, " int s = 100;"
, " return operation(g, s);"
, " }"
, " return operation(g, s);"
, "}"
]
let (transformedAst, finalState) = runScopePass ast
let expected = Text.unlines
[ "const int g_1 = 1;"
, "typedef int math_op_cb(int a_2, int b_3);"
, "int add_4(int a_5, int b_6) {"
, " return a_5 + b_6;"
, "}"
, "int complex_scope_7(int g_8) {"
, " int s_9 = 0;"
, ""
, " s_9 = s_9 + 1;"
, ""
, " math_op_cb* operation_10 = &add_4;"
, ""
, " if (s_9 > 1) {"
, " int s_11 = 100;"
, ""
, " return operation_10(g_8, s_11);"
, " }"
, ""
, " return operation_10(g_8, s_9);"
, "}"
]
let actual = Text.unlines (map showNodePlain transformedAst)
ssErrors finalState `shouldBe` []
Text.stripEnd actual `shouldBe` Text.stripEnd expected
it "reports an error for variables used out of scope" $ do
ast <- mustParseNodes
[ "int main() {"
, " for (int i = 0; i < 1; ++i) { continue; }"
, " return i;"
, "}"
]
let (_transformedAst, finalState) = runScopePass ast
ssErrors finalState `shouldBe` ["Undeclared variable: \"i\""]