jaclang 0.7.16__py3-none-any.whl → 0.7.18__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of jaclang might be problematic. Click here for more details.

Files changed (81) hide show
  1. jaclang/cli/cli.py +140 -77
  2. jaclang/compiler/absyntree.py +9 -4
  3. jaclang/compiler/constant.py +8 -8
  4. jaclang/compiler/parser.py +10 -2
  5. jaclang/compiler/passes/main/__init__.py +1 -1
  6. jaclang/compiler/passes/main/access_modifier_pass.py +96 -147
  7. jaclang/compiler/passes/main/fuse_typeinfo_pass.py +152 -50
  8. jaclang/compiler/passes/main/import_pass.py +88 -59
  9. jaclang/compiler/passes/main/py_collect_dep_pass.py +70 -0
  10. jaclang/compiler/passes/main/pyast_gen_pass.py +46 -6
  11. jaclang/compiler/passes/main/pyast_load_pass.py +1 -0
  12. jaclang/compiler/passes/main/pyjac_ast_link_pass.py +7 -0
  13. jaclang/compiler/passes/main/schedules.py +9 -2
  14. jaclang/compiler/passes/main/sym_tab_build_pass.py +9 -5
  15. jaclang/compiler/passes/main/tests/fixtures/autoimpl.empty.impl.jac +0 -0
  16. jaclang/compiler/passes/main/tests/fixtures/autoimpl.jac +1 -1
  17. jaclang/compiler/passes/main/tests/fixtures/py_imp_test.jac +29 -0
  18. jaclang/compiler/passes/main/tests/fixtures/pygame_mock/__init__.py +3 -0
  19. jaclang/compiler/passes/main/tests/fixtures/pygame_mock/color.py +3 -0
  20. jaclang/compiler/passes/main/tests/fixtures/pygame_mock/constants.py +5 -0
  21. jaclang/compiler/passes/main/tests/fixtures/pygame_mock/display.py +2 -0
  22. jaclang/compiler/passes/main/tests/test_import_pass.py +72 -13
  23. jaclang/compiler/passes/main/type_check_pass.py +15 -5
  24. jaclang/compiler/passes/tool/jac_formatter_pass.py +13 -3
  25. jaclang/compiler/passes/tool/tests/fixtures/corelib.jac +37 -41
  26. jaclang/compiler/passes/tool/tests/fixtures/corelib_fmt.jac +37 -41
  27. jaclang/compiler/passes/tool/tests/fixtures/general_format_checks/access_mod_check.jac +27 -0
  28. jaclang/compiler/passes/utils/mypy_ast_build.py +45 -0
  29. jaclang/compiler/symtable.py +16 -11
  30. jaclang/compiler/tests/test_importer.py +17 -9
  31. jaclang/langserve/engine.py +64 -16
  32. jaclang/langserve/server.py +16 -1
  33. jaclang/langserve/tests/fixtures/import_include_statements.jac +3 -3
  34. jaclang/langserve/tests/fixtures/rename.jac +30 -0
  35. jaclang/langserve/tests/test_server.py +224 -6
  36. jaclang/langserve/utils.py +28 -98
  37. jaclang/plugin/builtin.py +8 -4
  38. jaclang/plugin/default.py +86 -64
  39. jaclang/plugin/feature.py +13 -13
  40. jaclang/plugin/spec.py +10 -11
  41. jaclang/plugin/tests/fixtures/other_root_access.jac +82 -0
  42. jaclang/plugin/tests/test_jaseci.py +414 -42
  43. jaclang/runtimelib/architype.py +481 -333
  44. jaclang/runtimelib/constructs.py +5 -8
  45. jaclang/runtimelib/context.py +89 -69
  46. jaclang/runtimelib/importer.py +16 -15
  47. jaclang/runtimelib/machine.py +66 -2
  48. jaclang/runtimelib/memory.py +134 -75
  49. jaclang/runtimelib/utils.py +17 -10
  50. jaclang/settings.py +2 -4
  51. jaclang/tests/fixtures/access_checker.jac +12 -17
  52. jaclang/tests/fixtures/access_modifier.jac +88 -33
  53. jaclang/tests/fixtures/baddy.jac +3 -0
  54. jaclang/tests/fixtures/bar.jac +34 -0
  55. jaclang/tests/fixtures/builtin_dotgen.jac +1 -0
  56. jaclang/tests/fixtures/edge_node_walk.jac +1 -1
  57. jaclang/tests/fixtures/edge_ops.jac +1 -1
  58. jaclang/tests/fixtures/edges_walk.jac +1 -1
  59. jaclang/tests/fixtures/foo.jac +43 -0
  60. jaclang/tests/fixtures/game1.jac +1 -1
  61. jaclang/tests/fixtures/gendot_bubble_sort.jac +1 -1
  62. jaclang/tests/fixtures/import.jac +9 -0
  63. jaclang/tests/fixtures/index_slice.jac +30 -0
  64. jaclang/tests/fixtures/objref.jac +12 -0
  65. jaclang/tests/fixtures/pyfunc_1.py +1 -1
  66. jaclang/tests/fixtures/pyfunc_2.py +2 -2
  67. jaclang/tests/fixtures/pygame_mock/__init__.py +3 -0
  68. jaclang/tests/fixtures/pygame_mock/color.py +3 -0
  69. jaclang/tests/fixtures/pygame_mock/constants.py +5 -0
  70. jaclang/tests/fixtures/pygame_mock/display.py +2 -0
  71. jaclang/tests/fixtures/pygame_mock/inner/__init__.py +0 -0
  72. jaclang/tests/fixtures/pygame_mock/inner/iner_mod.py +2 -0
  73. jaclang/tests/test_cli.py +49 -6
  74. jaclang/tests/test_language.py +126 -80
  75. jaclang/tests/test_reference.py +2 -9
  76. jaclang/utils/treeprinter.py +30 -3
  77. {jaclang-0.7.16.dist-info → jaclang-0.7.18.dist-info}/METADATA +3 -1
  78. {jaclang-0.7.16.dist-info → jaclang-0.7.18.dist-info}/RECORD +81 -59
  79. /jaclang/tests/fixtures/{err.test.jac → baddy.test.jac} +0 -0
  80. {jaclang-0.7.16.dist-info → jaclang-0.7.18.dist-info}/WHEEL +0 -0
  81. {jaclang-0.7.16.dist-info → jaclang-0.7.18.dist-info}/entry_points.txt +0 -0
