jaclang 0.7.16__py3-none-any.whl → 0.7.17__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.
- jaclang/cli/cli.py +140 -75
- jaclang/compiler/absyntree.py +9 -4
- jaclang/compiler/constant.py +8 -8
- jaclang/compiler/parser.py +10 -2
- jaclang/compiler/passes/main/__init__.py +1 -1
- jaclang/compiler/passes/main/access_modifier_pass.py +96 -147
- jaclang/compiler/passes/main/fuse_typeinfo_pass.py +152 -50
- jaclang/compiler/passes/main/import_pass.py +88 -59
- jaclang/compiler/passes/main/py_collect_dep_pass.py +70 -0
- jaclang/compiler/passes/main/pyast_gen_pass.py +21 -6
- jaclang/compiler/passes/main/pyast_load_pass.py +1 -0
- jaclang/compiler/passes/main/pyjac_ast_link_pass.py +7 -0
- jaclang/compiler/passes/main/schedules.py +9 -2
- jaclang/compiler/passes/main/sym_tab_build_pass.py +9 -5
- jaclang/compiler/passes/main/tests/fixtures/autoimpl.empty.impl.jac +0 -0
- jaclang/compiler/passes/main/tests/fixtures/autoimpl.jac +1 -1
- jaclang/compiler/passes/main/tests/fixtures/py_imp_test.jac +29 -0
- jaclang/compiler/passes/main/tests/fixtures/pygame_mock/__init__.py +3 -0
- jaclang/compiler/passes/main/tests/fixtures/pygame_mock/color.py +3 -0
- jaclang/compiler/passes/main/tests/fixtures/pygame_mock/constants.py +5 -0
- jaclang/compiler/passes/main/tests/fixtures/pygame_mock/display.py +2 -0
- jaclang/compiler/passes/main/tests/test_import_pass.py +72 -13
- jaclang/compiler/passes/main/type_check_pass.py +15 -5
- jaclang/compiler/passes/tool/jac_formatter_pass.py +11 -3
- jaclang/compiler/passes/tool/tests/fixtures/corelib.jac +37 -41
- jaclang/compiler/passes/tool/tests/fixtures/corelib_fmt.jac +37 -41
- jaclang/compiler/passes/tool/tests/fixtures/general_format_checks/access_mod_check.jac +27 -0
- jaclang/compiler/passes/utils/mypy_ast_build.py +45 -0
- jaclang/compiler/symtable.py +16 -11
- jaclang/compiler/tests/test_importer.py +17 -9
- jaclang/langserve/engine.py +39 -0
- jaclang/langserve/server.py +16 -1
- jaclang/langserve/tests/fixtures/rename.jac +30 -0
- jaclang/langserve/tests/test_server.py +216 -2
- jaclang/langserve/utils.py +21 -2
- jaclang/plugin/default.py +78 -70
- jaclang/plugin/feature.py +7 -17
- jaclang/plugin/spec.py +6 -19
- jaclang/plugin/tests/fixtures/other_root_access.jac +82 -0
- jaclang/plugin/tests/test_jaseci.py +414 -42
- jaclang/runtimelib/architype.py +481 -333
- jaclang/runtimelib/constructs.py +5 -8
- jaclang/runtimelib/context.py +89 -69
- jaclang/runtimelib/importer.py +15 -15
- jaclang/runtimelib/machine.py +66 -2
- jaclang/runtimelib/memory.py +134 -75
- jaclang/runtimelib/utils.py +17 -10
- jaclang/settings.py +2 -4
- jaclang/tests/fixtures/access_checker.jac +12 -17
- jaclang/tests/fixtures/access_modifier.jac +88 -33
- jaclang/tests/fixtures/baddy.jac +3 -0
- jaclang/tests/fixtures/bar.jac +34 -0
- jaclang/tests/fixtures/edge_node_walk.jac +1 -1
- jaclang/tests/fixtures/edge_ops.jac +1 -1
- jaclang/tests/fixtures/edges_walk.jac +1 -1
- jaclang/tests/fixtures/foo.jac +43 -0
- jaclang/tests/fixtures/game1.jac +1 -1
- jaclang/tests/fixtures/gendot_bubble_sort.jac +1 -1
- jaclang/tests/fixtures/import.jac +9 -0
- jaclang/tests/fixtures/index_slice.jac +30 -0
- jaclang/tests/fixtures/pyfunc_1.py +1 -1
- jaclang/tests/fixtures/pyfunc_2.py +2 -2
- jaclang/tests/fixtures/pygame_mock/__init__.py +3 -0
- jaclang/tests/fixtures/pygame_mock/color.py +3 -0
- jaclang/tests/fixtures/pygame_mock/constants.py +5 -0
- jaclang/tests/fixtures/pygame_mock/display.py +2 -0
- jaclang/tests/fixtures/pygame_mock/inner/__init__.py +0 -0
- jaclang/tests/fixtures/pygame_mock/inner/iner_mod.py +2 -0
- jaclang/tests/test_cli.py +49 -6
- jaclang/tests/test_language.py +113 -78
- jaclang/tests/test_reference.py +2 -9
- jaclang/utils/treeprinter.py +30 -3
- {jaclang-0.7.16.dist-info → jaclang-0.7.17.dist-info}/METADATA +1 -1
- {jaclang-0.7.16.dist-info → jaclang-0.7.17.dist-info}/RECORD +77 -56
- /jaclang/tests/fixtures/{err.test.jac → baddy.test.jac} +0 -0
- {jaclang-0.7.16.dist-info → jaclang-0.7.17.dist-info}/WHEEL +0 -0
- {jaclang-0.7.16.dist-info → jaclang-0.7.17.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
|
|
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
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
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
|
-
|
|
47
|
-
and node.
|
|
48
|
-
and
|
|
49
|
-
and
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
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
|
-
|
|
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
|
-
|
|
67
|
+
# TODO: Enums are not considered at the moment, I'll need to test and add them bellow.
|
|
174
68
|
|
|
175
|
-
|
|
176
|
-
|
|
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
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
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,
|
|
32
|
+
def __debug_print(self, msg: str) -> None:
|
|
33
33
|
if settings.fuse_type_info_debug:
|
|
34
|
-
self.log_info("FuseTypeInfo::"
|
|
34
|
+
self.log_info("FuseTypeInfo::" + msg)
|
|
35
35
|
|
|
36
36
|
def __call_type_handler(
|
|
37
|
-
self, node: ast.AstSymbolNode, mypy_type: MypyTypes.
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
450
|
-
"""
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
if isinstance(
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
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)
|