Repository: Jaseci-Labs/jaseci (language: python)

Issue:
[FEATURE]: Create System Admin CLI
# Argument Input
> jac create_system_admin  jac_cloud/tests/simple_graph.jac -e testing@gmail.com -p 12345678
> System Admin created with id: 671f9d28f7bc1bbe0d4a6813

or

# Manual Input
> jac create_system_admin  jac_cloud/tests/simple_graph.jac
> Email: testing@gmail.com
> Confirm Email: testing@gmail.com
> Password: 
> Confirm Password: 
> System Admin created with id: 671f9d28f7bc1bbe0d4a6813


Discussion / hints:

Fix patch:
diff --git a/jac-cloud/jac_cloud/jaseci/models/user.py b/jac-cloud/jac_cloud/jaseci/models/user.py
index 07ebb4cea8..8a8e4fd278 100644
--- a/jac-cloud/jac_cloud/jaseci/models/user.py
+++ b/jac-cloud/jac_cloud/jaseci/models/user.py
@@ -20,13 +20,12 @@ class UserRegistration(BaseModel):
     """User Common Functionalities."""
 
     email: EmailStr
-    password: bytes
+    password: str
 
     def obfuscate(self) -> dict:
         """Return BaseModel.model_dump where the password is hashed."""
         data = self.model_dump(exclude={"password"})
-        if isinstance(self.password, str):
-            data["password"] = pbkdf2_sha512.hash(self.password).encode()
+        data["password"] = pbkdf2_sha512.hash(self.password).encode()
         return data
 
     def printable(self) -> dict:
@@ -45,6 +44,7 @@ class User:
     password: bytes
     root_id: ObjectId
     is_activated: bool = False
+    is_admin: bool = False
     sso: dict[str, dict[str, str]] = field(default_factory=dict)
 
     class Collection(BaseCollection["User"]):
@@ -119,6 +119,7 @@ def register_type() -> Type[UserRegistration]:
         user_model.pop("id", None)
         user_model.pop("root_id", None)
         user_model.pop("is_activated", None)
+        user_model.pop("is_admin", None)
         user_model.pop("sso", None)
 
         return create_model("UserRegister", __base__=UserRegistration, **user_model)
@@ -137,3 +138,8 @@ def send_reset_code(code: str, email: str) -> None:
     def sso_mapper(open_id: OpenID) -> dict[str, object]:
         """Send verification code."""
         return {}
+
+    @staticmethod
+    def system_admin_default() -> dict[str, object]:
+        """System Admin default data."""
+        return {}
diff --git a/jac-cloud/jac_cloud/jaseci/routers/user.py b/jac-cloud/jac_cloud/jaseci/routers/user.py
index aca14c1ac9..52fa23969f 100644
--- a/jac-cloud/jac_cloud/jaseci/routers/user.py
+++ b/jac-cloud/jac_cloud/jaseci/routers/user.py
@@ -45,6 +45,7 @@ def register(req: User.register_type()) -> ORJSONResponse:  # type: ignore
 
         req_obf: dict = req.obfuscate()
         req_obf["root_id"] = root.id
+        req_obf["is_admin"] = False
         is_activated = req_obf["is_activated"] = not Emailer.has_client()
 
         retry = 0
diff --git a/jac-cloud/jac_cloud/plugin/cli.py b/jac-cloud/jac_cloud/plugin/cli.py
index 4b3c82050c..3f67238866 100644
--- a/jac-cloud/jac_cloud/plugin/cli.py
+++ b/jac-cloud/jac_cloud/plugin/cli.py
@@ -1,5 +1,6 @@
 """Module for registering CLI plugins for jaseci."""
 
+from getpass import getpass
 from os import getenv
 from os.path import split
 from pickle import load
@@ -10,7 +11,14 @@
 from jaclang.runtimelib.context import ExecutionContext
 from jaclang.runtimelib.machine import JacMachine, JacProgram
 
+from pymongo.errors import ConnectionFailure, OperationFailure
+
 from .mini.cli_mini import serve_mini
+from ..core.architype import BulkWrite
+from ..core.context import SUPER_ROOT_ID
+from ..jaseci.datasources import Collection
+from ..jaseci.models import User as BaseUser
+from ..jaseci.utils import logger
 
 
 class JacCmd:
@@ -64,3 +72,96 @@ def serve(filename: str, host: str = "0.0.0.0", port: int = 8000) -> None:
 
             jctx.close()
             JacMachine.detach()
+
+        @cmd_registry.register
+        def create_system_admin(
+            filename: str, email: str = "", password: str = ""
+        ) -> str:
+            if not getenv("DATABASE_HOST"):
+                raise NotImplementedError(
+                    "DATABASE_HOST env-var is required for this API!"
+                )
+
+            base, mod = split(filename)
+            base = base if base else "./"
+            mod = mod[:-4]
+
+            if filename.endswith(".jac"):
+                jac_import(
+                    target=mod,
+                    base_path=base,
+                    cachable=True,
+                    override_name="__main__",
+                )
+            elif filename.endswith(".jir"):
+                with open(filename, "rb") as f:
+                    JacMachine(base).attach_program(
+                        JacProgram(mod_bundle=load(f), bytecode=None)
+                    )
+                    jac_import(
+                        target=mod,
+                        base_path=base,
+                        cachable=True,
+                        override_name="__main__",
+                    )
+
+            if not email:
+                trial = 0
+                while (email := input("Email: ")) != input("Confirm Email: "):
+                    if trial > 2:
+                        raise ValueError("Email don't match! Aborting...")
+                    print("Email don't match! Please try again.")
+                    trial += 1
+
+            if not password:
+                trial = 0
+                while (password := getpass()) != getpass(prompt="Confirm Password: "):
+                    if trial > 2:
+                        raise ValueError("Password don't match! Aborting...")
+                    print("Password don't match! Please try again.")
+                    trial += 1
+
+            user_model = BaseUser.model()
+            user_request = user_model.register_type()(
+                email=email,
+                password=password,
+                **user_model.system_admin_default(),
+            )
+
+            Collection.apply_indexes()
+            with user_model.Collection.get_session() as session, session.start_transaction():
+                req_obf: dict = user_request.obfuscate()
+                req_obf.update(
+                    {
+                        "root_id": SUPER_ROOT_ID,
+                        "is_activated": True,
+                        "is_admin": True,
+                    }
+                )
+
+                retry = 0
+                max_retry = BulkWrite.SESSION_MAX_TRANSACTION_RETRY
+                while retry <= max_retry:
+                    try:
+                        if id := (
+                            user_model.Collection.insert_one(req_obf, session=session)
+                        ).inserted_id:
+                            BulkWrite.commit(session)
+                            return f"System Admin created with id: {id}"
+                    except (ConnectionFailure, OperationFailure) as ex:
+                        if ex.has_error_label("TransientTransactionError"):
+                            retry += 1
+                            logger.error(
+                                "Error executing bulk write! "
+                                f"Retrying [{retry}/{max_retry}] ..."
+                            )
+                            continue
+                        logger.exception("Error executing bulk write!")
+                        session.abort_transaction()
+                        raise
+                    except Exception:
+                        logger.exception("Error executing bulk write!")
+                        session.abort_transaction()
+                        raise
+
+            raise Exception("Can't process registration. Please try again!")
diff --git a/jac/jaclang/cli/cli.py b/jac/jaclang/cli/cli.py
index 3bfac181eb..25826958bb 100644
--- a/jac/jaclang/cli/cli.py
+++ b/jac/jaclang/cli/cli.py
@@ -6,6 +6,7 @@
 import os
 import pickle
 import shutil
+import sys
 import types
 from typing import Optional
 
@@ -38,7 +39,10 @@ def format(path: str, outfile: str = "", debug: bool = False) -> None:
     def format_file(filename: str) -> None:
         code_gen_format = jac_file_to_pass(filename, schedule=format_pass)
         if code_gen_format.errors_had:
-            print(f"Errors occurred while formatting the file {filename}.")
+            print(
+                f"Errors occurred while formatting the file {filename}.",
+                file=sys.stderr,
+            )
         elif debug:
             print(code_gen_format.ir.gen.jac)
         elif outfile:
@@ -52,7 +56,7 @@ def format_file(filename: str) -> None:
         if os.path.exists(path):
             format_file(path)
         else:
-            print("File does not exist.")
+            print("File does not exist.", file=sys.stderr)
     elif os.path.isdir(path):
         count = 0
         for root, _, files in os.walk(path):
@@ -61,9 +65,9 @@ def format_file(filename: str) -> None:
                     file_path = os.path.join(root, file)
                     format_file(file_path)
                     count += 1
-        print(f"Formatted {count} '.jac' files.")
+        print(f"Formatted {count} '.jac' files.", file=sys.stderr)
     else:
-        print("Not a .jac file or directory.")
+        print("Not a .jac file or directory.", file=sys.stderr)
 
 
 @cmd_registry.register