@@ -3,162 +3,56 @@
3
3
  This pass checks for access to attributes in the Jac language.
4
4
  """
5
5
 
6
- import os
7
6
  from typing import Optional
8
7
 
9
8
  import jaclang.compiler.absyntree as ast
10
9
  from jaclang.compiler.constant import SymbolAccess
11
10
  from jaclang.compiler.passes import Pass
12
- from jaclang.compiler.symtable import SymbolTable
11
+ from jaclang.compiler.symtable import Symbol
13
12
  from jaclang.settings import settings
14
13
 
15
14
 
16
15
  class AccessCheckPass(Pass):
17
16
  """Jac Ast Access Check pass."""
18
17
 
19
- def after_pass(self) -> None:
20
- """After pass."""
21
- pass
22
-
23
- def exit_node(self, node: ast.AstNode) -> None:
18
+ # NOTE: This method is a hacky way to detect if the drivied class is inherit from base class, it
19
+ # doesn't work if the base class was provided as an expression (ex. obj Dri :module.Base: {...}).
20
+ def is_class_inherited_from(
21
+ self, dri_class: ast.Architype, base_class: ast.Architype
22
+ ) -> bool:
23
+ """Return true if the dri_class inherited from base_class."""
24
+ if dri_class.base_classes is None:
25
+ return False
26
+ for expr in dri_class.base_classes.items:
27
+ if not isinstance(expr, ast.Name):
28
+ continue
29
+ if not isinstance(expr.name_of, ast.Architype):
30
+ continue # Unlikely.
31
+ if expr.name_of == base_class:
32
+ return True
33
+ if self.is_class_inherited_from(expr.name_of, base_class):
34
+ return True
35
+ return False
36
+
37
+ def report_error(self, message: str, node: Optional[ast.AstNode] = None) -> None:
38
+ """Report error message related to illegal access of attributes and objects."""
39
+ self.error(message, node)
40
+
41
+ def exit_node(self, node: ast.AstNode) -> None: # TODO: Move to debug pass
24
42
  """Exit node."""
25
43
  super().exit_node(node)
26
- if settings.lsp_debug and isinstance(node, ast.NameAtom) and not node.sym:
27
- self.warning(f"Name {node.sym_name} not present in symbol table")
28
-
29
- def access_check(self, node: ast.Name) -> None:
30
- """Access check."""
31
- node_info = (
32
- node.sym_tab.lookup(node.sym_name)
33
- if isinstance(node.sym_tab, SymbolTable)
34
- else None
35
- )
36
-
37
- if node.sym:
38
- decl_package_path = os.path.dirname(
39
- os.path.abspath(node.sym.defn[-1].loc.mod_path)
40
- )
41
- use_package_path = os.path.dirname(os.path.abspath(node.loc.mod_path))
42
- else:
43
- decl_package_path = use_package_path = ""
44
-
45
44
  if (
46
- node_info
47
- and node.sym
48
- and node_info.access == SymbolAccess.PROTECTED
49
- and decl_package_path != use_package_path
50
- ):
51
- return self.error(
52
- f'Can not access protected variable "{node.sym_name}" from {decl_package_path}'
53
- f" to {use_package_path}."
45
+ settings.lsp_debug
46
+ and isinstance(node, ast.NameAtom)
47
+ and not node.sym
48
+ and not node.parent_of_type(ast.Module).is_raised_from_py
49
+ and not (
50
+ node.sym_name == "py"
51
+ and node.parent
52
+ and isinstance(node.parent.parent, ast.Import)
54
53
  )
55
-
56
- if (
57
- node_info
58
- and node.sym
59
- and node_info.access == SymbolAccess.PRIVATE
60
- and node.sym.defn[-1].loc.mod_path != node.loc.mod_path
61
54
  ):
62
- return self.error(
63
- f'Can not access private variable "{node.sym_name}" from {node.sym.defn[-1].loc.mod_path}'
64
- f" to {node.loc.mod_path}."
65
- )
66
-
67
- def access_register(
68
- self, node: ast.AstSymbolNode, acc_tag: Optional[SymbolAccess] = None
69
- ) -> None:
70
- """Access register."""
71
-
72
- def enter_global_vars(self, node: ast.GlobalVars) -> None:
73
- """Sub objects.
74
-
75
- access: Optional[SubTag[Token]],
76
- assignments: SubNodeList[Assignment],
77
- is_frozen: bool,
78
- """
79
- pass
80
-
81
- def enter_module(self, node: ast.Module) -> None:
82
- """Sub objects.
83
-
84
- name: str,
85
- doc: Token,
86
- body: Optional['Elements'],
87
- mod_path: str,
88
- is_imported: bool,
89
- """
90
-
91
- def enter_architype(self, node: ast.Architype) -> None:
92
- """Sub objects.
93
-
94
- name: Name,
95
- arch_type: Token,
96
- access: Optional[SubTag[Token]],
97
- base_classes: Optional[SubNodeList[Expr]],
98
- body: Optional[SubNodeList[ArchBlockStmt] | ArchDef],
99
- decorators: Optional[SubNodeList[Expr]] = None,
100
- """
101
- pass
102
-
103
- def enter_enum(self, node: ast.Enum) -> None:
104
- """Sub objects.
105
-
106
- name: Name,
107
- access: Optional[SubTag[Token]],
108
- base_classes: Optional[SubNodeList[Expr]],
109
- body: Optional[SubNodeList[EnumBlockStmt] | EnumDef],
110
- decorators: Optional[SubNodeList[Expr]] = None,
111
- """
112
- pass
113
-
114
- def enter_ability(self, node: ast.Ability) -> None:
115
- """Sub objects.
116
-
117
- name_ref: NameSpec,
118
- is_func: bool,
119
- is_async: bool,
120
- is_override: bool,
121
- is_static: bool,
122
- is_abstract: bool,
123
- access: Optional[SubTag[Token]],
124
- signature: Optional[FuncSignature | EventSignature],
125
- body: Optional[SubNodeList[CodeBlockStmt] | AbilityDef],
126
- decorators: Optional[SubNodeList[Expr]] = None,
127
- """
128
- pass
129
-
130
- def enter_sub_node_list(self, node: ast.SubNodeList) -> None:
131
- """Sub objects.
132
-
133
- items: list[T]
134
- """
135
-
136
- def enter_arch_has(self, node: ast.ArchHas) -> None:
137
- """Sub objects.
138
-
139
- is_static: bool,
140
- access: Optional[SubTag[Token]],
141
- vars: SubNodeList[HasVar],
142
- is_frozen: bool,
143
- """
144
- pass
145
-
146
- def enter_atom_trailer(self, node: ast.AtomTrailer) -> None:
147
- """Sub objects.
148
-
149
- access: Optional[SubTag[Token]],
150
- """
151
- pass
152
-
153
- def enter_func_call(self, node: ast.FuncCall) -> None:
154
- """Sub objects.
155
-
156
- target: Expr,
157
- params: Optional[SubNodeList[Expr | KWPair]],
158
- genai_call: Optional[FuncCall],
159
- kid: Sequence[AstNode],
160
- """
161
- pass
55
+ self.warning(f"Name {node.sym_name} not present in symbol table")
162
56
 
163
57
  def enter_name(self, node: ast.Name) -> None:
164
58
  """Sub objects.
@@ -170,12 +64,67 @@ class AccessCheckPass(Pass):
170
64
  pos_start: int,
171
65
  pos_end: int,
172
66
  """
173
- from jaclang.compiler.passes import Pass
67
+ # TODO: Enums are not considered at the moment, I'll need to test and add them bellow.
174
68
 
175
- if isinstance(node.parent, ast.FuncCall):
176
- self.access_check(node)
69
+ # If the current node is a global variable's name there is no access, it's just the declaration.
70
+ if Pass.find_parent_of_type(node, ast.GlobalVars) is not None:
71
+ return
177
72
 
178
- if node.sym and Pass.find_parent_of_type(
179
- node=node.sym.defn[-1], typ=ast.GlobalVars
180
- ):
181
- self.access_check(node)
73
+ # Class name, and ability name are declarations and there is no access here as well.
74
+ if isinstance(node.name_of, (ast.Ability, ast.Architype, ast.Enum)):
75
+ return
76
+
77
+ # Get the context to check the access.
78
+ curr_class: Optional[ast.Architype] = Pass.find_parent_of_type(
79
+ node, ast.Architype
80
+ )
81
+ curr_module: Optional[ast.Module] = Pass.find_parent_of_type(node, ast.Module)
82
+ if curr_module is None:
83
+ return
84
+
85
+ # Note that currently we can only check for name + symbols, because expressions are not associated with the
86
+ # typeinfo thus they don't have a symbol. In the future the name nodes will become expression nodes.
87
+ if not isinstance(node.sym, Symbol):
88
+ return
89
+
90
+ # Public symbols are fine.
91
+ if node.sym.access == SymbolAccess.PUBLIC:
92
+ return
93
+
94
+ # Note that from bellow the access is either private or protected.
95
+ is_portect = node.sym.access == SymbolAccess.PROTECTED
96
+ access_type = "protected" if is_portect else "private"
97
+
98
+ # The class we're currently in (None if we're not inside any).
99
+ sym_owner: ast.AstNode = node.sym.parent_tab.owner
100
+
101
+ # If the symbol belongs to a class, we need to check if the access used properly
102
+ # within the class and in it's inherited classes.
103
+ if isinstance(sym_owner, ast.Architype):
104
+
105
+ # Accessing a private/protected member within the top level scope illegal.
106
+ if curr_class is None:
107
+ return self.report_error(
108
+ f'Error: Invalid access of {access_type} member "{node.sym_name}".',
109
+ node,
110
+ )
111
+
112
+ if curr_class != node.sym.parent_tab.owner:
113
+ if not is_portect: # private member accessed in a different class.
114
+ return self.report_error(
115
+ f'Error: Invalid access of {access_type} member "{node.sym_name}".',
116
+ node,
117
+ )
118
+ else: # Accessing a protected member, check we're in an inherited class.
119
+ if not self.is_class_inherited_from(curr_class, sym_owner):
120
+ return self.report_error(
121
+ f'Error: Invalid access of {access_type} member "{node.sym_name}".',
122
+ node,
123
+ )
124
+
125
+ elif isinstance(sym_owner, ast.Module) and sym_owner != curr_module:
126
+ # Accessing a private/public member in a different module.
127
+ return self.report_error(
128
+ f'Error: Invalid access of {access_type} member "{node.sym_name}".',
129
+ node,
130
+ )
@@ -6,7 +6,7 @@ mypy apis into Jac and use jac py ast in it.
6
6
 
7
7
  from __future__ import annotations
8
8
 
9
- from typing import Callable, TypeVar
9
+ from typing import Callable, Optional, TypeVar
10
10
 
11
11
  import jaclang.compiler.absyntree as ast
12
12
  from jaclang.compiler.passes import Pass
@@ -29,12 +29,12 @@ class FuseTypeInfoPass(Pass):
29
29
 
30
30
  node_type_hash: dict[MypyNodes.Node | VNode, MyType] = {}
31
31
 
32
- def __debug_print(self, *argv: object) -> None:
32
+ def __debug_print(self, msg: str) -> None:
33
33
  if settings.fuse_type_info_debug:
34
- self.log_info("FuseTypeInfo::", *argv)
34
+ self.log_info("FuseTypeInfo::" + msg)
35
35
 
36
36
  def __call_type_handler(
37
- self, node: ast.AstSymbolNode, mypy_type: MypyTypes.ProperType
37
+ self, node: ast.AstSymbolNode, mypy_type: MypyTypes.Type
38
38
  ) -> None:
39
39
  mypy_type_name = pascal_to_snake(mypy_type.__class__.__name__)
40
40
  type_handler_name = f"get_type_from_{mypy_type_name}"
@@ -52,6 +52,9 @@ class FuseTypeInfoPass(Pass):
52
52
  if typ[0] == "builtins":
53
53
  return
54
54
 
55
+ if node.sym_type == "types.ModuleType" and node.sym:
56
+ node.name_spec.type_sym_tab = node.sym.parent_tab.find_scope(node.sym_name)
57
+
55
58
  assert isinstance(self.ir, ast.Module)
56
59
 
57
60
  if typ_sym_table:
@@ -65,6 +68,35 @@ class FuseTypeInfoPass(Pass):
65
68
  if typ_sym_table != self.ir.sym_tab:
66
69
  node.name_spec.type_sym_tab = typ_sym_table
67
70
 
71
+ def __collect_python_dependencies(self, node: ast.AstNode) -> None:
72
+ assert isinstance(node, ast.AstSymbolNode)
73
+ assert isinstance(self.ir, ast.Module)
74
+
75
+ mypy_node = node.gen.mypy_ast[0]
76
+
77
+ if isinstance(mypy_node, MypyNodes.RefExpr) and mypy_node.node:
78
+ node_full_name = mypy_node.node.fullname
79
+ if "." in node_full_name:
80
+ mod_name = node_full_name[: node_full_name.rindex(".")]
81
+ else:
82
+ mod_name = node_full_name
83
+
84
+ if mod_name not in self.ir.py_mod_dep_map:
85
+ self.__debug_print(
86
+ f"Can't find a python file associated with {type(node)}::{node.loc}"
87
+ )
88
+ return
89
+
90
+ mode_path = self.ir.py_mod_dep_map[mod_name]
91
+ if mode_path.endswith(".jac"):
92
+ return
93
+
94
+ self.ir.py_raise_map[mod_name] = mode_path
95
+ else:
96
+ self.__debug_print(
97
+ f"Collect python dependencies is not supported in {type(node)}::{node.loc}"
98
+ )
99
+
68
100
  @staticmethod
69
101
  def __handle_node(
70
102
  func: Callable[[FuseTypeInfoPass, T], None]
@@ -86,6 +118,7 @@ class FuseTypeInfoPass(Pass):
86
118
  if len(node.gen.mypy_ast) == 1:
87
119
  func(self, node)
88
120
  self.__set_sym_table_link(node)
121
+ self.__collect_python_dependencies(node)
89
122
 
90
123
  # Jac node has multiple mypy nodes linked to it
91
124
  elif len(node.gen.mypy_ast) > 1:
@@ -101,24 +134,27 @@ class FuseTypeInfoPass(Pass):
101
134
  # Check the number of unique mypy nodes linked
102
135
  if len(temp) > 1:
103
136
  self.__debug_print(
104
- jac_node_str, "has multiple mypy nodes associated to it"
137
+ f"{jac_node_str} has multiple mypy nodes associated to it"
105
138
  )
106
139
  else:
107
140
  self.__debug_print(
108
- jac_node_str, "has duplicate mypy nodes associated to it"
141
+ f"{jac_node_str} has duplicate mypy nodes associated to it"
109
142
  )
110
143
  func(self, node)
111
144
  self.__set_sym_table_link(node)
145
+ self.__collect_python_dependencies(node)
112
146
 
113
147
  # Jac node doesn't have mypy nodes linked to it
114
148
  else:
115
149
  self.__debug_print(
116
- jac_node_str, "doesn't have mypy node associated to it"
150
+ f"{jac_node_str} doesn't have mypy node associated to it"
117
151
  )
118
152
 
119
153
  except AttributeError as e:
154
+ if settings.fuse_type_info_debug:
155
+ raise e
120
156
  self.__debug_print(
121
- f'Internal error happened while parsing "{e.obj.__class__.__name__}"'
157
+ f'{node.loc}: Internal error happened while parsing "{e.obj.__class__.__name__}"'
122
158
  )
123
159
 
124
160
  return node_handler
@@ -128,12 +164,7 @@ class FuseTypeInfoPass(Pass):
128
164
 
129
165
  if isinstance(mypy_node, MypyNodes.MemberExpr):
130
166
  if mypy_node in self.node_type_hash:
131
- t = str(self.node_type_hash[mypy_node])
132
- if "def" in t and "->" in t:
133
- t = t.split("->")[1].strip()
134
- elif "def" in t:
135
- t = "None"
136
- node.name_spec.sym_type = t
167
+ self.__call_type_handler(node, self.node_type_hash[mypy_node])
137
168
  else:
138
169
  self.__debug_print(f"{node.loc} MemberExpr type is not found")
139
170
 
@@ -158,8 +189,8 @@ class FuseTypeInfoPass(Pass):
158
189
 
159
190
  else:
160
191
  self.__debug_print(
161
- f'"{node.loc}::{node.__class__.__name__}" mypy (with node attr) node isn\'t supported',
162
- type(mypy_node),
192
+ f'"{node.loc}::{node.__class__.__name__}" mypy (with node attr) node isn\'t supported'
193
+ f"{type(mypy_node)}"
163
194
  )
164
195
 
165
196
  else:
@@ -174,8 +205,8 @@ class FuseTypeInfoPass(Pass):
174
205
  self.__call_type_handler(node, mypy_node.func.type.ret_type)
175
206
  else:
176
207
  self.__debug_print(
177
- f'"{node.loc}::{node.__class__.__name__}" mypy node isn\'t supported',
178
- type(mypy_node),
208
+ f'"{node.loc}::{node.__class__.__name__}" mypy node isn\'t supported'
209
+ f"{type(mypy_node)}"
179
210
  )
180
211
 
181
212
  @__handle_node
@@ -186,12 +217,12 @@ class FuseTypeInfoPass(Pass):
186
217
  @__handle_node
187
218
  def enter_module_path(self, node: ast.ModulePath) -> None:
188
219
  """Pass handler for ModulePath nodes."""
189
- self.__debug_print("Getting type not supported in", type(node))
220
+ self.__debug_print(f"Getting type not supported in {type(node)}")
190
221
 
191
222
  @__handle_node
192
223
  def enter_module_item(self, node: ast.ModuleItem) -> None:
193
224
  """Pass handler for ModuleItem nodes."""
194
- self.__debug_print("Getting type not supported in", type(node))
225
+ self.__debug_print(f"Getting type not supported in {type(node)}")
195
226
 
196
227
  @__handle_node
197
228
  def enter_architype(self, node: ast.Architype) -> None:
@@ -201,7 +232,7 @@ class FuseTypeInfoPass(Pass):
201
232
  @__handle_node
202
233
  def enter_arch_def(self, node: ast.ArchDef) -> None:
203
234
  """Pass handler for ArchDef nodes."""
204
- self.__debug_print("Getting type not supported in", type(node))
235
+ self.__debug_print(f"Getting type not supported in {type(node)}")
205
236
 
206
237
  @__handle_node
207
238
  def enter_enum(self, node: ast.Enum) -> None:
@@ -211,7 +242,7 @@ class FuseTypeInfoPass(Pass):
211
242
  @__handle_node
212
243
  def enter_enum_def(self, node: ast.EnumDef) -> None:
213
244
  """Pass handler for EnumDef nodes."""
214
- self.__debug_print("Getting type not supported in", type(node))
245
+ self.__debug_print(f"Getting type not supported in {type(node)}")
215
246
 
216
247
  @__handle_node
217
248
  def enter_ability(self, node: ast.Ability) -> None:
@@ -220,8 +251,8 @@ class FuseTypeInfoPass(Pass):
220
251
  self.__call_type_handler(node, node.gen.mypy_ast[0].type.ret_type)
221
252
  else:
222
253
  self.__debug_print(
223
- f"{node.loc}: Can't get type of an ability from mypy node other than Ability.",
224
- type(node.gen.mypy_ast[0]),
254
+ f"{node.loc}: Can't get type of an ability from mypy node other than Ability. "
255
+ f"{type(node.gen.mypy_ast[0])}"
225
256
  )
226
257
 
227
258
  @__handle_node
@@ -231,8 +262,8 @@ class FuseTypeInfoPass(Pass):
231
262
  self.__call_type_handler(node, node.gen.mypy_ast[0].type.ret_type)
232
263
  else:
233
264
  self.__debug_print(
234
- f"{node.loc}: Can't get type of an AbilityDef from mypy node other than FuncDef.",
235
- type(node.gen.mypy_ast[0]),
265
+ f"{node.loc}: Can't get type of an AbilityDef from mypy node other than FuncDef. "
266
+ f"{type(node.gen.mypy_ast[0])}"
236
267
  )
237
268
 
238
269
  @__handle_node
@@ -278,7 +309,7 @@ class FuseTypeInfoPass(Pass):
278
309
  @__handle_node
279
310
  def enter_f_string(self, node: ast.FString) -> None:
280
311
  """Pass handler for FString nodes."""
281
- self.__debug_print("Getting type not supported in", type(node))
312
+ self.__debug_print(f"Getting type not supported in {type(node)}")
282
313
 
283
314
  @__handle_node
284
315
  def enter_list_val(self, node: ast.ListVal) -> None:
@@ -331,7 +362,7 @@ class FuseTypeInfoPass(Pass):
331
362
  @__handle_node
332
363
  def enter_index_slice(self, node: ast.IndexSlice) -> None:
333
364
  """Pass handler for IndexSlice nodes."""
334
- self.__debug_print("Getting type not supported in", type(node))
365
+ self.__debug_print(f"Getting type not supported in {type(node)}")
335
366
 
336
367
  @__handle_node
337
368
  def enter_arch_ref(self, node: ast.ArchRef) -> None:
@@ -345,8 +376,8 @@ class FuseTypeInfoPass(Pass):
345
376
  self.__call_type_handler(node, mypy_node2.type)
346
377
  else:
347
378
  self.__debug_print(
348
- f"{node.loc}: Can't get ArchRef value from mypyNode other than ClassDef",
349
- type(node.gen.mypy_ast[0]),
379
+ f"{node.loc}: Can't get ArchRef value from mypyNode other than ClassDef "
380
+ f"type(node.gen.mypy_ast[0])"
350
381
  )
351
382
 
352
383
  @__handle_node
@@ -362,12 +393,12 @@ class FuseTypeInfoPass(Pass):
362
393
  @__handle_node
363
394
  def enter_filter_compr(self, node: ast.FilterCompr) -> None:
364
395
  """Pass handler for FilterCompr nodes."""
365
- self.__debug_print("Getting type not supported in", type(node))
396
+ self.__debug_print(f"Getting type not supported in {type(node)}")
366
397
 
367
398
  @__handle_node
368
399
  def enter_assign_compr(self, node: ast.AssignCompr) -> None:
369
400
  """Pass handler for AssignCompr nodes."""
370
- self.__debug_print("Getting type not supported in", type(node))
401
+ self.__debug_print(f"Getting type not supported in {type(node)}")
371
402
 
372
403
  @__handle_node
373
404
  def enter_int(self, node: ast.Int) -> None:
@@ -404,7 +435,7 @@ class FuseTypeInfoPass(Pass):
404
435
  self, node: ast.AstSymbolNode, mypy_type: MypyTypes.CallableType
405
436
  ) -> None:
406
437
  """Get type info from mypy type CallableType."""
407
- node.name_spec.sym_type = str(mypy_type.ret_type)
438
+ self.__call_type_handler(node, mypy_type.ret_type)
408
439
 
409
440
  # TODO: Which overloaded function to get the return value from?
410
441
  def get_type_from_overloaded(
@@ -431,6 +462,12 @@ class FuseTypeInfoPass(Pass):
431
462
  """Get type info from mypy type TupleType."""
432
463
  node.name_spec.sym_type = "builtins.tuple"
433
464
 
465
+ def get_type_from_type_type(
466
+ self, node: ast.AstSymbolNode, mypy_type: MypyTypes.TypeType
467
+ ) -> None:
468
+ """Get type info from mypy type TypeType."""
469
+ node.name_spec.sym_type = str(mypy_type.item)
470
+
434
471
  def exit_assignment(self, node: ast.Assignment) -> None:
435
472
  """Add new symbols in the symbol table in case of self."""
436
473
  # This will fix adding new items to the class through self
@@ -446,20 +483,85 @@ class FuseTypeInfoPass(Pass):
446
483
  if self_obj.type_sym_tab and isinstance(right_obj, ast.AstSymbolNode):
447
484
  self_obj.type_sym_tab.def_insert(right_obj)
448
485
 
449
- def exit_name(self, node: ast.Name) -> None:
450
- """Add new symbols in the symbol table in case of atom trailer."""
451
- if isinstance(node.parent, ast.AtomTrailer):
452
- target_node = node.parent.target
453
- if isinstance(target_node, ast.AstSymbolNode):
454
- parent_symbol_table = target_node.type_sym_tab
455
- if isinstance(parent_symbol_table, SymbolTable):
456
- node.sym = parent_symbol_table.lookup(node.sym_name)
457
-
458
- # def exit_in_for_stmt(self, node: ast.InForStmt):
459
- # print(node.loc.mod_path, node.loc)
460
- # print(node.target, node.target.loc)
461
- # # node.sym_tab.def_insert()
462
- # # exit()
463
-
464
- # def after_pass(self) -> None:
465
- # exit()
486
+ def expand_atom_trailer(self, node_list: list[ast.AstNode]) -> list[ast.AstNode]:
487
+ """Expand the atom trailer object in a list of AstNode."""
488
+ out: list[ast.AstNode] = []
489
+ for i in node_list:
490
+ if isinstance(i, ast.AtomTrailer):
491
+ out.append(i.target)
492
+ out.append(i.right)
493
+ elif isinstance(i, ast.FuncCall):
494
+ out.append(i.target)
495
+ else:
496
+ out.append(i)
497
+ return out
498
+
499
+ def exit_atom_trailer(self, node: ast.AtomTrailer) -> None:
500
+ """Adding symbol links to AtomTrailer right nodes."""
501
+ # This will fix adding the symbol links to nodes in atom trailer
502
+ # self.x.z = 5 # will add symbol links to both x and z
503
+
504
+ # This function originally used `as_attr_list` in AtomTrailer
505
+ # but an issue happened when doing stuff like fool_me().CONST_VALUE2
506
+ # The issue was due to the way `as_attr_list` implemented so the fix
507
+ # was to implement it again to get all items in AtomTrailer even if
508
+ # their type is not an AstSymbolNode
509
+ atom_trailer_unwind = self.expand_atom_trailer([node])
510
+ iteration_count = 0
511
+ while any(
512
+ isinstance(i, (ast.AtomTrailer, ast.FuncCall)) for i in atom_trailer_unwind
513
+ ):
514
+ atom_trailer_unwind = self.expand_atom_trailer(atom_trailer_unwind)
515
+ iteration_count += 1
516
+ if iteration_count > 50:
517
+ break
518
+
519
+ for i in range(1, len(atom_trailer_unwind)):
520
+ left = atom_trailer_unwind[i - 1]
521
+ right = atom_trailer_unwind[i]
522
+
523
+ assert isinstance(left, ast.AstSymbolNode)
524
+ assert isinstance(right, ast.AstSymbolNode)
525
+
526
+ if isinstance(right, ast.IndexSlice):
527
+ # In case of index slice, left won't have a symbol table as it's a list/dict/set
528
+ node_type: str = ""
529
+
530
+ # left type is a list
531
+ if left.sym_type.startswith("builtins.list["):
532
+ node_type = left.sym_type[len("builtins.list[") : -1]
533
+
534
+ # right index slice is a range then it's type is the same as left
535
+ if right.is_range:
536
+ right.name_spec.sym_type = left.sym_type
537
+ continue
538
+
539
+ # left type is a dictionary
540
+ elif left.sym_type.startswith("builtins.dict["):
541
+ node_type = (
542
+ left.sym_type[len("builtins.dict[") : -1].split(",")[1].strip()
543
+ )
544
+
545
+ # unsupported type
546
+ else:
547
+ continue
548
+
549
+ right.name_spec.sym_type = node_type
550
+
551
+ # Getting the correct symbol table and link it
552
+ type_symtab: Optional[SymbolTable] = self.ir.sym_tab
553
+ assert isinstance(self.ir, ast.Module)
554
+ assert isinstance(type_symtab, SymbolTable)
555
+ for j in node_type.split("."):
556
+ if j == self.ir.name:
557
+ continue
558
+ type_symtab = type_symtab.find_scope(j)
559
+ if type_symtab is None:
560
+ break
561
+ right.name_spec.type_sym_tab = type_symtab
562
+
563
+ else:
564
+ if left.type_sym_tab:
565
+ right.name_spec.sym = left.type_sym_tab.lookup(right.sym_name)
566
+ if right.name_spec.sym:
567
+ right.name_spec.sym.add_use(right.name_spec)