@@ -89,23 +93,30 @@ def run(
     jctx = ExecutionContext.create(session=session)
 
     if filename.endswith(".jac"):
-        jac_import(
-            target=mod,
-            base_path=base,
-            cachable=cache,
-            override_name="__main__" if main else None,
-        )
-    elif filename.endswith(".jir"):
-        with open(filename, "rb") as f:
-            JacMachine(base).attach_program(
-                JacProgram(mod_bundle=pickle.load(f), bytecode=None)
-            )
+        try:
             jac_import(
                 target=mod,
                 base_path=base,
                 cachable=cache,
                 override_name="__main__" if main else None,
             )
+        except Exception as e:
+            print(e, file=sys.stderr)
+    elif filename.endswith(".jir"):
+        try:
+            with open(filename, "rb") as f:
+                JacMachine(base).attach_program(
+                    JacProgram(mod_bundle=pickle.load(f), bytecode=None)
+                )
+                jac_import(
+                    target=mod,
+                    base_path=base,
+                    cachable=cache,
+                    override_name="__main__" if main else None,
+                )
+        except Exception as e:
+            print(e, file=sys.stderr)
+
     else:
         jctx.close()
         JacMachine.detach()
@@ -163,7 +174,7 @@ def get_object(
     if obj:
         data = obj.__jac__.__getstate__()
     else:
-        print(f"Object with id {id} not found.")
+        print(f"Object with id {id} not found.", file=sys.stderr)
 
     jctx.close()
     JacMachine.detach()
@@ -183,7 +194,7 @@ def build(filename: str) -> None:
         with open(filename[:-4] + ".jir", "wb") as f:
             pickle.dump(out.ir, f)
     else:
-        print("Not a .jac file.")
+        print("Not a .jac file.", file=sys.stderr)
 
 
 @cmd_registry.register
@@ -202,10 +213,10 @@ def check(filename: str, print_errs: bool = True) -> None:
         warnings = len(out.warnings_had)
         if print_errs:
             for e in out.errors_had:
-                print("Error:", e)
+                print("Error:", e, file=sys.stderr)
         print(f"Errors: {errs}, Warnings: {warnings}")
     else:
-        print("Not a .jac file.")
+        print("Not a .jac file.", file=sys.stderr)
 
 
 @cmd_registry.register
@@ -278,7 +289,7 @@ def enter(
     if ret_module:
         (loaded_mod,) = ret_module
         if not loaded_mod:
-            print("Errors occurred while importing the module.")
+            print("Errors occurred while importing the module.", file=sys.stderr)
         else:
             architype = getattr(loaded_mod, entrypoint)(*args)
 
@@ -344,10 +355,12 @@ def tool(tool: str, args: Optional[list] = None) -> None:
             else:
                 print(getattr(AstTool(), tool)())
         except Exception as e:
-            print(f"Error while running ast tool {tool}, check args: {e}")
+            print(
+                f"Error while running ast tool {tool}, check args: {e}", file=sys.stderr
+            )
             raise e
     else:
-        print(f"Ast tool {tool} not found.")
+        print(f"Ast tool {tool} not found.", file=sys.stderr)
 
 
 @cmd_registry.register
@@ -385,9 +398,9 @@ def debug(filename: str, main: bool = True, cache: bool = False) -> None:
                 db.runcall(func)
                 print("Done debugging.")
         else:
-            print(f"Error while generating bytecode in {filename}.")
+            print(f"Error while generating bytecode in {filename}.", file=sys.stderr)
     else:
-        print("Not a .jac file.")
+        print("Not a .jac file.", file=sys.stderr)
 
 
 @cmd_registry.register
@@ -458,7 +471,7 @@ def dot(
             file.write(graph)
         print(f">>> Graph content saved to {os.path.join(os.getcwd(), file_name)}")
     else:
-        print("Not a .jac file.")
+        print("Not a .jac file.", file=sys.stderr)
 
     jctx.close()
 
@@ -494,7 +507,7 @@ def jac2py(filename: str) -> None:
             code = jac_file_to_pass(file_path=filename).ir.gen.py
         print(code)
     else:
-        print("Not a .jac file.")
+        print("Not a .jac file.", file=sys.stderr)
 
 
 def start_cli() -> None:
diff --git a/jac/jaclang/compiler/__init__.py b/jac/jaclang/compiler/__init__.py
index d1b178b4fe..092f1e48a8 100644
--- a/jac/jaclang/compiler/__init__.py
+++ b/jac/jaclang/compiler/__init__.py
@@ -59,7 +59,7 @@ def generate_static_parser(force: bool = False) -> None:
         "KW_OR": "|", "ARROW_BI": "<-->", "ARROW_L": "<--",
         "ARROW_R": "-->", "ARROW_L_P1": "<-:", "ARROW_R_P2": ":->",
         "ARROW_L_P2": ":-", "ARROW_R_P1": "-:", "CARROW_BI": "<++>",
-        "CARROW_L": "<++", "CARROW_R": "++>", "CARROW_L_P1": "<+:",
+        "CARROW_L_P1": "<+:", "RSHIFT_EQ": ">>=", "ELLIPSIS": "...",
         "CARROW_R_P2": ":+>", "CARROW_L_P2": ":+", "CARROW_R_P1": "+:",
         "PIPE_FWD": "|>", "PIPE_BKWD": "<|", "A_PIPE_FWD": ":>",
         "A_PIPE_BKWD": "<:", "DOT_FWD": ".>", "STAR_POW": "**",
@@ -68,7 +68,7 @@ def generate_static_parser(force: bool = False) -> None:
         "STAR_POW_EQ": "**=", "MUL_EQ": "*=", "FLOOR_DIV_EQ": "//=",
         "DIV_EQ": "/=", "MOD_EQ": "%=", "BW_AND_EQ": "&=",
         "BW_OR_EQ": "|=", "BW_XOR_EQ": "^=", "BW_NOT_EQ": "~=",
-        "LSHIFT_EQ": "<<=", "RSHIFT_EQ": ">>=", "ELLIPSIS": "...",
+        "LSHIFT_EQ": "<<=",
     }
 )
 # fmt: on
diff --git a/jac/jaclang/compiler/absyntree.py b/jac/jaclang/compiler/absyntree.py
index 8bf05adfe6..c0bacc3558 100644
--- a/jac/jaclang/compiler/absyntree.py
+++ b/jac/jaclang/compiler/absyntree.py
@@ -248,9 +248,9 @@ def py_ctx_func(self) -> Type[ast3.AST]:
         return self.name_spec.py_ctx_func
 
     @property
-    def sym_type(self) -> str:
+    def expr_type(self) -> str:
         """Get symbol type."""
-        return self.name_spec.sym_type
+        return self.name_spec.expr_type
 
     @property
     def type_sym_tab(self) -> Optional[SymbolTable]:
@@ -346,6 +346,32 @@ def __init__(self) -> None:
 class Expr(AstNode):
     """Expr node type for Jac Ast."""
 
+    def __init__(self, type_src: Optional[Expr] = None) -> None:
+        """Initialize expression node."""
+        self.type_src = type_src or self  # Only used for ArchRef
+        self._sym_type: str = "NoType"
+        self._type_sym_tab: Optional[SymbolTable] = None
+
+    @property
+    def expr_type(self) -> str:
+        """Get symbol type."""
+        return self.type_src._sym_type
+
+    @expr_type.setter
+    def expr_type(self, sym_type: str) -> None:
+        """Set symbol type."""
+        self.type_src._sym_type = sym_type
+
+    @property
+    def type_sym_tab(self) -> Optional[SymbolTable]:
+        """Get type symbol table."""
+        return self.type_src._type_sym_tab
+
+    @type_sym_tab.setter
+    def type_sym_tab(self, type_sym_tab: SymbolTable) -> None:
+        """Set type symbol table."""
+        self.type_src._type_sym_tab = type_sym_tab
+
 
 class AtomExpr(Expr, AstSymbolStubNode):
     """AtomExpr node type for Jac Ast."""
@@ -435,8 +461,7 @@ def __init__(self) -> None:
         self._sym_name: str = ""
         self._sym_category: SymbolType = SymbolType.UNKNOWN
         self._py_ctx_func: Type[ast3.AST] = ast3.Load
-        self._sym_type: str = "NoType"
-        self._type_sym_tab: Optional[SymbolTable] = None
+        AtomExpr.__init__(self)
 
     @property
     def sym(self) -> Optional[Symbol]:
@@ -461,7 +486,7 @@ def sym_category(self) -> SymbolType:
     @property
     def clean_type(self) -> str:
         """Get clean type."""
-        ret_type = self.sym_type.replace("builtins.", "").replace("NoType", "")
+        ret_type = self.expr_type.replace("builtins.", "").replace("NoType", "")
         return ret_type
 
     @property
@@ -474,26 +499,6 @@ def py_ctx_func(self, py_ctx_func: Type[ast3.AST]) -> None:
         """Set python context function."""
         self._py_ctx_func = py_ctx_func
 
-    @property
-    def sym_type(self) -> str:
-        """Get symbol type."""
-        return self._sym_type
-
-    @sym_type.setter
-    def sym_type(self, sym_type: str) -> None:
-        """Set symbol type."""
-        self._sym_type = sym_type
-
-    @property
-    def type_sym_tab(self) -> Optional[SymbolTable]:
-        """Get type symbol table."""
-        return self._type_sym_tab
-
-    @type_sym_tab.setter
-    def type_sym_tab(self, type_sym_tab: SymbolTable) -> None:
-        """Set type symbol table."""
-        self._type_sym_tab = type_sym_tab
-
     @property
     def sem_token(self) -> Optional[tuple[SemTokType, SemTokMod]]:
         """Resolve semantic token."""
@@ -2530,6 +2535,7 @@ def __init__(
         """Initialize sync statement node."""
         self.target = target
         AstNode.__init__(self, kid=kid)
+        Expr.__init__(self)
 
     def normalize(self, deep: bool = False) -> bool:
         """Normalize sync statement node."""
@@ -2658,6 +2664,7 @@ def __init__(
         self.right = right
         self.op = op
         AstNode.__init__(self, kid=kid)
+        Expr.__init__(self)
 
     def normalize(self, deep: bool = False) -> bool:
         """Normalize ast node."""
@@ -2692,6 +2699,7 @@ def __init__(
         self.rights = rights
         self.ops = ops
         AstNode.__init__(self, kid=kid)
+        Expr.__init__(self)
 
     def normalize(self, deep: bool = False) -> bool:
         """Normalize ast node."""
@@ -2723,6 +2731,7 @@ def __init__(
         self.values = values
         self.op = op
         AstNode.__init__(self, kid=kid)
+        Expr.__init__(self)
 
     def normalize(self, deep: bool = False) -> bool:
         """Normalize ast node."""
@@ -2753,6 +2762,7 @@ def __init__(
         self.signature = signature
         self.body = body
         AstNode.__init__(self, kid=kid)
+        Expr.__init__(self)
 
     def normalize(self, deep: bool = False) -> bool:
         """Normalize ast node."""
@@ -2785,6 +2795,7 @@ def __init__(
         self.operand = operand
         self.op = op
         AstNode.__init__(self, kid=kid)
+        Expr.__init__(self)
 
     def normalize(self, deep: bool = False) -> bool:
         """Normalize ast node."""
@@ -2812,6 +2823,7 @@ def __init__(
         self.value = value
         self.else_value = else_value
         AstNode.__init__(self, kid=kid)
+        Expr.__init__(self)
 
     def normalize(self, deep: bool = False) -> bool:
         """Normalize ast node."""
@@ -2842,6 +2854,7 @@ def __init__(
         """Initialize multi string expression node."""
         self.strings = strings
         AstNode.__init__(self, kid=kid)
+        Expr.__init__(self)
         AstSymbolStubNode.__init__(self, sym_type=SymbolType.STRING)
 
     def normalize(self, deep: bool = False) -> bool:
@@ -2868,6 +2881,7 @@ def __init__(
         """Initialize fstring expression node."""
         self.parts = parts
         AstNode.__init__(self, kid=kid)
+        Expr.__init__(self)
         AstSymbolStubNode.__init__(self, sym_type=SymbolType.STRING)
 
     def normalize(self, deep: bool = False) -> bool:
@@ -2909,6 +2923,7 @@ def __init__(
         """Initialize value node."""
         self.values = values
         AstNode.__init__(self, kid=kid)
+        Expr.__init__(self)
         AstSymbolStubNode.__init__(self, sym_type=SymbolType.SEQUENCE)
 
     def normalize(self, deep: bool = False) -> bool:
@@ -2937,6 +2952,7 @@ def __init__(
         """Initialize value node."""
         self.values = values
         AstNode.__init__(self, kid=kid)
+        Expr.__init__(self)
         AstSymbolStubNode.__init__(self, sym_type=SymbolType.SEQUENCE)
 
     def normalize(self, deep: bool = False) -> bool:
@@ -2965,6 +2981,7 @@ def __init__(
         """Initialize tuple value node."""
         self.values = values
         AstNode.__init__(self, kid=kid)
+        Expr.__init__(self)
         AstSymbolStubNode.__init__(self, sym_type=SymbolType.SEQUENCE)
 
     def normalize(self, deep: bool = False) -> bool:
@@ -3008,6 +3025,7 @@ def __init__(
         """Initialize dict expression node."""
         self.kv_pairs = kv_pairs
         AstNode.__init__(self, kid=kid)
+        Expr.__init__(self)
         AstSymbolStubNode.__init__(self, sym_type=SymbolType.SEQUENCE)
 
     def normalize(self, deep: bool = False) -> bool:
@@ -3141,6 +3159,7 @@ def __init__(
         self.out_expr = out_expr
         self.compr = compr
         AstNode.__init__(self, kid=kid)
+        Expr.__init__(self)
         AstSymbolStubNode.__init__(self, sym_type=SymbolType.SEQUENCE)
 
     def normalize(self, deep: bool = False) -> bool:
@@ -3216,6 +3235,7 @@ def __init__(
         self.kv_pair = kv_pair
         self.compr = compr
         AstNode.__init__(self, kid=kid)
+        Expr.__init__(self)
         AstSymbolStubNode.__init__(self, sym_type=SymbolType.SEQUENCE)
 
     def normalize(self, deep: bool = False) -> bool:
@@ -3254,6 +3274,7 @@ def __init__(
         self.is_null_ok = is_null_ok
         self.is_genai = is_genai
         AstNode.__init__(self, kid=kid)
+        Expr.__init__(self)
 
     def normalize(self, deep: bool = True) -> bool:
         """Normalize ast node."""
@@ -3299,6 +3320,7 @@ def __init__(
         """Initialize atom unit expression node."""
         self.value = value
         AstNode.__init__(self, kid=kid)
+        Expr.__init__(self)
 
     def normalize(self, deep: bool = True) -> bool:
         """Normalize ast node."""
@@ -3326,6 +3348,7 @@ def __init__(
         self.expr = expr
         self.with_from = with_from
         AstNode.__init__(self, kid=kid)
+        Expr.__init__(self)
 
     def normalize(self, deep: bool = False) -> bool:
         """Normalize yield statement node."""
@@ -3357,6 +3380,7 @@ def __init__(
         self.params = params
         self.genai_call = genai_call
         AstNode.__init__(self, kid=kid)
+        Expr.__init__(self)
 
     def normalize(self, deep: bool = True) -> bool:
         """Normalize ast node."""
@@ -3391,6 +3415,7 @@ def __init__(
         self.step = step
         self.is_range = is_range
         AstNode.__init__(self, kid=kid)
+        Expr.__init__(self)
         AstSymbolStubNode.__init__(self, sym_type=SymbolType.SEQUENCE)
 
     def normalize(self, deep: bool = True) -> bool:
@@ -3433,6 +3458,7 @@ def __init__(
         self.arch_name = arch_name
         self.arch_type = arch_type
         AstNode.__init__(self, kid=kid)
+        Expr.__init__(self, type_src=arch_name)
         AstSymbolNode.__init__(
             self,
             sym_name=arch_name.sym_name,
@@ -3463,6 +3489,7 @@ def __init__(
         self.chain = chain
         self.edges_only = edges_only
         AstNode.__init__(self, kid=kid)
+        Expr.__init__(self)
 
     def normalize(self, deep: bool = True) -> bool:
         """Normalize ast node."""
@@ -3492,6 +3519,7 @@ def __init__(
         self.filter_cond = filter_cond
         self.edge_dir = edge_dir
         AstNode.__init__(self, kid=kid)
+        Expr.__init__(self)
         WalkerStmtOnlyNode.__init__(self)
         AstSymbolStubNode.__init__(self, sym_type=SymbolType.SEQUENCE)
 
@@ -3622,6 +3650,7 @@ def __init__(
         self.f_type = f_type
         self.compares = compares
         AstNode.__init__(self, kid=kid)
+        Expr.__init__(self)
         AstSymbolStubNode.__init__(self, sym_type=SymbolType.SEQUENCE)
 
     def normalize(self, deep: bool = False) -> bool:
@@ -3659,6 +3688,7 @@ def __init__(
         """Initialize assign compr expression node."""
         self.assigns = assigns
         AstNode.__init__(self, kid=kid)
+        Expr.__init__(self)
         AstSymbolStubNode.__init__(self, sym_type=SymbolType.SEQUENCE)
 
     def normalize(self, deep: bool = False) -> bool:
@@ -4217,6 +4247,7 @@ def __init__(
             pos_end=pos_end,
         )
         AstSymbolStubNode.__init__(self, sym_type=self.SYMBOL_TYPE)
+        Expr.__init__(self)
 
     @property
     def lit_value(
diff --git a/jac/jaclang/compiler/compile.py b/jac/jaclang/compiler/compile.py
index 95f054121a..a0e88fed02 100644
--- a/jac/jaclang/compiler/compile.py
+++ b/jac/jaclang/compiler/compile.py
@@ -16,11 +16,13 @@ def compile_jac(file_path: str, cache_result: bool = False) -> Pass:
         file_path=file_path,
         schedule=pass_schedule,
     )
-    if cache_result and isinstance(code.ir, ast.Module):
+    # If there is syntax error, the code will be an instance of JacParser as there is
+    # no more passes were processed, in that case we can ignore it.
+    had_syntax_error = isinstance(code, JacParser) and len(code.errors_had) != 0
+    if cache_result and (not had_syntax_error) and isinstance(code.ir, ast.Module):
         print_pass = PyOutPass(input_ir=code.ir, prior=code)
         return print_pass
-    else:
-        return code
+    return code
 
 
 def jac_file_to_pass(
@@ -49,6 +51,11 @@ def jac_str_to_pass(
         target = schedule[-1] if schedule else None
     source = ast.JacSource(jac_str, mod_path=file_path)
     ast_ret: Pass = JacParser(input_ir=source)
+
+    # If there is syntax error, no point in processing in further passes.
+    if len(ast_ret.errors_had) != 0:
+        return ast_ret
+
     for i in schedule:
         if i == target:
             break
diff --git a/jac/jaclang/compiler/passes/main/def_impl_match_pass.py b/jac/jaclang/compiler/passes/main/def_impl_match_pass.py
index 5dffb1f2eb..76e703091a 100644
--- a/jac/jaclang/compiler/passes/main/def_impl_match_pass.py
+++ b/jac/jaclang/compiler/passes/main/def_impl_match_pass.py
@@ -90,6 +90,10 @@ def connect_def_impl(self, sym_tab: SymbolTable) -> None:
                     raise self.ice(
                         f"Expected AstImplNeedingNode, got {valid_decl.__class__.__name__}. Not possible."
                     )
+
+                # Ensure if it's an ability def impl, the parameters are matched.
+                self.validate_params_match(sym, decl_node.name_of)
+
                 valid_decl.body = sym.decl.name_of
                 sym.decl.name_of.decl_link = valid_decl
                 for idx, a in enumerate(sym.decl.name_of.target.archs):
@@ -97,5 +101,47 @@ def connect_def_impl(self, sym_tab: SymbolTable) -> None:
                     a.name_spec.sym = name_of_links[idx].sym
                 sym.decl.name_of.sym_tab.tab.update(valid_decl.sym_tab.tab)
                 valid_decl.sym_tab.tab = sym.decl.name_of.sym_tab.tab
+
         for i in sym_tab.kid:
             self.connect_def_impl(i)
+
+    def validate_params_match(self, sym: Symbol, valid_decl: ast.AstSymbolNode) -> None:
+        """Validate if the parameters match."""
+        if (
+            isinstance(valid_decl, ast.Ability)
+            and isinstance(sym.decl.name_of, ast.AbilityDef)
+            and isinstance(valid_decl.signature, ast.FuncSignature)
+            and isinstance(sym.decl.name_of.signature, ast.FuncSignature)
+        ):
+
+            params_decl = valid_decl.signature.params
+            params_defn = sym.decl.name_of.signature.params
+
+            if params_decl and params_defn:
+                # Check if the parameter count is matched.
+                if len(params_defn.items) != len(params_decl.items):
+                    self.error(
+                        f"Parameter count mismatch for ability {sym.sym_name}.",
+                        sym.decl.name_of.name_spec,
+                    )
+                    self.error(
+                        f"From the declaration of {valid_decl.name_spec.sym_name}.",
+                        valid_decl.name_spec,
+                    )
+                else:
+                    for idx in range(len(params_defn.items)):
+                        # Check if all the parameter names are matched.
+                        # TODO: This shouldn't be an issue however if the names are not matched, it doesn't
+                        # work as expected like in C++, for now I'm adding this validation, however this
+                        # needs to be fixed to have a C++ style.
+                        param_name_decl = params_decl.items[idx].name.value
+                        param_name_defn = params_defn.items[idx].name.value
+                        if param_name_defn != param_name_decl:
+                            self.error(
+                                f"Parameter name mismatch for ability {sym.sym_name}.",
+                                params_defn.items[idx].name,
+                            )
+                            self.error(
+                                f"From the declaration of {valid_decl.name_spec.sym_name}.",
+                                params_decl.items[idx].name,
+                            )
diff --git a/jac/jaclang/compiler/passes/main/fuse_typeinfo_pass.py b/jac/jaclang/compiler/passes/main/fuse_typeinfo_pass.py
index 392b928a02..765a874b65 100644
--- a/jac/jaclang/compiler/passes/main/fuse_typeinfo_pass.py
+++ b/jac/jaclang/compiler/passes/main/fuse_typeinfo_pass.py
@@ -30,33 +30,40 @@ class FuseTypeInfoPass(Pass):
 
     node_type_hash: dict[MypyNodes.Node | VNode, MyType] = {}
 
+    # Override this to support enter expression.
+    def enter_node(self, node: ast.AstNode) -> None:
+        """Run on entering node."""
+        super().enter_node(node)
+
+        if isinstance(node, ast.Expr):
+            self.enter_expr(node)
+
     def __debug_print(self, msg: str) -> None:
         if settings.fuse_type_info_debug:
             self.log_info("FuseTypeInfo::" + msg)
 
-    def __call_type_handler(
-        self, node: ast.AstSymbolNode, mypy_type: MypyTypes.Type
-    ) -> None:
+    def __call_type_handler(self, mypy_type: MypyTypes.Type) -> Optional[str]:
         mypy_type_name = pascal_to_snake(mypy_type.__class__.__name__)
         type_handler_name = f"get_type_from_{mypy_type_name}"
         if hasattr(self, type_handler_name):
-            getattr(self, type_handler_name)(node, mypy_type)
-        else:
-            self.__debug_print(
-                f'{node.loc}"MypyTypes::{mypy_type.__class__.__name__}" isn\'t supported yet'
-            )
-
-    def __set_sym_table_link(self, node: ast.AstSymbolNode) -> None:
+            return getattr(self, type_handler_name)(mypy_type)
+        self.__debug_print(
+            f'"MypyTypes::{mypy_type.__class__.__name__}" isn\'t supported yet'
+        )
+        return None
+
+    def __set_type_sym_table_link(self, node: ast.AstSymbolNode) -> None:
+        typ = node.expr_type.split(".")
         typ_sym_table = self.ir.sym_tab
         assert isinstance(self.ir, ast.Module)
 
-        sym_type = node.sym_type
+        sym_type = node.expr_type
         if re.match(r"builtins.(list|dict|tuple)", sym_type):
             sym_type = re.sub(r"\[.*\]", "", sym_type)
 
         typ = sym_type.split(".")
 
-        if node.sym_type == "types.ModuleType" and node.sym:
+        if node.expr_type == "types.ModuleType" and node.sym:
             node.name_spec.type_sym_tab = node.sym.parent_tab.find_scope(node.sym_name)
 
         for i in typ:
@@ -118,7 +125,7 @@ def node_handler(self: FuseTypeInfoPass, node: T) -> None:
                 # Jac node has only one mypy node linked to it
                 if len(node.gen.mypy_ast) == 1:
                     func(self, node)
-                    self.__set_sym_table_link(node)
+                    self.__set_type_sym_table_link(node)
                     self.__collect_python_dependencies(node)
 
                 # Jac node has multiple mypy nodes linked to it
@@ -142,13 +149,13 @@ def node_handler(self: FuseTypeInfoPass, node: T) -> None:
                             f"{jac_node_str} has duplicate mypy nodes associated to it"
                         )
                         func(self, node)
-                        self.__set_sym_table_link(node)
+                        self.__set_type_sym_table_link(node)
                         self.__collect_python_dependencies(node)
 
                 # Special handing for BuiltinType
                 elif isinstance(node, ast.BuiltinType):
                     func(self, node)  # type: ignore
-                    self.__set_sym_table_link(node)
+                    self.__set_type_sym_table_link(node)
 
                 # Jac node doesn't have mypy nodes linked to it
                 else:
@@ -170,7 +177,10 @@ def __collect_type_from_symbol(self, node: ast.AstSymbolNode) -> None:
 
         if isinstance(mypy_node, MypyNodes.MemberExpr):
             if mypy_node in self.node_type_hash:
-                self.__call_type_handler(node, self.node_type_hash[mypy_node])
+                node.name_spec.expr_type = (
+                    self.__call_type_handler(self.node_type_hash[mypy_node])
+                    or node.name_spec.expr_type
+                )
             else:
                 self.__debug_print(f"{node.loc} MemberExpr type is not found")
 
@@ -179,19 +189,24 @@ def __collect_type_from_symbol(self, node: ast.AstSymbolNode) -> None:
             mypy_node = mypy_node.node
 
             if isinstance(mypy_node, (MypyNodes.Var, MypyNodes.FuncDef)):
-                self.__call_type_handler(node, mypy_node.type)
+                node.name_spec.expr_type = (
+                    self.__call_type_handler(mypy_node.type) or node.name_spec.expr_type
+                )
 
             elif isinstance(mypy_node, MypyNodes.MypyFile):
-                node.name_spec.sym_type = "types.ModuleType"
+                node.name_spec.expr_type = "types.ModuleType"
 
             elif isinstance(mypy_node, MypyNodes.TypeInfo):
-                node.name_spec.sym_type = mypy_node.fullname
+                node.name_spec.expr_type = mypy_node.fullname
 
             elif isinstance(mypy_node, MypyNodes.OverloadedFuncDef):
-                self.__call_type_handler(node, mypy_node.items[0].func.type)
+                node.name_spec.expr_type = (
+                    self.__call_type_handler(mypy_node.items[0].func.type)
+                    or node.name_spec.expr_type
+                )
 
             elif mypy_node is None:
-                node.name_spec.sym_type = "None"
+                node.name_spec.expr_type = "None"
 
             else:
                 self.__debug_print(
@@ -201,14 +216,22 @@ def __collect_type_from_symbol(self, node: ast.AstSymbolNode) -> None:
 
         else:
             if isinstance(mypy_node, MypyNodes.ClassDef):
-                node.name_spec.sym_type = mypy_node.fullname
-                self.__set_sym_table_link(node)
+                node.name_spec.expr_type = mypy_node.fullname
+                self.__set_type_sym_table_link(node)
             elif isinstance(mypy_node, MypyNodes.FuncDef):
-                self.__call_type_handler(node, mypy_node.type)
+                node.name_spec.expr_type = (
+                    self.__call_type_handler(mypy_node.type) or node.name_spec.expr_type
+                )
             elif isinstance(mypy_node, MypyNodes.Argument):
-                self.__call_type_handler(node, mypy_node.variable.type)
+                node.name_spec.expr_type = (
+                    self.__call_type_handler(mypy_node.variable.type)
+                    or node.name_spec.expr_type
+                )
             elif isinstance(mypy_node, MypyNodes.Decorator):
-                self.__call_type_handler(node, mypy_node.func.type.ret_type)
+                node.name_spec.expr_type = (
+                    self.__call_type_handler(mypy_node.func.type.ret_type)
+                    or node.name_spec.expr_type
+                )
             else:
                 self.__debug_print(
                     f'"{node.loc}::{node.__class__.__name__}" mypy node isn\'t supported'
@@ -224,6 +247,45 @@ def __check_builltin_symbol(self, node: ast.NameAtom) -> None:
         if builtins_sym:
             node.name_spec._sym = builtins_sym
 
+    collection_types_map = {
+        ast.ListVal: "builtins.list",
+        ast.SetVal: "builtins.set",
+        ast.TupleVal: "builtins.tuple",
+        ast.DictVal: "builtins.dict",
+        ast.ListCompr: None,
+        ast.DictCompr: None,
+    }
+
+    # NOTE (Thakee): Since expression nodes are not AstSymbolNodes, I'm not decorating this with __handle_node
+    # and IMO instead of checking if it's a symbol node or an expression, we somehow mark expressions as
+    # valid nodes that can have symbols. At this point I'm leaving this like this and lemme know
+    # otherwise.
+    # NOTE (GAMAL): This will be fixed through the AstTypedNode
+    def enter_expr(self: FuseTypeInfoPass, node: ast.Expr) -> None:
+        """Enter an expression node."""
+        if len(node.gen.mypy_ast) == 0:
+            return
+
+        # If the corrosponding mypy ast node type has stored here, get the values.
+        mypy_node = node.gen.mypy_ast[0]
+        if mypy_node in self.node_type_hash:
+            mytype: MyType = self.node_type_hash[mypy_node]
+            node.expr_type = self.__call_type_handler(mytype) or node.expr_type
+
+        # Set they symbol type for collection expression.
+        #
+        # GenCompr is an instance of ListCompr but we don't handle it here.
+        # so the isinstace (node, <classes>) doesn't work, I'm going with type(...) == ...
+        if type(node) in self.collection_types_map:
+            assert isinstance(node, ast.AtomExpr)  # To make mypy happy.
+            collection_type = self.collection_types_map[type(node)]
+            if collection_type is not None:
+                node.name_spec.expr_type = collection_type
+            if mypy_node in self.node_type_hash:
+                node.name_spec.expr_type = (
+                    self.__call_type_handler(mytype) or node.name_spec.expr_type
+                )
+
     @__handle_node
     def enter_name(self, node: ast.NameAtom) -> None:
         """Pass handler for name nodes."""
@@ -265,7 +327,10 @@ def enter_enum_def(self, node: ast.EnumDef) -> None:
     def enter_ability(self, node: ast.Ability) -> None:
         """Pass handler for Ability nodes."""
         if isinstance(node.gen.mypy_ast[0], MypyNodes.FuncDef):
-            self.__call_type_handler(node, node.gen.mypy_ast[0].type.ret_type)
+            node.name_spec.expr_type = (
+                self.__call_type_handler(node.gen.mypy_ast[0].type.ret_type)
+                or node.name_spec.expr_type
+            )
         else:
             self.__debug_print(
                 f"{node.loc}: Can't get type of an ability from mypy node other than Ability. "
@@ -276,7 +341,10 @@ def enter_ability(self, node: ast.Ability) -> None:
     def enter_ability_def(self, node: ast.AbilityDef) -> None:
         """Pass handler for AbilityDef nodes."""
         if isinstance(node.gen.mypy_ast[0], MypyNodes.FuncDef):
-            self.__call_type_handler(node, node.gen.mypy_ast[0].type.ret_type)
+            node.name_spec.expr_type = (
+                self.__call_type_handler(node.gen.mypy_ast[0].type.ret_type)
+                or node.name_spec.expr_type
+            )
         else:
             self.__debug_print(
                 f"{node.loc}: Can't get type of an AbilityDef from mypy node other than FuncDef. "
@@ -289,7 +357,10 @@ def enter_param_var(self, node: ast.ParamVar) -> None:
         if isinstance(node.gen.mypy_ast[0], MypyNodes.Argument):
             mypy_node: MypyNodes.Argument = node.gen.mypy_ast[0]
             if mypy_node.variable.type:
-                self.__call_type_handler(node, mypy_node.variable.type)
+                node.name_spec.expr_type = (
+                    self.__call_type_handler(mypy_node.variable.type)
+                    or node.name_spec.expr_type
+                )
         else:
             self.__debug_print(
                 f"{node.loc}: Can't get parameter value from mypyNode other than Argument"
@@ -303,7 +374,9 @@ def enter_has_var(self, node: ast.HasVar) -> None:
         if isinstance(mypy_node, MypyNodes.AssignmentStmt):
             n = mypy_node.lvalues[0].node
             if isinstance(n, (MypyNodes.Var, MypyNodes.FuncDef)):
-                self.__call_type_handler(node, n.type)
+                node.name_spec.expr_type = (
+                    self.__call_type_handler(n.type) or node.name_spec.expr_type
+                )
             else:
                 self.__debug_print(
                     "Getting type of 'AssignmentStmt' is only supported with Var and FuncDef"
@@ -315,67 +388,19 @@ def enter_has_var(self, node: ast.HasVar) -> None:
 
     def exit_has_var(self, node: ast.HasVar) -> None:
         """Pass handler for HasVar nodes."""
-        node.name_spec.sym_type = node.name.sym_type
+        node.name_spec.expr_type = node.name.expr_type
         node.name_spec.type_sym_tab = node.name.type_sym_tab
 
     @__handle_node
     def enter_multi_string(self, node: ast.MultiString) -> None:
         """Pass handler for MultiString nodes."""
-        node.name_spec.sym_type = "builtins.str"
+        node.name_spec.expr_type = "builtins.str"
 
     @__handle_node
     def enter_f_string(self, node: ast.FString) -> None:
         """Pass handler for FString nodes."""
         self.__debug_print(f"Getting type not supported in {type(node)}")
 
-    @__handle_node
-    def enter_list_val(self, node: ast.ListVal) -> None:
-        """Pass handler for ListVal nodes."""
-        mypy_node = node.gen.mypy_ast[0]
-        if mypy_node in self.node_type_hash:
-            node.name_spec.sym_type = str(self.node_type_hash[mypy_node])
-        else:
-            node.name_spec.sym_type = "builtins.list"
-
-    @__handle_node
-    def enter_set_val(self, node: ast.SetVal) -> None:
-        """Pass handler for SetVal nodes."""
-        mypy_node = node.gen.mypy_ast[0]
-        if mypy_node in self.node_type_hash:
-            node.name_spec.sym_type = str(self.node_type_hash[mypy_node])
-        else:
-            node.name_spec.sym_type = "builtins.set"
-
-    @__handle_node
-    def enter_tuple_val(self, node: ast.TupleVal) -> None:
-        """Pass handler for TupleVal nodes."""
-        mypy_node = node.gen.mypy_ast[0]
-        if mypy_node in self.node_type_hash:
-            node.name_spec.sym_type = str(self.node_type_hash[mypy_node])
-        else:
-            node.name_spec.sym_type = "builtins.tuple"
-
-    @__handle_node
-    def enter_dict_val(self, node: ast.DictVal) -> None:
-        """Pass handler for DictVal nodes."""
-        mypy_node = node.gen.mypy_ast[0]
-        if mypy_node in self.node_type_hash:
-            node.name_spec.sym_type = str(self.node_type_hash[mypy_node])
-        else:
-            node.name_spec.sym_type = "builtins.dict"
-
-    @__handle_node
-    def enter_list_compr(self, node: ast.ListCompr) -> None:
-        """Pass handler for ListCompr nodes."""
-        mypy_node = node.gen.mypy_ast[0]
-        node.name_spec.sym_type = str(self.node_type_hash[mypy_node])
-
-    @__handle_node
-    def enter_dict_compr(self, node: ast.DictCompr) -> None:
-        """Pass handler for DictCompr nodes."""
-        mypy_node = node.gen.mypy_ast[0]
-        node.name_spec.sym_type = str(self.node_type_hash[mypy_node])
-
     @__handle_node
     def enter_index_slice(self, node: ast.IndexSlice) -> None:
         """Pass handler for IndexSlice nodes."""
@@ -386,11 +411,13 @@ def enter_arch_ref(self, node: ast.ArchRef) -> None:
         """Pass handler for ArchRef nodes."""
         if isinstance(node.gen.mypy_ast[0], MypyNodes.ClassDef):
             mypy_node: MypyNodes.ClassDef = node.gen.mypy_ast[0]
-            node.name_spec.sym_type = mypy_node.fullname
-            self.__set_sym_table_link(node)
+            node.name_spec.expr_type = mypy_node.fullname
+            self.__set_type_sym_table_link(node)
         elif isinstance(node.gen.mypy_ast[0], MypyNodes.FuncDef):
             mypy_node2: MypyNodes.FuncDef = node.gen.mypy_ast[0]
-            self.__call_type_handler(node, mypy_node2.type)
+            node.name_spec.expr_type = (
+                self.__call_type_handler(mypy_node2.type) or node.name_spec.expr_type
+            )
         else:
             self.__debug_print(
                 f"{node.loc}: Can't get ArchRef value from mypyNode other than ClassDef "
@@ -420,71 +447,67 @@ def enter_assign_compr(self, node: ast.AssignCompr) -> None:
     @__handle_node
     def enter_int(self, node: ast.Int) -> None:
         """Pass handler for Int nodes."""
-        node.name_spec.sym_type = "builtins.int"
+        node.name_spec.expr_type = "builtins.int"
 
     @__handle_node
     def enter_float(self, node: ast.Float) -> None:
         """Pass handler for Float nodes."""
-        node.name_spec.sym_type = "builtins.float"
+        node.name_spec.expr_type = "builtins.float"
 
     @__handle_node
     def enter_string(self, node: ast.String) -> None:
         """Pass handler for String nodes."""
-        node.name_spec.sym_type = "builtins.str"
+        node.name_spec.expr_type = "builtins.str"
 
     @__handle_node
     def enter_bool(self, node: ast.Bool) -> None:
         """Pass handler for Bool nodes."""
-        node.name_spec.sym_type = "builtins.bool"
+        node.name_spec.expr_type = "builtins.bool"
 
     @__handle_node
     def enter_builtin_type(self, node: ast.BuiltinType) -> None:
         """Pass handler for BuiltinType nodes."""
         self.__check_builltin_symbol(node)
-        node.name_spec.sym_type = f"builtins.{node.sym_name}"
+        node.name_spec.expr_type = f"builtins.{node.sym_name}"
 
-    def get_type_from_instance(
-        self, node: ast.AstSymbolNode, mypy_type: MypyTypes.Instance
-    ) -> None:
+    def get_type_from_instance(self, mypy_type: MypyTypes.Instance) -> Optional[str]:
         """Get type info from mypy type Instance."""
-        node.name_spec.sym_type = str(mypy_type)
+        #  FIXME: Returning str(mypy_type) won't work for literal values since it would be
+        # like Literal['foo'] instead of builtins.str, so we need to get the type fullname.
+        # Not sure if this is the best way to do it.
+        ret = str(mypy_type)
+        if ret.startswith("Literal[") and ret.endswith("]"):
+            return mypy_type.type.fullname
+        return ret
 
     def get_type_from_callable_type(
-        self, node: ast.AstSymbolNode, mypy_type: MypyTypes.CallableType
-    ) -> None:
+        self, mypy_type: MypyTypes.CallableType
+    ) -> Optional[str]:
         """Get type info from mypy type CallableType."""
-        self.__call_type_handler(node, mypy_type.ret_type)
+        return self.__call_type_handler(mypy_type.ret_type)
 
     # TODO: Which overloaded function to get the return value from?
     def get_type_from_overloaded(
-        self, node: ast.AstSymbolNode, mypy_type: MypyTypes.Overloaded
-    ) -> None:
+        self, mypy_type: MypyTypes.Overloaded
+    ) -> Optional[str]:
         """Get type info from mypy type Overloaded."""
-        self.__call_type_handler(node, mypy_type.items[0])
+        return self.__call_type_handler(mypy_type.items[0])
 
-    def get_type_from_none_type(
-        self, node: ast.AstSymbolNode, mypy_type: MypyTypes.NoneType
-    ) -> None:
+    def get_type_from_none_type(self, mypy_type: MypyTypes.NoneType) -> Optional[str]:
         """Get type info from mypy type NoneType."""
-        node.name_spec.sym_type = "None"
+        return "None"
 
-    def get_type_from_any_type(
-        self, node: ast.AstSymbolNode, mypy_type: MypyTypes.AnyType
-    ) -> None:
+    def get_type_from_any_type(self, mypy_type: MypyTypes.AnyType) -> Optional[str]:
         """Get type info from mypy type NoneType."""
-        node.name_spec.sym_type = "Any"
+        return "Any"
 
-    def get_type_from_tuple_type(
-        self, node: ast.AstSymbolNode, mypy_type: MypyTypes.TupleType
-    ) -> None:
+    def get_type_from_tuple_type(self, mypy_type: MypyTypes.TupleType) -> Optional[str]:
         """Get type info from mypy type TupleType."""
-        node.name_spec.sym_type = "builtins.tuple"
+        return "builtins.tuple"
 
-    def get_type_from_type_type(
-        self, node: ast.AstSymbolNode, mypy_type: MypyTypes.TypeType
-    ) -> None:
+    def get_type_from_type_type(self, mypy_type: MypyTypes.TypeType) -> Optional[str]:
         """Get type info from mypy type TypeType."""
-        node.name_spec.sym_type = str(mypy_type.item)
+        return str(mypy_type.item)
 
     def exit_assignment(self, node: ast.Assignment) -> None:
         """Add new symbols in the symbol table in case of self."""
@@ -511,7 +534,7 @@ def exit_assignment(self, node: ast.Assignment) -> None:
         for target in node.target.items:
             if (
                 isinstance(target, ast.Name)
-                and target.sym_type == "types.ModuleType"
+                and target.expr_type == "types.ModuleType"
                 and isinstance(node.value, ast.Name)
             ):
                 target.type_sym_tab = node.value.type_sym_tab
@@ -553,7 +576,7 @@ def exit_atom_trailer(self, node: ast.AtomTrailer) -> None:
             left = atom_trailer_unwind[i - 1]
             right = atom_trailer_unwind[i]
 
-            assert isinstance(left, ast.AstSymbolNode)
+            assert isinstance(left, (ast.Expr, ast.AstSymbolNode))
             assert isinstance(right, ast.AstSymbolNode)
 
             if isinstance(right, ast.IndexSlice):
@@ -561,25 +584,25 @@ def exit_atom_trailer(self, node: ast.AtomTrailer) -> None:
                 node_type: str = ""
 
                 # left type is a list
-                if left.sym_type.startswith("builtins.list["):
-                    node_type = left.sym_type[len("builtins.list[") : -1]
+                if left.expr_type.startswith("builtins.list["):
+                    node_type = left.expr_type[len("builtins.list[") : -1]
 
                     # right index slice is a range then it's type is the same as left
                     if right.is_range:
-                        right.name_spec.sym_type = left.sym_type
+                        right.expr_type = left.expr_type
                         continue
 
                 # left type is a dictionary
-                elif left.sym_type.startswith("builtins.dict["):
+                elif left.expr_type.startswith("builtins.dict["):
                     node_type = (
-                        left.sym_type[len("builtins.dict[") : -1].split(",")[1].strip()
+                        left.expr_type[len("builtins.dict[") : -1].split(",")[1].strip()
                     )
 
                 # unsupported type
                 else:
                     continue
 
-                right.name_spec.sym_type = node_type
+                right.expr_type = node_type
 
                 # Getting the correct symbol table and link it
                 type_symtab: Optional[SymbolTable] = self.ir.sym_tab
@@ -595,7 +618,7 @@ def exit_atom_trailer(self, node: ast.AtomTrailer) -> None:
                     type_symtab = type_symtab.find_scope(j)
                     if type_symtab is None:
                         break
-                right.name_spec.type_sym_tab = type_symtab
+                right.type_sym_tab = type_symtab
 
             else:
                 if left.type_sym_tab:
diff --git a/jac/jaclang/compiler/passes/main/pyjac_ast_link_pass.py b/jac/jaclang/compiler/passes/main/pyjac_ast_link_pass.py
index 00376fd795..1ace4d0cbb 100644
--- a/jac/jaclang/compiler/passes/main/pyjac_ast_link_pass.py
+++ b/jac/jaclang/compiler/passes/main/pyjac_ast_link_pass.py
@@ -134,14 +134,14 @@ def exit_ability_def(self, node: ast.AbilityDef) -> None:
                     py_nodes=i.name_spec.name_of.sym.decl.gen.py_ast,
                 )
 
-        if isinstance(node.parent, ast.Ability) and node.parent.signature:
+        if isinstance(node.decl_link, ast.Ability) and node.decl_link.signature:
             if isinstance(node.signature, ast.FuncSignature) and node.signature.params:
                 for src_prm in node.signature.params.items:
                     if (
-                        isinstance(node.parent.signature, ast.FuncSignature)
-                        and node.parent.signature.params
+                        isinstance(node.decl_link.signature, ast.FuncSignature)
+                        and node.decl_link.signature.params
                     ):
-                        for trg_prm in node.parent.signature.params.items:
+                        for trg_prm in node.decl_link.signature.params.items:
                             if src_prm.name.sym_name == trg_prm.name.sym_name:
                                 self.link_jac_py_nodes(
                                     jac_node=src_prm, py_nodes=trg_prm.gen.py_ast
@@ -154,12 +154,12 @@ def exit_ability_def(self, node: ast.AbilityDef) -> None:
                 isinstance(node.signature, ast.FuncSignature)
                 and node.signature.return_type
             ) and (
-                isinstance(node.parent.signature, ast.FuncSignature)
-                and node.parent.signature.return_type
+                isinstance(node.decl_link.signature, ast.FuncSignature)
+                and node.decl_link.signature.return_type
             ):
                 self.link_jac_py_nodes(
                     jac_node=node.signature.return_type,
-                    py_nodes=node.parent.signature.return_type.gen.py_ast,
+                    py_nodes=node.decl_link.signature.return_type.gen.py_ast,
                 )
 
         if isinstance(node.decl_link, ast.Ability) and isinstance(
diff --git a/jac/jaclang/compiler/passes/utils/mypy_ast_build.py b/jac/jaclang/compiler/passes/utils/mypy_ast_build.py
index 536bdbeee4..53fd75dad5 100644
--- a/jac/jaclang/compiler/passes/utils/mypy_ast_build.py
+++ b/jac/jaclang/compiler/passes/utils/mypy_ast_build.py
@@ -16,6 +16,7 @@
 import mypy.checkexpr as mycke
 import mypy.errors as mye
 import mypy.fastparse as myfp
+import mypy.nodes as mypy_nodes
 from mypy.build import BuildSource
 from mypy.build import BuildSourceSet
 from mypy.build import FileSystemCache
@@ -36,6 +37,55 @@
     from mypy.report import Reports  # Avoid unconditional slow import
 
 
+# All the expression nodes of mypy.
+EXPRESSION_NODES = (
+    mypy_nodes.AssertTypeExpr,
+    mypy_nodes.AssignmentExpr,
+    mypy_nodes.AwaitExpr,
+    mypy_nodes.BytesExpr,
+    mypy_nodes.CallExpr,
+    mypy_nodes.CastExpr,
+    mypy_nodes.ComparisonExpr,
+    mypy_nodes.ComplexExpr,
+    mypy_nodes.ConditionalExpr,
+    mypy_nodes.DictionaryComprehension,
+    mypy_nodes.DictExpr,
+    mypy_nodes.EllipsisExpr,
+    mypy_nodes.EnumCallExpr,
+    mypy_nodes.Expression,
+    mypy_nodes.FloatExpr,
+    mypy_nodes.GeneratorExpr,
+    mypy_nodes.IndexExpr,
+    mypy_nodes.IntExpr,
+    mypy_nodes.LambdaExpr,
+    mypy_nodes.ListComprehension,
+    mypy_nodes.ListExpr,
+    mypy_nodes.MemberExpr,
+    mypy_nodes.NamedTupleExpr,
+    mypy_nodes.NameExpr,
+    mypy_nodes.NewTypeExpr,
+    mypy_nodes.OpExpr,
+    mypy_nodes.ParamSpecExpr,
+    mypy_nodes.PromoteExpr,
+    mypy_nodes.RefExpr,
+    mypy_nodes.RevealExpr,
+    mypy_nodes.SetComprehension,
+    mypy_nodes.SetExpr,
+    mypy_nodes.SliceExpr,
+    mypy_nodes.StarExpr,
+    mypy_nodes.StrExpr,
+    mypy_nodes.SuperExpr,
+    mypy_nodes.TupleExpr,
+    mypy_nodes.TypeAliasExpr,
+    mypy_nodes.TypedDictExpr,
+    mypy_nodes.TypeVarExpr,
+    mypy_nodes.TypeVarTupleExpr,
+    mypy_nodes.UnaryExpr,
+    mypy_nodes.YieldExpr,
+    mypy_nodes.YieldFromExpr,
+)
+
+
 mypy_to_jac_node_map: dict[
     tuple[int, int | None, int | None, int | None], list[AstNode]
 ] = {}
@@ -131,61 +181,231 @@ def __init__(
         """Override to mypy expression checker for direct AST pass through."""
         super().__init__(tc, msg, plugin, per_line_checking_time_ns)
 
+    def visit_assert_type_expr(self, e: mycke.AssertTypeExpr) -> mycke.Type:
+        """Type check AssertTypeExpr expression."""
+        out = super().visit_assert_type_expr(e)
+        FuseTypeInfoPass.node_type_hash[e] = out
+        return out
+
+    def visit_assignment_expr(self, e: mycke.AssignmentExpr) -> mycke.Type:
+        """Type check AssignmentExpr expression."""
+        out = super().visit_assignment_expr(e)
+        FuseTypeInfoPass.node_type_hash[e] = out
+        return out
+
+    def visit_await_expr(
+        self, e: mycke.AwaitExpr, allow_none_return: bool = False
+    ) -> mycke.Type:
+        """Type check AwaitExpr expression."""
+        out = super().visit_await_expr(e, allow_none_return)
+        FuseTypeInfoPass.node_type_hash[e] = out
+        return out
+
+    def visit_bytes_expr(self, e: mycke.BytesExpr) -> mycke.Type:
+        """Type check BytesExpr expression."""
+        out = super().visit_bytes_expr(e)
+        FuseTypeInfoPass.node_type_hash[e] = out
+        return out
+
+    def visit_call_expr(
+        self, e: mycke.CallExpr, allow_none_return: bool = False
+    ) -> mycke.Type:
+        """Type check CallExpr expression."""
+        out = super().visit_call_expr(e, allow_none_return)
+        FuseTypeInfoPass.node_type_hash[e] = out
+        return out
+
+    def visit_cast_expr(self, e: mycke.CastExpr) -> mycke.Type:
+        """Type check CastExpr expression."""
+        out = super().visit_cast_expr(e)
+        FuseTypeInfoPass.node_type_hash[e] = out
+        return out
+
+    def visit_comparison_expr(self, e: mycke.ComparisonExpr) -> mycke.Type:
+        """Type check ComparisonExpr expression."""
+        out = super().visit_comparison_expr(e)
+        FuseTypeInfoPass.node_type_hash[e] = out
+        return out
+
+    def visit_complex_expr(self, e: mycke.ComplexExpr) -> mycke.Type:
+        """Type check ComplexExpr expression."""
+        out = super().visit_complex_expr(e)
+        FuseTypeInfoPass.node_type_hash[e] = out
+        return out
+
+    def visit_conditional_expr(
+        self, e: mycke.ConditionalExpr, allow_none_return: bool = False
+    ) -> mycke.Type:
+        """Type check ConditionalExpr expression."""
+        out = super().visit_conditional_expr(e, allow_none_return)
+        FuseTypeInfoPass.node_type_hash[e] = out
+        return out
+
+    def visit_dictionary_comprehension(
+        self, e: mycke.DictionaryComprehension
+    ) -> mycke.Type:
+        """Type check DictionaryComprehension expression."""
+        out = super().visit_dictionary_comprehension(e)
+        FuseTypeInfoPass.node_type_hash[e] = out
+        return out
+
+    def visit_dict_expr(self, e: mycke.DictExpr) -> mycke.Type:
+        """Type check DictExpr expression."""
+        out = super().visit_dict_expr(e)
+        FuseTypeInfoPass.node_type_hash[e] = out
+        return out
+
+    def visit_enum_call_expr(self, e: mycke.EnumCallExpr) -> mycke.Type:
+        """Type check EnumCallExpr expression."""
+        out = super().visit_enum_call_expr(e)
+        FuseTypeInfoPass.node_type_hash[e] = out
+        return out
+
+    def visit_float_expr(self, e: mycke.FloatExpr) -> mycke.Type:
+        """Type check FloatExpr expression."""
+        out = super().visit_float_expr(e)
+        FuseTypeInfoPass.node_type_hash[e] = out
+        return out
+
+    def visit_generator_expr(self, e: mycke.GeneratorExpr) -> mycke.Type:
+        """Type check GeneratorExpr expression."""
+        out = super().visit_generator_expr(e)
+        FuseTypeInfoPass.node_type_hash[e] = out
+        return out
+
+    def visit_index_expr(self, e: mycke.IndexExpr) -> mycke.Type:
+        """Type check IndexExpr expression."""
+        out = super().visit_index_expr(e)
+        FuseTypeInfoPass.node_type_hash[e] = out
+        return out
+
+    def visit_int_expr(self, e: mycke.IntExpr) -> mycke.Type:
+        """Type check IntExpr expression."""
+        out = super().visit_int_expr(e)
+        FuseTypeInfoPass.node_type_hash[e] = out
+        return out
+
+    def visit_lambda_expr(self, e: mycke.LambdaExpr) -> mycke.Type:
+        """Type check LambdaExpr expression."""
+        out = super().visit_lambda_expr(e)
+        FuseTypeInfoPass.node_type_hash[e] = out
+        return out
+
+    def visit_list_comprehension(self, e: mycke.ListComprehension) -> mycke.Type:
+        """Type check ListComprehension expression."""
+        out = super().visit_list_comprehension(e)
+        FuseTypeInfoPass.node_type_hash[e] = out
+        return out
+
     def visit_list_expr(self, e: mycke.ListExpr) -> mycke.Type:
-        """Type check a list expression [...]."""
+        """Type check ListExpr expression."""
         out = super().visit_list_expr(e)
         FuseTypeInfoPass.node_type_hash[e] = out
         return out
 
+    def visit_member_expr(
+        self, e: mycke.MemberExpr, is_lvalue: bool = False
+    ) -> mycke.Type:
+        """Type check MemberExpr expression."""
+        out = super().visit_member_expr(e, is_lvalue)
+        FuseTypeInfoPass.node_type_hash[e] = out
+        return out
+
+    def visit_name_expr(self, e: mycke.NameExpr) -> mycke.Type:
+        """Type check NameExpr expression."""
+        out = super().visit_name_expr(e)
+        FuseTypeInfoPass.node_type_hash[e] = out
+        return out
+
+    def visit_op_expr(self, e: mycke.OpExpr) -> mycke.Type:
+        """Type check OpExpr expression."""
+        out = super().visit_op_expr(e)
+        FuseTypeInfoPass.node_type_hash[e] = out
+        return out
+
+    def visit_reveal_expr(self, e: mycke.RevealExpr) -> mycke.Type:
+        """Type check RevealExpr expression."""
+        out = super().visit_reveal_expr(e)
+        FuseTypeInfoPass.node_type_hash[e] = out
+        return out
+
+    def visit_set_comprehension(self, e: mycke.SetComprehension) -> mycke.Type:
+        """Type check SetComprehension expression."""
+        out = super().visit_set_comprehension(e)
+        FuseTypeInfoPass.node_type_hash[e] = out
+        return out
+
     def visit_set_expr(self, e: mycke.SetExpr) -> mycke.Type:
-        """Type check a set expression {...}."""
+        """Type check SetExpr expression."""
         out = super().visit_set_expr(e)
         FuseTypeInfoPass.node_type_hash[e] = out
         return out
 
-    def visit_tuple_expr(self, e: myfp.TupleExpr) -> myb.Type:
-        """Type check a tuple expression (...)."""
+    def visit_slice_expr(self, e: mycke.SliceExpr) -> mycke.Type:
+        """Type check SliceExpr expression."""
+        out = super().visit_slice_expr(e)
+        FuseTypeInfoPass.node_type_hash[e] = out
+        return out
+
+    def visit_star_expr(self, e: mycke.StarExpr) -> mycke.Type:
+        """Type check StarExpr expression."""
+        out = super().visit_star_expr(e)
+        FuseTypeInfoPass.node_type_hash[e] = out
+        return out
+
+    def visit_str_expr(self, e: mycke.StrExpr) -> mycke.Type:
+        """Type check StrExpr expression."""
+        out = super().visit_str_expr(e)
+        FuseTypeInfoPass.node_type_hash[e] = out
+        return out
+
+    def visit_super_expr(self, e: mycke.SuperExpr) -> mycke.Type:
+        """Type check SuperExpr expression."""
+        out = super().visit_super_expr(e)
+        FuseTypeInfoPass.node_type_hash[e] = out
+        return out
+
+    def visit_tuple_expr(self, e: mycke.TupleExpr) -> mycke.Type:
+        """Type check TupleExpr expression."""
         out = super().visit_tuple_expr(e)
         FuseTypeInfoPass.node_type_hash[e] = out
         return out
 
-    def visit_dict_expr(self, e: myfp.DictExpr) -> myb.Type:
-        """Type check a dictionary expression {...}."""
-        out = super().visit_dict_expr(e)
+    def visit_type_alias_expr(self, e: mycke.TypeAliasExpr) -> mycke.Type:
+        """Type check TypeAliasExpr expression."""
+        out = super().visit_type_alias_expr(e)
         FuseTypeInfoPass.node_type_hash[e] = out
         return out
 
-    def visit_list_comprehension(self, e: myfp.ListComprehension) -> myb.Type:
-        """Type check a list comprehension."""
-        out = super().visit_list_comprehension(e)
+    def visit_type_var_expr(self, e: mycke.TypeVarExpr) -> mycke.Type:
+        """Type check TypeVarExpr expression."""
+        out = super().visit_type_var_expr(e)
         FuseTypeInfoPass.node_type_hash[e] = out
         return out
 
-    def visit_set_comprehension(self, e: myfp.SetComprehension) -> myb.Type:
-        """Type check a set comprehension."""
-        out = super().visit_set_comprehension(e)
+    def visit_type_var_tuple_expr(self, e: mycke.TypeVarTupleExpr) -> mycke.Type:
+        """Type check TypeVarTupleExpr expression."""
+        out = super().visit_type_var_tuple_expr(e)
         FuseTypeInfoPass.node_type_hash[e] = out
         return out
 
-    def visit_generator_expr(self, e: myfp.GeneratorExpr) -> myb.Type:
-        """Type check a generator expression."""
-        out = super().visit_generator_expr(e)
+    def visit_unary_expr(self, e: mycke.UnaryExpr) -> mycke.Type:
+        """Type check UnaryExpr expression."""
+        out = super().visit_unary_expr(e)
         FuseTypeInfoPass.node_type_hash[e] = out
         return out
 
-    def visit_dictionary_comprehension(
-        self, e: myfp.DictionaryComprehension
-    ) -> myb.Type:
-        """Type check a dict comprehension."""
-        out = super().visit_dictionary_comprehension(e)
+    def visit_yield_expr(self, e: mycke.YieldExpr) -> mycke.Type:
+        """Type check YieldExpr expression."""
+        out = super().visit_yield_expr(e)
         FuseTypeInfoPass.node_type_hash[e] = out
         return out
 
-    def visit_member_expr(
-        self, e: myfp.MemberExpr, is_lvalue: bool = False
-    ) -> myb.Type:
-        """Type check a member expr."""
-        out = super().visit_member_expr(e, is_lvalue)
+    def visit_yield_from_expr(
+        self, e: mycke.YieldFromExpr, allow_none_return: bool = False
+    ) -> mycke.Type:
+        """Type check YieldFromExpr expression."""
+        out = super().visit_yield_from_expr(e, allow_none_return)
         FuseTypeInfoPass.node_type_hash[e] = out
         return out
 
diff --git a/jac/jaclang/compiler/symtable.py b/jac/jaclang/compiler/symtable.py
index 10b9ae6209..c711c4197d 100644
--- a/jac/jaclang/compiler/symtable.py
+++ b/jac/jaclang/compiler/symtable.py
@@ -88,6 +88,12 @@ def __init__(
         self.tab: dict[str, Symbol] = {}
         self.inherit: list[SymbolTable] = []
 
+    def get_type(self) -> SymbolType:
+        """Get type."""
+        if isinstance(self.owner, ast.AstSymbolNode):
+            return self.owner.sym_category
+        return SymbolType.VAR
+
     def get_parent(self) -> Optional[SymbolTable]:
         """Get parent."""
         return self.parent
diff --git a/jac/jaclang/langserve/engine.py b/jac/jaclang/langserve/engine.py
index 5e1aa5037f..871de5a6ec 100644
--- a/jac/jaclang/langserve/engine.py
+++ b/jac/jaclang/langserve/engine.py
@@ -18,6 +18,7 @@
 from jaclang.langserve.utils import (
     add_unique_text_edit,
     collect_all_symbols_in_scope,
+    collect_child_tabs,
     create_range,
     find_deepest_symbol_node_at_pos,
     find_index,
@@ -44,6 +45,7 @@ def __init__(
         self.ir = ir
         self.impl_parent: Optional[ModuleInfo] = impl_parent
         self.sem_manager = SemTokManager(ir=ir)
+        self.is_modified: bool = False
 
     @property
     def uri(self) -> str:
@@ -171,22 +173,20 @@ def get_completion(
         self, file_path: str, position: lspt.Position, completion_trigger: Optional[str]
     ) -> lspt.CompletionList:
         """Return completion for a file."""
-        completion_items = []
         document = self.workspace.get_text_document(file_path)
+        mod_ir = self.modules[file_path].ir
         current_line = document.lines[position.line]
         current_pos = position.character
         current_symbol_path = parse_symbol_path(current_line, current_pos)
+        builtin_tab = mod_ir.sym_tab.kid[-1]
+        completion_items = []
 
         node_selected = find_deepest_symbol_node_at_pos(
-            self.modules[file_path].ir,
+            mod_ir,
             position.line,
             position.character - 2,
         )
-        mod_tab = (
-            self.modules[file_path].ir.sym_tab
-            if not node_selected
-            else node_selected.sym_tab
-        )
+        mod_tab = mod_ir.sym_tab if not node_selected else node_selected.sym_tab
         current_symbol_table = mod_tab
 
         if completion_trigger == ".":
@@ -238,7 +238,9 @@ def get_completion(
                             )
                     else:
                         break
-                completion_items = collect_all_symbols_in_scope(temp_tab, up_tree=False)
+                completion_items += collect_all_symbols_in_scope(
+                    temp_tab, up_tree=False
+                )
                 if (
                     isinstance(temp_tab.owner, ast.Architype)
                     and temp_tab.owner.base_classes
@@ -254,8 +256,6 @@ def get_completion(
                                 up_tree=False,
                             )
 
-            else:
-                completion_items = []
         else:
             if node_selected and (
                 node_selected.find_parent_of_type(ast.Architype)
@@ -269,8 +269,10 @@ def get_completion(
             else:
                 self_symbol = []
 
-            completion_items = (
-                collect_all_symbols_in_scope(current_symbol_table) + self_symbol
+            completion_items += (
+                collect_all_symbols_in_scope(current_symbol_table)
+                + self_symbol
+                + collect_child_tabs(builtin_tab)
             )
         return lspt.CompletionList(is_incomplete=False, items=completion_items)
 
diff --git a/jac/jaclang/langserve/server.py b/jac/jaclang/langserve/server.py
index 54fa4c8e29..cfbdaad4a1 100644
--- a/jac/jaclang/langserve/server.py
+++ b/jac/jaclang/langserve/server.py
@@ -26,8 +26,11 @@ async def did_open(ls: JacLangServer, params: lspt.DidOpenTextDocumentParams) ->
 @server.feature(lspt.TEXT_DOCUMENT_DID_SAVE)
 async def did_save(ls: JacLangServer, params: lspt.DidOpenTextDocumentParams) -> None:
     """Check syntax on change."""
-    await ls.launch_deep_check(params.text_document.uri)
-    ls.lsp.send_request(lspt.WORKSPACE_SEMANTIC_TOKENS_REFRESH)
+    file_path = params.text_document.uri
+    if ls.modules[file_path].is_modified:
+        await ls.launch_deep_check(file_path)
+        ls.lsp.send_request(lspt.WORKSPACE_SEMANTIC_TOKENS_REFRESH)
+        ls.modules[file_path].is_modified = False
 
 
 @server.feature(lspt.TEXT_DOCUMENT_DID_CHANGE)
@@ -35,6 +38,8 @@ async def did_change(
     ls: JacLangServer, params: lspt.DidChangeTextDocumentParams
 ) -> None:
     """Check syntax on change."""
+    module = ls.modules[params.text_document.uri]
+    module.is_modified = True
     await ls.launch_quick_check(file_path := params.text_document.uri)
     if file_path in ls.modules:
         document = ls.workspace.get_text_document(file_path)
diff --git a/jac/jaclang/langserve/utils.py b/jac/jaclang/langserve/utils.py
index 5961819bc7..5147fb2f14 100644
--- a/jac/jaclang/langserve/utils.py
+++ b/jac/jaclang/langserve/utils.py
@@ -133,9 +133,12 @@ def find_index(
 ) -> Optional[int]:
     """Find index."""
     index = None
-    for i, j in enumerate(
-        [get_token_start(i, sem_tokens) for i in range(0, len(sem_tokens), 5)]
-    ):
+
+    # A list contains all the token start positions.
+    token_start_list = [
+        get_token_start(i, sem_tokens) for i in range(0, len(sem_tokens), 5)
+    ]
+    for i, j in enumerate(token_start_list):
         if j[0] == line and j[1] <= char <= j[2]:
             return i
 
@@ -302,6 +305,17 @@ def collect_all_symbols_in_scope(
     return symbols
 
 
+def collect_child_tabs(sym_tab: SymbolTable) -> list[lspt.CompletionItem]:
+    """Return all child tab's as completion items."""
+    symbols: list[lspt.CompletionItem] = []
+    for tab in sym_tab.kid:
+        if tab.name not in [i.label for i in symbols]:
+            symbols.append(
+                lspt.CompletionItem(label=tab.name, kind=label_map(tab.get_type()))
+            )
+    return symbols
+
+
 def parse_symbol_path(text: str, dot_position: int) -> list[str]:
     """Parse text and return a list of symbols."""
     text = text[:dot_position][:-1].strip()
diff --git a/jac/jaclang/runtimelib/importer.py b/jac/jaclang/runtimelib/importer.py
index 1e91b25b2c..c865da6603 100644
--- a/jac/jaclang/runtimelib/importer.py
+++ b/jac/jaclang/runtimelib/importer.py
@@ -344,14 +344,19 @@ def run_import(
                     cachable=spec.cachable,
                     reload=reload if reload else False,
                 )
+
+                # Since this is a compile time error, we can safely raise an exception here.
+                if not codeobj:
+                    raise ImportError(f"No bytecode found for {spec.full_target}")
+
                 try:
-                    if not codeobj:
-                        raise ImportError(f"No bytecode found for {spec.full_target}")
                     with sys_path_context(spec.caller_dir):
                         exec(codeobj, module.__dict__)
                 except Exception as e:
+                    logger.error(e)
                     logger.error(dump_traceback(e))
                     raise e
+
         import_return = ImportReturn(module, unique_loaded_items, self)
         if spec.items:
             import_return.process_items(
diff --git a/jac/jaclang/runtimelib/machine.py b/jac/jaclang/runtimelib/machine.py
index f1b5fecf53..f90c3fc138 100644
--- a/jac/jaclang/runtimelib/machine.py
+++ b/jac/jaclang/runtimelib/machine.py
@@ -293,6 +293,8 @@ def get_bytecode(
         result = compile_jac(full_target, cache_result=cachable)
         if result.errors_had or not result.ir.gen.py_bytecode:
             for alrt in result.errors_had:
+                # We're not logging here, it already gets logged as the errors were added to the errors_had list.
+                # Regardless of the logging, this needs to be sent to the end user, so we'll printing it to stderr.
                 logger.error(alrt.pretty_print())
             return None
         if result.ir.gen.py_bytecode is not None:
diff --git a/jac/jaclang/utils/helpers.py b/jac/jaclang/utils/helpers.py
index 2e2f32b1b3..a79a6f83c5 100644
--- a/jac/jaclang/utils/helpers.py
+++ b/jac/jaclang/utils/helpers.py
@@ -147,7 +147,7 @@ def byte_offset_to_char_offset(string: str, offset: int) -> int:
         return len(string.encode("utf-8")[:offset].decode("utf-8", errors="replace"))
 
     tb = TracebackException(type(e), e, e.__traceback__, limit=None, compact=True)
-    trace_dump += f"Error: {str(e)}"
+    trace_dump += f"Error: {str(e)}\n"
 
     # The first frame is the call the to the above `exec` function, not usefull to the enduser,
     # and Make the most recent call first.
@@ -155,28 +155,36 @@ def byte_offset_to_char_offset(string: str, offset: int) -> int:
     tb.stack.reverse()
 
     # FIXME: should be some settings, we should replace to ensure the anchors length match.
-    dump_tab_width = 4
+    dump_tab_width = 2
 
     for idx, frame in enumerate(tb.stack):
         func_signature = frame.name + ("()" if frame.name.isidentifier() else "")
 
         # Pretty print the most recent call's location.
-        if idx == 0 and (frame.line and frame.line.strip() != ""):
+        if idx == 0 and (
+            (frame.lineno is not None) and frame.line and frame.line.strip() != ""
+        ):
+
             line_o = frame._original_line.rstrip()  # type: ignore [attr-defined]
-            line_s = frame.line.rstrip() if frame.line else ""
-            stripped_chars = len(line_o) - len(line_s)
-            trace_dump += f'\n{" " * (dump_tab_width * 2)}{line_s}'
             if frame.colno is not None and frame.end_colno is not None:
-                off_start = byte_offset_to_char_offset(line_o, frame.colno)
-                off_end = byte_offset_to_char_offset(line_o, frame.end_colno)
-
-                # A bunch of caret '^' characters under the error location.
-                anchors = (" " * (off_start - stripped_chars - 1)) + "^" * len(
-                    line_o[off_start:off_end].replace("\t", " " * dump_tab_width)
+                off_start = byte_offset_to_char_offset(line_o, frame.colno) - 1
+                off_end = byte_offset_to_char_offset(line_o, frame.end_colno) - 1
+
+                # Get the source.
+                file_source = None
+                with open(frame.filename, "r") as file:
+                    file_source = file.read()
+
+                # Get the source offset.
+                lines = file_source.split("\n")
+                for i in range(frame.lineno - 1):
+                    off_start += len(lines[i]) + 1
+                    off_end += len(lines[i]) + 1
+
+                trace_dump += pretty_print_source_location(
+                    frame.filename, file_source, frame.lineno, off_start, off_end
                 )
 
-                trace_dump += f'\n{" " * (dump_tab_width * 2)}{anchors}'
-
         trace_dump += f'\n{" " * dump_tab_width}at {func_signature} {frame.filename}:{frame.lineno}'
 
     return trace_dump
@@ -242,14 +250,14 @@ def pretty_print_source_location(
                 spaces += "\t" if file_source[idx_pre] == "\t" else " "
 
             err_token_len = pos_end - pos_start
-            pretty_dump += spaces + ("~" * err_token_len) + "\n"
+            pretty_dump += spaces + ("^" * err_token_len) + "\n"
 
         if idx == len(file_source):
             break
         curr_line += 1
         idx += 1
 
-    return pretty_dump
+    return pretty_dump[:-1]  # Get rid of the last newline (of the last line).
 
 
 class Jdb(pdb.Pdb):
diff --git a/jac/jaclang/utils/treeprinter.py b/jac/jaclang/utils/treeprinter.py
index 1b636a3f1e..5c953c3dd0 100644
--- a/jac/jaclang/utils/treeprinter.py
+++ b/jac/jaclang/utils/treeprinter.py
@@ -105,7 +105,7 @@ def __node_repr_in_tree(node: AstNode) -> str:
         if isinstance(node, Token) and isinstance(node, AstSymbolNode):
             out = (
                 f"{node.__class__.__name__} - {node.value} - "
-                f"Type: {node.sym_type}, {access} {sym_table_link}"
+                f"Type: {node.expr_type}, {access} {sym_table_link}"
             )
             if settings.ast_symbol_info_detailed:
                 symbol = (
@@ -133,7 +133,7 @@ def __node_repr_in_tree(node: AstNode) -> str:
         elif isinstance(node, AstSymbolNode):
             out = (
                 f"{node.__class__.__name__} - {node.sym_name} - "
-                f"Type: {node.sym_type}, {access} {sym_table_link}"
+                f"Type: {node.expr_type}, {access} {sym_table_link}"
             )
             if settings.ast_symbol_info_detailed:
                 symbol = (
@@ -143,6 +143,8 @@ def __node_repr_in_tree(node: AstNode) -> str:
                 )
                 out += f" SymbolPath: {symbol}"
             return out
+        elif isinstance(node, ast.Expr):
+            return f"{node.__class__.__name__} - Type: {node.expr_type}"
         else:
             return f"{node.__class__.__name__}, {access}"
 
diff --git a/jac/support/jac-lang.org/docs/learn/coders/jac-cloud/docs/permission.md b/jac/support/jac-lang.org/docs/learn/coders/jac-cloud/docs/permission.md
new file mode 100644
index 0000000000..936672b9e9
--- /dev/null
+++ b/jac/support/jac-lang.org/docs/learn/coders/jac-cloud/docs/permission.md
@@ -0,0 +1,1 @@
+### Permission Management (Work in progress)
\ No newline at end of file
diff --git a/jac/support/jac-lang.org/mkdocs.yml b/jac/support/jac-lang.org/mkdocs.yml
index 1493a8e20d..f877c4ee66 100644
--- a/jac/support/jac-lang.org/mkdocs.yml
+++ b/jac/support/jac-lang.org/mkdocs.yml
@@ -17,6 +17,7 @@ nav:
               - "learn/data_spatial/FAQ.md"
           - Jac Cloud:
               - "learn/coders/jac-cloud/docs/jac_cloud.md"
+              - "learn/coders/jac-cloud/docs/permission.md"
               - "learn/coders/jac-cloud/docs/jac_cloud_logging.md"
               - "learn/coders/jac-cloud/docs/jac_cloud_env_vars.md"
           - MTLLM:


Test patch:
diff --git a/jac-cloud/jac_cloud/tests/simple_graph.jac b/jac-cloud/jac_cloud/tests/simple_graph.jac
index 1a05bf5010..3fcc4753b9 100644
--- a/jac-cloud/jac_cloud/tests/simple_graph.jac
+++ b/jac-cloud/jac_cloud/tests/simple_graph.jac
@@ -37,6 +37,12 @@ class User:BaseUser: {
             "name": f"{open_id.first_name} {open_id.last_name}"
         };
     }
+
+    static can system_admin_default() -> dict[str, object] {
+        return {
+            "name": f"System Administrator"
+        };
+    }
 }
 
 
diff --git a/jac-cloud/jac_cloud/tests/test.json b/jac-cloud/jac_cloud/tests/test.json
new file mode 100644
index 0000000000..e9bdb8309b
--- /dev/null
+++ b/jac-cloud/jac_cloud/tests/test.json
@@ -0,0 +1,3 @@
+{
+    "testing": 1
+}
\ No newline at end of file
diff --git a/jac-cloud/jac_cloud/tests/test_simple_graph.py b/jac-cloud/jac_cloud/tests/test_simple_graph.py
index 80ef287769..5cc1b82395 100644
--- a/jac-cloud/jac_cloud/tests/test_simple_graph.py
+++ b/jac-cloud/jac_cloud/tests/test_simple_graph.py
@@ -612,19 +612,19 @@ def trigger_upload_file(self) -> None:
                             "single": {
                                 "name": "simple_graph.jac",
                                 "content_type": "application/octet-stream",
-                                "size": 6852,
+                                "size": 6992,
                             }
                         },
                         "multiple": [
                             {
                                 "name": "simple_graph.jac",
                                 "content_type": "application/octet-stream",
-                                "size": 6852,
+                                "size": 6992,
                             },
                             {
                                 "name": "simple_graph.jac",
                                 "content_type": "application/octet-stream",
-                                "size": 6852,
+                                "size": 6992,
                             },
                         ],
                         "singleOptional": None,
diff --git a/jac-cloud/jac_cloud/tests/test_simple_graph_mini.py b/jac-cloud/jac_cloud/tests/test_simple_graph_mini.py
index a489793779..9c24a23416 100644
--- a/jac-cloud/jac_cloud/tests/test_simple_graph_mini.py
+++ b/jac-cloud/jac_cloud/tests/test_simple_graph_mini.py
@@ -390,19 +390,19 @@ def trigger_upload_file(self) -> None:
                             "single": {
                                 "name": "simple_graph.jac",
                                 "content_type": "application/octet-stream",
-                                "size": 6852,
+                                "size": 6992,
                             }
                         },
                         "multiple": [
                             {
                                 "name": "simple_graph.jac",
                                 "content_type": "application/octet-stream",
-                                "size": 6852,
+                                "size": 6992,
                             },
                             {
                                 "name": "simple_graph.jac",
                                 "content_type": "application/octet-stream",
-                                "size": 6852,
+                                "size": 6992,
                             },
                         ],
                         "singleOptional": None,
diff --git a/jac/jaclang/compiler/passes/main/tests/fixtures/defn_decl_mismatch.jac b/jac/jaclang/compiler/passes/main/tests/fixtures/defn_decl_mismatch.jac
new file mode 100644
index 0000000000..e50ac35345
--- /dev/null
+++ b/jac/jaclang/compiler/passes/main/tests/fixtures/defn_decl_mismatch.jac
@@ -0,0 +1,19 @@
+
+
+obj SomeObj {
+    can foo(param1: str, param2:int) -> str;
+    can bar(param1: str, param2:int) -> str;
+}
+
+
+# Miss match parameter count.
+:obj:SomeObj:can:foo(param1: str) -> str {
+    return "foo";
+}
+
+
+# Mis matching parameter name.
+:obj:SomeObj:can:bar(param1: str, praam2:int) -> str {
+    return "bar";
+}
+
diff --git a/jac/jaclang/compiler/passes/main/tests/test_decl_def_match_pass.py b/jac/jaclang/compiler/passes/main/tests/test_decl_def_match_pass.py
index 6db6f4d837..17ef767235 100644
--- a/jac/jaclang/compiler/passes/main/tests/test_decl_def_match_pass.py
+++ b/jac/jaclang/compiler/passes/main/tests/test_decl_def_match_pass.py
@@ -13,6 +13,65 @@ def setUp(self) -> None:
         """Set up test."""
         return super().setUp()
 
+    def test_parameter_count_mismatch(self) -> None:
+        """Basic test for pass."""
+        state = jac_file_to_pass(
+            self.fixture_abs_path("defn_decl_mismatch.jac"), DeclImplMatchPass
+        )
+
+        expected_stdout_values = (
+            "Parameter count mismatch for ability (o)SomeObj.(c)foo.",
+            "    8 |",
+            "    9 | # Miss match parameter count.",
+            "   10 | :obj:SomeObj:can:foo(param1: str) -> str {",
+            "      | ^^^^^^^^^^^^^^^^^^^^",
+            '   11 |     return "foo";',
+            "   12 | }",
+            "From the declaration of foo.",
+            "    2 |",
+            "    3 | obj SomeObj {",
+            "    4 |     can foo(param1: str, param2:int) -> str;",
+            "      |         ^^^",
+            "    5 |     can bar(param1: str, param2:int) -> str;",
+            "    6 | }",
+        )
+
+        errors_output = ""
+        for error in state.errors_had:
+            errors_output += error.pretty_print() + "\n"
+
+        for exp in expected_stdout_values:
+            self.assertIn(exp, errors_output)
+
+    def test_parameter_name_mismatch(self) -> None:
+        """Basic test for pass."""
+        state = jac_file_to_pass(
+            self.fixture_abs_path("defn_decl_mismatch.jac"), DeclImplMatchPass
+        )
+
+        expected_stdout_values = (
+            "Parameter name mismatch for ability (o)SomeObj.(c)bar.",
+            "   14 |",
+            "   15 | # Mis matching parameter name.",
+            "   16 | :obj:SomeObj:can:bar(param1: str, praam2:int) -> str {",
+            "      |                                   ^^^^^^",
+            '   17 |     return "bar";',
+            "   18 | }",
+            "From the declaration of bar.",
+            "    3 | obj SomeObj {",
+            "    4 |     can foo(param1: str, param2:int) -> str;",
+            "    5 |     can bar(param1: str, param2:int) -> str;",
+            "      |                          ^^^^^^",
+            "    6 | }",
+        )
+
+        errors_output = ""
+        for error in state.errors_had:
+            errors_output += error.pretty_print() + "\n"
+
+        for exp in expected_stdout_values:
+            self.assertIn(exp, errors_output)
+
     def test_ability_connected_to_decl(self) -> None:
         """Basic test for pass."""
         state = jac_file_to_pass(self.fixture_abs_path("base.jac"), DeclImplMatchPass)
diff --git a/jac/jaclang/compiler/passes/main/tests/test_type_check_pass.py b/jac/jaclang/compiler/passes/main/tests/test_type_check_pass.py
index efb3cc5627..09a18e2e4e 100644
--- a/jac/jaclang/compiler/passes/main/tests/test_type_check_pass.py
+++ b/jac/jaclang/compiler/passes/main/tests/test_type_check_pass.py
@@ -59,6 +59,6 @@ def test_type_coverage(self) -> None:
         self.assertIn("HasVar - species - Type: builtins.str", out)
         self.assertIn("myDog - Type: type_info.Dog", out)
         self.assertIn("Body - Type: type_info.Dog.Body", out)
-        self.assertEqual(out.count("Type: builtins.str"), 34)
+        self.assertEqual(out.count("Type: builtins.str"), 35)
         for i in lis:
             self.assertNotIn(i, out)
diff --git a/jac/jaclang/langserve/tests/test_server.py b/jac/jaclang/langserve/tests/test_server.py
index 204ff826ee..375b8317c5 100644
--- a/jac/jaclang/langserve/tests/test_server.py
+++ b/jac/jaclang/langserve/tests/test_server.py
@@ -369,7 +369,7 @@ def test_completion(self) -> None:
             (
                 lspt.Position(37, 12),
                 ["self", "add", "subtract", "x", "Colorenum", "Colour1", "red", "r"],
-                8,
+                160,
                 None,
             ),
         ]
diff --git a/jac/jaclang/tests/fixtures/expr_type.jac b/jac/jaclang/tests/fixtures/expr_type.jac
new file mode 100644
index 0000000000..0dd3c8e0da
--- /dev/null
+++ b/jac/jaclang/tests/fixtures/expr_type.jac
@@ -0,0 +1,54 @@
+with entry {
+    a = 4;
+    b = 7;
+    c = a + b; # OpExpr.
+    d = a + b + c;
+
+    h = float(a); # CallExpr.
+
+    (1 < 2);  # ComparisonExpr.
+    ('a' + 'b').upper(); # CallExpr.
+    [1,2][0]; # IndexExpr.
+    not False; # UnaryExpr.
+    "a" if True else "b"; # ConditionalExpr.
+    [i for i in range(10)]; # ListComprehension.
+}
+
+# Remaining expressions to test:
+# AssertTypeExpr,
+# AssignmentExpr,
+# AwaitExpr,
+# BytesExpr,
+# CastExpr,
+# ComplexExpr,
+# DictionaryComprehension,
+# DictExpr,
+# EllipsisExpr,
+# EnumCallExpr,
+# Expression,
+# FloatExpr,
+# GeneratorExpr,
+# IntExpr,
+# LambdaExpr,
+# ListExpr,
+# MemberExpr,
+# NamedTupleExpr,
+# NameExpr,
+# NewTypeExpr,
+# ParamSpecExpr,
+# PromoteExpr,
+# RefExpr,
+# RevealExpr,
+# SetComprehension,
+# SetExpr,
+# SliceExpr,
+# StarExpr,
+# StrExpr,
+# SuperExpr,
+# TupleExpr,
+# TypeAliasExpr,
+# TypedDictExpr,
+# TypeVarExpr,
+# TypeVarTupleExpr,
+# YieldExpr,
+# YieldFromExpr,
diff --git a/jac/jaclang/tests/foo/__init__.jac b/jac/jaclang/tests/foo/__init__.jac
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/jac/jaclang/tests/main.jac b/jac/jaclang/tests/main.jac
new file mode 100644
index 0000000000..cdd8f20075
--- /dev/null
+++ b/jac/jaclang/tests/main.jac
@@ -0,0 +1,2 @@
+import foo;
+
diff --git a/jac/jaclang/tests/test_cli.py b/jac/jaclang/tests/test_cli.py
index b5635022b2..f65505cebf 100644
--- a/jac/jaclang/tests/test_cli.py
+++ b/jac/jaclang/tests/test_cli.py
@@ -1,5 +1,6 @@
 """Test Jac cli module."""
 
+import contextlib
 import inspect
 import io
 import os
@@ -55,10 +56,8 @@ def test_jac_cli_alert_based_runtime_err(self) -> None:
         sys.stdout = captured_output
         sys.stderr = captured_output
 
-        try:
+        with contextlib.suppress(Exception):
             cli.run(self.fixture_abs_path("err_runtime.jac"))
-        except Exception as e:
-            print(f"Error: {e}")
 
         sys.stdout = sys.__stdout__
         sys.stderr = sys.__stderr__
@@ -71,7 +70,6 @@ def test_jac_cli_alert_based_runtime_err(self) -> None:
             "  at foo() ",
             "  at <module> ",
         )
-
         logger_capture = "\n".join([rec.message for rec in self.caplog.records])
         for exp in expected_stdout_values:
             self.assertIn(exp, logger_capture)
@@ -217,6 +215,42 @@ def test_builtins_loading(self) -> None:
             r"13\:12 \- 13\:18.*Name - append - .*SymbolPath: builtins_test.builtins.list.append",
         )
 
+    def test_expr_types(self) -> None:
+        """Testing for print AstTool."""
+        captured_output = io.StringIO()
+        sys.stdout = captured_output
+
+        cli.tool("ir", ["ast", f"{self.fixture_abs_path('expr_type.jac')}"])
+
+        sys.stdout = sys.__stdout__
+        stdout_value = captured_output.getvalue()
+
+        self.assertRegex(
+            stdout_value, r"4\:9 \- 4\:14.*BinaryExpr \- Type\: builtins.int"
+        )
+        self.assertRegex(
+            stdout_value, r"7\:9 \- 7\:17.*FuncCall \- Type\: builtins.float"
+        )
+        self.assertRegex(
+            stdout_value, r"9\:6 \- 9\:11.*CompareExpr \- Type\: builtins.bool"
+        )
+        self.assertRegex(
+            stdout_value, r"10\:6 - 10\:15.*BinaryExpr \- Type\: builtins.str"
+        )
+        self.assertRegex(
+            stdout_value, r"11\:5 \- 11\:13.*AtomTrailer \- Type\: builtins.int"
+        )
+        self.assertRegex(
+            stdout_value, r"12\:5 \- 12\:14.*UnaryExpr \- Type\: builtins.bool"
+        )
+        self.assertRegex(
+            stdout_value, r"13\:5 \- 13\:25.*IfElseExpr \- Type\: Literal\['a']\?"
+        )
+        self.assertRegex(
+            stdout_value,
+            r"14\:5 \- 14\:27.*ListCompr - \[ListCompr] \- Type\: builtins.list\[builtins.int]",
+        )
+
     def test_ast_dotgen(self) -> None:
         """Testing for print AstTool."""
         captured_output = io.StringIO()
@@ -247,6 +281,8 @@ def test_type_info(self) -> None:
         self.assertEqual(stdout_value.count("type_info.ServerWrapper"), 7)
         self.assertEqual(stdout_value.count("builtins.int"), 3)
         self.assertEqual(stdout_value.count("builtins.str"), 10)
+        self.assertIn("Literal['test_server']", stdout_value)
+        self.assertIn("Literal['1']", stdout_value)
 
     def test_build_and_run(self) -> None:
         """Testing for print AstTool."""
diff --git a/jac/jaclang/tests/test_language.py b/jac/jaclang/tests/test_language.py
index 1b7a06706c..82fd28c429 100644
--- a/jac/jaclang/tests/test_language.py
+++ b/jac/jaclang/tests/test_language.py
@@ -209,8 +209,10 @@ def test_semstr(self) -> None:
         """Test semstring."""
         captured_output = io.StringIO()
         sys.stdout = captured_output
+        sys.stderr = captured_output
         jac_import("semstr", base_path=self.fixture_abs_path("./"))
         sys.stdout = sys.__stdout__
+        sys.stderr = sys.__stderr__
         stdout_value = captured_output.getvalue()
         self.assertNotIn("Error", stdout_value)
 
@@ -466,8 +468,10 @@ def test_registry(self) -> None:
         """Test Jac registry feature."""
         captured_output = io.StringIO()
         sys.stdout = captured_output
+        sys.stderr = captured_output
         jac_import("registry", base_path=self.fixture_abs_path("./"))
         sys.stdout = sys.__stdout__
+        sys.stderr = sys.__stderr__
         stdout_value = captured_output.getvalue()
         self.assertNotIn("Error", stdout_value)
 
@@ -806,11 +810,13 @@ def test_access_modifier(self) -> None:
         """Test for access tags working."""
         captured_output = io.StringIO()
         sys.stdout = captured_output
+        sys.stderr = captured_output
         cli.check(
             self.fixture_abs_path("../../tests/fixtures/access_modifier.jac"),
             print_errs=True,
         )
         sys.stdout = sys.__stdout__
+        sys.stderr = sys.__stderr__
         stdout_value = captured_output.getvalue()
         self.assertEqual(stdout_value.count("Invalid access"), 18)
 
@@ -828,10 +834,12 @@ def test_deep_convert(self) -> None:
             parsed_ast = py_ast.parse(f.read())
             try:
                 py_ast_build_pass = PyastBuildPass(
-                    input_ir=ast.PythonModuleAst(parsed_ast, mod_path=file_name),
+                    input_ir=ast.PythonModuleAst(
+                        parsed_ast, orig_src=ast.JacSource(f.read(), file_name)
+                    )
                 )
             except Exception as e:
-                return f"Error While Jac to Py AST conversion: {e}"
+                raise Exception(f"Error While Jac to Py AST conversion: {e}")
 
         settings.print_py_raised_ast = True
         ir = jac_pass_to_pass(py_ast_build_pass, schedule=py_code_gen_typed).ir